summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2026-07-09 21:26:44 -0700
committerIrene Knapp <ireneista@irenes.space>2026-07-09 21:26:44 -0700
commitcd3b77fea9307d9c15718a9e5e17035f7e71ac25 (patch)
treef6cc1020ba2e871408d93bd45f51cfa3eb9b1809 /src
parentee29403c132f4ba0babf27639f2a63299de7edca (diff)
allocate and use a vertex buffer
kinda cool how the driver interfaces with the kernel to map memory on our behalf

Force-Push: yes
Change-Id: I906f68b59dcedc1fd6d9bfc52c9b299abc9cd828
Diffstat (limited to 'src')
-rw-r--r--src/graphics_window_dressing.rs115
-rw-r--r--src/linear_algebra.rs62
-rw-r--r--src/main.rs1
3 files changed, 173 insertions, 5 deletions
diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs
index 63fc693..7fa25cf 100644
--- a/src/graphics_window_dressing.rs
+++ b/src/graphics_window_dressing.rs
@@ -3,10 +3,12 @@ use crate::error::*;
 use crate::graphics_permanent::{
   PermanentGraphicsState, GraphicsStateForReinit, QueueFamilyIndices
 };
+use crate::linear_algebra::{ Vertex, VERTICES };
 
 use std::collections::BTreeSet;
+use std::ptr;
 use vulkanalia::{ Device, Instance };
-use vulkanalia::vk::{ self, Handle, HasBuilder, DeviceV1_0,
+use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0,
                       KhrSwapchainExtensionDeviceCommands };
 use winit::window::Window;
 
