From 9a8a0fef221c5f5e56da7f0082b8f2ad58af3666 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Fri, 19 Dec 2025 13:28:36 +0100 Subject: [PATCH 1/6] buffer: switch shader from push constant to storage buffer --- generated/graphics/ash/cargo-gpu/mygraphics-shaders/src/lib.rs | 2 +- .../graphics/ash/spirv-builder/mygraphics-shaders/src/lib.rs | 2 +- generated/graphics/wgpu/cargo-gpu/mygraphics-shaders/src/lib.rs | 2 +- .../graphics/wgpu/spirv-builder/mygraphics-shaders/src/lib.rs | 2 +- graphics/mygraphics-shaders/src/lib.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/generated/graphics/ash/cargo-gpu/mygraphics-shaders/src/lib.rs b/generated/graphics/ash/cargo-gpu/mygraphics-shaders/src/lib.rs index acf55ee..d2b343a 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics-shaders/src/lib.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics-shaders/src/lib.rs @@ -23,7 +23,7 @@ pub fn main_fs(vtx_color: Vec3, output: &mut Vec4) { #[spirv(vertex)] pub fn main_vs( #[spirv(vertex_index)] vert_id: i32, - #[spirv(push_constant)] constants: &ShaderConstants, + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] constants: &ShaderConstants, #[spirv(position)] vtx_pos: &mut Vec4, vtx_color: &mut Vec3, ) { diff --git a/generated/graphics/ash/spirv-builder/mygraphics-shaders/src/lib.rs b/generated/graphics/ash/spirv-builder/mygraphics-shaders/src/lib.rs index acf55ee..d2b343a 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics-shaders/src/lib.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics-shaders/src/lib.rs @@ -23,7 +23,7 @@ pub fn main_fs(vtx_color: Vec3, output: &mut Vec4) { #[spirv(vertex)] pub fn main_vs( #[spirv(vertex_index)] vert_id: i32, - #[spirv(push_constant)] constants: &ShaderConstants, + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] constants: &ShaderConstants, #[spirv(position)] vtx_pos: &mut Vec4, vtx_color: &mut Vec3, ) { diff --git a/generated/graphics/wgpu/cargo-gpu/mygraphics-shaders/src/lib.rs b/generated/graphics/wgpu/cargo-gpu/mygraphics-shaders/src/lib.rs index acf55ee..d2b343a 100644 --- a/generated/graphics/wgpu/cargo-gpu/mygraphics-shaders/src/lib.rs +++ b/generated/graphics/wgpu/cargo-gpu/mygraphics-shaders/src/lib.rs @@ -23,7 +23,7 @@ pub fn main_fs(vtx_color: Vec3, output: &mut Vec4) { #[spirv(vertex)] pub fn main_vs( #[spirv(vertex_index)] vert_id: i32, - #[spirv(push_constant)] constants: &ShaderConstants, + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] constants: &ShaderConstants, #[spirv(position)] vtx_pos: &mut Vec4, vtx_color: &mut Vec3, ) { diff --git a/generated/graphics/wgpu/spirv-builder/mygraphics-shaders/src/lib.rs b/generated/graphics/wgpu/spirv-builder/mygraphics-shaders/src/lib.rs index acf55ee..d2b343a 100644 --- a/generated/graphics/wgpu/spirv-builder/mygraphics-shaders/src/lib.rs +++ b/generated/graphics/wgpu/spirv-builder/mygraphics-shaders/src/lib.rs @@ -23,7 +23,7 @@ pub fn main_fs(vtx_color: Vec3, output: &mut Vec4) { #[spirv(vertex)] pub fn main_vs( #[spirv(vertex_index)] vert_id: i32, - #[spirv(push_constant)] constants: &ShaderConstants, + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] constants: &ShaderConstants, #[spirv(position)] vtx_pos: &mut Vec4, vtx_color: &mut Vec3, ) { diff --git a/graphics/mygraphics-shaders/src/lib.rs b/graphics/mygraphics-shaders/src/lib.rs index acf55ee..d2b343a 100644 --- a/graphics/mygraphics-shaders/src/lib.rs +++ b/graphics/mygraphics-shaders/src/lib.rs @@ -23,7 +23,7 @@ pub fn main_fs(vtx_color: Vec3, output: &mut Vec4) { #[spirv(vertex)] pub fn main_vs( #[spirv(vertex_index)] vert_id: i32, - #[spirv(push_constant)] constants: &ShaderConstants, + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] constants: &ShaderConstants, #[spirv(position)] vtx_pos: &mut Vec4, vtx_color: &mut Vec3, ) { From 0d925d76167c3f20692f507d234da822e236e872 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Fri, 19 Dec 2025 13:34:49 +0100 Subject: [PATCH 2/6] buffer: adjust wgpu to use buffers --- .../src/wgpu_renderer/render_pipeline.rs | 18 ++--- .../mygraphics/src/wgpu_renderer/renderer.rs | 71 +++++++++++++++++-- .../src/wgpu_renderer/render_pipeline.rs | 18 ++--- .../mygraphics/src/wgpu_renderer/renderer.rs | 71 +++++++++++++++++-- .../src/wgpu_renderer/render_pipeline.rs | 18 ++--- .../mygraphics/src/wgpu_renderer/renderer.rs | 71 +++++++++++++++++-- 6 files changed, 231 insertions(+), 36 deletions(-) diff --git a/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/render_pipeline.rs b/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/render_pipeline.rs index ee2097e..ba1f793 100644 --- a/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/render_pipeline.rs +++ b/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/render_pipeline.rs @@ -1,3 +1,4 @@ +use crate::wgpu_renderer::renderer::{GlobalBindGroup, GlobalBindGroupLayout}; use mygraphics_shaders::ShaderConstants; use wgpu::{ ColorTargetState, ColorWrites, Device, FragmentState, FrontFace, MultisampleState, @@ -6,17 +7,22 @@ use wgpu::{ include_spirv, }; +#[derive(Debug, Clone)] pub struct MyRenderPipeline { pipeline: RenderPipeline, } impl MyRenderPipeline { - pub fn new(device: &Device, out_format: TextureFormat) -> anyhow::Result { + pub fn new( + device: &Device, + global_bind_group_layout: &GlobalBindGroupLayout, + out_format: TextureFormat, + ) -> anyhow::Result { let module = device.create_shader_module(include_spirv!(env!("SHADER_SPV_PATH"))); let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: Some("MyRenderPipeline layout"), - bind_group_layouts: &[], + bind_group_layouts: &[&global_bind_group_layout.0], push_constant_ranges: &[PushConstantRange { stages: ShaderStages::VERTEX_FRAGMENT, range: 0..size_of::() as u32, @@ -60,13 +66,9 @@ impl MyRenderPipeline { }) } - pub fn draw(&self, rpass: &mut RenderPass<'_>, shader_constants: &ShaderConstants) { + pub fn draw(&self, rpass: &mut RenderPass<'_>, global_bind_group: &GlobalBindGroup) { rpass.set_pipeline(&self.pipeline); - rpass.set_push_constants( - ShaderStages::VERTEX_FRAGMENT, - 0, - bytemuck::bytes_of(shader_constants), - ); + rpass.set_bind_group(0, &global_bind_group.0, &[]); rpass.draw(0..3, 0..1); } } diff --git a/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/renderer.rs b/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/renderer.rs index c5213df..2b4349e 100644 --- a/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/renderer.rs +++ b/generated/graphics/wgpu/cargo-gpu/mygraphics/src/wgpu_renderer/renderer.rs @@ -1,27 +1,38 @@ use crate::wgpu_renderer::render_pipeline::MyRenderPipeline; use mygraphics_shaders::ShaderConstants; +use wgpu::util::{BufferInitDescriptor, DeviceExt}; use wgpu::wgt::CommandEncoderDescriptor; use wgpu::{ - Color, Device, LoadOp, Operations, Queue, RenderPassColorAttachment, RenderPassDescriptor, - StoreOp, TextureFormat, TextureView, + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, + BindGroupLayoutEntry, BindingResource, BindingType, Buffer, BufferBinding, BufferBindingType, + BufferUsages, Color, Device, LoadOp, Operations, Queue, RenderPassColorAttachment, + RenderPassDescriptor, ShaderStages, StoreOp, TextureFormat, TextureView, }; pub struct MyRenderer { pub device: Device, pub queue: Queue, + global_bind_group_layout: GlobalBindGroupLayout, pipeline: MyRenderPipeline, } impl MyRenderer { pub fn new(device: Device, queue: Queue, out_format: TextureFormat) -> anyhow::Result { + let global_bind_group_layout = GlobalBindGroupLayout::new(&device); + let pipeline = MyRenderPipeline::new(&device, &global_bind_group_layout, out_format)?; Ok(Self { - pipeline: MyRenderPipeline::new(&device, out_format)?, + global_bind_group_layout, + pipeline, device, queue, }) } pub fn render(&self, shader_constants: &ShaderConstants, output: TextureView) { + let global_bind_group = self + .global_bind_group_layout + .create(&self.device, shader_constants); + let mut cmd = self .device .create_command_encoder(&CommandEncoderDescriptor { @@ -43,9 +54,61 @@ impl MyRenderer { timestamp_writes: None, occlusion_query_set: None, }); - self.pipeline.draw(&mut rpass, shader_constants); + self.pipeline.draw(&mut rpass, &global_bind_group); drop(rpass); self.queue.submit(std::iter::once(cmd.finish())); } } + +#[derive(Debug, Clone)] +pub struct GlobalBindGroupLayout(pub BindGroupLayout); + +impl GlobalBindGroupLayout { + pub fn new(device: &Device) -> Self { + Self(device.create_bind_group_layout(&BindGroupLayoutDescriptor { + label: Some("GlobalBindGroupLayout"), + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX_FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + })) + } + + pub fn create(&self, device: &Device, shader_constants: &ShaderConstants) -> GlobalBindGroup { + let shader_constants = device.create_buffer_init(&BufferInitDescriptor { + label: Some("ShaderConstants"), + contents: bytemuck::bytes_of(shader_constants), + usage: BufferUsages::STORAGE, + }); + self.create_from_buffer(device, &shader_constants) + } + + pub fn create_from_buffer( + &self, + device: &Device, + shader_constants: &Buffer, + ) -> GlobalBindGroup { + GlobalBindGroup(device.create_bind_group(&BindGroupDescriptor { + label: Some("GlobalBindGroup"), + layout: &self.0, + entries: &[BindGroupEntry { + binding: 0, + resource: BindingResource::Buffer(BufferBinding { + buffer: shader_constants, + offset: 0, + size: None, + }), + }], + })) + } +} + +#[derive(Debug, Clone)] +pub struct GlobalBindGroup(pub BindGroup); diff --git a/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/render_pipeline.rs b/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/render_pipeline.rs index ee2097e..ba1f793 100644 --- a/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/render_pipeline.rs +++ b/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/render_pipeline.rs @@ -1,3 +1,4 @@ +use crate::wgpu_renderer::renderer::{GlobalBindGroup, GlobalBindGroupLayout}; use mygraphics_shaders::ShaderConstants; use wgpu::{ ColorTargetState, ColorWrites, Device, FragmentState, FrontFace, MultisampleState, @@ -6,17 +7,22 @@ use wgpu::{ include_spirv, }; +#[derive(Debug, Clone)] pub struct MyRenderPipeline { pipeline: RenderPipeline, } impl MyRenderPipeline { - pub fn new(device: &Device, out_format: TextureFormat) -> anyhow::Result { + pub fn new( + device: &Device, + global_bind_group_layout: &GlobalBindGroupLayout, + out_format: TextureFormat, + ) -> anyhow::Result { let module = device.create_shader_module(include_spirv!(env!("SHADER_SPV_PATH"))); let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: Some("MyRenderPipeline layout"), - bind_group_layouts: &[], + bind_group_layouts: &[&global_bind_group_layout.0], push_constant_ranges: &[PushConstantRange { stages: ShaderStages::VERTEX_FRAGMENT, range: 0..size_of::() as u32, @@ -60,13 +66,9 @@ impl MyRenderPipeline { }) } - pub fn draw(&self, rpass: &mut RenderPass<'_>, shader_constants: &ShaderConstants) { + pub fn draw(&self, rpass: &mut RenderPass<'_>, global_bind_group: &GlobalBindGroup) { rpass.set_pipeline(&self.pipeline); - rpass.set_push_constants( - ShaderStages::VERTEX_FRAGMENT, - 0, - bytemuck::bytes_of(shader_constants), - ); + rpass.set_bind_group(0, &global_bind_group.0, &[]); rpass.draw(0..3, 0..1); } } diff --git a/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/renderer.rs b/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/renderer.rs index c5213df..2b4349e 100644 --- a/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/renderer.rs +++ b/generated/graphics/wgpu/spirv-builder/mygraphics/src/wgpu_renderer/renderer.rs @@ -1,27 +1,38 @@ use crate::wgpu_renderer::render_pipeline::MyRenderPipeline; use mygraphics_shaders::ShaderConstants; +use wgpu::util::{BufferInitDescriptor, DeviceExt}; use wgpu::wgt::CommandEncoderDescriptor; use wgpu::{ - Color, Device, LoadOp, Operations, Queue, RenderPassColorAttachment, RenderPassDescriptor, - StoreOp, TextureFormat, TextureView, + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, + BindGroupLayoutEntry, BindingResource, BindingType, Buffer, BufferBinding, BufferBindingType, + BufferUsages, Color, Device, LoadOp, Operations, Queue, RenderPassColorAttachment, + RenderPassDescriptor, ShaderStages, StoreOp, TextureFormat, TextureView, }; pub struct MyRenderer { pub device: Device, pub queue: Queue, + global_bind_group_layout: GlobalBindGroupLayout, pipeline: MyRenderPipeline, } impl MyRenderer { pub fn new(device: Device, queue: Queue, out_format: TextureFormat) -> anyhow::Result { + let global_bind_group_layout = GlobalBindGroupLayout::new(&device); + let pipeline = MyRenderPipeline::new(&device, &global_bind_group_layout, out_format)?; Ok(Self { - pipeline: MyRenderPipeline::new(&device, out_format)?, + global_bind_group_layout, + pipeline, device, queue, }) } pub fn render(&self, shader_constants: &ShaderConstants, output: TextureView) { + let global_bind_group = self + .global_bind_group_layout + .create(&self.device, shader_constants); + let mut cmd = self .device .create_command_encoder(&CommandEncoderDescriptor { @@ -43,9 +54,61 @@ impl MyRenderer { timestamp_writes: None, occlusion_query_set: None, }); - self.pipeline.draw(&mut rpass, shader_constants); + self.pipeline.draw(&mut rpass, &global_bind_group); drop(rpass); self.queue.submit(std::iter::once(cmd.finish())); } } + +#[derive(Debug, Clone)] +pub struct GlobalBindGroupLayout(pub BindGroupLayout); + +impl GlobalBindGroupLayout { + pub fn new(device: &Device) -> Self { + Self(device.create_bind_group_layout(&BindGroupLayoutDescriptor { + label: Some("GlobalBindGroupLayout"), + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX_FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + })) + } + + pub fn create(&self, device: &Device, shader_constants: &ShaderConstants) -> GlobalBindGroup { + let shader_constants = device.create_buffer_init(&BufferInitDescriptor { + label: Some("ShaderConstants"), + contents: bytemuck::bytes_of(shader_constants), + usage: BufferUsages::STORAGE, + }); + self.create_from_buffer(device, &shader_constants) + } + + pub fn create_from_buffer( + &self, + device: &Device, + shader_constants: &Buffer, + ) -> GlobalBindGroup { + GlobalBindGroup(device.create_bind_group(&BindGroupDescriptor { + label: Some("GlobalBindGroup"), + layout: &self.0, + entries: &[BindGroupEntry { + binding: 0, + resource: BindingResource::Buffer(BufferBinding { + buffer: shader_constants, + offset: 0, + size: None, + }), + }], + })) + } +} + +#[derive(Debug, Clone)] +pub struct GlobalBindGroup(pub BindGroup); diff --git a/graphics/mygraphics/src/wgpu_renderer/render_pipeline.rs b/graphics/mygraphics/src/wgpu_renderer/render_pipeline.rs index ee2097e..ba1f793 100644 --- a/graphics/mygraphics/src/wgpu_renderer/render_pipeline.rs +++ b/graphics/mygraphics/src/wgpu_renderer/render_pipeline.rs @@ -1,3 +1,4 @@ +use crate::wgpu_renderer::renderer::{GlobalBindGroup, GlobalBindGroupLayout}; use mygraphics_shaders::ShaderConstants; use wgpu::{ ColorTargetState, ColorWrites, Device, FragmentState, FrontFace, MultisampleState, @@ -6,17 +7,22 @@ use wgpu::{ include_spirv, }; +#[derive(Debug, Clone)] pub struct MyRenderPipeline { pipeline: RenderPipeline, } impl MyRenderPipeline { - pub fn new(device: &Device, out_format: TextureFormat) -> anyhow::Result { + pub fn new( + device: &Device, + global_bind_group_layout: &GlobalBindGroupLayout, + out_format: TextureFormat, + ) -> anyhow::Result { let module = device.create_shader_module(include_spirv!(env!("SHADER_SPV_PATH"))); let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: Some("MyRenderPipeline layout"), - bind_group_layouts: &[], + bind_group_layouts: &[&global_bind_group_layout.0], push_constant_ranges: &[PushConstantRange { stages: ShaderStages::VERTEX_FRAGMENT, range: 0..size_of::() as u32, @@ -60,13 +66,9 @@ impl MyRenderPipeline { }) } - pub fn draw(&self, rpass: &mut RenderPass<'_>, shader_constants: &ShaderConstants) { + pub fn draw(&self, rpass: &mut RenderPass<'_>, global_bind_group: &GlobalBindGroup) { rpass.set_pipeline(&self.pipeline); - rpass.set_push_constants( - ShaderStages::VERTEX_FRAGMENT, - 0, - bytemuck::bytes_of(shader_constants), - ); + rpass.set_bind_group(0, &global_bind_group.0, &[]); rpass.draw(0..3, 0..1); } } diff --git a/graphics/mygraphics/src/wgpu_renderer/renderer.rs b/graphics/mygraphics/src/wgpu_renderer/renderer.rs index c5213df..2b4349e 100644 --- a/graphics/mygraphics/src/wgpu_renderer/renderer.rs +++ b/graphics/mygraphics/src/wgpu_renderer/renderer.rs @@ -1,27 +1,38 @@ use crate::wgpu_renderer::render_pipeline::MyRenderPipeline; use mygraphics_shaders::ShaderConstants; +use wgpu::util::{BufferInitDescriptor, DeviceExt}; use wgpu::wgt::CommandEncoderDescriptor; use wgpu::{ - Color, Device, LoadOp, Operations, Queue, RenderPassColorAttachment, RenderPassDescriptor, - StoreOp, TextureFormat, TextureView, + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, + BindGroupLayoutEntry, BindingResource, BindingType, Buffer, BufferBinding, BufferBindingType, + BufferUsages, Color, Device, LoadOp, Operations, Queue, RenderPassColorAttachment, + RenderPassDescriptor, ShaderStages, StoreOp, TextureFormat, TextureView, }; pub struct MyRenderer { pub device: Device, pub queue: Queue, + global_bind_group_layout: GlobalBindGroupLayout, pipeline: MyRenderPipeline, } impl MyRenderer { pub fn new(device: Device, queue: Queue, out_format: TextureFormat) -> anyhow::Result { + let global_bind_group_layout = GlobalBindGroupLayout::new(&device); + let pipeline = MyRenderPipeline::new(&device, &global_bind_group_layout, out_format)?; Ok(Self { - pipeline: MyRenderPipeline::new(&device, out_format)?, + global_bind_group_layout, + pipeline, device, queue, }) } pub fn render(&self, shader_constants: &ShaderConstants, output: TextureView) { + let global_bind_group = self + .global_bind_group_layout + .create(&self.device, shader_constants); + let mut cmd = self .device .create_command_encoder(&CommandEncoderDescriptor { @@ -43,9 +54,61 @@ impl MyRenderer { timestamp_writes: None, occlusion_query_set: None, }); - self.pipeline.draw(&mut rpass, shader_constants); + self.pipeline.draw(&mut rpass, &global_bind_group); drop(rpass); self.queue.submit(std::iter::once(cmd.finish())); } } + +#[derive(Debug, Clone)] +pub struct GlobalBindGroupLayout(pub BindGroupLayout); + +impl GlobalBindGroupLayout { + pub fn new(device: &Device) -> Self { + Self(device.create_bind_group_layout(&BindGroupLayoutDescriptor { + label: Some("GlobalBindGroupLayout"), + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::VERTEX_FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + })) + } + + pub fn create(&self, device: &Device, shader_constants: &ShaderConstants) -> GlobalBindGroup { + let shader_constants = device.create_buffer_init(&BufferInitDescriptor { + label: Some("ShaderConstants"), + contents: bytemuck::bytes_of(shader_constants), + usage: BufferUsages::STORAGE, + }); + self.create_from_buffer(device, &shader_constants) + } + + pub fn create_from_buffer( + &self, + device: &Device, + shader_constants: &Buffer, + ) -> GlobalBindGroup { + GlobalBindGroup(device.create_bind_group(&BindGroupDescriptor { + label: Some("GlobalBindGroup"), + layout: &self.0, + entries: &[BindGroupEntry { + binding: 0, + resource: BindingResource::Buffer(BufferBinding { + buffer: shader_constants, + offset: 0, + size: None, + }), + }], + })) + } +} + +#[derive(Debug, Clone)] +pub struct GlobalBindGroup(pub BindGroup); From f7884b2269a27bb5b036f39978a92c75bbbda415 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Sun, 21 Dec 2025 00:06:59 +0100 Subject: [PATCH 3/6] ash: make ash abort on validation error --- .../ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs | 8 +++++++- .../spirv-builder/mygraphics/src/ash_renderer/device.rs | 8 +++++++- graphics/mygraphics/src/ash_renderer/device.rs | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs index 65066fa..480d04f 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs @@ -178,6 +178,12 @@ unsafe extern "system" fn vulkan_debug_callback( .map_or(Cow::Borrowed(""), CStr::to_string_lossy); println!("{message_severity:?}: [{message_id_name}] : {message}"); - vk::FALSE + if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING) + | message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR) + { + vk::TRUE + } else { + vk::FALSE + } } } diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs index 65066fa..480d04f 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs @@ -178,6 +178,12 @@ unsafe extern "system" fn vulkan_debug_callback( .map_or(Cow::Borrowed(""), CStr::to_string_lossy); println!("{message_severity:?}: [{message_id_name}] : {message}"); - vk::FALSE + if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING) + | message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR) + { + vk::TRUE + } else { + vk::FALSE + } } } diff --git a/graphics/mygraphics/src/ash_renderer/device.rs b/graphics/mygraphics/src/ash_renderer/device.rs index 65066fa..480d04f 100644 --- a/graphics/mygraphics/src/ash_renderer/device.rs +++ b/graphics/mygraphics/src/ash_renderer/device.rs @@ -178,6 +178,12 @@ unsafe extern "system" fn vulkan_debug_callback( .map_or(Cow::Borrowed(""), CStr::to_string_lossy); println!("{message_severity:?}: [{message_id_name}] : {message}"); - vk::FALSE + if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING) + | message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR) + { + vk::TRUE + } else { + vk::FALSE + } } } From 292cd5014c5c49751f001d0d7f611530aa96db92 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Sun, 21 Dec 2025 00:10:50 +0100 Subject: [PATCH 4/6] ash: split `mod graphics` into `renderer` and `render_pipeline`, like wgpu --- .../mygraphics/src/ash_renderer/mod.rs | 6 +- .../{graphics.rs => render_pipeline.rs} | 103 +---------------- .../mygraphics/src/ash_renderer/renderer.rs | 106 ++++++++++++++++++ .../mygraphics/src/ash_renderer/mod.rs | 6 +- .../{graphics.rs => render_pipeline.rs} | 103 +---------------- .../mygraphics/src/ash_renderer/renderer.rs | 106 ++++++++++++++++++ graphics/mygraphics/src/ash_renderer/mod.rs | 6 +- .../{graphics.rs => render_pipeline.rs} | 103 +---------------- .../mygraphics/src/ash_renderer/renderer.rs | 106 ++++++++++++++++++ 9 files changed, 333 insertions(+), 312 deletions(-) rename generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/{graphics.rs => render_pipeline.rs} (65%) create mode 100644 generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs rename generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/{graphics.rs => render_pipeline.rs} (65%) create mode 100644 generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs rename graphics/mygraphics/src/ash_renderer/{graphics.rs => render_pipeline.rs} (65%) create mode 100644 graphics/mygraphics/src/ash_renderer/renderer.rs diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs index f72f697..f9c6f85 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs @@ -1,5 +1,6 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::graphics::{MyRenderPipelineManager, MyRenderer}; +use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; +use crate::ash_renderer::renderer::MyRenderer; use crate::ash_renderer::swapchain::MySwapchainManager; use crate::util::enable_debug_layer; use ash::util::read_spv; @@ -12,7 +13,8 @@ use winit::{ }; pub mod device; -pub mod graphics; +pub mod render_pipeline; +pub mod renderer; pub mod single_command_buffer; pub mod swapchain; diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/graphics.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/render_pipeline.rs similarity index 65% rename from generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/graphics.rs rename to generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/render_pipeline.rs index b860caf..4121ecd 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/graphics.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/render_pipeline.rs @@ -1,13 +1,11 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; -use crate::ash_renderer::swapchain::DrawFrame; use anyhow::Context; use ash::vk; use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// Manages the creation and recreation of [`MyRenderPipeline`], whenever new shader code ([`Self::set_shader_code`]) -/// is submitted or the spec constant is changed ([`Self::set_sky_fs_sun_intensity_factor`]) +/// is submitted pub struct MyRenderPipelineManager { pub device: Arc, color_out_format: vk::Format, @@ -239,102 +237,3 @@ impl Drop for MyRenderPipelineManager { } } } - -/// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. -pub struct MyRenderer { - pub device: Arc, - pub pipeline: MyRenderPipelineManager, - pub command: SingleCommandBuffer, -} - -impl MyRenderer { - pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { - Ok(Self { - command: SingleCommandBuffer::new(pipeline.device.clone())?, - device: pipeline.device.clone(), - pipeline, - }) - } - - pub fn render_frame( - &mut self, - frame: DrawFrame, - push_constants: ShaderConstants, - ) -> anyhow::Result<()> { - unsafe { - let device = &self.device; - let pipeline = self.pipeline.get_pipeline()?; - let cmd = self.command.cmd; - - device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; - - { - device.begin_command_buffer( - cmd, - &vk::CommandBufferBeginInfo::default() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), - )?; - device.cmd_pipeline_barrier2( - cmd, - &vk::DependencyInfo::default().image_memory_barriers(&[ - vk::ImageMemoryBarrier2::default() - .image(frame.image) - .src_access_mask(vk::AccessFlags2::NONE) - .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) - .old_layout(vk::ImageLayout::UNDEFINED) - .dst_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) - .dst_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) - .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1), - ), - ]), - ); - pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; - device.cmd_pipeline_barrier2( - cmd, - &vk::DependencyInfo::default().image_memory_barriers(&[ - vk::ImageMemoryBarrier2::default() - .image(frame.image) - .src_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) - .src_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) - .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .dst_access_mask(vk::AccessFlags2::NONE) - .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) - .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1), - ), - ]), - ); - device.end_command_buffer(cmd)?; - } - - device.queue_submit2( - device.main_queue, - &[vk::SubmitInfo2::default() - .wait_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() - .semaphore(frame.acquire_semaphore) - .stage_mask(vk::PipelineStageFlags2::TOP_OF_PIPE)]) - .command_buffer_infos(&[ - vk::CommandBufferSubmitInfo::default().command_buffer(cmd) - ]) - .signal_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() - .semaphore(frame.draw_finished_semaphore) - .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], - frame.draw_finished_fence, - )?; - Ok(()) - } - } -} diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs new file mode 100644 index 0000000..6104fd1 --- /dev/null +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs @@ -0,0 +1,106 @@ +use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; +use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; +use crate::ash_renderer::swapchain::DrawFrame; +use ash::vk; +use mygraphics_shaders::ShaderConstants; +use std::sync::Arc; + +/// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. +pub struct MyRenderer { + pub device: Arc, + pub pipeline: MyRenderPipelineManager, + pub command: SingleCommandBuffer, +} + +impl MyRenderer { + pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { + Ok(Self { + command: SingleCommandBuffer::new(pipeline.device.clone())?, + device: pipeline.device.clone(), + pipeline, + }) + } + + pub fn render_frame( + &mut self, + frame: DrawFrame, + push_constants: ShaderConstants, + ) -> anyhow::Result<()> { + unsafe { + let device = &self.device; + let pipeline = self.pipeline.get_pipeline()?; + let cmd = self.command.cmd; + + device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; + + { + device.begin_command_buffer( + cmd, + &vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), + )?; + device.cmd_pipeline_barrier2( + cmd, + &vk::DependencyInfo::default().image_memory_barriers(&[ + vk::ImageMemoryBarrier2::default() + .image(frame.image) + .src_access_mask(vk::AccessFlags2::NONE) + .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) + .old_layout(vk::ImageLayout::UNDEFINED) + .dst_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .dst_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .subresource_range( + vk::ImageSubresourceRange::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1), + ), + ]), + ); + pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; + device.cmd_pipeline_barrier2( + cmd, + &vk::DependencyInfo::default().image_memory_barriers(&[ + vk::ImageMemoryBarrier2::default() + .image(frame.image) + .src_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .src_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .dst_access_mask(vk::AccessFlags2::NONE) + .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) + .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) + .subresource_range( + vk::ImageSubresourceRange::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1), + ), + ]), + ); + device.end_command_buffer(cmd)?; + } + + device.queue_submit2( + device.main_queue, + &[vk::SubmitInfo2::default() + .wait_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() + .semaphore(frame.acquire_semaphore) + .stage_mask(vk::PipelineStageFlags2::TOP_OF_PIPE)]) + .command_buffer_infos(&[ + vk::CommandBufferSubmitInfo::default().command_buffer(cmd) + ]) + .signal_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() + .semaphore(frame.draw_finished_semaphore) + .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], + frame.draw_finished_fence, + )?; + Ok(()) + } + } +} diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs index f72f697..f9c6f85 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs @@ -1,5 +1,6 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::graphics::{MyRenderPipelineManager, MyRenderer}; +use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; +use crate::ash_renderer::renderer::MyRenderer; use crate::ash_renderer::swapchain::MySwapchainManager; use crate::util::enable_debug_layer; use ash::util::read_spv; @@ -12,7 +13,8 @@ use winit::{ }; pub mod device; -pub mod graphics; +pub mod render_pipeline; +pub mod renderer; pub mod single_command_buffer; pub mod swapchain; diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/graphics.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/render_pipeline.rs similarity index 65% rename from generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/graphics.rs rename to generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/render_pipeline.rs index b860caf..4121ecd 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/graphics.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/render_pipeline.rs @@ -1,13 +1,11 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; -use crate::ash_renderer::swapchain::DrawFrame; use anyhow::Context; use ash::vk; use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// Manages the creation and recreation of [`MyRenderPipeline`], whenever new shader code ([`Self::set_shader_code`]) -/// is submitted or the spec constant is changed ([`Self::set_sky_fs_sun_intensity_factor`]) +/// is submitted pub struct MyRenderPipelineManager { pub device: Arc, color_out_format: vk::Format, @@ -239,102 +237,3 @@ impl Drop for MyRenderPipelineManager { } } } - -/// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. -pub struct MyRenderer { - pub device: Arc, - pub pipeline: MyRenderPipelineManager, - pub command: SingleCommandBuffer, -} - -impl MyRenderer { - pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { - Ok(Self { - command: SingleCommandBuffer::new(pipeline.device.clone())?, - device: pipeline.device.clone(), - pipeline, - }) - } - - pub fn render_frame( - &mut self, - frame: DrawFrame, - push_constants: ShaderConstants, - ) -> anyhow::Result<()> { - unsafe { - let device = &self.device; - let pipeline = self.pipeline.get_pipeline()?; - let cmd = self.command.cmd; - - device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; - - { - device.begin_command_buffer( - cmd, - &vk::CommandBufferBeginInfo::default() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), - )?; - device.cmd_pipeline_barrier2( - cmd, - &vk::DependencyInfo::default().image_memory_barriers(&[ - vk::ImageMemoryBarrier2::default() - .image(frame.image) - .src_access_mask(vk::AccessFlags2::NONE) - .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) - .old_layout(vk::ImageLayout::UNDEFINED) - .dst_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) - .dst_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) - .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1), - ), - ]), - ); - pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; - device.cmd_pipeline_barrier2( - cmd, - &vk::DependencyInfo::default().image_memory_barriers(&[ - vk::ImageMemoryBarrier2::default() - .image(frame.image) - .src_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) - .src_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) - .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .dst_access_mask(vk::AccessFlags2::NONE) - .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) - .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1), - ), - ]), - ); - device.end_command_buffer(cmd)?; - } - - device.queue_submit2( - device.main_queue, - &[vk::SubmitInfo2::default() - .wait_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() - .semaphore(frame.acquire_semaphore) - .stage_mask(vk::PipelineStageFlags2::TOP_OF_PIPE)]) - .command_buffer_infos(&[ - vk::CommandBufferSubmitInfo::default().command_buffer(cmd) - ]) - .signal_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() - .semaphore(frame.draw_finished_semaphore) - .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], - frame.draw_finished_fence, - )?; - Ok(()) - } - } -} diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs new file mode 100644 index 0000000..6104fd1 --- /dev/null +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs @@ -0,0 +1,106 @@ +use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; +use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; +use crate::ash_renderer::swapchain::DrawFrame; +use ash::vk; +use mygraphics_shaders::ShaderConstants; +use std::sync::Arc; + +/// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. +pub struct MyRenderer { + pub device: Arc, + pub pipeline: MyRenderPipelineManager, + pub command: SingleCommandBuffer, +} + +impl MyRenderer { + pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { + Ok(Self { + command: SingleCommandBuffer::new(pipeline.device.clone())?, + device: pipeline.device.clone(), + pipeline, + }) + } + + pub fn render_frame( + &mut self, + frame: DrawFrame, + push_constants: ShaderConstants, + ) -> anyhow::Result<()> { + unsafe { + let device = &self.device; + let pipeline = self.pipeline.get_pipeline()?; + let cmd = self.command.cmd; + + device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; + + { + device.begin_command_buffer( + cmd, + &vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), + )?; + device.cmd_pipeline_barrier2( + cmd, + &vk::DependencyInfo::default().image_memory_barriers(&[ + vk::ImageMemoryBarrier2::default() + .image(frame.image) + .src_access_mask(vk::AccessFlags2::NONE) + .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) + .old_layout(vk::ImageLayout::UNDEFINED) + .dst_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .dst_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .subresource_range( + vk::ImageSubresourceRange::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1), + ), + ]), + ); + pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; + device.cmd_pipeline_barrier2( + cmd, + &vk::DependencyInfo::default().image_memory_barriers(&[ + vk::ImageMemoryBarrier2::default() + .image(frame.image) + .src_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .src_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .dst_access_mask(vk::AccessFlags2::NONE) + .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) + .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) + .subresource_range( + vk::ImageSubresourceRange::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1), + ), + ]), + ); + device.end_command_buffer(cmd)?; + } + + device.queue_submit2( + device.main_queue, + &[vk::SubmitInfo2::default() + .wait_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() + .semaphore(frame.acquire_semaphore) + .stage_mask(vk::PipelineStageFlags2::TOP_OF_PIPE)]) + .command_buffer_infos(&[ + vk::CommandBufferSubmitInfo::default().command_buffer(cmd) + ]) + .signal_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() + .semaphore(frame.draw_finished_semaphore) + .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], + frame.draw_finished_fence, + )?; + Ok(()) + } + } +} diff --git a/graphics/mygraphics/src/ash_renderer/mod.rs b/graphics/mygraphics/src/ash_renderer/mod.rs index f72f697..f9c6f85 100644 --- a/graphics/mygraphics/src/ash_renderer/mod.rs +++ b/graphics/mygraphics/src/ash_renderer/mod.rs @@ -1,5 +1,6 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::graphics::{MyRenderPipelineManager, MyRenderer}; +use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; +use crate::ash_renderer::renderer::MyRenderer; use crate::ash_renderer::swapchain::MySwapchainManager; use crate::util::enable_debug_layer; use ash::util::read_spv; @@ -12,7 +13,8 @@ use winit::{ }; pub mod device; -pub mod graphics; +pub mod render_pipeline; +pub mod renderer; pub mod single_command_buffer; pub mod swapchain; diff --git a/graphics/mygraphics/src/ash_renderer/graphics.rs b/graphics/mygraphics/src/ash_renderer/render_pipeline.rs similarity index 65% rename from graphics/mygraphics/src/ash_renderer/graphics.rs rename to graphics/mygraphics/src/ash_renderer/render_pipeline.rs index b860caf..4121ecd 100644 --- a/graphics/mygraphics/src/ash_renderer/graphics.rs +++ b/graphics/mygraphics/src/ash_renderer/render_pipeline.rs @@ -1,13 +1,11 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; -use crate::ash_renderer::swapchain::DrawFrame; use anyhow::Context; use ash::vk; use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// Manages the creation and recreation of [`MyRenderPipeline`], whenever new shader code ([`Self::set_shader_code`]) -/// is submitted or the spec constant is changed ([`Self::set_sky_fs_sun_intensity_factor`]) +/// is submitted pub struct MyRenderPipelineManager { pub device: Arc, color_out_format: vk::Format, @@ -239,102 +237,3 @@ impl Drop for MyRenderPipelineManager { } } } - -/// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. -pub struct MyRenderer { - pub device: Arc, - pub pipeline: MyRenderPipelineManager, - pub command: SingleCommandBuffer, -} - -impl MyRenderer { - pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { - Ok(Self { - command: SingleCommandBuffer::new(pipeline.device.clone())?, - device: pipeline.device.clone(), - pipeline, - }) - } - - pub fn render_frame( - &mut self, - frame: DrawFrame, - push_constants: ShaderConstants, - ) -> anyhow::Result<()> { - unsafe { - let device = &self.device; - let pipeline = self.pipeline.get_pipeline()?; - let cmd = self.command.cmd; - - device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; - - { - device.begin_command_buffer( - cmd, - &vk::CommandBufferBeginInfo::default() - .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), - )?; - device.cmd_pipeline_barrier2( - cmd, - &vk::DependencyInfo::default().image_memory_barriers(&[ - vk::ImageMemoryBarrier2::default() - .image(frame.image) - .src_access_mask(vk::AccessFlags2::NONE) - .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) - .old_layout(vk::ImageLayout::UNDEFINED) - .dst_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) - .dst_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) - .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1), - ), - ]), - ); - pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; - device.cmd_pipeline_barrier2( - cmd, - &vk::DependencyInfo::default().image_memory_barriers(&[ - vk::ImageMemoryBarrier2::default() - .image(frame.image) - .src_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) - .src_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) - .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .dst_access_mask(vk::AccessFlags2::NONE) - .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) - .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) - .subresource_range( - vk::ImageSubresourceRange::default() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1), - ), - ]), - ); - device.end_command_buffer(cmd)?; - } - - device.queue_submit2( - device.main_queue, - &[vk::SubmitInfo2::default() - .wait_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() - .semaphore(frame.acquire_semaphore) - .stage_mask(vk::PipelineStageFlags2::TOP_OF_PIPE)]) - .command_buffer_infos(&[ - vk::CommandBufferSubmitInfo::default().command_buffer(cmd) - ]) - .signal_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() - .semaphore(frame.draw_finished_semaphore) - .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], - frame.draw_finished_fence, - )?; - Ok(()) - } - } -} diff --git a/graphics/mygraphics/src/ash_renderer/renderer.rs b/graphics/mygraphics/src/ash_renderer/renderer.rs new file mode 100644 index 0000000..6104fd1 --- /dev/null +++ b/graphics/mygraphics/src/ash_renderer/renderer.rs @@ -0,0 +1,106 @@ +use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; +use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; +use crate::ash_renderer::swapchain::DrawFrame; +use ash::vk; +use mygraphics_shaders::ShaderConstants; +use std::sync::Arc; + +/// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. +pub struct MyRenderer { + pub device: Arc, + pub pipeline: MyRenderPipelineManager, + pub command: SingleCommandBuffer, +} + +impl MyRenderer { + pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { + Ok(Self { + command: SingleCommandBuffer::new(pipeline.device.clone())?, + device: pipeline.device.clone(), + pipeline, + }) + } + + pub fn render_frame( + &mut self, + frame: DrawFrame, + push_constants: ShaderConstants, + ) -> anyhow::Result<()> { + unsafe { + let device = &self.device; + let pipeline = self.pipeline.get_pipeline()?; + let cmd = self.command.cmd; + + device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; + + { + device.begin_command_buffer( + cmd, + &vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), + )?; + device.cmd_pipeline_barrier2( + cmd, + &vk::DependencyInfo::default().image_memory_barriers(&[ + vk::ImageMemoryBarrier2::default() + .image(frame.image) + .src_access_mask(vk::AccessFlags2::NONE) + .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) + .old_layout(vk::ImageLayout::UNDEFINED) + .dst_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .dst_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .subresource_range( + vk::ImageSubresourceRange::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1), + ), + ]), + ); + pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; + device.cmd_pipeline_barrier2( + cmd, + &vk::DependencyInfo::default().image_memory_barriers(&[ + vk::ImageMemoryBarrier2::default() + .image(frame.image) + .src_access_mask(vk::AccessFlags2::COLOR_ATTACHMENT_WRITE) + .src_stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) + .old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .dst_access_mask(vk::AccessFlags2::NONE) + .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS) + .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) + .subresource_range( + vk::ImageSubresourceRange::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1), + ), + ]), + ); + device.end_command_buffer(cmd)?; + } + + device.queue_submit2( + device.main_queue, + &[vk::SubmitInfo2::default() + .wait_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() + .semaphore(frame.acquire_semaphore) + .stage_mask(vk::PipelineStageFlags2::TOP_OF_PIPE)]) + .command_buffer_infos(&[ + vk::CommandBufferSubmitInfo::default().command_buffer(cmd) + ]) + .signal_semaphore_infos(&[vk::SemaphoreSubmitInfo::default() + .semaphore(frame.draw_finished_semaphore) + .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], + frame.draw_finished_fence, + )?; + Ok(()) + } + } +} From 032b9389e5d8e5ce8412ea83790d596b985db7a7 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Sun, 21 Dec 2025 00:59:16 +0100 Subject: [PATCH 5/6] ash: adjust ash to use buffers --- Cargo.lock | 19 ++++ Cargo.toml | 7 +- generated/graphics/ash/cargo-gpu/Cargo.toml | 1 + .../ash/cargo-gpu/mygraphics/Cargo.toml | 1 + .../mygraphics/src/ash_renderer/device.rs | 19 +++- .../src/ash_renderer/global_descriptor_set.rs | 100 ++++++++++++++++++ .../mygraphics/src/ash_renderer/mod.rs | 12 +-- .../src/ash_renderer/render_pipeline.rs | 22 ++-- .../mygraphics/src/ash_renderer/renderer.rs | 56 +++++++++- .../graphics/ash/spirv-builder/Cargo.toml | 1 + .../ash/spirv-builder/mygraphics/Cargo.toml | 1 + .../mygraphics/src/ash_renderer/device.rs | 19 +++- .../src/ash_renderer/global_descriptor_set.rs | 100 ++++++++++++++++++ .../mygraphics/src/ash_renderer/mod.rs | 12 +-- .../src/ash_renderer/render_pipeline.rs | 22 ++-- .../mygraphics/src/ash_renderer/renderer.rs | 56 +++++++++- graphics/Cargo.toml.liquid | 1 + graphics/mygraphics/Cargo.toml | 1 + graphics/mygraphics/Cargo.toml.liquid | 1 + .../mygraphics/src/ash_renderer/device.rs | 19 +++- .../src/ash_renderer/global_descriptor_set.rs | 100 ++++++++++++++++++ graphics/mygraphics/src/ash_renderer/mod.rs | 12 +-- .../src/ash_renderer/render_pipeline.rs | 22 ++-- .../mygraphics/src/ash_renderer/renderer.rs | 56 +++++++++- 24 files changed, 582 insertions(+), 78 deletions(-) create mode 100644 generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs create mode 100644 generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs create mode 100644 graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs diff --git a/Cargo.lock b/Cargo.lock index 6745f1f..6f7a76d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1431,6 +1431,18 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "log", + "presser", + "thiserror 2.0.17", +] + [[package]] name = "gpu-descriptor" version = "0.3.2" @@ -2045,6 +2057,7 @@ dependencies = [ "bytemuck", "cargo-gpu", "env_logger", + "gpu-allocator", "mygraphics-shaders", "pollster", "raw-window-handle", @@ -2677,6 +2690,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "proc-macro-crate" version = "3.4.0" diff --git a/Cargo.toml b/Cargo.toml index b035c6d..9062a0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ - "graphics/mygraphics", - "graphics/mygraphics-shaders", - "xtask", + "graphics/mygraphics", + "graphics/mygraphics-shaders", + "xtask", ] resolver = "3" @@ -20,6 +20,7 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spir # API ash = "0.38" ash-window = "0.13" +gpu-allocator = { version = "0.28.0", default-features = false, features = ["std", "vulkan"] } wgpu = { version = "27.0.1", default-features = false, features = ["std", "parking_lot", "vulkan", "vulkan-portability", "spirv", "wgsl"] } pollster = "0.4.0" diff --git a/generated/graphics/ash/cargo-gpu/Cargo.toml b/generated/graphics/ash/cargo-gpu/Cargo.toml index c765d0b..f395198 100644 --- a/generated/graphics/ash/cargo-gpu/Cargo.toml +++ b/generated/graphics/ash/cargo-gpu/Cargo.toml @@ -19,6 +19,7 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spir # API ash = "0.38" ash-window = "0.13" +gpu-allocator = { version = "0.28.0", default-features = false, features = ["std", "vulkan"] } # rust-gpu cargo-gpu = { git = "https://github.com/Rust-GPU/cargo-gpu", rev = "bb74342b90066ce8b7d50ba8e058df356a54acf4" } diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/Cargo.toml b/generated/graphics/ash/cargo-gpu/mygraphics/Cargo.toml index 38d9e61..7137e96 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/Cargo.toml +++ b/generated/graphics/ash/cargo-gpu/mygraphics/Cargo.toml @@ -17,6 +17,7 @@ mygraphics-shaders = { path = "../mygraphics-shaders" } # API ash.workspace = true ash-window.workspace = true +gpu-allocator.workspace = true # other raw-window-handle.workspace = true diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs index 480d04f..3ccaa62 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/device.rs @@ -1,9 +1,10 @@ use anyhow::{Context, anyhow}; use ash::{ext, khr, vk}; +use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; use std::borrow::Cow; use std::ffi::{CStr, c_char}; use std::ops::Deref; -use std::sync::Arc; +use std::sync::{Arc, Mutex, MutexGuard}; /// Central struct containing the Vulkan instance and device, among others pub struct MyDevice { @@ -13,6 +14,7 @@ pub struct MyDevice { pub device: ash::Device, pub main_queue_family: u32, pub main_queue: vk::Queue, + allocator: Option>, pub debug_ext_instance: ext::debug_utils::Instance, pub debug_ext_device: ext::debug_utils::Device, pub surface_ext: khr::surface::Instance, @@ -134,6 +136,15 @@ impl MyDevice { .context("create_device")?; let main_queue = device.get_device_queue(main_queue_family, 0); + let allocator = Allocator::new(&AllocatorCreateDesc { + instance: instance.clone(), + device: device.clone(), + physical_device, + debug_settings: Default::default(), + buffer_device_address: false, + allocation_sizes: Default::default(), + })?; + Ok(Arc::new(Self { debug_ext_device: ext::debug_utils::Device::new(&instance, &device), surface_ext: khr::surface::Instance::new(&entry, &instance), @@ -144,16 +155,22 @@ impl MyDevice { device, main_queue_family, main_queue, + allocator: Some(Mutex::new(allocator)), debug_ext_instance: debug_instance, debug_callback, })) } } + + pub fn borrow_allocator(&self) -> MutexGuard<'_, Allocator> { + self.allocator.as_ref().unwrap().lock().unwrap() + } } impl Drop for MyDevice { fn drop(&mut self) { unsafe { + drop(self.allocator.take()); self.debug_ext_instance .destroy_debug_utils_messenger(self.debug_callback, None); self.device.destroy_device(None); diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs new file mode 100644 index 0000000..bf18adc --- /dev/null +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs @@ -0,0 +1,100 @@ +use crate::ash_renderer::device::MyDevice; +use ash::vk; +use std::sync::Arc; + +pub struct GlobalDescriptorSetLayout { + pub device: Arc, + pub layout: vk::DescriptorSetLayout, +} + +impl GlobalDescriptorSetLayout { + pub fn new(device: Arc) -> anyhow::Result> { + unsafe { + Ok(Arc::new(Self { + layout: device.create_descriptor_set_layout( + &vk::DescriptorSetLayoutCreateInfo::default().bindings(&[ + vk::DescriptorSetLayoutBinding::default() + .binding(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .stage_flags(vk::ShaderStageFlags::ALL_GRAPHICS) + .descriptor_count(1), + ]), + None, + )?, + device, + })) + } + } +} + +impl Drop for GlobalDescriptorSetLayout { + fn drop(&mut self) { + unsafe { + self.device.destroy_descriptor_set_layout(self.layout, None); + } + } +} + +/// This implementation of descriptor sets is kept simple on purpose, even at the cost of being quite inefficient. +/// Don't use this as a reference on how it should be done! +pub struct GlobalDescriptorSet { + pub layout: Arc, + pub pool: vk::DescriptorPool, + pub set: vk::DescriptorSet, +} + +impl GlobalDescriptorSet { + /// # Safety + /// * `shader_constants` must not be dropped before `GlobalDescriptorSet` is dropped + /// * you must only drop this `GlobalDescriptorSet` when it is unused, e.g. by GPU execution + pub unsafe fn new( + layout: &Arc, + shader_constants: vk::Buffer, + ) -> anyhow::Result { + unsafe { + let device = &layout.device; + let pool = device.create_descriptor_pool( + &vk::DescriptorPoolCreateInfo::default() + .pool_sizes(&[vk::DescriptorPoolSize::default() + .ty(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1)]) + .max_sets(1), + None, + )?; + let set = device.allocate_descriptor_sets( + &vk::DescriptorSetAllocateInfo::default() + .descriptor_pool(pool) + .set_layouts(&[layout.layout]), + )?[0]; + device.update_descriptor_sets( + &[vk::WriteDescriptorSet::default() + .dst_set(set) + .dst_binding(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1) + .buffer_info(&[vk::DescriptorBufferInfo::default() + .buffer(shader_constants) + .offset(0) + .range(vk::WHOLE_SIZE)])], + &[], + ); + Ok(Self { + layout: layout.clone(), + pool, + set, + }) + } + } +} + +impl Drop for GlobalDescriptorSet { + fn drop(&mut self) { + let device = &self.layout.device; + unsafe { + device + .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) + .ok(); + device.destroy_descriptor_pool(self.pool, None); + } + } +} diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs index f9c6f85..98961ab 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs @@ -1,5 +1,4 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; use crate::ash_renderer::renderer::MyRenderer; use crate::ash_renderer::swapchain::MySwapchainManager; use crate::util::enable_debug_layer; @@ -13,6 +12,7 @@ use winit::{ }; pub mod device; +pub mod global_descriptor_set; pub mod render_pipeline; pub mod renderer; pub mod single_command_buffer; @@ -36,24 +36,20 @@ pub fn main() -> anyhow::Result<()> { let extensions = ash_window::enumerate_required_extensions(window.display_handle()?.as_raw())?; let device = MyDevice::new(extensions, enable_debug_layer())?; let mut swapchain = MySwapchainManager::new(device.clone(), window)?; - let mut renderer = MyRenderer::new(MyRenderPipelineManager::new( - device.clone(), - swapchain.surface_format.format, - get_shaders()?, - )?)?; + let mut renderer = MyRenderer::new(device.clone(), swapchain.surface_format.format)?; let start = std::time::Instant::now(); let mut event_handler = move |event: Event<_>, event_loop_window_target: &ActiveEventLoop| match event { Event::AboutToWait => swapchain.render(|frame| { let extent = frame.extent; - let push_constants = ShaderConstants { + let shader_constants = ShaderConstants { width: extent.width, height: extent.height, time: start.elapsed().as_secs_f32(), }; - renderer.render_frame(frame, push_constants)?; + renderer.render_frame(frame, &shader_constants)?; Ok(()) }), Event::WindowEvent { event, .. } => { diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/render_pipeline.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/render_pipeline.rs index 4121ecd..dbb863e 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/render_pipeline.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/render_pipeline.rs @@ -1,13 +1,14 @@ use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; use anyhow::Context; use ash::vk; -use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// Manages the creation and recreation of [`MyRenderPipeline`], whenever new shader code ([`Self::set_shader_code`]) /// is submitted pub struct MyRenderPipelineManager { pub device: Arc, + global_descriptor_set_layout: Arc, color_out_format: vk::Format, shader_code: Vec, pipeline: Option, @@ -22,11 +23,13 @@ pub struct MyRenderPipeline { impl MyRenderPipelineManager { pub fn new( device: Arc, + global_descriptor_set_layout: Arc, color_out_format: vk::Format, shader_code: Vec, ) -> anyhow::Result { Ok(Self { device, + global_descriptor_set_layout, color_out_format, shader_code, pipeline: None, @@ -63,12 +66,8 @@ impl MyRenderPipelineManager { )?; let pipeline_layout = self.device.create_pipeline_layout( - &vk::PipelineLayoutCreateInfo::default().push_constant_ranges(&[ - vk::PushConstantRange::default() - .offset(0) - .size(size_of::() as u32) - .stage_flags(vk::ShaderStageFlags::ALL), - ]), + &vk::PipelineLayoutCreateInfo::default() + .set_layouts(&[self.global_descriptor_set_layout.layout]), None, )?; @@ -177,7 +176,7 @@ impl MyRenderPipeline { cmd: vk::CommandBuffer, color_out: vk::ImageView, extent: vk::Extent2D, - push_constants: ShaderConstants, + global_descriptor_set: &GlobalDescriptorSet, ) -> anyhow::Result<()> { unsafe { let render_area = vk::Rect2D { @@ -216,12 +215,13 @@ impl MyRenderPipeline { }], ); device.cmd_set_scissor(cmd, 0, &[render_area]); - device.cmd_push_constants( + device.cmd_bind_descriptor_sets( cmd, + vk::PipelineBindPoint::GRAPHICS, self.pipeline_layout, - vk::ShaderStageFlags::ALL, 0, - bytemuck::bytes_of(&push_constants), + &[global_descriptor_set.set], + &[], ); device.cmd_draw(cmd, 3, 1, 0, 0); device.cmd_end_rendering(cmd); diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs index 6104fd1..8a7710c 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs @@ -1,37 +1,74 @@ use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::get_shaders; +use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; use crate::ash_renderer::swapchain::DrawFrame; use ash::vk; +use gpu_allocator::MemoryLocation; +use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. pub struct MyRenderer { pub device: Arc, + pub global_descriptor_set_layout: Arc, pub pipeline: MyRenderPipelineManager, pub command: SingleCommandBuffer, } impl MyRenderer { - pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { + pub fn new(device: Arc, out_format: vk::Format) -> anyhow::Result { + let global_descriptor_set_layout = GlobalDescriptorSetLayout::new(device.clone())?; + let pipeline = MyRenderPipelineManager::new( + device.clone(), + global_descriptor_set_layout.clone(), + out_format, + get_shaders()?, + )?; + let command = SingleCommandBuffer::new(pipeline.device.clone())?; Ok(Self { - command: SingleCommandBuffer::new(pipeline.device.clone())?, - device: pipeline.device.clone(), + device, + global_descriptor_set_layout, pipeline, + command, }) } pub fn render_frame( &mut self, frame: DrawFrame, - push_constants: ShaderConstants, + shader_constants: &ShaderConstants, ) -> anyhow::Result<()> { unsafe { let device = &self.device; let pipeline = self.pipeline.get_pipeline()?; let cmd = self.command.cmd; + let buffer = device.create_buffer( + &vk::BufferCreateInfo::default() + .size(size_of::() as u64) + .usage(vk::BufferUsageFlags::STORAGE_BUFFER), + None, + )?; + let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { + name: "ShaderConstants", + requirements: device.get_buffer_memory_requirements(buffer), + location: MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: AllocationScheme::GpuAllocatorManaged, + })?; + { + let mapped = + &mut allocation.mapped_slice_mut().unwrap()[..size_of::()]; + mapped.copy_from_slice(bytemuck::bytes_of(shader_constants)); + } + device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; + + let descriptor_set = + GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer)?; + device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; { @@ -61,7 +98,7 @@ impl MyRenderer { ), ]), ); - pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; + pipeline.render(device, cmd, frame.image_view, frame.extent, &descriptor_set)?; device.cmd_pipeline_barrier2( cmd, &vk::DependencyInfo::default().image_memory_barriers(&[ @@ -100,6 +137,15 @@ impl MyRenderer { .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], frame.draw_finished_fence, )?; + + // free resources of the frame + // For a production renderer, you'd obviously don't want to immediately wait for the previous frame to + // finish rendering, but start recording the next one already while waiting. For simplicity's sake, + // we just wait immediately. + self.device.device_wait_idle()?; + drop(descriptor_set); + self.device.destroy_buffer(buffer, None); + drop(allocation); Ok(()) } } diff --git a/generated/graphics/ash/spirv-builder/Cargo.toml b/generated/graphics/ash/spirv-builder/Cargo.toml index e8d1af9..20b97fd 100644 --- a/generated/graphics/ash/spirv-builder/Cargo.toml +++ b/generated/graphics/ash/spirv-builder/Cargo.toml @@ -19,6 +19,7 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spir # API ash = "0.38" ash-window = "0.13" +gpu-allocator = { version = "0.28.0", default-features = false, features = ["std", "vulkan"] } # rust-gpu # The version of the dependencies `spirv-builder` and `spirv-std` must match exactly! diff --git a/generated/graphics/ash/spirv-builder/mygraphics/Cargo.toml b/generated/graphics/ash/spirv-builder/mygraphics/Cargo.toml index 55ed561..2e19cf8 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/Cargo.toml +++ b/generated/graphics/ash/spirv-builder/mygraphics/Cargo.toml @@ -21,6 +21,7 @@ mygraphics-shaders = { path = "../mygraphics-shaders" } # API ash.workspace = true ash-window.workspace = true +gpu-allocator.workspace = true # other raw-window-handle.workspace = true diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs index 480d04f..3ccaa62 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/device.rs @@ -1,9 +1,10 @@ use anyhow::{Context, anyhow}; use ash::{ext, khr, vk}; +use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; use std::borrow::Cow; use std::ffi::{CStr, c_char}; use std::ops::Deref; -use std::sync::Arc; +use std::sync::{Arc, Mutex, MutexGuard}; /// Central struct containing the Vulkan instance and device, among others pub struct MyDevice { @@ -13,6 +14,7 @@ pub struct MyDevice { pub device: ash::Device, pub main_queue_family: u32, pub main_queue: vk::Queue, + allocator: Option>, pub debug_ext_instance: ext::debug_utils::Instance, pub debug_ext_device: ext::debug_utils::Device, pub surface_ext: khr::surface::Instance, @@ -134,6 +136,15 @@ impl MyDevice { .context("create_device")?; let main_queue = device.get_device_queue(main_queue_family, 0); + let allocator = Allocator::new(&AllocatorCreateDesc { + instance: instance.clone(), + device: device.clone(), + physical_device, + debug_settings: Default::default(), + buffer_device_address: false, + allocation_sizes: Default::default(), + })?; + Ok(Arc::new(Self { debug_ext_device: ext::debug_utils::Device::new(&instance, &device), surface_ext: khr::surface::Instance::new(&entry, &instance), @@ -144,16 +155,22 @@ impl MyDevice { device, main_queue_family, main_queue, + allocator: Some(Mutex::new(allocator)), debug_ext_instance: debug_instance, debug_callback, })) } } + + pub fn borrow_allocator(&self) -> MutexGuard<'_, Allocator> { + self.allocator.as_ref().unwrap().lock().unwrap() + } } impl Drop for MyDevice { fn drop(&mut self) { unsafe { + drop(self.allocator.take()); self.debug_ext_instance .destroy_debug_utils_messenger(self.debug_callback, None); self.device.destroy_device(None); diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs new file mode 100644 index 0000000..bf18adc --- /dev/null +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs @@ -0,0 +1,100 @@ +use crate::ash_renderer::device::MyDevice; +use ash::vk; +use std::sync::Arc; + +pub struct GlobalDescriptorSetLayout { + pub device: Arc, + pub layout: vk::DescriptorSetLayout, +} + +impl GlobalDescriptorSetLayout { + pub fn new(device: Arc) -> anyhow::Result> { + unsafe { + Ok(Arc::new(Self { + layout: device.create_descriptor_set_layout( + &vk::DescriptorSetLayoutCreateInfo::default().bindings(&[ + vk::DescriptorSetLayoutBinding::default() + .binding(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .stage_flags(vk::ShaderStageFlags::ALL_GRAPHICS) + .descriptor_count(1), + ]), + None, + )?, + device, + })) + } + } +} + +impl Drop for GlobalDescriptorSetLayout { + fn drop(&mut self) { + unsafe { + self.device.destroy_descriptor_set_layout(self.layout, None); + } + } +} + +/// This implementation of descriptor sets is kept simple on purpose, even at the cost of being quite inefficient. +/// Don't use this as a reference on how it should be done! +pub struct GlobalDescriptorSet { + pub layout: Arc, + pub pool: vk::DescriptorPool, + pub set: vk::DescriptorSet, +} + +impl GlobalDescriptorSet { + /// # Safety + /// * `shader_constants` must not be dropped before `GlobalDescriptorSet` is dropped + /// * you must only drop this `GlobalDescriptorSet` when it is unused, e.g. by GPU execution + pub unsafe fn new( + layout: &Arc, + shader_constants: vk::Buffer, + ) -> anyhow::Result { + unsafe { + let device = &layout.device; + let pool = device.create_descriptor_pool( + &vk::DescriptorPoolCreateInfo::default() + .pool_sizes(&[vk::DescriptorPoolSize::default() + .ty(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1)]) + .max_sets(1), + None, + )?; + let set = device.allocate_descriptor_sets( + &vk::DescriptorSetAllocateInfo::default() + .descriptor_pool(pool) + .set_layouts(&[layout.layout]), + )?[0]; + device.update_descriptor_sets( + &[vk::WriteDescriptorSet::default() + .dst_set(set) + .dst_binding(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1) + .buffer_info(&[vk::DescriptorBufferInfo::default() + .buffer(shader_constants) + .offset(0) + .range(vk::WHOLE_SIZE)])], + &[], + ); + Ok(Self { + layout: layout.clone(), + pool, + set, + }) + } + } +} + +impl Drop for GlobalDescriptorSet { + fn drop(&mut self) { + let device = &self.layout.device; + unsafe { + device + .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) + .ok(); + device.destroy_descriptor_pool(self.pool, None); + } + } +} diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs index f9c6f85..98961ab 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs @@ -1,5 +1,4 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; use crate::ash_renderer::renderer::MyRenderer; use crate::ash_renderer::swapchain::MySwapchainManager; use crate::util::enable_debug_layer; @@ -13,6 +12,7 @@ use winit::{ }; pub mod device; +pub mod global_descriptor_set; pub mod render_pipeline; pub mod renderer; pub mod single_command_buffer; @@ -36,24 +36,20 @@ pub fn main() -> anyhow::Result<()> { let extensions = ash_window::enumerate_required_extensions(window.display_handle()?.as_raw())?; let device = MyDevice::new(extensions, enable_debug_layer())?; let mut swapchain = MySwapchainManager::new(device.clone(), window)?; - let mut renderer = MyRenderer::new(MyRenderPipelineManager::new( - device.clone(), - swapchain.surface_format.format, - get_shaders()?, - )?)?; + let mut renderer = MyRenderer::new(device.clone(), swapchain.surface_format.format)?; let start = std::time::Instant::now(); let mut event_handler = move |event: Event<_>, event_loop_window_target: &ActiveEventLoop| match event { Event::AboutToWait => swapchain.render(|frame| { let extent = frame.extent; - let push_constants = ShaderConstants { + let shader_constants = ShaderConstants { width: extent.width, height: extent.height, time: start.elapsed().as_secs_f32(), }; - renderer.render_frame(frame, push_constants)?; + renderer.render_frame(frame, &shader_constants)?; Ok(()) }), Event::WindowEvent { event, .. } => { diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/render_pipeline.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/render_pipeline.rs index 4121ecd..dbb863e 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/render_pipeline.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/render_pipeline.rs @@ -1,13 +1,14 @@ use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; use anyhow::Context; use ash::vk; -use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// Manages the creation and recreation of [`MyRenderPipeline`], whenever new shader code ([`Self::set_shader_code`]) /// is submitted pub struct MyRenderPipelineManager { pub device: Arc, + global_descriptor_set_layout: Arc, color_out_format: vk::Format, shader_code: Vec, pipeline: Option, @@ -22,11 +23,13 @@ pub struct MyRenderPipeline { impl MyRenderPipelineManager { pub fn new( device: Arc, + global_descriptor_set_layout: Arc, color_out_format: vk::Format, shader_code: Vec, ) -> anyhow::Result { Ok(Self { device, + global_descriptor_set_layout, color_out_format, shader_code, pipeline: None, @@ -63,12 +66,8 @@ impl MyRenderPipelineManager { )?; let pipeline_layout = self.device.create_pipeline_layout( - &vk::PipelineLayoutCreateInfo::default().push_constant_ranges(&[ - vk::PushConstantRange::default() - .offset(0) - .size(size_of::() as u32) - .stage_flags(vk::ShaderStageFlags::ALL), - ]), + &vk::PipelineLayoutCreateInfo::default() + .set_layouts(&[self.global_descriptor_set_layout.layout]), None, )?; @@ -177,7 +176,7 @@ impl MyRenderPipeline { cmd: vk::CommandBuffer, color_out: vk::ImageView, extent: vk::Extent2D, - push_constants: ShaderConstants, + global_descriptor_set: &GlobalDescriptorSet, ) -> anyhow::Result<()> { unsafe { let render_area = vk::Rect2D { @@ -216,12 +215,13 @@ impl MyRenderPipeline { }], ); device.cmd_set_scissor(cmd, 0, &[render_area]); - device.cmd_push_constants( + device.cmd_bind_descriptor_sets( cmd, + vk::PipelineBindPoint::GRAPHICS, self.pipeline_layout, - vk::ShaderStageFlags::ALL, 0, - bytemuck::bytes_of(&push_constants), + &[global_descriptor_set.set], + &[], ); device.cmd_draw(cmd, 3, 1, 0, 0); device.cmd_end_rendering(cmd); diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs index 6104fd1..8a7710c 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs @@ -1,37 +1,74 @@ use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::get_shaders; +use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; use crate::ash_renderer::swapchain::DrawFrame; use ash::vk; +use gpu_allocator::MemoryLocation; +use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. pub struct MyRenderer { pub device: Arc, + pub global_descriptor_set_layout: Arc, pub pipeline: MyRenderPipelineManager, pub command: SingleCommandBuffer, } impl MyRenderer { - pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { + pub fn new(device: Arc, out_format: vk::Format) -> anyhow::Result { + let global_descriptor_set_layout = GlobalDescriptorSetLayout::new(device.clone())?; + let pipeline = MyRenderPipelineManager::new( + device.clone(), + global_descriptor_set_layout.clone(), + out_format, + get_shaders()?, + )?; + let command = SingleCommandBuffer::new(pipeline.device.clone())?; Ok(Self { - command: SingleCommandBuffer::new(pipeline.device.clone())?, - device: pipeline.device.clone(), + device, + global_descriptor_set_layout, pipeline, + command, }) } pub fn render_frame( &mut self, frame: DrawFrame, - push_constants: ShaderConstants, + shader_constants: &ShaderConstants, ) -> anyhow::Result<()> { unsafe { let device = &self.device; let pipeline = self.pipeline.get_pipeline()?; let cmd = self.command.cmd; + let buffer = device.create_buffer( + &vk::BufferCreateInfo::default() + .size(size_of::() as u64) + .usage(vk::BufferUsageFlags::STORAGE_BUFFER), + None, + )?; + let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { + name: "ShaderConstants", + requirements: device.get_buffer_memory_requirements(buffer), + location: MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: AllocationScheme::GpuAllocatorManaged, + })?; + { + let mapped = + &mut allocation.mapped_slice_mut().unwrap()[..size_of::()]; + mapped.copy_from_slice(bytemuck::bytes_of(shader_constants)); + } + device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; + + let descriptor_set = + GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer)?; + device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; { @@ -61,7 +98,7 @@ impl MyRenderer { ), ]), ); - pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; + pipeline.render(device, cmd, frame.image_view, frame.extent, &descriptor_set)?; device.cmd_pipeline_barrier2( cmd, &vk::DependencyInfo::default().image_memory_barriers(&[ @@ -100,6 +137,15 @@ impl MyRenderer { .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], frame.draw_finished_fence, )?; + + // free resources of the frame + // For a production renderer, you'd obviously don't want to immediately wait for the previous frame to + // finish rendering, but start recording the next one already while waiting. For simplicity's sake, + // we just wait immediately. + self.device.device_wait_idle()?; + drop(descriptor_set); + self.device.destroy_buffer(buffer, None); + drop(allocation); Ok(()) } } diff --git a/graphics/Cargo.toml.liquid b/graphics/Cargo.toml.liquid index 8342047..c85be10 100644 --- a/graphics/Cargo.toml.liquid +++ b/graphics/Cargo.toml.liquid @@ -20,6 +20,7 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spir {% if api == "ash" -%} ash = "0.38" ash-window = "0.13" +gpu-allocator = { version = "0.28.0", default-features = false, features = ["std", "vulkan"] } {%- endif -%} {%- if api == "wgpu" -%} wgpu = { version = "27.0.1", default-features = false, features = ["std", "parking_lot", "vulkan", "vulkan-portability", "spirv", "wgsl"] } diff --git a/graphics/mygraphics/Cargo.toml b/graphics/mygraphics/Cargo.toml index 0ce4276..84b21f5 100644 --- a/graphics/mygraphics/Cargo.toml +++ b/graphics/mygraphics/Cargo.toml @@ -17,6 +17,7 @@ mygraphics-shaders = { path = "../mygraphics-shaders" } # API ash.workspace = true ash-window.workspace = true +gpu-allocator.workspace = true wgpu.workspace = true pollster.workspace = true env_logger.workspace = true diff --git a/graphics/mygraphics/Cargo.toml.liquid b/graphics/mygraphics/Cargo.toml.liquid index 209002e..8667745 100644 --- a/graphics/mygraphics/Cargo.toml.liquid +++ b/graphics/mygraphics/Cargo.toml.liquid @@ -25,6 +25,7 @@ mygraphics-shaders = { path = "../mygraphics-shaders" } {% if api == "ash" -%} ash.workspace = true ash-window.workspace = true +gpu-allocator.workspace = true {%- endif -%} {%- if api == "wgpu" -%} wgpu.workspace = true diff --git a/graphics/mygraphics/src/ash_renderer/device.rs b/graphics/mygraphics/src/ash_renderer/device.rs index 480d04f..3ccaa62 100644 --- a/graphics/mygraphics/src/ash_renderer/device.rs +++ b/graphics/mygraphics/src/ash_renderer/device.rs @@ -1,9 +1,10 @@ use anyhow::{Context, anyhow}; use ash::{ext, khr, vk}; +use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc}; use std::borrow::Cow; use std::ffi::{CStr, c_char}; use std::ops::Deref; -use std::sync::Arc; +use std::sync::{Arc, Mutex, MutexGuard}; /// Central struct containing the Vulkan instance and device, among others pub struct MyDevice { @@ -13,6 +14,7 @@ pub struct MyDevice { pub device: ash::Device, pub main_queue_family: u32, pub main_queue: vk::Queue, + allocator: Option>, pub debug_ext_instance: ext::debug_utils::Instance, pub debug_ext_device: ext::debug_utils::Device, pub surface_ext: khr::surface::Instance, @@ -134,6 +136,15 @@ impl MyDevice { .context("create_device")?; let main_queue = device.get_device_queue(main_queue_family, 0); + let allocator = Allocator::new(&AllocatorCreateDesc { + instance: instance.clone(), + device: device.clone(), + physical_device, + debug_settings: Default::default(), + buffer_device_address: false, + allocation_sizes: Default::default(), + })?; + Ok(Arc::new(Self { debug_ext_device: ext::debug_utils::Device::new(&instance, &device), surface_ext: khr::surface::Instance::new(&entry, &instance), @@ -144,16 +155,22 @@ impl MyDevice { device, main_queue_family, main_queue, + allocator: Some(Mutex::new(allocator)), debug_ext_instance: debug_instance, debug_callback, })) } } + + pub fn borrow_allocator(&self) -> MutexGuard<'_, Allocator> { + self.allocator.as_ref().unwrap().lock().unwrap() + } } impl Drop for MyDevice { fn drop(&mut self) { unsafe { + drop(self.allocator.take()); self.debug_ext_instance .destroy_debug_utils_messenger(self.debug_callback, None); self.device.destroy_device(None); diff --git a/graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs b/graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs new file mode 100644 index 0000000..bf18adc --- /dev/null +++ b/graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs @@ -0,0 +1,100 @@ +use crate::ash_renderer::device::MyDevice; +use ash::vk; +use std::sync::Arc; + +pub struct GlobalDescriptorSetLayout { + pub device: Arc, + pub layout: vk::DescriptorSetLayout, +} + +impl GlobalDescriptorSetLayout { + pub fn new(device: Arc) -> anyhow::Result> { + unsafe { + Ok(Arc::new(Self { + layout: device.create_descriptor_set_layout( + &vk::DescriptorSetLayoutCreateInfo::default().bindings(&[ + vk::DescriptorSetLayoutBinding::default() + .binding(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .stage_flags(vk::ShaderStageFlags::ALL_GRAPHICS) + .descriptor_count(1), + ]), + None, + )?, + device, + })) + } + } +} + +impl Drop for GlobalDescriptorSetLayout { + fn drop(&mut self) { + unsafe { + self.device.destroy_descriptor_set_layout(self.layout, None); + } + } +} + +/// This implementation of descriptor sets is kept simple on purpose, even at the cost of being quite inefficient. +/// Don't use this as a reference on how it should be done! +pub struct GlobalDescriptorSet { + pub layout: Arc, + pub pool: vk::DescriptorPool, + pub set: vk::DescriptorSet, +} + +impl GlobalDescriptorSet { + /// # Safety + /// * `shader_constants` must not be dropped before `GlobalDescriptorSet` is dropped + /// * you must only drop this `GlobalDescriptorSet` when it is unused, e.g. by GPU execution + pub unsafe fn new( + layout: &Arc, + shader_constants: vk::Buffer, + ) -> anyhow::Result { + unsafe { + let device = &layout.device; + let pool = device.create_descriptor_pool( + &vk::DescriptorPoolCreateInfo::default() + .pool_sizes(&[vk::DescriptorPoolSize::default() + .ty(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1)]) + .max_sets(1), + None, + )?; + let set = device.allocate_descriptor_sets( + &vk::DescriptorSetAllocateInfo::default() + .descriptor_pool(pool) + .set_layouts(&[layout.layout]), + )?[0]; + device.update_descriptor_sets( + &[vk::WriteDescriptorSet::default() + .dst_set(set) + .dst_binding(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1) + .buffer_info(&[vk::DescriptorBufferInfo::default() + .buffer(shader_constants) + .offset(0) + .range(vk::WHOLE_SIZE)])], + &[], + ); + Ok(Self { + layout: layout.clone(), + pool, + set, + }) + } + } +} + +impl Drop for GlobalDescriptorSet { + fn drop(&mut self) { + let device = &self.layout.device; + unsafe { + device + .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) + .ok(); + device.destroy_descriptor_pool(self.pool, None); + } + } +} diff --git a/graphics/mygraphics/src/ash_renderer/mod.rs b/graphics/mygraphics/src/ash_renderer/mod.rs index f9c6f85..98961ab 100644 --- a/graphics/mygraphics/src/ash_renderer/mod.rs +++ b/graphics/mygraphics/src/ash_renderer/mod.rs @@ -1,5 +1,4 @@ use crate::ash_renderer::device::MyDevice; -use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; use crate::ash_renderer::renderer::MyRenderer; use crate::ash_renderer::swapchain::MySwapchainManager; use crate::util::enable_debug_layer; @@ -13,6 +12,7 @@ use winit::{ }; pub mod device; +pub mod global_descriptor_set; pub mod render_pipeline; pub mod renderer; pub mod single_command_buffer; @@ -36,24 +36,20 @@ pub fn main() -> anyhow::Result<()> { let extensions = ash_window::enumerate_required_extensions(window.display_handle()?.as_raw())?; let device = MyDevice::new(extensions, enable_debug_layer())?; let mut swapchain = MySwapchainManager::new(device.clone(), window)?; - let mut renderer = MyRenderer::new(MyRenderPipelineManager::new( - device.clone(), - swapchain.surface_format.format, - get_shaders()?, - )?)?; + let mut renderer = MyRenderer::new(device.clone(), swapchain.surface_format.format)?; let start = std::time::Instant::now(); let mut event_handler = move |event: Event<_>, event_loop_window_target: &ActiveEventLoop| match event { Event::AboutToWait => swapchain.render(|frame| { let extent = frame.extent; - let push_constants = ShaderConstants { + let shader_constants = ShaderConstants { width: extent.width, height: extent.height, time: start.elapsed().as_secs_f32(), }; - renderer.render_frame(frame, push_constants)?; + renderer.render_frame(frame, &shader_constants)?; Ok(()) }), Event::WindowEvent { event, .. } => { diff --git a/graphics/mygraphics/src/ash_renderer/render_pipeline.rs b/graphics/mygraphics/src/ash_renderer/render_pipeline.rs index 4121ecd..dbb863e 100644 --- a/graphics/mygraphics/src/ash_renderer/render_pipeline.rs +++ b/graphics/mygraphics/src/ash_renderer/render_pipeline.rs @@ -1,13 +1,14 @@ use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; use anyhow::Context; use ash::vk; -use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// Manages the creation and recreation of [`MyRenderPipeline`], whenever new shader code ([`Self::set_shader_code`]) /// is submitted pub struct MyRenderPipelineManager { pub device: Arc, + global_descriptor_set_layout: Arc, color_out_format: vk::Format, shader_code: Vec, pipeline: Option, @@ -22,11 +23,13 @@ pub struct MyRenderPipeline { impl MyRenderPipelineManager { pub fn new( device: Arc, + global_descriptor_set_layout: Arc, color_out_format: vk::Format, shader_code: Vec, ) -> anyhow::Result { Ok(Self { device, + global_descriptor_set_layout, color_out_format, shader_code, pipeline: None, @@ -63,12 +66,8 @@ impl MyRenderPipelineManager { )?; let pipeline_layout = self.device.create_pipeline_layout( - &vk::PipelineLayoutCreateInfo::default().push_constant_ranges(&[ - vk::PushConstantRange::default() - .offset(0) - .size(size_of::() as u32) - .stage_flags(vk::ShaderStageFlags::ALL), - ]), + &vk::PipelineLayoutCreateInfo::default() + .set_layouts(&[self.global_descriptor_set_layout.layout]), None, )?; @@ -177,7 +176,7 @@ impl MyRenderPipeline { cmd: vk::CommandBuffer, color_out: vk::ImageView, extent: vk::Extent2D, - push_constants: ShaderConstants, + global_descriptor_set: &GlobalDescriptorSet, ) -> anyhow::Result<()> { unsafe { let render_area = vk::Rect2D { @@ -216,12 +215,13 @@ impl MyRenderPipeline { }], ); device.cmd_set_scissor(cmd, 0, &[render_area]); - device.cmd_push_constants( + device.cmd_bind_descriptor_sets( cmd, + vk::PipelineBindPoint::GRAPHICS, self.pipeline_layout, - vk::ShaderStageFlags::ALL, 0, - bytemuck::bytes_of(&push_constants), + &[global_descriptor_set.set], + &[], ); device.cmd_draw(cmd, 3, 1, 0, 0); device.cmd_end_rendering(cmd); diff --git a/graphics/mygraphics/src/ash_renderer/renderer.rs b/graphics/mygraphics/src/ash_renderer/renderer.rs index 6104fd1..8a7710c 100644 --- a/graphics/mygraphics/src/ash_renderer/renderer.rs +++ b/graphics/mygraphics/src/ash_renderer/renderer.rs @@ -1,37 +1,74 @@ use crate::ash_renderer::device::MyDevice; +use crate::ash_renderer::get_shaders; +use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; use crate::ash_renderer::render_pipeline::MyRenderPipelineManager; use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; use crate::ash_renderer::swapchain::DrawFrame; use ash::vk; +use gpu_allocator::MemoryLocation; +use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use mygraphics_shaders::ShaderConstants; use std::sync::Arc; /// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. pub struct MyRenderer { pub device: Arc, + pub global_descriptor_set_layout: Arc, pub pipeline: MyRenderPipelineManager, pub command: SingleCommandBuffer, } impl MyRenderer { - pub fn new(pipeline: MyRenderPipelineManager) -> anyhow::Result { + pub fn new(device: Arc, out_format: vk::Format) -> anyhow::Result { + let global_descriptor_set_layout = GlobalDescriptorSetLayout::new(device.clone())?; + let pipeline = MyRenderPipelineManager::new( + device.clone(), + global_descriptor_set_layout.clone(), + out_format, + get_shaders()?, + )?; + let command = SingleCommandBuffer::new(pipeline.device.clone())?; Ok(Self { - command: SingleCommandBuffer::new(pipeline.device.clone())?, - device: pipeline.device.clone(), + device, + global_descriptor_set_layout, pipeline, + command, }) } pub fn render_frame( &mut self, frame: DrawFrame, - push_constants: ShaderConstants, + shader_constants: &ShaderConstants, ) -> anyhow::Result<()> { unsafe { let device = &self.device; let pipeline = self.pipeline.get_pipeline()?; let cmd = self.command.cmd; + let buffer = device.create_buffer( + &vk::BufferCreateInfo::default() + .size(size_of::() as u64) + .usage(vk::BufferUsageFlags::STORAGE_BUFFER), + None, + )?; + let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { + name: "ShaderConstants", + requirements: device.get_buffer_memory_requirements(buffer), + location: MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: AllocationScheme::GpuAllocatorManaged, + })?; + { + let mapped = + &mut allocation.mapped_slice_mut().unwrap()[..size_of::()]; + mapped.copy_from_slice(bytemuck::bytes_of(shader_constants)); + } + device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; + + let descriptor_set = + GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer)?; + device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; { @@ -61,7 +98,7 @@ impl MyRenderer { ), ]), ); - pipeline.render(device, cmd, frame.image_view, frame.extent, push_constants)?; + pipeline.render(device, cmd, frame.image_view, frame.extent, &descriptor_set)?; device.cmd_pipeline_barrier2( cmd, &vk::DependencyInfo::default().image_memory_barriers(&[ @@ -100,6 +137,15 @@ impl MyRenderer { .stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)])], frame.draw_finished_fence, )?; + + // free resources of the frame + // For a production renderer, you'd obviously don't want to immediately wait for the previous frame to + // finish rendering, but start recording the next one already while waiting. For simplicity's sake, + // we just wait immediately. + self.device.device_wait_idle()?; + drop(descriptor_set); + self.device.destroy_buffer(buffer, None); + drop(allocation); Ok(()) } } From 342ba6fb02134cd8d8dcbc0a9c18f3650870ef12 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 23 Dec 2025 14:42:23 +0100 Subject: [PATCH 6/6] ash: MyBuffer abstraction with explicit destroy --- .../mygraphics/src/ash_renderer/buffer.rs | 84 +++++++++++++++++++ .../src/ash_renderer/global_descriptor_set.rs | 23 +++-- .../mygraphics/src/ash_renderer/mod.rs | 1 + .../mygraphics/src/ash_renderer/renderer.rs | 39 ++++----- .../mygraphics/src/ash_renderer/buffer.rs | 84 +++++++++++++++++++ .../src/ash_renderer/global_descriptor_set.rs | 23 +++-- .../mygraphics/src/ash_renderer/mod.rs | 1 + .../mygraphics/src/ash_renderer/renderer.rs | 39 ++++----- .../mygraphics/src/ash_renderer/buffer.rs | 84 +++++++++++++++++++ .../src/ash_renderer/global_descriptor_set.rs | 23 +++-- graphics/mygraphics/src/ash_renderer/mod.rs | 1 + .../mygraphics/src/ash_renderer/renderer.rs | 39 ++++----- 12 files changed, 348 insertions(+), 93 deletions(-) create mode 100644 generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/buffer.rs create mode 100644 generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/buffer.rs create mode 100644 graphics/mygraphics/src/ash_renderer/buffer.rs diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/buffer.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/buffer.rs new file mode 100644 index 0000000..3094c52 --- /dev/null +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/buffer.rs @@ -0,0 +1,84 @@ +use crate::ash_renderer::device::MyDevice; +use ash::vk; +use bytemuck::NoUninit; +use gpu_allocator::MemoryLocation; +use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme}; +use std::borrow::Cow; +use std::sync::Arc; + +pub struct MyBuffer { + pub buffer: vk::Buffer, + pub allocation: Allocation, + pub name: String, + destroyed: bool, +} + +#[derive(Clone)] +pub struct BufferCreateInfo<'a> { + pub usage: vk::BufferUsageFlags, + pub location: MemoryLocation, + pub name: Option>, +} + +impl MyBuffer { + pub fn from_data( + device: &Arc, + info: BufferCreateInfo<'_>, + data: &T, + ) -> anyhow::Result { + Self::from_slice(device, info, bytemuck::bytes_of(data)) + } + + pub fn from_slice( + device: &Arc, + info: BufferCreateInfo<'_>, + data: &[T], + ) -> anyhow::Result { + unsafe { + let buffer = device.create_buffer( + &vk::BufferCreateInfo::default() + .size(size_of_val(data) as u64) + .usage(vk::BufferUsageFlags::STORAGE_BUFFER), + None, + )?; + let name = info.name.map(|a| a.into_owned()).unwrap_or_default(); + let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { + name: &name, + requirements: device.get_buffer_memory_requirements(buffer), + location: MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: AllocationScheme::GpuAllocatorManaged, + })?; + device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; + let mapped = &mut allocation.mapped_slice_mut().unwrap()[..size_of_val(data)]; + mapped.copy_from_slice(bytemuck::cast_slice(data)); + Ok(Self { + buffer, + allocation, + name, + destroyed: false, + }) + } + } + + /// Destroy this buffer + /// + /// # Safety + /// Buffer must not be in use + pub unsafe fn destroy(&mut self, device: &Arc) { + if !self.destroyed { + self.destroyed = true; + unsafe { + device.destroy_buffer(self.buffer, None); + } + } + } +} + +impl Drop for MyBuffer { + fn drop(&mut self) { + if !self.destroyed { + panic!("dropping Buffer {} without destroying it", &self.name); + } + } +} diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs index bf18adc..bf15fb0 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/global_descriptor_set.rs @@ -41,6 +41,7 @@ pub struct GlobalDescriptorSet { pub layout: Arc, pub pool: vk::DescriptorPool, pub set: vk::DescriptorSet, + destroyed: bool, } impl GlobalDescriptorSet { @@ -82,19 +83,29 @@ impl GlobalDescriptorSet { layout: layout.clone(), pool, set, + destroyed: false, }) } } + + pub fn destroy(&mut self) { + if !self.destroyed { + self.destroyed = true; + unsafe { + let device = &self.layout.device; + device + .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) + .ok(); + device.destroy_descriptor_pool(self.pool, None); + } + } + } } impl Drop for GlobalDescriptorSet { fn drop(&mut self) { - let device = &self.layout.device; - unsafe { - device - .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) - .ok(); - device.destroy_descriptor_pool(self.pool, None); + if !self.destroyed { + panic!("dropping GlobalDescriptorSet without destroying it"); } } } diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs index 98961ab..289fc7d 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/mod.rs @@ -11,6 +11,7 @@ use winit::{ event_loop::{ControlFlow, EventLoop}, }; +pub mod buffer; pub mod device; pub mod global_descriptor_set; pub mod render_pipeline; diff --git a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs index 8a7710c..99f0267 100644 --- a/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs +++ b/generated/graphics/ash/cargo-gpu/mygraphics/src/ash_renderer/renderer.rs @@ -1,3 +1,4 @@ +use crate::ash_renderer::buffer::{BufferCreateInfo, MyBuffer}; use crate::ash_renderer::device::MyDevice; use crate::ash_renderer::get_shaders; use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; @@ -6,8 +7,8 @@ use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; use crate::ash_renderer::swapchain::DrawFrame; use ash::vk; use gpu_allocator::MemoryLocation; -use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use mygraphics_shaders::ShaderConstants; +use std::borrow::Cow; use std::sync::Arc; /// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. @@ -46,28 +47,17 @@ impl MyRenderer { let pipeline = self.pipeline.get_pipeline()?; let cmd = self.command.cmd; - let buffer = device.create_buffer( - &vk::BufferCreateInfo::default() - .size(size_of::() as u64) - .usage(vk::BufferUsageFlags::STORAGE_BUFFER), - None, + let mut buffer = MyBuffer::from_data( + device, + BufferCreateInfo { + usage: vk::BufferUsageFlags::STORAGE_BUFFER, + location: MemoryLocation::CpuToGpu, + name: Some(Cow::from("ShaderConstants")), + }, + shader_constants, )?; - let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { - name: "ShaderConstants", - requirements: device.get_buffer_memory_requirements(buffer), - location: MemoryLocation::CpuToGpu, - linear: true, - allocation_scheme: AllocationScheme::GpuAllocatorManaged, - })?; - { - let mapped = - &mut allocation.mapped_slice_mut().unwrap()[..size_of::()]; - mapped.copy_from_slice(bytemuck::bytes_of(shader_constants)); - } - device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; - - let descriptor_set = - GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer)?; + let mut descriptor_set = + GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer.buffer)?; device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; @@ -143,9 +133,8 @@ impl MyRenderer { // finish rendering, but start recording the next one already while waiting. For simplicity's sake, // we just wait immediately. self.device.device_wait_idle()?; - drop(descriptor_set); - self.device.destroy_buffer(buffer, None); - drop(allocation); + descriptor_set.destroy(); + buffer.destroy(device); Ok(()) } } diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/buffer.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/buffer.rs new file mode 100644 index 0000000..3094c52 --- /dev/null +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/buffer.rs @@ -0,0 +1,84 @@ +use crate::ash_renderer::device::MyDevice; +use ash::vk; +use bytemuck::NoUninit; +use gpu_allocator::MemoryLocation; +use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme}; +use std::borrow::Cow; +use std::sync::Arc; + +pub struct MyBuffer { + pub buffer: vk::Buffer, + pub allocation: Allocation, + pub name: String, + destroyed: bool, +} + +#[derive(Clone)] +pub struct BufferCreateInfo<'a> { + pub usage: vk::BufferUsageFlags, + pub location: MemoryLocation, + pub name: Option>, +} + +impl MyBuffer { + pub fn from_data( + device: &Arc, + info: BufferCreateInfo<'_>, + data: &T, + ) -> anyhow::Result { + Self::from_slice(device, info, bytemuck::bytes_of(data)) + } + + pub fn from_slice( + device: &Arc, + info: BufferCreateInfo<'_>, + data: &[T], + ) -> anyhow::Result { + unsafe { + let buffer = device.create_buffer( + &vk::BufferCreateInfo::default() + .size(size_of_val(data) as u64) + .usage(vk::BufferUsageFlags::STORAGE_BUFFER), + None, + )?; + let name = info.name.map(|a| a.into_owned()).unwrap_or_default(); + let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { + name: &name, + requirements: device.get_buffer_memory_requirements(buffer), + location: MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: AllocationScheme::GpuAllocatorManaged, + })?; + device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; + let mapped = &mut allocation.mapped_slice_mut().unwrap()[..size_of_val(data)]; + mapped.copy_from_slice(bytemuck::cast_slice(data)); + Ok(Self { + buffer, + allocation, + name, + destroyed: false, + }) + } + } + + /// Destroy this buffer + /// + /// # Safety + /// Buffer must not be in use + pub unsafe fn destroy(&mut self, device: &Arc) { + if !self.destroyed { + self.destroyed = true; + unsafe { + device.destroy_buffer(self.buffer, None); + } + } + } +} + +impl Drop for MyBuffer { + fn drop(&mut self) { + if !self.destroyed { + panic!("dropping Buffer {} without destroying it", &self.name); + } + } +} diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs index bf18adc..bf15fb0 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/global_descriptor_set.rs @@ -41,6 +41,7 @@ pub struct GlobalDescriptorSet { pub layout: Arc, pub pool: vk::DescriptorPool, pub set: vk::DescriptorSet, + destroyed: bool, } impl GlobalDescriptorSet { @@ -82,19 +83,29 @@ impl GlobalDescriptorSet { layout: layout.clone(), pool, set, + destroyed: false, }) } } + + pub fn destroy(&mut self) { + if !self.destroyed { + self.destroyed = true; + unsafe { + let device = &self.layout.device; + device + .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) + .ok(); + device.destroy_descriptor_pool(self.pool, None); + } + } + } } impl Drop for GlobalDescriptorSet { fn drop(&mut self) { - let device = &self.layout.device; - unsafe { - device - .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) - .ok(); - device.destroy_descriptor_pool(self.pool, None); + if !self.destroyed { + panic!("dropping GlobalDescriptorSet without destroying it"); } } } diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs index 98961ab..289fc7d 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/mod.rs @@ -11,6 +11,7 @@ use winit::{ event_loop::{ControlFlow, EventLoop}, }; +pub mod buffer; pub mod device; pub mod global_descriptor_set; pub mod render_pipeline; diff --git a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs index 8a7710c..99f0267 100644 --- a/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs +++ b/generated/graphics/ash/spirv-builder/mygraphics/src/ash_renderer/renderer.rs @@ -1,3 +1,4 @@ +use crate::ash_renderer::buffer::{BufferCreateInfo, MyBuffer}; use crate::ash_renderer::device::MyDevice; use crate::ash_renderer::get_shaders; use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; @@ -6,8 +7,8 @@ use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; use crate::ash_renderer::swapchain::DrawFrame; use ash::vk; use gpu_allocator::MemoryLocation; -use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use mygraphics_shaders::ShaderConstants; +use std::borrow::Cow; use std::sync::Arc; /// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. @@ -46,28 +47,17 @@ impl MyRenderer { let pipeline = self.pipeline.get_pipeline()?; let cmd = self.command.cmd; - let buffer = device.create_buffer( - &vk::BufferCreateInfo::default() - .size(size_of::() as u64) - .usage(vk::BufferUsageFlags::STORAGE_BUFFER), - None, + let mut buffer = MyBuffer::from_data( + device, + BufferCreateInfo { + usage: vk::BufferUsageFlags::STORAGE_BUFFER, + location: MemoryLocation::CpuToGpu, + name: Some(Cow::from("ShaderConstants")), + }, + shader_constants, )?; - let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { - name: "ShaderConstants", - requirements: device.get_buffer_memory_requirements(buffer), - location: MemoryLocation::CpuToGpu, - linear: true, - allocation_scheme: AllocationScheme::GpuAllocatorManaged, - })?; - { - let mapped = - &mut allocation.mapped_slice_mut().unwrap()[..size_of::()]; - mapped.copy_from_slice(bytemuck::bytes_of(shader_constants)); - } - device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; - - let descriptor_set = - GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer)?; + let mut descriptor_set = + GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer.buffer)?; device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; @@ -143,9 +133,8 @@ impl MyRenderer { // finish rendering, but start recording the next one already while waiting. For simplicity's sake, // we just wait immediately. self.device.device_wait_idle()?; - drop(descriptor_set); - self.device.destroy_buffer(buffer, None); - drop(allocation); + descriptor_set.destroy(); + buffer.destroy(device); Ok(()) } } diff --git a/graphics/mygraphics/src/ash_renderer/buffer.rs b/graphics/mygraphics/src/ash_renderer/buffer.rs new file mode 100644 index 0000000..3094c52 --- /dev/null +++ b/graphics/mygraphics/src/ash_renderer/buffer.rs @@ -0,0 +1,84 @@ +use crate::ash_renderer::device::MyDevice; +use ash::vk; +use bytemuck::NoUninit; +use gpu_allocator::MemoryLocation; +use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme}; +use std::borrow::Cow; +use std::sync::Arc; + +pub struct MyBuffer { + pub buffer: vk::Buffer, + pub allocation: Allocation, + pub name: String, + destroyed: bool, +} + +#[derive(Clone)] +pub struct BufferCreateInfo<'a> { + pub usage: vk::BufferUsageFlags, + pub location: MemoryLocation, + pub name: Option>, +} + +impl MyBuffer { + pub fn from_data( + device: &Arc, + info: BufferCreateInfo<'_>, + data: &T, + ) -> anyhow::Result { + Self::from_slice(device, info, bytemuck::bytes_of(data)) + } + + pub fn from_slice( + device: &Arc, + info: BufferCreateInfo<'_>, + data: &[T], + ) -> anyhow::Result { + unsafe { + let buffer = device.create_buffer( + &vk::BufferCreateInfo::default() + .size(size_of_val(data) as u64) + .usage(vk::BufferUsageFlags::STORAGE_BUFFER), + None, + )?; + let name = info.name.map(|a| a.into_owned()).unwrap_or_default(); + let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { + name: &name, + requirements: device.get_buffer_memory_requirements(buffer), + location: MemoryLocation::CpuToGpu, + linear: true, + allocation_scheme: AllocationScheme::GpuAllocatorManaged, + })?; + device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; + let mapped = &mut allocation.mapped_slice_mut().unwrap()[..size_of_val(data)]; + mapped.copy_from_slice(bytemuck::cast_slice(data)); + Ok(Self { + buffer, + allocation, + name, + destroyed: false, + }) + } + } + + /// Destroy this buffer + /// + /// # Safety + /// Buffer must not be in use + pub unsafe fn destroy(&mut self, device: &Arc) { + if !self.destroyed { + self.destroyed = true; + unsafe { + device.destroy_buffer(self.buffer, None); + } + } + } +} + +impl Drop for MyBuffer { + fn drop(&mut self) { + if !self.destroyed { + panic!("dropping Buffer {} without destroying it", &self.name); + } + } +} diff --git a/graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs b/graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs index bf18adc..bf15fb0 100644 --- a/graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs +++ b/graphics/mygraphics/src/ash_renderer/global_descriptor_set.rs @@ -41,6 +41,7 @@ pub struct GlobalDescriptorSet { pub layout: Arc, pub pool: vk::DescriptorPool, pub set: vk::DescriptorSet, + destroyed: bool, } impl GlobalDescriptorSet { @@ -82,19 +83,29 @@ impl GlobalDescriptorSet { layout: layout.clone(), pool, set, + destroyed: false, }) } } + + pub fn destroy(&mut self) { + if !self.destroyed { + self.destroyed = true; + unsafe { + let device = &self.layout.device; + device + .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) + .ok(); + device.destroy_descriptor_pool(self.pool, None); + } + } + } } impl Drop for GlobalDescriptorSet { fn drop(&mut self) { - let device = &self.layout.device; - unsafe { - device - .reset_descriptor_pool(self.pool, vk::DescriptorPoolResetFlags::empty()) - .ok(); - device.destroy_descriptor_pool(self.pool, None); + if !self.destroyed { + panic!("dropping GlobalDescriptorSet without destroying it"); } } } diff --git a/graphics/mygraphics/src/ash_renderer/mod.rs b/graphics/mygraphics/src/ash_renderer/mod.rs index 98961ab..289fc7d 100644 --- a/graphics/mygraphics/src/ash_renderer/mod.rs +++ b/graphics/mygraphics/src/ash_renderer/mod.rs @@ -11,6 +11,7 @@ use winit::{ event_loop::{ControlFlow, EventLoop}, }; +pub mod buffer; pub mod device; pub mod global_descriptor_set; pub mod render_pipeline; diff --git a/graphics/mygraphics/src/ash_renderer/renderer.rs b/graphics/mygraphics/src/ash_renderer/renderer.rs index 8a7710c..99f0267 100644 --- a/graphics/mygraphics/src/ash_renderer/renderer.rs +++ b/graphics/mygraphics/src/ash_renderer/renderer.rs @@ -1,3 +1,4 @@ +use crate::ash_renderer::buffer::{BufferCreateInfo, MyBuffer}; use crate::ash_renderer::device::MyDevice; use crate::ash_renderer::get_shaders; use crate::ash_renderer::global_descriptor_set::{GlobalDescriptorSet, GlobalDescriptorSetLayout}; @@ -6,8 +7,8 @@ use crate::ash_renderer::single_command_buffer::SingleCommandBuffer; use crate::ash_renderer::swapchain::DrawFrame; use ash::vk; use gpu_allocator::MemoryLocation; -use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use mygraphics_shaders::ShaderConstants; +use std::borrow::Cow; use std::sync::Arc; /// The renderer manages our command buffer and submits the commands, using [`MyRenderPipeline`] for drawing. @@ -46,28 +47,17 @@ impl MyRenderer { let pipeline = self.pipeline.get_pipeline()?; let cmd = self.command.cmd; - let buffer = device.create_buffer( - &vk::BufferCreateInfo::default() - .size(size_of::() as u64) - .usage(vk::BufferUsageFlags::STORAGE_BUFFER), - None, + let mut buffer = MyBuffer::from_data( + device, + BufferCreateInfo { + usage: vk::BufferUsageFlags::STORAGE_BUFFER, + location: MemoryLocation::CpuToGpu, + name: Some(Cow::from("ShaderConstants")), + }, + shader_constants, )?; - let mut allocation = device.borrow_allocator().allocate(&AllocationCreateDesc { - name: "ShaderConstants", - requirements: device.get_buffer_memory_requirements(buffer), - location: MemoryLocation::CpuToGpu, - linear: true, - allocation_scheme: AllocationScheme::GpuAllocatorManaged, - })?; - { - let mapped = - &mut allocation.mapped_slice_mut().unwrap()[..size_of::()]; - mapped.copy_from_slice(bytemuck::bytes_of(shader_constants)); - } - device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; - - let descriptor_set = - GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer)?; + let mut descriptor_set = + GlobalDescriptorSet::new(&self.global_descriptor_set_layout, buffer.buffer)?; device.reset_command_pool(self.command.pool, vk::CommandPoolResetFlags::default())?; @@ -143,9 +133,8 @@ impl MyRenderer { // finish rendering, but start recording the next one already while waiting. For simplicity's sake, // we just wait immediately. self.device.device_wait_idle()?; - drop(descriptor_set); - self.device.destroy_buffer(buffer, None); - drop(allocation); + descriptor_set.destroy(); + buffer.destroy(device); Ok(()) } }