Initial Commit
This commit is contained in:
commit
84019305a9
16 changed files with 3010 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.env
|
||||
1763
Cargo.lock
generated
Normal file
1763
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "vulkan-intro"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ash = "0.38.0"
|
||||
ash-window = "0.13.0"
|
||||
dotenvy = "0.15.7"
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.22"
|
||||
thiserror = "1.0.62"
|
||||
vk-mem = "0.4.0"
|
||||
vk-shader-macros = "0.2.10"
|
||||
winit = { version = "0.29", features = ["rwh_06"] }
|
||||
9
shaders/shader.frag
Normal file
9
shaders/shader.frag
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 theColour;
|
||||
|
||||
layout(location = 1) in vec4 colourdata_from_the_vertexshader;
|
||||
|
||||
void main() {
|
||||
theColour = colourdata_from_the_vertexshader;
|
||||
}
|
||||
9
shaders/shader.vert
Normal file
9
shaders/shader.vert
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 1) out vec4 colourdata_from_the_vertexshader;
|
||||
|
||||
void main() {
|
||||
gl_PointSize = 10.0;
|
||||
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
colourdata_from_the_vertexshader = vec4(0.0, 0.6, 1.0, 1.0);
|
||||
}
|
||||
39
src/frame_counter.rs
Normal file
39
src/frame_counter.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct FrameCounter {
|
||||
counter: usize,
|
||||
frame_start: Instant,
|
||||
}
|
||||
|
||||
impl FrameCounter {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: 0,
|
||||
frame_start: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.counter = 0;
|
||||
self.frame_start = Instant::now();
|
||||
}
|
||||
|
||||
pub fn frame_count(&self) -> usize {
|
||||
self.counter
|
||||
}
|
||||
|
||||
pub fn get_frame_start(&self) -> Instant {
|
||||
self.frame_start
|
||||
}
|
||||
|
||||
pub fn frame_time(&self) -> Duration {
|
||||
self.frame_start.elapsed()
|
||||
}
|
||||
|
||||
pub fn new_frame(&mut self) -> Duration {
|
||||
self.counter += 1;
|
||||
let r = self.frame_start.elapsed();
|
||||
self.frame_start = Instant::now();
|
||||
r
|
||||
}
|
||||
}
|
||||
70
src/instance/debug.rs
Normal file
70
src/instance/debug.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
use std::ffi::CStr;
|
||||
|
||||
use ash::{ext, vk};
|
||||
|
||||
use super::EngineError;
|
||||
|
||||
pub struct DebugInstance {
|
||||
pub loader: ext::debug_utils::Instance,
|
||||
pub debug_messenger: vk::DebugUtilsMessengerEXT,
|
||||
}
|
||||
|
||||
impl DebugInstance {
|
||||
pub fn debug_utils_messenger_create_info() -> vk::DebugUtilsMessengerCreateInfoEXT<'static> {
|
||||
// Setup debug utils
|
||||
vk::DebugUtilsMessengerCreateInfoEXT::default()
|
||||
.message_severity(
|
||||
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
|
||||
| vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE
|
||||
| vk::DebugUtilsMessageSeverityFlagsEXT::INFO
|
||||
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
|
||||
)
|
||||
.message_type(
|
||||
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
|
||||
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
|
||||
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION,
|
||||
)
|
||||
.pfn_user_callback(Some(vulkan_debug_utils_callback))
|
||||
}
|
||||
|
||||
pub fn init<'a>(
|
||||
entry: &'a ash::Entry,
|
||||
instance: &'a ash::Instance,
|
||||
) -> Result<Self, EngineError> {
|
||||
// start debug messenger
|
||||
let loader = ext::debug_utils::Instance::new(entry, instance);
|
||||
let debug_messenger = unsafe {
|
||||
loader.create_debug_utils_messenger(&Self::debug_utils_messenger_create_info(), None)?
|
||||
};
|
||||
Ok(Self {
|
||||
loader,
|
||||
debug_messenger,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn destroy(&self) {
|
||||
unsafe {
|
||||
self.loader
|
||||
.destroy_debug_utils_messenger(self.debug_messenger, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DebugInstance {
|
||||
fn drop(&mut self) {
|
||||
self.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn vulkan_debug_utils_callback(
|
||||
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
|
||||
message_type: vk::DebugUtilsMessageTypeFlagsEXT,
|
||||
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
|
||||
_p_user_data: *mut std::ffi::c_void,
|
||||
) -> vk::Bool32 {
|
||||
let message = CStr::from_ptr((*p_callback_data).p_message);
|
||||
let severity = format!("{:?}", message_severity).to_lowercase();
|
||||
let ty = format!("{:?}", message_type).to_lowercase();
|
||||
println!("[Debug][{}][{}] {:?}", severity, ty, message);
|
||||
vk::FALSE
|
||||
}
|
||||
375
src/instance/mod.rs
Normal file
375
src/instance/mod.rs
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
use std::ffi::{c_char, CString};
|
||||
|
||||
use ash::vk;
|
||||
|
||||
pub mod debug;
|
||||
pub mod pipeline;
|
||||
pub mod pools;
|
||||
pub mod queue;
|
||||
pub mod surface;
|
||||
pub mod swapchain;
|
||||
pub mod window;
|
||||
|
||||
pub use debug::DebugInstance;
|
||||
pub use pipeline::Pipeline;
|
||||
use pools::Pools;
|
||||
pub use queue::{Queue, QueueFamilies, Queues};
|
||||
pub use surface::Surface;
|
||||
pub use swapchain::Swapchain;
|
||||
pub use window::Window;
|
||||
|
||||
use crate::frame_counter::FrameCounter;
|
||||
|
||||
const ENGINE_NAME: &core::ffi::CStr = c"UnknownGameEngine";
|
||||
const APP_NAME: &core::ffi::CStr = c"Vulkan Tutorial";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EngineError {
|
||||
#[error(transparent)]
|
||||
VkError(#[from] vk::Result),
|
||||
#[error(transparent)]
|
||||
AshLoadingError(#[from] ash::LoadingError),
|
||||
#[error(transparent)]
|
||||
WinitOsError(#[from] winit::error::OsError),
|
||||
#[error(transparent)]
|
||||
WinitEventLoopError(#[from] winit::error::EventLoopError),
|
||||
#[error(transparent)]
|
||||
RWHError(#[from] winit::raw_window_handle::HandleError),
|
||||
}
|
||||
|
||||
pub struct EngineInstance {
|
||||
pub entry: ash::Entry,
|
||||
pub ash_instance: ash::Instance,
|
||||
pub debug: std::mem::ManuallyDrop<DebugInstance>,
|
||||
pub surface: std::mem::ManuallyDrop<Surface>,
|
||||
pub surface_format: vk::SurfaceFormatKHR,
|
||||
pub physical_device: vk::PhysicalDevice,
|
||||
pub physical_device_properties: vk::PhysicalDeviceProperties,
|
||||
pub physical_device_memory_properties: vk::PhysicalDeviceMemoryProperties,
|
||||
pub device: ash::Device,
|
||||
pub queues: Queues,
|
||||
pub swapchain: Swapchain,
|
||||
pub renderpass: vk::RenderPass,
|
||||
pub pipeline: Pipeline,
|
||||
pub pools: Pools,
|
||||
pub commandbuffers: Vec<vk::CommandBuffer>,
|
||||
pub framecount: FrameCounter,
|
||||
}
|
||||
|
||||
impl EngineInstance {
|
||||
pub fn init(window: &Window) -> Result<Self, EngineError> {
|
||||
log::debug!("Init window");
|
||||
let entry = unsafe { ash::Entry::load()? };
|
||||
|
||||
let layers = Self::layer_names();
|
||||
let layers_pointers = as_pointers(&layers);
|
||||
|
||||
// create ash instance
|
||||
log::debug!("Init instance");
|
||||
let extensions = window.extension_names()?;
|
||||
let ash_instance = Self::init_instance(&entry, &layers_pointers, &extensions)?;
|
||||
|
||||
log::debug!("Init debugger");
|
||||
let debug = DebugInstance::init(&entry, &ash_instance)?;
|
||||
|
||||
log::debug!("Init surface");
|
||||
let surface = Surface::init(&entry, &ash_instance, &window)?;
|
||||
// let (surface, surface_loader) = Self::init_surface(&entry, &ash_instance, &window)?;
|
||||
|
||||
log::debug!("Init device");
|
||||
let (physical_device, physical_device_properties) =
|
||||
Self::select_physical_device(&ash_instance)?;
|
||||
let physical_device_memory_properties =
|
||||
unsafe { ash_instance.get_physical_device_memory_properties(physical_device) };
|
||||
dbg!(&physical_device_properties);
|
||||
dbg!(&physical_device_memory_properties);
|
||||
|
||||
log::debug!("Select surface properties");
|
||||
let surface_capabilities = surface.get_capabilities(physical_device)?;
|
||||
let surface_formats = surface.get_formats(physical_device)?;
|
||||
|
||||
let surface_resolution = Surface::select_resolution(&surface_capabilities, &window.res);
|
||||
let surface_format = Surface::select_format(&surface_formats);
|
||||
|
||||
log::debug!("Select queues");
|
||||
let queue_families = QueueFamilies::init(&ash_instance, physical_device, &surface)?;
|
||||
let queueinfo = queue_families.make_creation_info()?;
|
||||
|
||||
log::debug!("Create device");
|
||||
let device =
|
||||
Self::create_device(&ash_instance, physical_device, &layers_pointers, &queueinfo)?;
|
||||
|
||||
log::debug!("Create queues");
|
||||
let queues = queue_families.create_queues(&device)?;
|
||||
|
||||
// create swapchain
|
||||
log::debug!("Create swapchain");
|
||||
let mut swapchain = Swapchain::init(
|
||||
&ash_instance,
|
||||
&device,
|
||||
&surface,
|
||||
&surface_capabilities,
|
||||
surface_format,
|
||||
surface_resolution,
|
||||
&queues.graphics_queue.as_ref().unwrap(),
|
||||
)?;
|
||||
|
||||
log::debug!("Create framebuffers");
|
||||
let renderpass = Self::init_renderpass(&device, surface_format.format)?;
|
||||
swapchain.create_framebuffers(&device, renderpass)?;
|
||||
|
||||
log::debug!("Init pipeline");
|
||||
let pipeline = Pipeline::init(&device, &swapchain, &renderpass)?;
|
||||
|
||||
log::debug!("Init pools");
|
||||
let pools = Pools::init(&device, &queue_families)?;
|
||||
|
||||
log::debug!("Create command buffers");
|
||||
let commandbuffers = pools.create_commandbuffers(&device, swapchain.framebuffers.len())?;
|
||||
for (i, &commandbuffer) in commandbuffers.iter().enumerate() {
|
||||
Self::fill_commandbuffer(&device, commandbuffer, renderpass, &swapchain, i, &pipeline)?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
entry,
|
||||
ash_instance,
|
||||
surface: std::mem::ManuallyDrop::new(surface),
|
||||
surface_format: *surface_format,
|
||||
debug: std::mem::ManuallyDrop::new(debug),
|
||||
physical_device,
|
||||
physical_device_properties,
|
||||
physical_device_memory_properties,
|
||||
device,
|
||||
queues,
|
||||
swapchain,
|
||||
renderpass,
|
||||
pipeline,
|
||||
pools,
|
||||
commandbuffers,
|
||||
framecount: FrameCounter::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy(&mut self) {
|
||||
unsafe {
|
||||
self.device
|
||||
.device_wait_idle()
|
||||
.expect("something wrong while waiting");
|
||||
self.pools.cleanup(&self.device);
|
||||
// self.queues.cleanup();
|
||||
self.pipeline.cleanup(&self.device);
|
||||
self.device.destroy_render_pass(self.renderpass, None);
|
||||
self.swapchain.cleanup(&self.device);
|
||||
std::mem::ManuallyDrop::drop(&mut self.surface);
|
||||
self.device.destroy_device(None);
|
||||
std::mem::ManuallyDrop::drop(&mut self.debug);
|
||||
self.ash_instance.destroy_instance(None);
|
||||
};
|
||||
}
|
||||
|
||||
fn application_info() -> vk::ApplicationInfo<'static> {
|
||||
vk::ApplicationInfo::default()
|
||||
.application_name(APP_NAME)
|
||||
.application_version(0)
|
||||
.engine_name(ENGINE_NAME)
|
||||
.engine_version(0)
|
||||
.api_version(vk::make_api_version(0, 1, 3, 281))
|
||||
}
|
||||
|
||||
fn instance_create_info<'a>(
|
||||
debugcreateinfo: &'a mut vk::DebugUtilsMessengerCreateInfoEXT,
|
||||
app_info: &'a vk::ApplicationInfo,
|
||||
layers: &'a [*const c_char],
|
||||
extensions: &'a [*const c_char],
|
||||
) -> vk::InstanceCreateInfo<'a> {
|
||||
// Creation info
|
||||
vk::InstanceCreateInfo::default()
|
||||
.push_next(debugcreateinfo)
|
||||
.application_info(app_info)
|
||||
.enabled_layer_names(layers)
|
||||
.enabled_extension_names(extensions)
|
||||
}
|
||||
|
||||
fn init_instance(
|
||||
entry: &ash::Entry,
|
||||
layers: &[*const c_char],
|
||||
extensions: &[*const c_char],
|
||||
) -> Result<ash::Instance, EngineError> {
|
||||
let app_info = Self::application_info();
|
||||
// enable debug for instance init
|
||||
let mut debugcreateinfo = DebugInstance::debug_utils_messenger_create_info();
|
||||
let instance_create_info =
|
||||
Self::instance_create_info(&mut debugcreateinfo, &app_info, &layers, &extensions);
|
||||
|
||||
let instance = unsafe { entry.create_instance(&instance_create_info, None)? };
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
pub fn layer_names() -> Vec<CString> {
|
||||
vec![CString::new("VK_LAYER_KHRONOS_validation").unwrap()]
|
||||
}
|
||||
|
||||
fn select_physical_device(
|
||||
instance: &ash::Instance,
|
||||
) -> Result<(vk::PhysicalDevice, vk::PhysicalDeviceProperties), EngineError> {
|
||||
let phys_devs: Vec<vk::PhysicalDevice> = unsafe { instance.enumerate_physical_devices()? };
|
||||
|
||||
let mut primary = None;
|
||||
let mut secondary = None;
|
||||
let mut tertiary = None;
|
||||
let mut fallback = None;
|
||||
|
||||
for p in phys_devs {
|
||||
let properties = unsafe { instance.get_physical_device_properties(p) };
|
||||
|
||||
match properties.device_type {
|
||||
vk::PhysicalDeviceType::DISCRETE_GPU => {
|
||||
// if a dedicated is present, prioritize it
|
||||
primary = Some((p, properties));
|
||||
break;
|
||||
}
|
||||
vk::PhysicalDeviceType::INTEGRATED_GPU => {
|
||||
// integrated as secondary
|
||||
secondary = Some((p, properties));
|
||||
}
|
||||
vk::PhysicalDeviceType::VIRTUAL_GPU => {
|
||||
// virtual as tertiary
|
||||
tertiary = Some((p, properties));
|
||||
}
|
||||
_ => {
|
||||
fallback = Some((p, properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let r = if let Some(v) = primary {
|
||||
v
|
||||
} else if let Some(v) = secondary {
|
||||
v
|
||||
} else if let Some(v) = tertiary {
|
||||
v
|
||||
} else {
|
||||
fallback.unwrap()
|
||||
};
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn device_extensions() -> Vec<CString> {
|
||||
vec![ash::khr::swapchain::NAME.into()]
|
||||
}
|
||||
|
||||
fn create_device(
|
||||
instance: &ash::Instance,
|
||||
physical_device: vk::PhysicalDevice,
|
||||
enabled_layer_names: &[*const i8],
|
||||
queue_infos: &[vk::DeviceQueueCreateInfo],
|
||||
) -> Result<ash::Device, EngineError> {
|
||||
//TODO: dedicated function
|
||||
let device_extension_names = Self::device_extensions();
|
||||
let device_extension_name_pointers = as_pointers(&device_extension_names);
|
||||
|
||||
let device_create_info = vk::DeviceCreateInfo::default()
|
||||
.queue_create_infos(&queue_infos)
|
||||
.enabled_extension_names(&device_extension_name_pointers)
|
||||
.enabled_layer_names(enabled_layer_names); //deprecated, only for compatibility reason
|
||||
|
||||
let logical_device =
|
||||
unsafe { instance.create_device(physical_device, &device_create_info, None)? };
|
||||
Ok(logical_device)
|
||||
}
|
||||
|
||||
fn init_renderpass(
|
||||
device: &ash::Device,
|
||||
surface_format: vk::Format,
|
||||
) -> Result<vk::RenderPass, vk::Result> {
|
||||
let attachments = [vk::AttachmentDescription::default()
|
||||
.format(surface_format)
|
||||
.load_op(vk::AttachmentLoadOp::CLEAR)
|
||||
.store_op(vk::AttachmentStoreOp::STORE)
|
||||
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
|
||||
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
|
||||
.initial_layout(vk::ImageLayout::UNDEFINED)
|
||||
.final_layout(vk::ImageLayout::PRESENT_SRC_KHR)
|
||||
.samples(vk::SampleCountFlags::TYPE_1)];
|
||||
|
||||
let color_attachment_references = [vk::AttachmentReference {
|
||||
attachment: 0,
|
||||
layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
|
||||
}];
|
||||
|
||||
let subpasses = [vk::SubpassDescription::default()
|
||||
.color_attachments(&color_attachment_references)
|
||||
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)];
|
||||
|
||||
let subpass_dependencies = [vk::SubpassDependency::default()
|
||||
.src_subpass(vk::SUBPASS_EXTERNAL)
|
||||
.src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
|
||||
.dst_subpass(0)
|
||||
.dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
|
||||
.dst_access_mask(
|
||||
vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
|
||||
)];
|
||||
|
||||
let renderpass_info = vk::RenderPassCreateInfo::default()
|
||||
.attachments(&attachments)
|
||||
.subpasses(&subpasses)
|
||||
.dependencies(&subpass_dependencies);
|
||||
let renderpass = unsafe { device.create_render_pass(&renderpass_info, None)? };
|
||||
Ok(renderpass)
|
||||
}
|
||||
|
||||
fn fill_commandbuffer(
|
||||
device: &ash::Device,
|
||||
commandbuffer: vk::CommandBuffer,
|
||||
renderpass: vk::RenderPass,
|
||||
swapchain: &Swapchain,
|
||||
framebuffer_index: usize,
|
||||
pipeline: &Pipeline,
|
||||
) -> Result<(), vk::Result> {
|
||||
let commandbuffer_begininfo = vk::CommandBufferBeginInfo::default();
|
||||
unsafe {
|
||||
device.begin_command_buffer(commandbuffer, &commandbuffer_begininfo)?;
|
||||
}
|
||||
|
||||
let clearvalues = [vk::ClearValue {
|
||||
color: vk::ClearColorValue {
|
||||
float32: [0.0, 0.0, 0.08, 1.0],
|
||||
},
|
||||
}];
|
||||
let renderpass_begininfo = vk::RenderPassBeginInfo::default()
|
||||
.render_pass(renderpass)
|
||||
.framebuffer(swapchain.framebuffers[framebuffer_index])
|
||||
.render_area(vk::Rect2D {
|
||||
offset: vk::Offset2D { x: 0, y: 0 },
|
||||
extent: swapchain.extent,
|
||||
})
|
||||
.clear_values(&clearvalues);
|
||||
|
||||
unsafe {
|
||||
device.cmd_begin_render_pass(
|
||||
commandbuffer,
|
||||
&renderpass_begininfo,
|
||||
vk::SubpassContents::INLINE,
|
||||
);
|
||||
device.cmd_bind_pipeline(
|
||||
commandbuffer,
|
||||
vk::PipelineBindPoint::GRAPHICS,
|
||||
pipeline.pipeline,
|
||||
);
|
||||
device.cmd_draw(commandbuffer, 1, 1, 0, 0);
|
||||
device.cmd_end_render_pass(commandbuffer);
|
||||
device.end_command_buffer(commandbuffer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EngineInstance {
|
||||
fn drop(&mut self) {
|
||||
self.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
fn as_pointers(value: &[CString]) -> Vec<*const i8> {
|
||||
value.iter().map(|layer_name| layer_name.as_ptr()).collect()
|
||||
}
|
||||
130
src/instance/pipeline.rs
Normal file
130
src/instance/pipeline.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use std::ffi::CString;
|
||||
|
||||
use ash::vk;
|
||||
|
||||
use super::Swapchain;
|
||||
|
||||
pub struct Pipeline {
|
||||
pub pipeline: vk::Pipeline,
|
||||
pub layout: vk::PipelineLayout,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn cleanup(&self, logical_device: &ash::Device) {
|
||||
unsafe {
|
||||
logical_device.destroy_pipeline(self.pipeline, None);
|
||||
logical_device.destroy_pipeline_layout(self.layout, None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
logical_device: &ash::Device,
|
||||
swapchain: &Swapchain,
|
||||
renderpass: &vk::RenderPass,
|
||||
) -> Result<Pipeline, vk::Result> {
|
||||
let vertexshader_createinfo = vk::ShaderModuleCreateInfo::default()
|
||||
.code(vk_shader_macros::include_glsl!("./shaders/shader.vert", kind: vert));
|
||||
let vertexshader_module =
|
||||
unsafe { logical_device.create_shader_module(&vertexshader_createinfo, None)? };
|
||||
let fragmentshader_createinfo = vk::ShaderModuleCreateInfo::default()
|
||||
.code(vk_shader_macros::include_glsl!("./shaders/shader.frag", kind: frag));
|
||||
let fragmentshader_module =
|
||||
unsafe { logical_device.create_shader_module(&fragmentshader_createinfo, None)? };
|
||||
|
||||
let mainfunctionname = CString::new("main").unwrap();
|
||||
let vertexshader_stage = vk::PipelineShaderStageCreateInfo::default()
|
||||
.stage(vk::ShaderStageFlags::VERTEX)
|
||||
.module(vertexshader_module)
|
||||
.name(&mainfunctionname);
|
||||
let fragmentshader_stage = vk::PipelineShaderStageCreateInfo::default()
|
||||
.stage(vk::ShaderStageFlags::FRAGMENT)
|
||||
.module(fragmentshader_module)
|
||||
.name(&mainfunctionname);
|
||||
let shader_stages = vec![vertexshader_stage, fragmentshader_stage];
|
||||
|
||||
let vertex_attrib_descs = [vk::VertexInputAttributeDescription {
|
||||
binding: 0,
|
||||
location: 0,
|
||||
offset: 0,
|
||||
format: vk::Format::R32G32B32A32_SFLOAT,
|
||||
}];
|
||||
let vertex_binding_descs = [vk::VertexInputBindingDescription {
|
||||
binding: 0,
|
||||
stride: 16,
|
||||
input_rate: vk::VertexInputRate::VERTEX,
|
||||
}];
|
||||
|
||||
let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::default()
|
||||
.vertex_attribute_descriptions(&vertex_attrib_descs)
|
||||
.vertex_binding_descriptions(&vertex_binding_descs);
|
||||
|
||||
let input_assembly_info = vk::PipelineInputAssemblyStateCreateInfo::default()
|
||||
.topology(vk::PrimitiveTopology::POINT_LIST);
|
||||
let viewports = [vk::Viewport {
|
||||
x: 0.,
|
||||
y: 0.,
|
||||
width: swapchain.extent.width as f32,
|
||||
height: swapchain.extent.height as f32,
|
||||
min_depth: 0.,
|
||||
max_depth: 1.,
|
||||
}];
|
||||
let scissors = [vk::Rect2D {
|
||||
offset: vk::Offset2D { x: 0, y: 0 },
|
||||
extent: swapchain.extent,
|
||||
}];
|
||||
|
||||
let viewport_info = vk::PipelineViewportStateCreateInfo::default()
|
||||
.viewports(&viewports)
|
||||
.scissors(&scissors);
|
||||
let rasterizer_info = vk::PipelineRasterizationStateCreateInfo::default()
|
||||
.line_width(1.0)
|
||||
.front_face(vk::FrontFace::COUNTER_CLOCKWISE)
|
||||
.cull_mode(vk::CullModeFlags::NONE)
|
||||
.polygon_mode(vk::PolygonMode::FILL);
|
||||
let multisampler_info = vk::PipelineMultisampleStateCreateInfo::default()
|
||||
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
|
||||
let colourblend_attachments = [vk::PipelineColorBlendAttachmentState::default()
|
||||
.blend_enable(true)
|
||||
.src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
|
||||
.dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||
.color_blend_op(vk::BlendOp::ADD)
|
||||
.src_alpha_blend_factor(vk::BlendFactor::SRC_ALPHA)
|
||||
.dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||
.alpha_blend_op(vk::BlendOp::ADD)
|
||||
.color_write_mask(
|
||||
vk::ColorComponentFlags::R
|
||||
| vk::ColorComponentFlags::G
|
||||
| vk::ColorComponentFlags::B
|
||||
| vk::ColorComponentFlags::A,
|
||||
)];
|
||||
let colourblend_info =
|
||||
vk::PipelineColorBlendStateCreateInfo::default().attachments(&colourblend_attachments);
|
||||
let pipelinelayout_info = vk::PipelineLayoutCreateInfo::default();
|
||||
let pipelinelayout =
|
||||
unsafe { logical_device.create_pipeline_layout(&pipelinelayout_info, None) }?;
|
||||
let pipeline_info = vk::GraphicsPipelineCreateInfo::default()
|
||||
.stages(&shader_stages)
|
||||
.vertex_input_state(&vertex_input_info)
|
||||
.input_assembly_state(&input_assembly_info)
|
||||
.viewport_state(&viewport_info)
|
||||
.rasterization_state(&rasterizer_info)
|
||||
.multisample_state(&multisampler_info)
|
||||
.color_blend_state(&colourblend_info)
|
||||
.layout(pipelinelayout)
|
||||
.render_pass(*renderpass)
|
||||
.subpass(0);
|
||||
let graphicspipeline = unsafe {
|
||||
logical_device
|
||||
.create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None)
|
||||
.expect("A problem with the pipeline creation")
|
||||
}[0];
|
||||
unsafe {
|
||||
logical_device.destroy_shader_module(fragmentshader_module, None);
|
||||
logical_device.destroy_shader_module(vertexshader_module, None);
|
||||
}
|
||||
Ok(Pipeline {
|
||||
pipeline: graphicspipeline,
|
||||
layout: pipelinelayout,
|
||||
})
|
||||
}
|
||||
}
|
||||
58
src/instance/pools.rs
Normal file
58
src/instance/pools.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use ash::vk;
|
||||
|
||||
use super::QueueFamilies;
|
||||
|
||||
pub struct Pools {
|
||||
commandpool_graphics: Option<vk::CommandPool>,
|
||||
commandpool_transfer: Option<vk::CommandPool>,
|
||||
}
|
||||
|
||||
impl Pools {
|
||||
pub fn init(
|
||||
logical_device: &ash::Device,
|
||||
queue_families: &QueueFamilies,
|
||||
) -> Result<Pools, vk::Result> {
|
||||
let commandpool_graphics = if let Some(graphics_index) = queue_families.graphics_index {
|
||||
let graphics_commandpool_info = vk::CommandPoolCreateInfo::default()
|
||||
.queue_family_index(graphics_index)
|
||||
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
|
||||
Some(unsafe { logical_device.create_command_pool(&graphics_commandpool_info, None) }?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let commandpool_transfer = if let Some(transfer_index) = queue_families.transfer_index {
|
||||
let transfer_commandpool_info = vk::CommandPoolCreateInfo::default()
|
||||
.queue_family_index(transfer_index)
|
||||
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
|
||||
Some(unsafe { logical_device.create_command_pool(&transfer_commandpool_info, None) }?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Pools {
|
||||
commandpool_graphics,
|
||||
commandpool_transfer,
|
||||
})
|
||||
}
|
||||
pub fn cleanup(&self, logical_device: &ash::Device) {
|
||||
unsafe {
|
||||
if let Some(v) = self.commandpool_graphics {
|
||||
logical_device.destroy_command_pool(v, None);
|
||||
}
|
||||
if let Some(v) = self.commandpool_transfer {
|
||||
logical_device.destroy_command_pool(v, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_commandbuffers(
|
||||
&self,
|
||||
logical_device: &ash::Device,
|
||||
amount: usize,
|
||||
) -> Result<Vec<vk::CommandBuffer>, vk::Result> {
|
||||
let commandbuf_allocate_info = vk::CommandBufferAllocateInfo::default()
|
||||
.command_pool(self.commandpool_graphics.unwrap())
|
||||
.command_buffer_count(amount as u32);
|
||||
unsafe { logical_device.allocate_command_buffers(&commandbuf_allocate_info) }
|
||||
}
|
||||
}
|
||||
107
src/instance/queue.rs
Normal file
107
src/instance/queue.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use super::{EngineError, Surface};
|
||||
|
||||
use ash::vk;
|
||||
|
||||
const PRIORITIES: [f32; 1] = [1.0f32];
|
||||
|
||||
pub struct Queue {
|
||||
pub queue: vk::Queue,
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn new(queue: vk::Queue, index: u32) -> Self {
|
||||
Self { queue, index }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queues {
|
||||
pub graphics_queue: Option<Queue>,
|
||||
pub transfer_queue: Option<Queue>,
|
||||
}
|
||||
|
||||
pub struct QueueFamilies {
|
||||
pub graphics_index: Option<u32>,
|
||||
pub transfer_index: Option<u32>,
|
||||
}
|
||||
|
||||
impl QueueFamilies {
|
||||
pub fn init(
|
||||
instance: &ash::Instance,
|
||||
physical_device: vk::PhysicalDevice,
|
||||
surface: &Surface,
|
||||
) -> Result<Self, EngineError> {
|
||||
let mut graphics_index = None;
|
||||
let mut transfer_index = None;
|
||||
let queue_families =
|
||||
unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
|
||||
dbg!(&queue_families);
|
||||
|
||||
for (index, qfam) in queue_families.iter().enumerate() {
|
||||
if qfam.queue_count > 0
|
||||
&& qfam.queue_flags.contains(vk::QueueFlags::GRAPHICS)
|
||||
// WARN: surface drawing capable queue could be different, but in our case it's not
|
||||
&& unsafe {
|
||||
surface.surface_loader.get_physical_device_surface_support(
|
||||
physical_device,
|
||||
index as u32,
|
||||
surface.surface,
|
||||
)?
|
||||
}
|
||||
{
|
||||
graphics_index = Some(index as u32);
|
||||
if qfam.queue_flags.contains(vk::QueueFlags::TRANSFER) && qfam.queue_count > 1 {
|
||||
transfer_index = Some(index as u32);
|
||||
}
|
||||
} else if qfam.queue_count > 0 && qfam.queue_flags.contains(vk::QueueFlags::TRANSFER) {
|
||||
if transfer_index.is_none() || !qfam.queue_flags.contains(vk::QueueFlags::GRAPHICS)
|
||||
{
|
||||
transfer_index = Some(index as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
graphics_index,
|
||||
transfer_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_creation_info<'a>(
|
||||
&self,
|
||||
) -> Result<Vec<vk::DeviceQueueCreateInfo<'a>>, EngineError> {
|
||||
let mut queue_infos = vec![vk::DeviceQueueCreateInfo::default()
|
||||
.queue_family_index(self.graphics_index.unwrap())
|
||||
.queue_priorities(&PRIORITIES)];
|
||||
if let Some(v) = self.transfer_index {
|
||||
queue_infos.push(
|
||||
vk::DeviceQueueCreateInfo::default()
|
||||
.queue_family_index(v)
|
||||
.queue_priorities(&PRIORITIES),
|
||||
)
|
||||
}
|
||||
Ok(queue_infos)
|
||||
}
|
||||
|
||||
pub fn create_queues(&self, logical_device: &ash::Device) -> Result<Queues, EngineError> {
|
||||
let graphics_queue = self
|
||||
.graphics_index
|
||||
.map(|x| unsafe { Queue::new(logical_device.get_device_queue(x, 0), x) });
|
||||
let transfer_queue = if let Some(v) = self.transfer_index {
|
||||
Some(Queue::new(
|
||||
if self.graphics_index.is_some() && self.graphics_index.unwrap() == v {
|
||||
// Graphics and transfer queue family indexes are identical, pick a second queue
|
||||
unsafe { logical_device.get_device_queue(v, 1) }
|
||||
} else {
|
||||
unsafe { logical_device.get_device_queue(v, 0) }
|
||||
},
|
||||
v,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Queues {
|
||||
graphics_queue,
|
||||
transfer_queue,
|
||||
})
|
||||
}
|
||||
}
|
||||
91
src/instance/surface.rs
Normal file
91
src/instance/surface.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
use ash::{khr, vk};
|
||||
|
||||
use super::window::Resolution;
|
||||
use super::EngineError;
|
||||
use super::Window;
|
||||
|
||||
pub struct Surface {
|
||||
pub surface: vk::SurfaceKHR,
|
||||
pub surface_loader: khr::surface::Instance,
|
||||
}
|
||||
|
||||
impl Surface {
|
||||
pub fn init(
|
||||
entry: &ash::Entry,
|
||||
instance: &ash::Instance,
|
||||
window: &Window,
|
||||
) -> Result<Self, EngineError> {
|
||||
let surface = unsafe {
|
||||
ash_window::create_surface(
|
||||
entry,
|
||||
instance,
|
||||
window.raw_display_handle()?,
|
||||
window.raw_window_handle()?,
|
||||
None,
|
||||
)?
|
||||
};
|
||||
let surface_loader = khr::surface::Instance::new(&entry, &instance);
|
||||
Ok(Self {
|
||||
surface,
|
||||
surface_loader,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_capabilities(
|
||||
&self,
|
||||
physical_device: vk::PhysicalDevice,
|
||||
) -> Result<vk::SurfaceCapabilitiesKHR, EngineError> {
|
||||
Ok(unsafe {
|
||||
self.surface_loader
|
||||
.get_physical_device_surface_capabilities(physical_device, self.surface)?
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_present_modes(
|
||||
&self,
|
||||
physical_device: vk::PhysicalDevice,
|
||||
) -> Result<Vec<vk::PresentModeKHR>, EngineError> {
|
||||
Ok(unsafe {
|
||||
self.surface_loader
|
||||
.get_physical_device_surface_present_modes(physical_device, self.surface)?
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_formats(
|
||||
&self,
|
||||
physical_device: vk::PhysicalDevice,
|
||||
) -> Result<Vec<vk::SurfaceFormatKHR>, EngineError> {
|
||||
Ok(unsafe {
|
||||
self.surface_loader
|
||||
.get_physical_device_surface_formats(physical_device, self.surface)?
|
||||
})
|
||||
}
|
||||
|
||||
pub fn select_resolution(
|
||||
surface_capabilities: &vk::SurfaceCapabilitiesKHR,
|
||||
res: &Resolution,
|
||||
) -> vk::Extent2D {
|
||||
match surface_capabilities.current_extent.width {
|
||||
u32::MAX => vk::Extent2D {
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
},
|
||||
_ => surface_capabilities.current_extent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_format(surface_formats: &[vk::SurfaceFormatKHR]) -> &vk::SurfaceFormatKHR {
|
||||
let selected_surface_format = surface_formats
|
||||
.iter()
|
||||
.filter(|x| x.format == vk::Format::R8G8B8A8_SRGB)
|
||||
.collect::<Vec<&vk::SurfaceFormatKHR>>()
|
||||
.remove(0);
|
||||
selected_surface_format
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Surface {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.surface_loader.destroy_surface(self.surface, None) };
|
||||
}
|
||||
}
|
||||
141
src/instance/swapchain.rs
Normal file
141
src/instance/swapchain.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
use ash::{khr, vk};
|
||||
|
||||
use super::{EngineError, Queue, Surface};
|
||||
|
||||
pub struct Swapchain {
|
||||
pub swapchain_loader: khr::swapchain::Device,
|
||||
pub swapchain: vk::SwapchainKHR,
|
||||
pub extent: vk::Extent2D,
|
||||
pub images: Vec<vk::Image>,
|
||||
pub image_views: Vec<vk::ImageView>,
|
||||
pub framebuffers: Vec<vk::Framebuffer>,
|
||||
pub image_available: Vec<vk::Semaphore>,
|
||||
pub rendering_finished: Vec<vk::Semaphore>,
|
||||
pub may_begin_drawing: Vec<vk::Fence>,
|
||||
pub amount_of_images: u32,
|
||||
pub current_image: usize,
|
||||
}
|
||||
|
||||
impl Swapchain {
|
||||
pub fn init(
|
||||
instance: &ash::Instance,
|
||||
logical_device: &ash::Device,
|
||||
surface: &Surface,
|
||||
surface_capabilities: &vk::SurfaceCapabilitiesKHR,
|
||||
surface_format: &vk::SurfaceFormatKHR,
|
||||
extent: vk::Extent2D,
|
||||
graphics_queue: &Queue,
|
||||
) -> Result<Self, EngineError> {
|
||||
let queuefamilies = [graphics_queue.index];
|
||||
let swapchain_create_info = vk::SwapchainCreateInfoKHR::default()
|
||||
.surface(surface.surface)
|
||||
.min_image_count(
|
||||
3.max(surface_capabilities.min_image_count),
|
||||
// .min(surface_capabilities.max_image_count), // currently broken for this device ?
|
||||
)
|
||||
.image_format(surface_format.format)
|
||||
.image_color_space(surface_format.color_space)
|
||||
.image_extent(extent)
|
||||
.image_array_layers(1)
|
||||
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
|
||||
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
|
||||
.queue_family_indices(&queuefamilies)
|
||||
.pre_transform(surface_capabilities.current_transform)
|
||||
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
|
||||
.present_mode(vk::PresentModeKHR::FIFO);
|
||||
let swapchain_loader = khr::swapchain::Device::new(&instance, &logical_device);
|
||||
let swapchain = unsafe { swapchain_loader.create_swapchain(&swapchain_create_info, None)? };
|
||||
|
||||
let swapchain_images = unsafe { swapchain_loader.get_swapchain_images(swapchain)? };
|
||||
|
||||
// create views
|
||||
let mut swapchain_imageviews = Vec::with_capacity(swapchain_images.len());
|
||||
for image in &swapchain_images {
|
||||
let subresource_range = vk::ImageSubresourceRange::default()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.base_mip_level(0)
|
||||
.level_count(1)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1);
|
||||
let imageview_create_info = vk::ImageViewCreateInfo::default()
|
||||
.image(*image)
|
||||
.view_type(vk::ImageViewType::TYPE_2D)
|
||||
.format(surface_format.format)
|
||||
.subresource_range(subresource_range);
|
||||
let imageview =
|
||||
unsafe { logical_device.create_image_view(&imageview_create_info, None) }?;
|
||||
swapchain_imageviews.push(imageview);
|
||||
}
|
||||
|
||||
let amount_of_images = swapchain_images.len() as u32;
|
||||
|
||||
let mut image_available = vec![];
|
||||
let mut rendering_finished = vec![];
|
||||
let mut may_begin_drawing = vec![];
|
||||
let semaphoreinfo = vk::SemaphoreCreateInfo::default();
|
||||
let fenceinfo = vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED);
|
||||
for _ in 0..amount_of_images {
|
||||
let semaphore_available =
|
||||
unsafe { logical_device.create_semaphore(&semaphoreinfo, None) }?;
|
||||
let semaphore_finished =
|
||||
unsafe { logical_device.create_semaphore(&semaphoreinfo, None) }?;
|
||||
image_available.push(semaphore_available);
|
||||
rendering_finished.push(semaphore_finished);
|
||||
let fence = unsafe { logical_device.create_fence(&fenceinfo, None) }?;
|
||||
may_begin_drawing.push(fence);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
swapchain_loader,
|
||||
swapchain,
|
||||
extent,
|
||||
images: swapchain_images,
|
||||
image_views: swapchain_imageviews,
|
||||
framebuffers: vec![],
|
||||
amount_of_images,
|
||||
current_image: 0,
|
||||
may_begin_drawing,
|
||||
image_available,
|
||||
rendering_finished,
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(&mut self, logical_device: &ash::Device) {
|
||||
for fence in &self.may_begin_drawing {
|
||||
logical_device.destroy_fence(*fence, None);
|
||||
}
|
||||
for semaphore in &self.image_available {
|
||||
logical_device.destroy_semaphore(*semaphore, None);
|
||||
}
|
||||
for semaphore in &self.rendering_finished {
|
||||
logical_device.destroy_semaphore(*semaphore, None);
|
||||
}
|
||||
for fb in &self.framebuffers {
|
||||
logical_device.destroy_framebuffer(*fb, None);
|
||||
}
|
||||
for iv in &self.image_views {
|
||||
logical_device.destroy_image_view(*iv, None);
|
||||
}
|
||||
self.swapchain_loader
|
||||
.destroy_swapchain(self.swapchain, None);
|
||||
}
|
||||
|
||||
pub fn create_framebuffers(
|
||||
&mut self,
|
||||
logical_device: &ash::Device,
|
||||
renderpass: vk::RenderPass,
|
||||
) -> Result<(), vk::Result> {
|
||||
for iv in &self.image_views {
|
||||
let iview = [*iv];
|
||||
let framebuffer_info = vk::FramebufferCreateInfo::default()
|
||||
.render_pass(renderpass)
|
||||
.attachments(&iview)
|
||||
.width(self.extent.width)
|
||||
.height(self.extent.height)
|
||||
.layers(1);
|
||||
let fb = unsafe { logical_device.create_framebuffer(&framebuffer_info, None) }?;
|
||||
self.framebuffers.push(fb);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
59
src/instance/window.rs
Normal file
59
src/instance/window.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use std::cell::RefCell;
|
||||
|
||||
use winit::event_loop::EventLoop as WinitEventLoop;
|
||||
use winit::raw_window_handle::RawDisplayHandle;
|
||||
use winit::raw_window_handle::RawWindowHandle;
|
||||
use winit::window::Window as WinitWindow;
|
||||
use winit::window::WindowBuilder as WinitWindowBuilder;
|
||||
|
||||
use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||
|
||||
use super::EngineError;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Resolution {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
pub eventloop: RefCell<WinitEventLoop<()>>,
|
||||
pub window: WinitWindow,
|
||||
pub res: Resolution,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn init(res: Resolution) -> Result<Self, EngineError> {
|
||||
let eventloop = WinitEventLoop::new()?;
|
||||
let window = WinitWindowBuilder::new()
|
||||
.with_title("Vulkan tutorial")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(
|
||||
f64::from(res.width),
|
||||
f64::from(res.height),
|
||||
))
|
||||
.build(&eventloop)?;
|
||||
|
||||
Ok(Self {
|
||||
eventloop: RefCell::new(eventloop),
|
||||
window,
|
||||
res,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn extension_names(&self) -> Result<Vec<*const i8>, EngineError> {
|
||||
let mut extension_name_pointers =
|
||||
ash_window::enumerate_required_extensions(self.window.display_handle()?.as_raw())
|
||||
.unwrap()
|
||||
.to_vec();
|
||||
extension_name_pointers.push(ash::ext::debug_utils::NAME.as_ptr());
|
||||
Ok(extension_name_pointers)
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> Result<RawDisplayHandle, EngineError> {
|
||||
Ok(self.window.display_handle()?.as_raw())
|
||||
}
|
||||
|
||||
pub fn raw_window_handle(&self) -> Result<RawWindowHandle, EngineError> {
|
||||
Ok(self.window.window_handle()?.as_raw())
|
||||
}
|
||||
}
|
||||
100
src/main.rs
Normal file
100
src/main.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
pub mod instance;
|
||||
pub mod render;
|
||||
|
||||
pub mod frame_counter;
|
||||
|
||||
pub use instance::EngineInstance;
|
||||
use instance::{window::Resolution, Window};
|
||||
|
||||
use ash::vk;
|
||||
|
||||
pub const WINDOW_WIDTH: u32 = 1280;
|
||||
pub const WINDOW_HEIGHT: u32 = 720;
|
||||
|
||||
pub const DEFAULT_RES: Resolution = Resolution {
|
||||
width: WINDOW_WIDTH,
|
||||
height: WINDOW_HEIGHT,
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dotenvy::dotenv().ok();
|
||||
env_logger::init();
|
||||
|
||||
let window = Window::init(DEFAULT_RES.clone())?;
|
||||
let mut instance = EngineInstance::init(&window)?;
|
||||
|
||||
render::render_loop(&window, &mut instance, move |e| {
|
||||
let next_image = (e.swapchain.current_image + 1) % e.swapchain.amount_of_images as usize;
|
||||
|
||||
unsafe {
|
||||
e.device
|
||||
.wait_for_fences(
|
||||
&[e.swapchain.may_begin_drawing[e.swapchain.current_image]],
|
||||
true,
|
||||
std::u64::MAX,
|
||||
)
|
||||
.expect("fence-waiting");
|
||||
|
||||
e.device
|
||||
.reset_fences(&[e.swapchain.may_begin_drawing[e.swapchain.current_image]])
|
||||
.expect("resetting fences");
|
||||
}
|
||||
|
||||
let (image_index, _) = unsafe {
|
||||
e.swapchain
|
||||
.swapchain_loader
|
||||
.acquire_next_image(
|
||||
e.swapchain.swapchain,
|
||||
std::u64::MAX,
|
||||
e.swapchain.image_available[e.swapchain.current_image],
|
||||
vk::Fence::null(),
|
||||
)
|
||||
.expect("image acquisition trouble")
|
||||
};
|
||||
|
||||
let semaphores_available = [e.swapchain.image_available[e.swapchain.current_image]];
|
||||
let waiting_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
|
||||
let semaphores_finished = [
|
||||
e.swapchain.rendering_finished[e.swapchain.current_image],
|
||||
// e.swapchain.next_frame[next_image],
|
||||
];
|
||||
let commandbuffers = [e.commandbuffers[image_index as usize]];
|
||||
let submit_info = [vk::SubmitInfo::default()
|
||||
.wait_semaphores(&semaphores_available)
|
||||
.wait_dst_stage_mask(&waiting_stages)
|
||||
.command_buffers(&commandbuffers)
|
||||
.signal_semaphores(&semaphores_finished)];
|
||||
|
||||
let queue = e.queues.graphics_queue.as_ref().unwrap().queue;
|
||||
unsafe {
|
||||
e.device
|
||||
.queue_submit(
|
||||
queue,
|
||||
&submit_info,
|
||||
e.swapchain.may_begin_drawing[e.swapchain.current_image],
|
||||
)
|
||||
.expect("queue submission");
|
||||
};
|
||||
|
||||
let swapchains = [e.swapchain.swapchain];
|
||||
let indices = [image_index];
|
||||
let present_info = vk::PresentInfoKHR::default()
|
||||
.wait_semaphores(&semaphores_finished)
|
||||
.swapchains(&swapchains)
|
||||
.image_indices(&indices);
|
||||
unsafe {
|
||||
e.swapchain
|
||||
.swapchain_loader
|
||||
.queue_present(queue, &present_info)
|
||||
.expect("queue presentation");
|
||||
};
|
||||
|
||||
let frameid = e.framecount.frame_count();
|
||||
let frametime = e.framecount.new_frame();
|
||||
println!("frame {}: {} ms", frameid, frametime.as_secs_f32() * 1000.0);
|
||||
|
||||
e.swapchain.current_image = next_image;
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
40
src/render.rs
Normal file
40
src/render.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use crate::instance::{EngineInstance, Window};
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
keyboard::{Key, NamedKey},
|
||||
platform::run_on_demand::EventLoopExtRunOnDemand,
|
||||
};
|
||||
|
||||
pub fn render_loop<F: Fn(&mut EngineInstance)>(
|
||||
window: &Window,
|
||||
instance: &mut EngineInstance,
|
||||
f: F,
|
||||
) -> Result<(), impl std::error::Error> {
|
||||
window
|
||||
.eventloop
|
||||
.borrow_mut()
|
||||
.run_on_demand(move |event, elwp| {
|
||||
elwp.set_control_flow(winit::event_loop::ControlFlow::Poll);
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key: Key::Named(NamedKey::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
}
|
||||
| Event::LoopExiting => {
|
||||
elwp.exit();
|
||||
}
|
||||
Event::AboutToWait => f(instance),
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Reference in a new issue