From cd3b77fea9307d9c15718a9e5e17035f7e71ac25 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Thu, 9 Jul 2026 21:26:44 -0700 Subject: 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 --- src/graphics_window_dressing.rs | 115 ++++++++++++++++++++++++++++++++++++++-- src/linear_algebra.rs | 62 ++++++++++++++++++++++ src/main.rs | 1 + 3 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 src/linear_algebra.rs (limited to 'src') 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, + vertex_buffer: vk::Buffer, + vertex_buffer_memory: vk::DeviceMemory, + command_pool: vk::CommandPool, pub command_buffers: Vec, @@ -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::::binding_description()]; + let attribute_descriptions = Vertex::::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() @@ -508,6 +526,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::>() * 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 @@ -528,6 +593,7 @@ fn init_commands(device: &Device, framebuffers: &Vec, render_pass: &vk::RenderPass, pipeline: &vk::Pipeline, + vertex_buffer: &vk::Buffer, command_pool: &vk::CommandPool) -> Result> { @@ -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 +{ + 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; 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); +#[derive(Clone, Debug)] +pub struct Vec3(T, T, T); +#[derive(Clone, Debug)] +#[allow(unused)] +pub struct Vec4(T, T, T, T); + + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct Vertex { + pub position: Vec2, + pub color: Vec3, +} + +impl Vertex { + const fn new(position: Vec2, color: Vec3) -> Self { + Self { position, color } + } + + pub fn binding_description() -> vk::VertexInputBindingDescription { + vk::VertexInputBindingDescription::builder() + .binding(0) + .stride(size_of::>() 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::>() 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 { -- cgit 1.4.1