summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-07-05 14:53:37 -0700
committerIrene Knapp <ireneista@irenes.space>2026-07-05 14:53:37 -0700
commit82cba53f60013699631be3f1fdc0bd1e47d5f6d6 (patch)
tree5527f14ff2389b332f7bd5bcc88a4eaf4561b27a /src
parentaea6d8b310dede44211fcf994f8ddb42df076d91 (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/main.rs225
1 files changed, 212 insertions, 13 deletions
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<vk::SwapchainKHR>,
   swapchain_images: OnceCell<Vec<vk::Image>>,
   swapchain_image_views: OnceCell<Vec<vk::ImageView>>,
+  format: OnceCell<vk::Format>,
+  extent: OnceCell<vk::Extent2D>,
+
+  render_pass: OnceCell<vk::RenderPass>,
+
+  pipeline: OnceCell<vk::Pipeline>,
+  pipeline_layout: OnceCell<vk::PipelineLayout>,
 }
 
 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<vk::ShaderModule>
-  {
-    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<vk::ShaderModule>
+  {
+    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()
     {