diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs index 63a1c74..83eafc7 100644 --- a/src/vulkan/mod.rs +++ b/src/vulkan/mod.rs @@ -1,3 +1,5 @@ +#![allow(unused_imports)] + pub(self) mod vk_render_context; pub use vk_render_context::VkRenderContext; @@ -16,5 +18,12 @@ pub use vk_device::VkDevice; pub(self) mod vk_swapchain; pub use vk_swapchain::VkSwapchain; +pub(self) mod vk_shader_module; +pub use vk_shader_module::VkShaderModule; + +pub(self) mod vk_graphics_pipeline; +pub use vk_graphics_pipeline::VkGraphicsPipeline; + +mod vk_render_pass; + mod utils; -mod vk_shader_module; diff --git a/src/vulkan/vk_device.rs b/src/vulkan/vk_device.rs index b6b550e..4fb1986 100644 --- a/src/vulkan/vk_device.rs +++ b/src/vulkan/vk_device.rs @@ -7,8 +7,7 @@ pub struct VkDevice { instance: Arc, pub(super) handle: ash::Device, pub(super) swapchain_loader: ash::khr::swapchain::Device, - - queue_family_index: u32, + pub(super) queue_family_index: u32, // Arc not used because vk::Queue is destroyed with Device automatically // so any references of vk::Queue must be destroyed with VkDevice diff --git a/src/vulkan/vk_graphics_pipeline.rs b/src/vulkan/vk_graphics_pipeline.rs new file mode 100644 index 0000000..7566620 --- /dev/null +++ b/src/vulkan/vk_graphics_pipeline.rs @@ -0,0 +1,131 @@ +use crate::vulkan::vk_render_pass::VkRenderPass; +use crate::vulkan::{VkDevice, VkShaderModule, VkSwapchain}; +use ash::vk; +use std::ffi::CStr; +use std::sync::Arc; + +pub struct VkGraphicsPipeline { + device: Arc, + swapchain: Arc, + render_pass: Arc, + + pipeline_layout: vk::PipelineLayout, + pipeline: vk::Pipeline, + vertex_shader: VkShaderModule, + fragment_shader: VkShaderModule, +} + +impl VkGraphicsPipeline { + pub fn new( + device: Arc, + swapchain: Arc, + render_pass: Arc, + ) -> anyhow::Result { + let shader_entry_name = CStr::from_bytes_with_nul(b"main\0")?; + + let vert_shader_module = + VkShaderModule::from_spv_file(device.clone(), "res/shaders/main.vert.spv")?; + + let vert_shader_info = vk::PipelineShaderStageCreateInfo::default() + .module(vert_shader_module.handle) + .name(shader_entry_name) + .stage(vk::ShaderStageFlags::VERTEX); + + let frag_shader_module = + VkShaderModule::from_spv_file(device.clone(), "res/shaders/main.frag.spv")?; + + let frag_shader_info = vk::PipelineShaderStageCreateInfo::default() + .module(frag_shader_module.handle) + .name(shader_entry_name) + .stage(vk::ShaderStageFlags::FRAGMENT); + + let shader_stage_create_infos = [vert_shader_info, frag_shader_info]; + + let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::default(); + + 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); + + let rasterizer = vk::PipelineRasterizationStateCreateInfo::default() + .polygon_mode(vk::PolygonMode::FILL) + .cull_mode(vk::CullModeFlags::BACK) + .front_face(vk::FrontFace::CLOCKWISE) + .line_width(1.0); + + let multisampling = vk::PipelineMultisampleStateCreateInfo::default() + .rasterization_samples(vk::SampleCountFlags::TYPE_1) + .min_sample_shading(1.0); + + let color_blend_attachment = vk::PipelineColorBlendAttachmentState::default() + .color_write_mask(vk::ColorComponentFlags::RGBA); + + let attachments = [color_blend_attachment]; + let color_blending = + vk::PipelineColorBlendStateCreateInfo::default().attachments(&attachments); + + let dynamic_state = vk::PipelineDynamicStateCreateInfo::default() + .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::LINE_WIDTH]); + + let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default(); + let pipeline_layout = unsafe { + device + .handle + .create_pipeline_layout(&pipeline_layout_info, None)? + }; + log::debug!("Pipeline layout created ({pipeline_layout:?})"); + + let pipeline_info = vk::GraphicsPipelineCreateInfo::default() + .stages(&shader_stage_create_infos) + .vertex_input_state(&vertex_input_info) + .input_assembly_state(&input_assembly) + .viewport_state(&viewport_state) + .rasterization_state(&rasterizer) + .multisample_state(&multisampling) + .color_blend_state(&color_blending) + .dynamic_state(&dynamic_state) + .layout(pipeline_layout) + .render_pass(render_pass.handle); + let pipeline = unsafe { + device + .handle + .create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None) + .map_err(|(_, error)| error)?[0] + }; + log::debug!("Pipeline created ({pipeline_layout:?})"); + + Ok(Self { + device, + swapchain, + render_pass, + pipeline_layout, + pipeline, + vertex_shader: vert_shader_module, + fragment_shader: frag_shader_module, + }) + } +} + +impl Drop for VkGraphicsPipeline { + fn drop(&mut self) { + unsafe { + self.device.handle.destroy_pipeline(self.pipeline, None); + log::debug!("Pipeline destroyed ({:?})", self.pipeline); + + self.device.handle.destroy_pipeline_layout(self.pipeline_layout, None); + log::debug!("Pipeline layout destroyed ({:?})", self.pipeline_layout); + } + } +} \ No newline at end of file diff --git a/src/vulkan/vk_render_context.rs b/src/vulkan/vk_render_context.rs index c862814..7f49251 100644 --- a/src/vulkan/vk_render_context.rs +++ b/src/vulkan/vk_render_context.rs @@ -1,8 +1,6 @@ -use crate::vulkan::vk_shader_module::VkShaderModule; -use crate::vulkan::{VkDevice, VkInstance, VkPhysicalDevice, VkSurface, VkSwapchain}; +use crate::vulkan::vk_render_pass::VkRenderPass; +use crate::vulkan::{VkDevice, VkGraphicsPipeline, VkInstance, VkPhysicalDevice, VkSurface, VkSwapchain}; use ash::vk; -use ash::vk::PrimitiveTopology; -use std::ffi::{CStr, CString}; use std::sync::Arc; pub struct VkRenderContext { @@ -29,7 +27,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() @@ -48,69 +46,51 @@ impl VkRenderContext { &physical_device, )?); - let shader_entry_name = CStr::from_bytes_with_nul(b"main\0")?; + let render_pass = Arc::new(VkRenderPass::new( + device.clone(), + swapchain.clone(), + )?); - let vert_shader_module = - VkShaderModule::from_spv_file(device.clone(), "res/shaders/main.vert.spv")?; + let pipeline = Arc::new(VkGraphicsPipeline::new( + device.clone(), + swapchain.clone(), + render_pass.clone(), + )?); - let vert_shader_info = vk::PipelineShaderStageCreateInfo::default() - .module(vert_shader_module.handle) - .name(shader_entry_name) - .stage(vk::ShaderStageFlags::VERTEX); + 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); - let frag_shader_module = - VkShaderModule::from_spv_file(device.clone(), "res/shaders/main.frag.spv")?; + unsafe { device.handle.create_framebuffer(&framebuffer_info, None).unwrap() } + }) + .collect::>(); - let frag_shader_info = vk::PipelineShaderStageCreateInfo::default() - .module(frag_shader_module.handle) - .name(shader_entry_name) - .stage(vk::ShaderStageFlags::FRAGMENT); + 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 shader_stage_create_infos = [vert_shader_info, frag_shader_info]; + let command_buffer_info = vk::CommandBufferAllocateInfo::default() + .command_pool(command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(framebuffers.len() as u32); - let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::default(); + // Destroyed with command pool + let command_buffers = unsafe { device.handle.allocate_command_buffers(&command_buffer_info)? }; - let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() - .topology(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 viewport_state = vk::PipelineViewportStateCreateInfo::default() - .viewports(&[viewport]) - .scissors(&[scissor]); - - let rasterizer = vk::PipelineRasterizationStateCreateInfo::default() - .polygon_mode(vk::PolygonMode::FILL) - .cull_mode(vk::CullModeFlags::BACK) - .front_face(vk::FrontFace::CLOCKWISE) - .line_width(1.0); - - let multisampling = vk::PipelineMultisampleStateCreateInfo::default() - .rasterization_samples(vk::SampleCountFlags::TYPE_1) - .min_sample_shading(1.0); - - let color_blend_attachment = vk::PipelineColorBlendAttachmentState::default() - .color_write_mask(vk::ColorComponentFlags::RGBA); - - let color_blending = - vk::PipelineColorBlendStateCreateInfo::default().attachments(&[color_blend_attachment]); - - let dynamic_state = vk::PipelineDynamicStateCreateInfo::default() - .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::LINE_WIDTH]); - - let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default(); - let pipeline_layout = unsafe { - device - .handle - .create_pipeline_layout(&pipeline_layout_info, None)? - }; - - unsafe { device.handle.destroy_pipeline_layout(pipeline_layout, None) }; + unsafe { device.handle.destroy_command_pool(command_pool, None) }; + for framebuffer in framebuffers { + unsafe { device.handle.destroy_framebuffer(framebuffer, None) }; + } Ok(Self { instance, diff --git a/src/vulkan/vk_render_pass.rs b/src/vulkan/vk_render_pass.rs new file mode 100644 index 0000000..ff6ac9e --- /dev/null +++ b/src/vulkan/vk_render_pass.rs @@ -0,0 +1,63 @@ +use crate::vulkan::{VkDevice, VkSwapchain}; +use ash::prelude::VkResult; +use ash::vk; +use std::sync::Arc; + +pub struct VkRenderPass { + device: Arc, + swapchain: Arc, + + pub(super) handle: vk::RenderPass, +} + +impl VkRenderPass { + pub fn new( + device: Arc, + swapchain: Arc, + ) -> VkResult { + let color_attachment = vk::AttachmentDescription::default() + .format(swapchain.surface_format.format) + .samples(vk::SampleCountFlags::TYPE_1) + .load_op(vk::AttachmentLoadOp::LOAD) + .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); + + let color_attachment_ref = vk::AttachmentReference::default() + .attachment(0) + .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); + + let color_attachments = [color_attachment_ref]; + let subpass = vk::SubpassDescription::default() + .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) + .color_attachments(&color_attachments); + + let attachments = [color_attachment]; + let subpasses = [subpass]; + let render_pass_info = vk::RenderPassCreateInfo::default() + .attachments(&attachments) + .subpasses(&subpasses); + + let render_pass = unsafe { + device.handle.create_render_pass(&render_pass_info, None)? + }; + log::debug!("Render pass created ({render_pass:?})"); + + Ok(Self { + device, + swapchain, + handle: render_pass, + }) + } +} + +impl Drop for VkRenderPass { + fn drop(&mut self) { + unsafe { + self.device.handle.destroy_render_pass(self.handle, None); + log::debug!("Render pass destroyed ({:?})", self.handle); + } + } +} \ No newline at end of file