diff --git a/src/display/app.rs b/src/display/app.rs index f163fa8..c0ebac7 100644 --- a/src/display/app.rs +++ b/src/display/app.rs @@ -32,6 +32,11 @@ impl ApplicationHandler for App { fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { match event { WindowEvent::CloseRequested => { + match self.render_context.as_ref() { + Some(render_context) => render_context.exit(), + None => log::warn!("Window closed but no render context found"), + }; + log::debug!("The close button was pressed; stopping"); event_loop.exit(); } diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 83eafc7..48a5d6b 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,5 +1,3 @@ -#![allow(unused_imports)] - pub(self) mod vk_render_context; pub use vk_render_context::VkRenderContext; @@ -25,5 +23,13 @@ pub(self) mod vk_graphics_pipeline; pub use vk_graphics_pipeline::VkGraphicsPipeline; mod vk_render_pass; +pub(self) use vk_render_pass::VkRenderPass; + +mod vk_semaphore; +pub(self) use vk_semaphore::VkSemaphore; + +mod vk_command_pool; + +mod vk_framebuffer; mod utils; diff --git a/src/vulkan/vk_command_pool.rs b/src/vulkan/vk_command_pool.rs new file mode 100644 index 0000000..14ff0d1 --- /dev/null +++ b/src/vulkan/vk_command_pool.rs @@ -0,0 +1,31 @@ +use crate::vulkan::VkDevice; +use ash::prelude::VkResult; +use ash::vk; +use std::sync::Arc; + +pub struct VkCommandPool { + device: Arc, + + pub(super) handle: vk::CommandPool, +} + +impl VkCommandPool { + pub fn new(device: Arc) -> VkResult { + let command_pool_info = vk::CommandPoolCreateInfo::default() + .queue_family_index(device.queue_family_index); + let command_pool = unsafe { device.handle.create_command_pool(&command_pool_info, None)? }; + log::debug!("Command pool created ({command_pool:?})"); + + Ok(Self { + device, + handle: command_pool, + }) + } +} + +impl Drop for VkCommandPool { + fn drop(&mut self) { + unsafe { self.device.handle.destroy_command_pool(self.handle, None) }; + log::debug!("Command pool destroyed ({:?})", self.handle); + } +} \ No newline at end of file diff --git a/src/vulkan/vk_framebuffer.rs b/src/vulkan/vk_framebuffer.rs new file mode 100644 index 0000000..44252a9 --- /dev/null +++ b/src/vulkan/vk_framebuffer.rs @@ -0,0 +1,52 @@ +use crate::vulkan::vk_render_pass::VkRenderPass; +use crate::vulkan::{VkDevice, VkSwapchain}; +use ash::prelude::VkResult; +use ash::vk; +use std::sync::Arc; + +pub struct VkSwapchainFramebuffer { + device: Arc, + swapchain: Arc, + render_pass: Arc, + image_view_index: usize, + + pub(super) handle: vk::Framebuffer, +} + +impl VkSwapchainFramebuffer { + pub fn new( + device: Arc, + swapchain: Arc, + render_pass: Arc, + image_view_index: usize, + ) -> VkResult { + let present_image_view = swapchain + .present_image_views + .as_ref() + .unwrap()[image_view_index]; + let attachments = [present_image_view]; + let framebuffer_info = vk::FramebufferCreateInfo::default() + .render_pass(render_pass.handle) + .width(swapchain.surface_resolution.width) + .height(swapchain.surface_resolution.height) + .attachments(&attachments) + .layers(1); + + let framebuffer = unsafe { device.handle.create_framebuffer(&framebuffer_info, None)? }; + + Ok(Self { + device, + swapchain, + render_pass, + image_view_index, + + handle: framebuffer, + }) + } +} + +impl Drop for VkSwapchainFramebuffer { + fn drop(&mut self) { + unsafe { self.device.handle.destroy_framebuffer(self.handle, None) }; + } +} \ No newline at end of file diff --git a/src/vulkan/vk_graphics_pipeline.rs b/src/vulkan/vk_graphics_pipeline.rs index 7566620..e306557 100644 --- a/src/vulkan/vk_graphics_pipeline.rs +++ b/src/vulkan/vk_graphics_pipeline.rs @@ -9,8 +9,8 @@ pub struct VkGraphicsPipeline { swapchain: Arc, render_pass: Arc, - pipeline_layout: vk::PipelineLayout, - pipeline: vk::Pipeline, + pub(super) pipeline_layout: vk::PipelineLayout, + pub(super) pipeline: vk::Pipeline, vertex_shader: VkShaderModule, fragment_shader: VkShaderModule, } diff --git a/src/vulkan/vk_render_context.rs b/src/vulkan/vk_render_context.rs index 7f49251..c8800be 100644 --- a/src/vulkan/vk_render_context.rs +++ b/src/vulkan/vk_render_context.rs @@ -1,6 +1,7 @@ -use crate::vulkan::vk_render_pass::VkRenderPass; -use crate::vulkan::{VkDevice, VkGraphicsPipeline, VkInstance, VkPhysicalDevice, VkSurface, VkSwapchain}; +use crate::vulkan::vk_command_pool::VkCommandPool; +use crate::vulkan::{VkDevice, VkGraphicsPipeline, VkInstance, VkPhysicalDevice, VkRenderPass, VkSemaphore, VkSurface, VkSwapchain}; use ash::vk; +use std::mem::swap; use std::sync::Arc; pub struct VkRenderContext { @@ -9,6 +10,12 @@ pub struct VkRenderContext { device: Arc, swapchain: Arc, + render_pass: Arc, + pipeline: Arc, + command_pool: VkCommandPool, + command_buffers: Vec, + image_available_semaphore: VkSemaphore, + render_finished_semaphore: VkSemaphore, } impl VkRenderContext { @@ -51,57 +58,88 @@ impl VkRenderContext { swapchain.clone(), )?); + let framebuffers = swapchain.create_framebuffers(render_pass.clone()) + .ok_or_else(|| anyhow::anyhow!("Failed to get framebuffers"))?; + let pipeline = Arc::new(VkGraphicsPipeline::new( device.clone(), swapchain.clone(), render_pass.clone(), )?); - let framebuffers = swapchain.present_image_views - .as_ref() - .ok_or_else(|| anyhow::anyhow!("No present image views found"))? - .iter() - .map(|present_image_view| { - let attachments = [*present_image_view]; - let framebuffer_info = vk::FramebufferCreateInfo::default() - .render_pass(render_pass.handle) - .width(swapchain.surface_resolution.width) - .height(swapchain.surface_resolution.height) - .attachments(&attachments) - .layers(1); - - unsafe { device.handle.create_framebuffer(&framebuffer_info, None).unwrap() } - }) - .collect::>(); - - let command_pool_info = vk::CommandPoolCreateInfo::default() - .queue_family_index(device.queue_family_index); - let command_pool = unsafe { device.handle.create_command_pool(&command_pool_info, None)? }; + let command_pool = VkCommandPool::new(device.clone())?; let command_buffer_info = vk::CommandBufferAllocateInfo::default() - .command_pool(command_pool) + .command_pool(command_pool.handle) .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(framebuffers.len() as u32); + .command_buffer_count(framebuffers.as_ref().unwrap().len() as u32); // Destroyed with command pool let command_buffers = unsafe { device.handle.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)? }; - unsafe { device.handle.destroy_command_pool(command_pool, None) }; - for framebuffer in framebuffers { - unsafe { device.handle.destroy_framebuffer(framebuffer, None) }; + let clear_values = [clear_value]; + let render_pass_begin_info = vk::RenderPassBeginInfo::default() + .render_pass(render_pass.handle) + .framebuffer(framebuffers[index]) + .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())?; + Ok(Self { instance, surface, device, swapchain, + render_pass, + pipeline, + + command_pool, + command_buffers, + + image_available_semaphore, + render_finished_semaphore, }) } 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"))?; + + let (index, _) = self.swapchain.acquire_next_image(&self.image_available_semaphore)?; + + 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 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); + + unsafe { self.device.handle.queue_submit(*queue, &[submit_info], vk::Fence::null())? }; + Ok(()) } @@ -112,4 +150,8 @@ impl VkRenderContext { Ok(()) } + + pub fn exit(&self) { + unsafe { self.device.handle.device_wait_idle().unwrap() } + } } diff --git a/src/vulkan/vk_semaphore.rs b/src/vulkan/vk_semaphore.rs new file mode 100644 index 0000000..f9c35b0 --- /dev/null +++ b/src/vulkan/vk_semaphore.rs @@ -0,0 +1,29 @@ +use crate::vulkan::VkDevice; +use ash::vk; +use std::sync::Arc; + +pub struct VkSemaphore { + device: Arc, + + pub(super) handle: vk::Semaphore, +} + +impl VkSemaphore { + 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, + handle: semaphore, + }) + } +} + +impl Drop for VkSemaphore { + fn drop(&mut self) { + unsafe { self.device.handle.destroy_semaphore(self.handle, None) }; + log::debug!("Semaphore destroyed ({:?})", self.handle); + } +} \ No newline at end of file diff --git a/src/vulkan/vk_swapchain.rs b/src/vulkan/vk_swapchain.rs index 9ec8873..12e556f 100644 --- a/src/vulkan/vk_swapchain.rs +++ b/src/vulkan/vk_swapchain.rs @@ -1,4 +1,7 @@ use crate::display::Window; +use crate::vulkan::vk_framebuffer::VkSwapchainFramebuffer; +use crate::vulkan::vk_render_pass::VkRenderPass; +use crate::vulkan::vk_semaphore::VkSemaphore; use crate::vulkan::vk_surface::SwapchainSupportDetails; use crate::vulkan::{VkDevice, VkPhysicalDevice, VkSurface}; use ash::prelude::VkResult; @@ -8,6 +11,7 @@ use std::sync::Arc; pub struct VkSwapchain { surface: Arc, device: Arc, + swapchain: Option, swapchain_support_details: SwapchainSupportDetails, @@ -59,6 +63,7 @@ impl VkSwapchain { let mut swapchain = Self { surface, device, + swapchain: None, swapchain_support_details, desired_image_count, @@ -116,6 +121,16 @@ impl VkSwapchain { Ok(()) } + pub(super) fn create_framebuffers(&self, render_pass: Arc) -> Option> { + let present_image_views = self.present_image_views.as_ref()?; + + present_image_views.iter().enumerate() + .map(|present_image_view| { + VkSwapchainFramebuffer::new() + }) + .collect::>() + } + pub(super) fn update_resolution(&mut self, width: u32, height: u32) -> VkResult<()> { log::debug!("New resolution requested ({width}x{height})"); @@ -141,6 +156,17 @@ impl VkSwapchain { Ok(()) } + pub(super) fn acquire_next_image(&self, semaphore: &VkSemaphore) -> VkResult<(u32, bool)> { + unsafe { + self.device.swapchain_loader.acquire_next_image( + self.swapchain.unwrap(), + u64::MAX, + semaphore.handle, + vk::Fence::null(), + ) + } + } + fn create_swapchain_info(&self, surface: &VkSurface) -> vk::SwapchainCreateInfoKHR { vk::SwapchainCreateInfoKHR::default() .surface(surface.surface)