summary refs log tree commit diff
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
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
-rw-r--r--shaders/shader.frag7
-rw-r--r--shaders/shader.vert12
-rw-r--r--src/main.rs225
3 files changed, 231 insertions, 13 deletions
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<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()
     {