From 82cba53f60013699631be3f1fdc0bd1e47d5f6d6 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Sun, 5 Jul 2026 14:53:37 -0700 Subject: successfully instantiate the render pipeline in the words of the tutorial, "We are already getting quite close to seeing something pop up on the screen"..... it took THREE DAYS. encouragement is great and all, but perhaps don't trivialize that effort Force-Push: yes Change-Id: Icf7fd651dbf12cae0320f98d1622fad5fcd3fd0d --- shaders/shader.frag | 7 ++ shaders/shader.vert | 12 +++ src/main.rs | 225 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 231 insertions(+), 13 deletions(-) create mode 100644 shaders/shader.frag create mode 100644 shaders/shader.vert diff --git a/shaders/shader.frag b/shaders/shader.frag new file mode 100644 index 0000000..f79ee65 --- /dev/null +++ b/shaders/shader.frag @@ -0,0 +1,7 @@ +#version 460 + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/shaders/shader.vert b/shaders/shader.vert new file mode 100644 index 0000000..c1bfd9f --- /dev/null +++ b/shaders/shader.vert @@ -0,0 +1,12 @@ +#version 460 + +vec2 triangle[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +void main() { + gl_Position = vec4(triangle[gl_VertexIndex], 0.0, 1.0); +} + diff --git a/src/main.rs b/src/main.rs index 62c6025..1528b1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -131,6 +131,13 @@ struct Surreality { swapchain: OnceCell, swapchain_images: OnceCell>, swapchain_image_views: OnceCell>, + format: OnceCell, + extent: OnceCell, + + render_pass: OnceCell, + + pipeline: OnceCell, + pipeline_layout: OnceCell, } impl Surreality { @@ -151,6 +158,11 @@ impl Surreality { swapchain: OnceCell::new(), swapchain_images: OnceCell::new(), swapchain_image_views: OnceCell::new(), + format: OnceCell::new(), + extent: OnceCell::new(), + render_pass: OnceCell::new(), + pipeline: OnceCell::new(), + pipeline_layout: OnceCell::new(), } } @@ -181,7 +193,13 @@ impl Surreality { self.init_vulkan_swapchain()?; } - self.init_pipeline()?; + if self.render_pass.get().is_none() { + self.init_render_pass()?; + } + + if self.pipeline.get().is_none() { + self.init_pipeline()?; + } Ok(()) } @@ -544,8 +562,6 @@ impl Surreality { let device = self.device.get().unwrap().clone(); let surface = self.surface.get().unwrap().clone(); - println!("features {:?}", features); - let format = self.pick_surface_format(&features.formats)?; let presentation_mode @@ -636,28 +652,174 @@ impl Surreality { self.swapchain.set(swapchain).unwrap(); self.swapchain_images.set(images).unwrap(); self.swapchain_image_views.set(image_views).unwrap(); + self.format.set(format.format).unwrap(); + self.extent.set(extent).unwrap(); } Ok(()) } #[allow(unsafe_code)] - fn init_pipeline(&mut self) -> Result<()> { - // TODO integrate with Cargo - let vertex_binary = include_bytes!("../vert.spv"); - let fragment_binary = include_bytes!("../frag.spv"); + fn init_render_pass(&mut self) -> Result<()> { + if self.render_pass.get().is_none() { + let device = self.device.get().unwrap(); + let format = self.format.get().unwrap(); + + let color_attachment + = vk::AttachmentDescription::builder() + .format(*format) + .samples(vk::SampleCountFlags::_1) + .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); + + let color_attachment_reference + = vk::AttachmentReference::builder() + .attachment(0) + .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); + + let subpass_attachments = [color_attachment_reference]; + let subpass = vk::SubpassDescription::builder() + .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) + .color_attachments(&subpass_attachments); + + let render_attachments = [color_attachment]; + let subpasses = [subpass]; + let render_pass_info = vk::RenderPassCreateInfo::builder() + .attachments(&render_attachments) + .subpasses(&subpasses); + + let render_pass = unsafe { + device.create_render_pass(&render_pass_info, None) + }?; - let _ = self.load_spirv_shader_module(vertex_binary)?; + self.render_pass.set(render_pass).unwrap(); + } Ok(()) } - fn load_spirv_shader_module(&mut self, binary: &[u8]) - -> Result - { - let bytecode = Bytecode::new(binary)?; + #[allow(unsafe_code)] + fn init_pipeline(&mut self) -> Result<()> { + if self.pipeline.get().is_none() { + let device = self.device.get().unwrap().clone(); + let extent = self.extent.get().unwrap().clone(); + let render_pass = self.render_pass.get().unwrap().clone(); + + // TODO integrate with Cargo + let vertex_binary = include_bytes!("../vert.spv"); + let fragment_binary = include_bytes!("../frag.spv"); + + let vertex_module = self.load_spirv_shader_module(vertex_binary)?; + let fragment_module = self.load_spirv_shader_module(fragment_binary)?; + + let vertex_stage_info = vk::PipelineShaderStageCreateInfo::builder() + .stage(vk::ShaderStageFlags::VERTEX) + .module(vertex_module) + .name(b"main\0"); + + let fragment_stage_info = vk::PipelineShaderStageCreateInfo::builder() + .stage(vk::ShaderStageFlags::FRAGMENT) + .module(fragment_module) + .name(b"main\0"); + + let vertex_input_state_info + = vk::PipelineVertexInputStateCreateInfo::builder(); + + let input_assembly_state_info + = vk::PipelineInputAssemblyStateCreateInfo::builder() + .topology(vk::PrimitiveTopology::TRIANGLE_LIST) + .primitive_restart_enable(false); + + let viewport = vk::Viewport::builder() + .x(0.0) + .y(0.0) + .width(extent.width as f32) + .height(extent.height as f32) + .min_depth(0.0) + .max_depth(1.0); + let viewports = [viewport]; + + let scissor = vk::Rect2D::builder() + .offset(vk::Offset2D { x: 0, y: 0 }) + .extent(extent); + let scissor_list = [scissor]; + + let viewport_state_info = vk::PipelineViewportStateCreateInfo::builder() + .viewports(&viewports) + .scissors(&scissor_list); + + let rasterizer_state_info + = vk::PipelineRasterizationStateCreateInfo::builder() + .depth_clamp_enable(false) + .rasterizer_discard_enable(false) + .polygon_mode(vk::PolygonMode::FILL) + .line_width(1.0) + .cull_mode(vk::CullModeFlags::BACK) + .front_face(vk::FrontFace::CLOCKWISE) + .depth_bias_enable(false); + + let multisample_state_info + = vk::PipelineMultisampleStateCreateInfo::builder() + .sample_shading_enable(false) + .rasterization_samples(vk::SampleCountFlags::_1); + + let blend_attachment_info + = vk::PipelineColorBlendAttachmentState::builder() + .color_write_mask(vk::ColorComponentFlags::all()) + .blend_enable(false) + .src_color_blend_factor(vk::BlendFactor::ONE) + .dst_color_blend_factor(vk::BlendFactor::ZERO) + .color_blend_op(vk::BlendOp::ADD) + .src_alpha_blend_factor(vk::BlendFactor::ONE) + .dst_alpha_blend_factor(vk::BlendFactor::ZERO) + .alpha_blend_op(vk::BlendOp::ADD); + let blend_attachments = [blend_attachment_info]; + + let blend_info = vk::PipelineColorBlendStateCreateInfo::builder() + .logic_op_enable(false) + .logic_op(vk::LogicOp::COPY) + .attachments(&blend_attachments) + .blend_constants([0.0, 0.0, 0.0, 0.0]); + + let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder(); + + let pipeline_layout = unsafe { + device.create_pipeline_layout(&pipeline_layout_info, None) + }?; + + let stages = [vertex_stage_info, fragment_stage_info]; + let pipeline_info + = vk::GraphicsPipelineCreateInfo::builder() + .stages(&stages) + .vertex_input_state(&vertex_input_state_info) + .input_assembly_state(&input_assembly_state_info) + .viewport_state(&viewport_state_info) + .rasterization_state(&rasterizer_state_info) + .multisample_state(&multisample_state_info) + .color_blend_state(&blend_info) + .layout(pipeline_layout) + .render_pass(render_pass) + .subpass(0); + + let pipeline = unsafe { + device.create_graphics_pipelines(vk::PipelineCache::null(), + &[pipeline_info], None) + }?.0[0]; + + self.pipeline_layout.set(pipeline_layout).unwrap(); + self.pipeline.set(pipeline).unwrap(); - Err(Error { message: "just because".to_string() }) + unsafe { + device.destroy_shader_module(vertex_module, None); + device.destroy_shader_module(fragment_module, None); + }; + } + + Ok(()) } // To Vulkan, a "physical" device is the actual GPU, and a "logical" @@ -918,6 +1080,25 @@ impl Surreality { } } + #[allow(unsafe_code)] + fn load_spirv_shader_module(&mut self, binary: &[u8]) + -> Result + { + let device = self.device.get().unwrap(); + + let bytecode = Bytecode::new(binary)?; + + let module_info = vk::ShaderModuleCreateInfo::builder() + .code(bytecode.code()) + .code_size(bytecode.code_size()); + + let module = unsafe { + device.create_shader_module(&module_info, None) + }?; + + Ok(module) + } + fn render(&mut self, window_id: WindowId) -> Result<()> { if let Some(window) = self.window.get() && window_id == window.id() @@ -934,6 +1115,24 @@ impl Surreality { impl Drop for Surreality { #[allow(unsafe_code)] fn drop(&mut self) { + if let Some(pipeline) = self.pipeline.get() + && let Some(device) = self.device.get() + { + unsafe { device.destroy_pipeline(*pipeline, None) }; + } + + if let Some(render_pass) = self.render_pass.get() + && let Some(device) = self.device.get() + { + unsafe { device.destroy_render_pass(*render_pass, None) }; + } + + if let Some(pipeline_layout) = self.pipeline_layout.get() + && let Some(device) = self.device.get() + { + unsafe { device.destroy_pipeline_layout(*pipeline_layout, None) }; + } + if let Some(image_views) = self.swapchain_image_views.get() && let Some(device) = self.device.get() { -- cgit 1.4.1