@@ -32,6 +34,9 @@ pub struct WindowDressing {
 
   framebuffers: Vec<vk::Framebuffer>,
 
+  vertex_buffer: vk::Buffer,
+  vertex_buffer_memory: vk::DeviceMemory,
+
   command_pool: vk::CommandPool,
   pub command_buffers: Vec<vk::CommandBuffer>,
 
@@ -96,10 +101,14 @@ impl WindowDressing {
     let framebuffers = init_framebuffers(
             device, &swapchain.extent, &swapchain.image_views, &render_pass)?;
 
+    let (vertex_buffer, vertex_buffer_memory)
+            = init_vertex_buffer(instance, physical_device, device)?;
+
     let command_pool = init_command_pool(device, indices)?;
     let command_buffers = init_commands(device, &swapchain.extent,
                                         &framebuffers, &render_pass,
-                                        &pipeline, &command_pool)?;
+                                        &pipeline, &vertex_buffer,
+                                        &command_pool)?;
 
     let concurrency = init_concurrency(device, &swapchain.images)?;
 
@@ -109,6 +118,8 @@ impl WindowDressing {
       pipeline,
       pipeline_layout,
       framebuffers,
+      vertex_buffer,
+      vertex_buffer_memory,
       command_pool,
       command_buffers,
       concurrency,
@@ -146,7 +157,7 @@ impl WindowDressing {
     // Notice that we reused the command pool.
     let command_buffers = init_commands(
             device, &swapchain.extent, &framebuffers, &render_pass,
-            &pipeline, &self.command_pool)?;
+            &pipeline, &self.vertex_buffer, &self.command_pool)?;
 
     self.concurrency.image_fences.resize(swapchain.images.len(),
                                          vk::Fence::null());
@@ -168,6 +179,9 @@ impl WindowDressing {
   pub fn destroy(mut self, device: &Device) {
     self.destroy_replaceable(device);
 
+    unsafe { device.destroy_buffer(self.vertex_buffer, None) };
+    unsafe { device.free_memory(self.vertex_buffer_memory, None) };
+
     for semaphore in self.concurrency.image_available_semaphores {
       unsafe { device.destroy_semaphore(semaphore, None) };
     }
@@ -386,8 +400,12 @@ fn init_pipeline(device: &Device, extent: &vk::Extent2D,
                                 .module(fragment_module)
                                 .name(b"main\0");
 
+  let binding_descriptions = [Vertex::<f32>::binding_description()];
+  let attribute_descriptions = Vertex::<f32>::attribute_descriptions();
   let vertex_input_state_info
-          = vk::PipelineVertexInputStateCreateInfo::builder();
+          = vk::PipelineVertexInputStateCreateInfo::builder()
+                .vertex_binding_descriptions(&binding_descriptions)
+                .vertex_attribute_descriptions(&attribute_descriptions);
 
   let input_assembly_state_info
           = vk::PipelineInputAssemblyStateCreateInfo::builder()
@@ -509,6 +527,53 @@ fn init_framebuffers(device: &Device, extent: &vk::Extent2D,
 
 
 #[allow(unsafe_code)]
+fn init_vertex_buffer(instance: &Instance,
+                      physical_device: &vk::PhysicalDevice, device: &Device)
+    -> Result<(vk::Buffer, vk::DeviceMemory)>
+{
+  let buffer_info
+          = vk::BufferCreateInfo::builder()
+                .size((size_of::<Vertex<f32>>() * VERTICES.len()) as u64)
+                .usage(vk::BufferUsageFlags::VERTEX_BUFFER)
+                .sharing_mode(vk::SharingMode::EXCLUSIVE);
+
+  let buffer = unsafe { device.create_buffer(&buffer_info, None) }?;
+
+  //   The requirements are mostly what you'd think: size and alignment. The
+  // bits field is something special; see pick_memory_type() for the
+  // explanation. Despite the simplicity of this data, Vulkan wants to be the
+  // one to tell us about it, and we let it.
+  let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
+
+  let flags = vk::MemoryPropertyFlags::HOST_COHERENT
+              | vk::MemoryPropertyFlags::HOST_VISIBLE;
+  let type_index = pick_memory_type(instance, physical_device,
+                                    &flags, &requirements)?;
+  let memory_info = vk::MemoryAllocateInfo::builder()
+                        .allocation_size(requirements.size)
+                        .memory_type_index(type_index);
+
+  let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?;
+
+  unsafe { device.bind_buffer_memory(buffer, device_memory, 0) }?;
+
+  let host_memory = unsafe {
+    device.map_memory(device_memory, 0, buffer_info.size,
+                      vk::MemoryMapFlags::empty())
+  }?;
+
+  unsafe {
+    ptr::copy_nonoverlapping(VERTICES.as_ptr(), host_memory.cast(),
+                             VERTICES.len())
+  };
+
+  unsafe { device.unmap_memory(device_memory) };
+
+  Ok((buffer, device_memory))
+}
+
+
+#[allow(unsafe_code)]
 fn init_command_pool(device: &Device, indices: &QueueFamilyIndices)
     -> Result<vk::CommandPool>
 {
@@ -528,6 +593,7 @@ fn init_commands(device: &Device,
                  framebuffers: &Vec<vk::Framebuffer>,
                  render_pass: &vk::RenderPass,
                  pipeline: &vk::Pipeline,
+                 vertex_buffer: &vk::Buffer,
                  command_pool: &vk::CommandPool)
     -> Result<Vec<vk::CommandBuffer>>
 {
@@ -583,7 +649,14 @@ fn init_commands(device: &Device,
                                *pipeline)
     };
 
-    unsafe { device.cmd_draw(command_buffer, 3, 1, 0, 0) };
+    unsafe {
+      device.cmd_bind_vertex_buffers(command_buffer, 0,
+                                     &[*vertex_buffer], &[0])
+    };
+
+    unsafe {
+      device.cmd_draw(command_buffer, VERTICES.len() as u32, 1, 0, 0)
+    };
 
     unsafe { device.cmd_end_render_pass(command_buffer) };
 
@@ -679,3 +752,35 @@ fn pick_image_extent(window: &Window,
   }
 }
 
+
+#[allow(unsafe_code)]
+fn pick_memory_type(instance: &Instance,
+                    physical_device: &vk::PhysicalDevice,
+                    properties: &vk::MemoryPropertyFlags,
+                    requirements: &vk::MemoryRequirements)
+    -> Result<u32>
+{
+  let memory_map = unsafe {
+    instance.get_physical_device_memory_properties(*physical_device)
+  };
+
+  //   So. The memory_type_bits field is a map of which indices are suitable,
+  // based on the buffer our caller passed to
+  // get_buffer_memory_requirements(). Yes, that means there's a hard cap on
+  // how many memory types there can be, based on the size of the bitfield.
+  for index in 0 .. memory_map.memory_type_count {
+    if requirements.memory_type_bits & (1 << index) == 0 {
+      continue;
+    }
+
+    let memory_type = memory_map.memory_types[index as usize];
+
+    if memory_type.property_flags.contains(*properties) {
+      return Ok(index);
+    }
+  }
+
+  Err(Error {
+    message: "The system has no suitable memory for a buffer.".to_string()
+  })
+}
diff --git a/src/linear_algebra.rs b/src/linear_algebra.rs
new file mode 100644
index 0000000..950877b
--- /dev/null
+++ b/src/linear_algebra.rs
@@ -0,0 +1,62 @@
+#![forbid(unsafe_code)]
+use std::mem::size_of;
+use vulkanalia::vk::{ self, HasBuilder };
+
+
+pub static VERTICES: [Vertex<f32>; 3] = [
+  Vertex::new(Vec2( 0.0, -0.5), Vec3(1.0, 0.0, 0.0)),
+  Vertex::new(Vec2( 0.5,  0.5), Vec3(0.0, 1.0, 0.0)),
+  Vertex::new(Vec2(-0.5,  0.5), Vec3(0.0, 0.0, 1.0)),
+];
+
+
+#[derive(Clone, Debug)]
+pub struct Vec2<T>(T, T);
+#[derive(Clone, Debug)]
+pub struct Vec3<T>(T, T, T);
+#[derive(Clone, Debug)]
+#[allow(unused)]
+pub struct Vec4<T>(T, T, T, T);
+
+
+#[repr(C)]
+#[derive(Clone, Debug)]
+pub struct Vertex<T> {
+  pub position: Vec2<T>,
+  pub color: Vec3<T>,
+}
+
+impl<T> Vertex<T> {
+  const fn new(position: Vec2<T>, color: Vec3<T>) -> Self {
+    Self { position, color }
+  }
+
+  pub fn binding_description() -> vk::VertexInputBindingDescription {
+    vk::VertexInputBindingDescription::builder()
+        .binding(0)
+        .stride(size_of::<Vertex<f32>>() as u32)
+        .input_rate(vk::VertexInputRate::VERTEX)
+        .build()
+  }
+
+  pub fn attribute_descriptions()
+      -> [vk::VertexInputAttributeDescription; 2]
+  {
+    let position = vk::VertexInputAttributeDescription::builder()
+                       .binding(0)
+                       .location(0)
+                       .format(vk::Format::R32G32_SFLOAT)
+                       .offset(0)
+                       .build();
+
+    let color = vk::VertexInputAttributeDescription::builder()
+                       .binding(0)
+                       .location(1)
+                       .format(vk::Format::R32G32B32_SFLOAT)
+                       .offset(size_of::<Vec2<f32>>() as u32)
+                       .build();
+
+    [position, color]
+  }
+}
+
diff --git a/src/main.rs b/src/main.rs
index ba4ffb7..e6e3c0a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -18,6 +18,7 @@ use winit::window::WindowId;
 mod error;
 mod graphics_permanent;
 mod graphics_window_dressing;
+mod linear_algebra;
 
 
 struct Surreality {