diff --git a/src/display/app.rs b/src/display/app.rs index c0ebac7..8eb5023 100644 --- a/src/display/app.rs +++ b/src/display/app.rs @@ -61,6 +61,7 @@ impl ApplicationHandler for App { Some(render_context) => { if let Err(error) = render_context.render() { log::error!("Failed to render with render context : {}", error); + event_loop.exit(); } } None => log::warn!("Window resized but no render context found"), diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 11bfe11..aff9b51 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -34,4 +34,7 @@ pub(self) use vk_command_pool::VkCommandPool; mod vk_framebuffer; pub(self) use vk_framebuffer::VkFramebuffer; +mod vk_fence; +pub(self) use vk_fence::VkFence; + mod utils; diff --git a/src/vulkan/vk_command_pool.rs b/src/vulkan/vk_command_pool.rs index fe79391..e0a662a 100644 --- a/src/vulkan/vk_command_pool.rs +++ b/src/vulkan/vk_command_pool.rs @@ -12,7 +12,9 @@ pub struct VkCommandPool { impl VkCommandPool { pub fn new(device: &Arc) -> VkResult { let command_pool_info = - vk::CommandPoolCreateInfo::default().queue_family_index(device.queue_family_index); + vk::CommandPoolCreateInfo::default() + .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) + .queue_family_index(device.queue_family_index); let command_pool = unsafe { device .handle diff --git a/src/vulkan/vk_fence.rs b/src/vulkan/vk_fence.rs new file mode 100644 index 0000000..9d8ac52 --- /dev/null +++ b/src/vulkan/vk_fence.rs @@ -0,0 +1,30 @@ +use crate::vulkan::VkDevice; +use ash::vk; +use std::sync::Arc; + +pub struct VkFence { + device: Arc, + + pub(super) handle: vk::Fence, +} + +impl VkFence { + pub fn new(device: &Arc) -> anyhow::Result { + let fence_info = vk::FenceCreateInfo::default() + .flags(vk::FenceCreateFlags::SIGNALED); + let fence = unsafe { device.handle.create_fence(&fence_info, None)? }; + log::debug!("Fence created ({fence:?})"); + + Ok(Self { + device: device.clone(), + handle: fence, + }) + } +} + +impl Drop for VkFence { + fn drop(&mut self) { + unsafe { self.device.handle.destroy_fence(self.handle, None) }; + log::debug!("Fence destroyed ({:?})", self.handle); + } +} \ No newline at end of file diff --git a/src/vulkan/vk_graphics_pipeline.rs b/src/vulkan/vk_graphics_pipeline.rs index 51bd628..bcc3805 100644 --- a/src/vulkan/vk_graphics_pipeline.rs +++ b/src/vulkan/vk_graphics_pipeline.rs @@ -46,18 +46,9 @@ impl VkGraphicsPipeline { let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() .topology(vk::PrimitiveTopology::TRIANGLE_LIST); - let viewport = vk::Viewport::default() - .width(swapchain.surface_resolution.width as f32) - .height(swapchain.surface_resolution.height as f32) - .max_depth(1.0); - - let scissor = vk::Rect2D::default().extent(swapchain.surface_resolution); - - let viewports = [viewport]; - let scissors = [scissor]; let viewport_state = vk::PipelineViewportStateCreateInfo::default() - .viewports(&viewports) - .scissors(&scissors); + .viewport_count(1) + .scissor_count(1); let rasterizer = vk::PipelineRasterizationStateCreateInfo::default() .polygon_mode(vk::PolygonMode::FILL) @@ -77,7 +68,7 @@ impl VkGraphicsPipeline { vk::PipelineColorBlendStateCreateInfo::default().attachments(&attachments); let dynamic_state = vk::PipelineDynamicStateCreateInfo::default() - .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::LINE_WIDTH]); + .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]); let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default(); let pipeline_layout = unsafe { diff --git a/src/vulkan/vk_render_context.rs b/src/vulkan/vk_render_context.rs index 755d2c4..214996f 100644 --- a/src/vulkan/vk_render_context.rs +++ b/src/vulkan/vk_render_context.rs @@ -1,5 +1,5 @@ use crate::vulkan::{ - VkCommandPool, VkDevice, VkFramebuffer, VkGraphicsPipeline, VkInstance, VkPhysicalDevice, + VkCommandPool, VkDevice, VkFence, VkFramebuffer, VkGraphicsPipeline, VkInstance, VkPhysicalDevice, VkRenderPass, VkSemaphore, VkSurface, VkSwapchain, }; use ash::vk; @@ -18,6 +18,7 @@ pub struct VkRenderContext { command_buffers: Vec, image_available_semaphore: VkSemaphore, render_finished_semaphore: VkSemaphore, + in_flight_fence: VkFence, } impl VkRenderContext { @@ -36,7 +37,7 @@ impl VkRenderContext { Some(vk::QueueFlags::GRAPHICS), Some(&surface), ) - .ok_or_else(|| anyhow::anyhow!("Unable to find physical device"))?; + .ok_or_else(|| anyhow::anyhow!("Unable to find physical device"))?; log::debug!( "Selected queue {properties:#?} for physical device {:?}", physical_device.properties.device_name_as_c_str() @@ -77,50 +78,9 @@ impl VkRenderContext { .allocate_command_buffers(&command_buffer_info)? }; - // Same in VkGraphicsPipeline (TODO: Refactor this) - let render_area = vk::Rect2D::default().extent(swapchain.surface_resolution); - let clear_value = vk::ClearValue::default(); - for (index, command_buffer) in command_buffers.iter().enumerate() { - let command_buffer_begin_info = vk::CommandBufferBeginInfo::default(); - unsafe { - device - .handle - .begin_command_buffer(*command_buffer, &command_buffer_begin_info)? - }; - - let clear_values = [clear_value]; - let framebuffer = framebuffers[index].as_ref(); - let render_pass_begin_info = vk::RenderPassBeginInfo::default() - .render_pass(render_pass.handle) - .framebuffer(framebuffer.handle) - .render_area(render_area) - .clear_values(&clear_values); - - unsafe { - device.handle.cmd_begin_render_pass( - *command_buffer, - &render_pass_begin_info, - vk::SubpassContents::INLINE, - ); - }; - - unsafe { - device.handle.cmd_bind_pipeline( - *command_buffer, - vk::PipelineBindPoint::GRAPHICS, - pipeline.pipeline, - ) - }; - - unsafe { device.handle.cmd_draw(*command_buffer, 3, 1, 0, 0) }; - - unsafe { device.handle.cmd_end_render_pass(*command_buffer) }; - - unsafe { device.handle.end_command_buffer(*command_buffer)? }; - } - - let image_available_semaphore = VkSemaphore::new(device.clone())?; - let render_finished_semaphore = VkSemaphore::new(device.clone())?; + let image_available_semaphore = VkSemaphore::new(&device)?; + let render_finished_semaphore = VkSemaphore::new(&device)?; + let in_flight_fence = VkFence::new(&device)?; Ok(Self { instance, @@ -137,35 +97,101 @@ impl VkRenderContext { image_available_semaphore, render_finished_semaphore, + in_flight_fence, }) } pub fn render(&mut self) -> anyhow::Result<()> { - let queue = self - .device - .get_device_queue(0) - .ok_or_else(|| anyhow::anyhow!("Failed to get a queue"))?; + unsafe { self.device.handle.wait_for_fences(&[self.in_flight_fence.handle], true, u64::MAX)? }; + unsafe { self.device.handle.reset_fences(&[self.in_flight_fence.handle])? }; let (index, _) = self .swapchain .acquire_next_image(&self.image_available_semaphore)?; + let command_buffer = self.command_buffers[index as usize]; + unsafe { self.device.handle.reset_command_buffer(command_buffer, vk::CommandBufferResetFlags::default())? }; + + let render_area = vk::Rect2D::default().extent(self.swapchain.surface_resolution); + let clear_value = vk::ClearValue::default(); + let command_buffer_begin_info = vk::CommandBufferBeginInfo::default(); + unsafe { + self.device + .handle + .begin_command_buffer(command_buffer, &command_buffer_begin_info)? + }; + + let clear_values = [clear_value]; + let framebuffer = self.framebuffers[index as usize].as_ref(); + let render_pass_begin_info = vk::RenderPassBeginInfo::default() + .render_pass(self.render_pass.handle) + .framebuffer(framebuffer.handle) + .render_area(render_area) + .clear_values(&clear_values); + + unsafe { + self.device.handle.cmd_begin_render_pass( + command_buffer, + &render_pass_begin_info, + vk::SubpassContents::INLINE, + ); + }; + + unsafe { + self.device.handle.cmd_bind_pipeline( + command_buffer, + vk::PipelineBindPoint::GRAPHICS, + self.pipeline.pipeline, + ) + }; + + let viewport = vk::Viewport::default() + .width(self.swapchain.surface_resolution.width as f32) + .height(self.swapchain.surface_resolution.height as f32) + .max_depth(1.0); + + unsafe { self.device.handle.cmd_set_viewport(command_buffer, 0, &[viewport]) } + + let scissor = vk::Rect2D::default().extent(self.swapchain.surface_resolution); + + unsafe { self.device.handle.cmd_set_scissor(command_buffer, 0, &[scissor]) } + + unsafe { self.device.handle.cmd_draw(command_buffer, 3, 1, 0, 0) }; + + unsafe { self.device.handle.cmd_end_render_pass(command_buffer) }; + + unsafe { self.device.handle.end_command_buffer(command_buffer)? }; + let wait_semaphores = [self.image_available_semaphore.handle]; let signal_semaphores = [self.render_finished_semaphore.handle]; let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; - let command_buffers_to_submit = [self.command_buffers[index as usize]]; + let command_buffers_to_submit = [command_buffer]; let submit_info = vk::SubmitInfo::default() .wait_semaphores(&wait_semaphores) .wait_dst_stage_mask(&wait_stages) .command_buffers(&command_buffers_to_submit) .signal_semaphores(&signal_semaphores); + let queue = self + .device + .get_device_queue(0) + .ok_or_else(|| anyhow::anyhow!("Failed to get a queue"))?; + unsafe { self.device .handle - .queue_submit(*queue, &[submit_info], vk::Fence::null())? + .queue_submit(*queue, &[submit_info], self.in_flight_fence.handle)? }; + let swapchains = [self.swapchain.swapchain.unwrap()]; + let indices = [index]; + let present_info = vk::PresentInfoKHR::default() + .wait_semaphores(&signal_semaphores) + .swapchains(&swapchains) + .image_indices(&indices); + + unsafe { self.device.swapchain_loader.queue_present(*queue, &present_info)? }; + Ok(()) } diff --git a/src/vulkan/vk_render_pass.rs b/src/vulkan/vk_render_pass.rs index fafe50e..c9fcb7c 100644 --- a/src/vulkan/vk_render_pass.rs +++ b/src/vulkan/vk_render_pass.rs @@ -15,7 +15,7 @@ impl VkRenderPass { let color_attachment = vk::AttachmentDescription::default() .format(swapchain.surface_format.format) .samples(vk::SampleCountFlags::TYPE_1) - .load_op(vk::AttachmentLoadOp::LOAD) + .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::STORE) .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) diff --git a/src/vulkan/vk_semaphore.rs b/src/vulkan/vk_semaphore.rs index f9c35b0..60f4b95 100644 --- a/src/vulkan/vk_semaphore.rs +++ b/src/vulkan/vk_semaphore.rs @@ -9,13 +9,13 @@ pub struct VkSemaphore { } impl VkSemaphore { - pub fn new(device: Arc) -> anyhow::Result { + pub fn new(device: &Arc) -> anyhow::Result { let semaphore_info = vk::SemaphoreCreateInfo::default(); let semaphore = unsafe { device.handle.create_semaphore(&semaphore_info, None)? }; log::debug!("Semaphore created ({semaphore:?})"); Ok(Self { - device, + device: device.clone(), handle: semaphore, }) } diff --git a/src/vulkan/vk_swapchain.rs b/src/vulkan/vk_swapchain.rs index 3aa4274..dc32d99 100644 --- a/src/vulkan/vk_swapchain.rs +++ b/src/vulkan/vk_swapchain.rs @@ -11,7 +11,7 @@ pub struct VkSwapchain { surface: Arc, device: Arc, - swapchain: Option, + pub(super) swapchain: Option, swapchain_support_details: SwapchainSupportDetails, pub(super) desired_image_count: u32, @@ -137,7 +137,7 @@ impl VkSwapchain { &image_view, &self, ) - .unwrap() + .unwrap() }) .map(|framebuffer| Arc::new(framebuffer)) .collect::>(),