diff options
| author | Irene Knapp <ireneista@irenes.space> | 2026-08-06 14:26:15 -0700 |
|---|---|---|
| committer | Irene Knapp <ireneista@irenes.space> | 2026-08-06 14:56:40 -0700 |
| commit | 5b5f7bb4b3cac1fa96b2325e7c3d0784ade28ebf (patch) | |
| tree | df536be69c964533bf19c75119a8e406f9daad31 /src | |
| parent | 562693fe2d0afbe9c828fa1acb4473db9752eeed (diff) | |
factor out the render commands into a new scene module
use push constants this concludes the tutorial; there's now a bit of fairly obvious refactoring to do to group objects in a way that reflects when they're needed, which can be followed by building a real scene graph Change-Id: Id3b76d00d08ae915270095bd606e41767b7ec177 Force-Push: yes
Diffstat (limited to 'src')
| -rw-r--r-- | src/graphics_scene.rs | 121 | ||||
| -rw-r--r-- | src/graphics_window_dressing.rs | 135 | ||||
| -rw-r--r-- | src/main.rs | 38 | ||||
| -rw-r--r-- | src/shader_data.rs | 9 |
4 files changed, 184 insertions, 119 deletions
diff --git a/src/graphics_scene.rs b/src/graphics_scene.rs new file mode 100644 index 0000000..771255a --- /dev/null +++ b/src/graphics_scene.rs @@ -0,0 +1,121 @@ +#![deny(unsafe_code)] +use crate::error::*; +use crate::linear_algebra::{ Vec3, Vec4, Transformation }; +use crate::shader_data::VertexPushBlock; + +use std::f32::consts::{ TAU }; +use std::mem::size_of; + +use vulkanalia::Device; +use vulkanalia::vk::{ self, HasBuilder, DeviceV1_0 }; + + +#[allow(unsafe_code)] +pub fn generate_scene_commands(command_buffer: &vk::CommandBuffer, + time: f32, device: &Device, + extent: &vk::Extent2D, + framebuffer: &vk::Framebuffer, + render_pass: &vk::RenderPass, + pipeline_layout: &vk::PipelineLayout, + pipeline: &vk::Pipeline, + vertex_buffer: &vk::Buffer, + index_buffer: &vk::Buffer, + index_count: usize, + descriptor_set: &vk::DescriptorSet) + -> Result<()> +{ + let inheritance_info = vk::CommandBufferInheritanceInfo::builder(); + + let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::empty()) + .inheritance_info(&inheritance_info); + + unsafe { + device.begin_command_buffer(*command_buffer, &command_buffer_begin_info) + }?; + + let render_area = vk::Rect2D::builder() + .offset(vk::Offset2D::default()) + .extent(*extent); + + let color_clear_value = vk::ClearValue { + color: vk::ClearColorValue { + float32: [0.0, 0.0, 0.0, 1.0] + } + }; + let depth_clear_value = vk::ClearValue { + depth_stencil: vk::ClearDepthStencilValue { + depth: 1.0, + stencil: 0, + } + }; + let clear_values = [color_clear_value, depth_clear_value]; + + let begin_pass_info = vk::RenderPassBeginInfo::builder() + .render_pass(*render_pass) + .framebuffer(*framebuffer) + .render_area(render_area) + .clear_values(&clear_values); + + unsafe { + device.cmd_begin_render_pass(*command_buffer, &begin_pass_info, + vk::SubpassContents::INLINE) + }; + + unsafe { + device.cmd_bind_pipeline(*command_buffer, + vk::PipelineBindPoint::GRAPHICS, + *pipeline) + }; + + unsafe { + device.cmd_bind_vertex_buffers(*command_buffer, 0, + &[*vertex_buffer], &[0]) + }; + + unsafe { + device.cmd_bind_index_buffer(*command_buffer, *index_buffer, 0, + vk::IndexType::UINT32) + }; + + unsafe { + device.cmd_bind_descriptor_sets(*command_buffer, + vk::PipelineBindPoint::GRAPHICS, + *pipeline_layout, + 0, + &[*descriptor_set], + &[]) + }; + + let scale = Vec3::new(1.0, 1.0, 1.0); + let model = Transformation { + rotation: Vec4::rotation_quaternion(&Vec3::new(0.0, 1.0, 0.0), time % TAU), + translation: Vec3::new(0.0, 0.0, 0.0), + }; + let push_block = VertexPushBlock::<f32> { scale, model }; + let size = size_of::<VertexPushBlock::<f32>>(); + let push_block_bytes = unsafe { + std::slice::from_raw_parts(&push_block as *const VertexPushBlock<f32> + as *const u8, + size) + }; + + unsafe { + device.cmd_push_constants(*command_buffer, *pipeline_layout, + vk::ShaderStageFlags::VERTEX, + 0, + push_block_bytes) + }; + + unsafe { + device.cmd_draw_indexed(*command_buffer, index_count as u32, + 1, 0, 0, 0) + }; + + unsafe { device.cmd_end_render_pass(*command_buffer) }; + + unsafe { device.end_command_buffer(*command_buffer) }?; + + Ok(()) +} + diff --git a/src/graphics_window_dressing.rs b/src/graphics_window_dressing.rs index 3c9291c..51483cf 100644 --- a/src/graphics_window_dressing.rs +++ b/src/graphics_window_dressing.rs @@ -5,10 +5,11 @@ use crate::graphics_permanent::{ EnableAnisotropy }; use crate::model_loader::load_model; -use crate::shader_data::{ Vertex, UniformBlock }; +use crate::shader_data::{ Vertex, UniformBlock, VertexPushBlock }; use std::collections::BTreeSet; use std::io::Cursor; +use std::mem::size_of; use std::ptr::copy_nonoverlapping; use png::Decoder; @@ -41,22 +42,22 @@ pub struct WindowDressing { depth_image_view: vk::ImageView, depth_format: vk::Format, - render_pass: vk::RenderPass, + pub render_pass: vk::RenderPass, - pipeline: vk::Pipeline, - pipeline_layout: vk::PipelineLayout, + pub pipeline: vk::Pipeline, + pub pipeline_layout: vk::PipelineLayout, - framebuffers: Vec<vk::Framebuffer>, + pub framebuffers: Vec<vk::Framebuffer>, primary_command_pool: vk::CommandPool, transient_command_pool: vk::CommandPool, - vertex_buffer: vk::Buffer, + pub vertex_buffer: vk::Buffer, vertex_buffer_memory: vk::DeviceMemory, - index_buffer: vk::Buffer, + pub index_buffer: vk::Buffer, index_buffer_memory: vk::DeviceMemory, - index_count: usize, + pub index_count: usize, texture_image: vk::Image, texture_image_memory: vk::DeviceMemory, @@ -68,7 +69,7 @@ pub struct WindowDressing { pub uniform_buffer_memory: Vec<vk::DeviceMemory>, descriptor_pool: vk::DescriptorPool, - descriptor_sets: Vec<vk::DescriptorSet>, + pub descriptor_sets: Vec<vk::DescriptorSet>, pub command_buffers: Vec<vk::CommandBuffer>, @@ -179,7 +180,7 @@ impl WindowDressing { swapchain.images.len(), &texture_image_view, &sampler)?; - let command_buffers = init_commands( + let command_buffers = init_command_buffers( device, &swapchain.extent, &framebuffers, &render_pass, &pipeline_layout, &pipeline, &vertex_buffer, &index_buffer, index_count, &descriptor_sets, &primary_command_pool)?; @@ -276,7 +277,7 @@ impl WindowDressing { &self.texture_image_view, &self.sampler)?; // Notice that we reused the command pool. - let command_buffers = init_commands( + let command_buffers = init_command_buffers( device, &swapchain.extent, &framebuffers, &render_pass, &pipeline_layout, &pipeline, &self.vertex_buffer, &self.index_buffer, self.index_count, &descriptor_sets, @@ -696,9 +697,16 @@ fn init_pipeline(device: &Device, .attachments(&blend_attachments) .blend_constants([0.0, 0.0, 0.0, 0.0]); + let vertex_push_constant_range = vk::PushConstantRange::builder() + .stage_flags(vk::ShaderStageFlags::VERTEX) + .offset(0) + .size(size_of::<VertexPushBlock<f32>>() as u32); + let layouts = [*descriptor_set_layout]; + let push_constant_ranges = [vertex_push_constant_range]; let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder() - .set_layouts(&layouts); + .set_layouts(&layouts) + .push_constant_ranges(&push_constant_ranges); let pipeline_layout = unsafe { device.create_pipeline_layout(&pipeline_layout_info, None) @@ -1014,8 +1022,9 @@ fn init_command_pools(device: &Device, indices: &QueueFamilyIndices) -> Result<(vk::CommandPool, vk::CommandPool)> { let command_pool_info = vk::CommandPoolCreateInfo::builder() - .flags(vk::CommandPoolCreateFlags::empty()) - .queue_family_index(indices.graphics); + .flags(vk::CommandPoolCreateFlags::TRANSIENT + | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) + .queue_family_index(indices.graphics); let primary = unsafe { device.create_command_pool(&command_pool_info, None) @@ -1031,17 +1040,17 @@ fn init_command_pools(device: &Device, indices: &QueueFamilyIndices) #[allow(unsafe_code)] -fn init_commands(device: &Device, - extent: &vk::Extent2D, - framebuffers: &Vec<vk::Framebuffer>, - render_pass: &vk::RenderPass, - pipeline_layout: &vk::PipelineLayout, - pipeline: &vk::Pipeline, - vertex_buffer: &vk::Buffer, - index_buffer: &vk::Buffer, - index_count: usize, - descriptor_sets: &Vec<vk::DescriptorSet>, - command_pool: &vk::CommandPool) +fn init_command_buffers(device: &Device, + extent: &vk::Extent2D, + framebuffers: &Vec<vk::Framebuffer>, + render_pass: &vk::RenderPass, + pipeline_layout: &vk::PipelineLayout, + pipeline: &vk::Pipeline, + vertex_buffer: &vk::Buffer, + index_buffer: &vk::Buffer, + index_count: usize, + descriptor_sets: &Vec<vk::DescriptorSet>, + command_pool: &vk::CommandPool) -> Result<Vec<vk::CommandBuffer>> { let command_buffer_allocation_info @@ -1053,82 +1062,6 @@ fn init_commands(device: &Device, device.allocate_command_buffers(&command_buffer_allocation_info) }?; - for (index, framebuffer) in framebuffers.iter().enumerate() { - let command_buffer = command_buffers[index]; - - let inheritance_info = vk::CommandBufferInheritanceInfo::builder(); - - let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::empty()) - .inheritance_info(&inheritance_info); - - unsafe { - device.begin_command_buffer(command_buffer, &command_buffer_begin_info) - }?; - - let render_area = vk::Rect2D::builder() - .offset(vk::Offset2D::default()) - .extent(*extent); - - let color_clear_value = vk::ClearValue { - color: vk::ClearColorValue { - float32: [0.0, 0.0, 0.0, 1.0] - } - }; - let depth_clear_value = vk::ClearValue { - depth_stencil: vk::ClearDepthStencilValue { - depth: 1.0, - stencil: 0, - } - }; - let clear_values = [color_clear_value, depth_clear_value]; - - let begin_pass_info = vk::RenderPassBeginInfo::builder() - .render_pass(*render_pass) - .framebuffer(*framebuffer) - .render_area(render_area) - .clear_values(&clear_values); - - unsafe { - device.cmd_begin_render_pass(command_buffer, &begin_pass_info, - vk::SubpassContents::INLINE) - }; - - unsafe { - device.cmd_bind_pipeline(command_buffer, - vk::PipelineBindPoint::GRAPHICS, - *pipeline) - }; - - unsafe { - device.cmd_bind_vertex_buffers(command_buffer, 0, - &[*vertex_buffer], &[0]) - }; - - unsafe { - device.cmd_bind_index_buffer(command_buffer, *index_buffer, 0, - vk::IndexType::UINT32) - }; - - unsafe { - device.cmd_bind_descriptor_sets(command_buffer, - vk::PipelineBindPoint::GRAPHICS, - *pipeline_layout, - 0, - &[descriptor_sets[index]], - &[]) - }; - - unsafe { - device.cmd_draw_indexed(command_buffer, index_count as u32, - 1, 0, 0, 0) - }; - - unsafe { device.cmd_end_render_pass(command_buffer) }; - - unsafe { device.end_command_buffer(command_buffer) }?; - } - Ok(command_buffers) } diff --git a/src/main.rs b/src/main.rs index 3267d0b..8a42147 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,11 +6,11 @@ use crate::graphics_permanent::{ use crate::graphics_window_dressing::{ WindowDressing, N_SIMULTANEOUS_FRAMES }; -use crate::linear_algebra::{ Vec3, Vec4, Mat4, Transformation }; +use crate::linear_algebra::{ Vec3, Mat4, Transformation }; use crate::shader_data::UniformBlock; use std::cell::RefCell; -use std::f32::consts::{ FRAC_PI_4, TAU }; +use std::f32::consts::FRAC_PI_4; use std::ptr::copy_nonoverlapping; use std::time::Instant; use vulkanalia::Device; @@ -23,6 +23,7 @@ use winit::window::WindowId; mod error; mod graphics_permanent; +mod graphics_scene; mod graphics_window_dressing; mod linear_algebra; mod model_loader; @@ -138,16 +139,28 @@ impl Surreality { concurrency.image_fences[image_index] = *frame_fence; - render_uniforms(device, + let time = self.simulation_start.elapsed().as_secs_f32(); + + let command_buffer = window_dressing.command_buffers[image_index]; + let framebuffer = window_dressing.framebuffers[image_index]; + let descriptor_set = window_dressing.descriptor_sets[image_index]; + + crate::graphics_scene::generate_scene_commands( + &command_buffer, time, device, &window_dressing.swapchain.extent, + &framebuffer, &window_dressing.render_pass, + &window_dressing.pipeline_layout, &window_dressing.pipeline, + &window_dressing.vertex_buffer, &window_dressing.index_buffer, + window_dressing.index_count, &descriptor_set)?; + + render_uniforms(time, device, &window_dressing.uniform_buffer_memory[image_index], - &window_dressing.swapchain.extent, - self.simulation_start)?; + &window_dressing.swapchain.extent)?; let first_semaphores = [*image_available_semaphore]; let second_semaphores = [*rendering_finished_semaphore]; let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; - let command_buffers = [window_dressing.command_buffers[image_index]]; + let command_buffers = [command_buffer]; let submit_info = vk::SubmitInfo::builder() .wait_semaphores(&first_semaphores) .wait_dst_stage_mask(&wait_stages) @@ -282,23 +295,16 @@ fn main() -> std::process::ExitCode { #[allow(unsafe_code)] -fn render_uniforms(device: &Device, device_memory: &vk::DeviceMemory, - extent: &vk::Extent2D, simulation_start: Instant) +fn render_uniforms(time: f32, device: &Device, + device_memory: &vk::DeviceMemory, extent: &vk::Extent2D) -> Result<()> { - let time = simulation_start.elapsed().as_secs_f32(); - - let scale = Vec3::new(1.0, 1.0, 1.0); - let model = Transformation { - rotation: Vec4::rotation_quaternion(&Vec3::new(0.0, 1.0, 0.0), time % TAU), - translation: Vec3::new(0.0, 0.0, 0.0), - }; let view = Transformation::look_at(&Vec3::new(0.0, -3.0, -10.0), &Vec3::new(0.0, -1.0, 0.0), &Vec3::new(0.0, -1.0, 0.0)); let aspect_ratio = extent.width as f32 / extent.height as f32; let projection = Mat4::perspective(FRAC_PI_4, aspect_ratio, 0.1, 100.0); - let block = UniformBlock::<f32> { scale, model, view, projection }; + let block = UniformBlock::<f32> { view, projection }; let size = size_of::<UniformBlock<f32>>() as u64; let host_memory = unsafe { diff --git a/src/shader_data.rs b/src/shader_data.rs index 8197bfc..892260d 100644 --- a/src/shader_data.rs +++ b/src/shader_data.rs @@ -16,12 +16,17 @@ pub struct Vertex<T: Copy> { #[repr(C)] #[derive(Clone, Debug)] pub struct UniformBlock<T: Copy> { - pub scale: Vec3<T>, - pub model: Transformation<T>, pub view: Transformation<T>, pub projection: Mat4<T>, } +#[repr(C)] +#[derive(Clone, Debug)] +pub struct VertexPushBlock<T: Copy> { + pub scale: Vec3<T>, + pub model: Transformation<T>, +} + impl<T: Copy> Vertex<T> { pub const fn new(position: Vec3<T>, color: Vec3<T>, texture_coordinates: Vec2<T>) |