From 5dddb64f6a2fd072f2a26d4400f9a2a84386dc4e Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Mon, 13 Jul 2026 13:25:16 -0700 Subject: it spins!!!! ahem. more formally: provide animated uniforms to the shader, and use them the tutorial just wants us to do three matrices, and to defer all the actual math to a library it recommends. that would certainly be simpler. anyway, we use quaterion+offset for everything but the projection frustum, and do all the math ourselves Change-Id: I399f432bebb1e7ca4d4342efa1d5aaf37b32f427 Force-Push: yes --- src/graphics_window_dressing.rs | 177 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 161 insertions(+), 16 deletions(-) (limited to 'src/graphics_window_dressing.rs') diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs index fb56fb3..38d4188 100644 --- a/src/graphics_window_dressing.rs +++ b/src/graphics_window_dressing.rs @@ -3,10 +3,10 @@ use crate::error::*; use crate::graphics_permanent::{ PermanentGraphicsState, GraphicsStateForReinit, QueueFamilyIndices }; -use crate::linear_algebra::{ Vertex, VERTICES, INDICES }; +use crate::linear_algebra::{ Vertex, VERTICES, INDICES, UniformBlock }; use std::collections::BTreeSet; -use std::ptr; +use std::ptr::copy_nonoverlapping; use vulkanalia::{ Device, Instance }; use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0, KhrSwapchainExtensionDeviceCommands }; @@ -43,6 +43,12 @@ pub struct WindowDressing { index_buffer: vk::Buffer, index_buffer_memory: vk::DeviceMemory, + uniform_buffers: Vec, + pub uniform_buffer_memory: Vec, + + descriptor_pool: vk::DescriptorPool, + descriptor_sets: Vec, + pub command_buffers: Vec, pub concurrency: Concurrency, @@ -58,7 +64,7 @@ pub struct Swapchain { images: Vec, image_views: Vec, format: vk::Format, - extent: vk::Extent2D, + pub extent: vk::Extent2D, } #[derive(Debug)] @@ -95,6 +101,7 @@ impl WindowDressing { let graphics_queue = &permanent.graphics_queue; let physical_device = &for_reinit.physical_device; let indices = &for_reinit.indices; + let descriptor_set_layout = &for_reinit.descriptor_set_layout; let swapchain = init_swapchain( window, instance, surface, &physical_device, device, &indices)?; @@ -102,7 +109,8 @@ impl WindowDressing { let render_pass = init_render_pass(device, &swapchain.format)?; let (pipeline_layout, pipeline) - = init_pipeline(device, &swapchain.extent, &render_pass)?; + = init_pipeline(device, descriptor_set_layout, &swapchain.extent, + &render_pass)?; let framebuffers = init_framebuffers( device, &swapchain.extent, &swapchain.image_views, &render_pass)?; @@ -116,12 +124,21 @@ impl WindowDressing { let (index_buffer, index_buffer_memory) = init_index_buffer(instance, physical_device, device, graphics_queue, &transient_command_pool)?; + let (uniform_buffers, uniform_buffer_memory) + = init_uniform_buffers(instance, physical_device, device, + swapchain.images.len())?; + + let descriptor_pool + = init_descriptor_pool(device, swapchain.images.len())?; + let descriptor_sets + = init_descriptor_sets(device, descriptor_set_layout, + &uniform_buffers, &descriptor_pool, + swapchain.images.len())?; - let command_buffers = init_commands(device, &swapchain.extent, - &framebuffers, &render_pass, - &pipeline, - &vertex_buffer, &index_buffer, - &primary_command_pool)?; + let command_buffers = init_commands( + device, &swapchain.extent, &framebuffers, &render_pass, + &pipeline_layout, &pipeline, &vertex_buffer, &index_buffer, + &descriptor_sets, &primary_command_pool)?; let concurrency = init_concurrency(device, &swapchain.images)?; @@ -135,6 +152,10 @@ impl WindowDressing { vertex_buffer_memory, index_buffer, index_buffer_memory, + uniform_buffers, + uniform_buffer_memory, + descriptor_pool, + descriptor_sets, primary_command_pool, transient_command_pool, command_buffers, @@ -154,6 +175,7 @@ impl WindowDressing { let device = &permanent.device; let physical_device = &for_reinit.physical_device; let indices = &for_reinit.indices; + let descriptor_set_layout = &for_reinit.descriptor_set_layout; unsafe { device.device_wait_idle() }.unwrap(); @@ -165,15 +187,30 @@ impl WindowDressing { let render_pass = init_render_pass(device, &swapchain.format)?; let (pipeline_layout, pipeline) - = init_pipeline(device, &swapchain.extent, &render_pass)?; + = init_pipeline(device, descriptor_set_layout, &swapchain.extent, + &render_pass)?; let framebuffers = init_framebuffers( device, &swapchain.extent, &swapchain.image_views, &render_pass)?; + let (uniform_buffers, uniform_buffer_memory) + = init_uniform_buffers(instance, physical_device, device, + swapchain.images.len())?; + + // Notice that we did NOT reuse the descriptor pool. + let descriptor_pool + = init_descriptor_pool(device, swapchain.images.len())?; + + let descriptor_sets + = init_descriptor_sets(device, descriptor_set_layout, + &uniform_buffers, &descriptor_pool, + swapchain.images.len())?; + // Notice that we reused the command pool. let command_buffers = init_commands( device, &swapchain.extent, &framebuffers, &render_pass, - &pipeline, &self.vertex_buffer, &self.index_buffer, + &pipeline_layout, &pipeline, &self.vertex_buffer, + &self.index_buffer, &descriptor_sets, &self.primary_command_pool)?; self.concurrency.image_fences.resize(swapchain.images.len(), @@ -184,6 +221,10 @@ impl WindowDressing { self.pipeline = pipeline; self.pipeline_layout = pipeline_layout; self.framebuffers = framebuffers; + self.uniform_buffers = uniform_buffers; + self.uniform_buffer_memory = uniform_buffer_memory; + self.descriptor_pool = descriptor_pool; + self.descriptor_sets = descriptor_sets; self.command_buffers = command_buffers; Ok(()) @@ -236,6 +277,20 @@ impl WindowDressing { &self.command_buffers) }; + // While the descriptor pool is also a pool, it has a preallocated size + // which will be different next time. So, we destroy it all the way. + unsafe { device.destroy_descriptor_pool(self.descriptor_pool, None) }; + + // Notice that, unlike the vertex and index buffers, we destroy and + // re-create these on every reinitialization. That's because the number of + // them depends on how many images the swapchain has. + for buffer in &self.uniform_buffers { + unsafe { device.destroy_buffer(*buffer, None) }; + } + for memory in &self.uniform_buffer_memory { + unsafe { device.free_memory(*memory, None) }; + } + unsafe { device.destroy_pipeline(self.pipeline, None) }; unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) }; unsafe { device.destroy_render_pass(self.render_pass, None) }; @@ -402,8 +457,9 @@ fn init_render_pass(device: &Device, format: &vk::Format) #[allow(unsafe_code)] -fn init_pipeline(device: &Device, extent: &vk::Extent2D, - render_pass: &vk::RenderPass) +fn init_pipeline(device: &Device, + descriptor_set_layout: &vk::DescriptorSetLayout, + extent: &vk::Extent2D, render_pass: &vk::RenderPass) -> Result<(vk::PipelineLayout, vk::Pipeline)> { let vertex_binary = include_bytes!( @@ -489,7 +545,9 @@ fn init_pipeline(device: &Device, extent: &vk::Extent2D, .attachments(&blend_attachments) .blend_constants([0.0, 0.0, 0.0, 0.0]); - let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder(); + let layouts = [*descriptor_set_layout]; + let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder() + .set_layouts(&layouts); let pipeline_layout = unsafe { device.create_pipeline_layout(&pipeline_layout_info, None) @@ -571,6 +629,28 @@ fn init_index_buffer(instance: &Instance, vk::BufferUsageFlags::INDEX_BUFFER, INDICES) } +fn init_uniform_buffers(instance: &Instance, + physical_device: &vk::PhysicalDevice, device: &Device, + count: usize) + -> Result<(Vec, Vec)> +{ + let mut buffers = Vec::new(); + let mut all_memory = Vec::new(); + + for _ in 0 .. count { + let (buffer, memory) = allocate_buffer( + instance, physical_device, device, + size_of::>() as u64, + vk::BufferUsageFlags::UNIFORM_BUFFER, + vk::MemoryPropertyFlags::HOST_COHERENT + | vk::MemoryPropertyFlags::HOST_VISIBLE)?; + buffers.push(buffer); + all_memory.push(memory); + } + + Ok((buffers, all_memory)) +} + #[allow(unsafe_code)] fn init_buffer(instance: &Instance, @@ -593,8 +673,7 @@ fn init_buffer(instance: &Instance, }?; unsafe { - ptr::copy_nonoverlapping( - contents.as_ptr(), host_memory.cast(), contents.len()) + copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len()) }; unsafe { device.unmap_memory(staging_memory) }; @@ -615,6 +694,61 @@ fn init_buffer(instance: &Instance, } +#[allow(unsafe_code)] +fn init_descriptor_pool(device: &Device, count: usize) + -> Result +{ + let uniform_block_size = vk::DescriptorPoolSize::builder() + .type_(vk::DescriptorType::UNIFORM_BUFFER) + .descriptor_count(count as u32); + + let sizes = [uniform_block_size]; + let pool_info = vk::DescriptorPoolCreateInfo::builder() + .pool_sizes(&sizes) + .max_sets(count as u32); + let pool = unsafe { device.create_descriptor_pool(&pool_info, None) }?; + + Ok(pool) +} + + +#[allow(unsafe_code)] +fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout, + buffers: &Vec, pool: &vk::DescriptorPool, + count: usize) + -> Result> +{ + let layouts = vec![*layout; count]; + let set_info = vk::DescriptorSetAllocateInfo::builder() + .descriptor_pool(*pool) + .set_layouts(&layouts); + let sets = unsafe { device.allocate_descriptor_sets(&set_info) }?; + + for index in 0 .. count { + let buffer_info = vk::DescriptorBufferInfo::builder() + .buffer(buffers[index]) + .offset(0) + .range(size_of::>() as u64); + + let buffer_info_list = [buffer_info]; + let descriptor_write_info = vk::WriteDescriptorSet::builder() + .dst_set(sets[index]) + .dst_binding(0) + .dst_array_element(0) + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) + .buffer_info(&buffer_info_list); + let write_info_list = [descriptor_write_info]; + let copy_info_list: [vk::CopyDescriptorSet; 0] = []; + + unsafe { + device.update_descriptor_sets(&write_info_list, ©_info_list) + }; + } + + Ok(sets) +} + + #[allow(unsafe_code)] fn init_command_pools(device: &Device, indices: &QueueFamilyIndices) -> Result<(vk::CommandPool, vk::CommandPool)> @@ -641,9 +775,11 @@ fn init_commands(device: &Device, extent: &vk::Extent2D, framebuffers: &Vec, render_pass: &vk::RenderPass, + pipeline_layout: &vk::PipelineLayout, pipeline: &vk::Pipeline, vertex_buffer: &vk::Buffer, index_buffer: &vk::Buffer, + descriptor_sets: &Vec, command_pool: &vk::CommandPool) -> Result> { @@ -707,6 +843,15 @@ fn init_commands(device: &Device, vk::IndexType::UINT16) }; + unsafe { + device.cmd_bind_descriptor_sets(command_buffer, + vk::PipelineBindPoint::GRAPHICS, + *pipeline_layout, + 0, + &[descriptor_sets[index]], + &[]) + }; + unsafe { device.cmd_draw_indexed(command_buffer, INDICES.len() as u32, 1, 0, 0, 0) -- cgit 1.4.1