From 6fd1bd07d682b4050263437f3fe928fa6cfeef6e Mon Sep 17 00:00:00 2001 From: Joshua Simmons Date: Sat, 29 Oct 2022 16:14:54 +0200 Subject: [PATCH] Add `BindGroupLayout` --- narcissus-gpu/src/lib.rs | 34 ++++++++++ narcissus-gpu/src/vulkan.rs | 128 ++++++++++++++++++++++++++++++++---- narcissus/src/main.rs | 60 +++++++++++++++-- 3 files changed, 204 insertions(+), 18 deletions(-) diff --git a/narcissus-gpu/src/lib.rs b/narcissus-gpu/src/lib.rs index e78813e..d1dd1f1 100644 --- a/narcissus-gpu/src/lib.rs +++ b/narcissus-gpu/src/lib.rs @@ -14,6 +14,12 @@ pub struct Buffer(Handle); #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Sampler(Handle); +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct BindGroupLayout(Handle); + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct BindGroup(Handle); + #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Pipeline(Handle); @@ -151,6 +157,27 @@ pub struct SamplerDesc { pub max_lod: f32, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BindingType { + Sampler, + Texture, + UniformBuffer, + StorageBuffer, + DynamicUniformBuffer, + DynamicStorageBuffer, +} + +pub struct BindGroupLayoutEntryDesc { + pub slot: u32, + pub stages: ShaderStageFlags, + pub binding_type: BindingType, + pub count: u32, +} + +pub struct BindGroupLayoutDesc<'a> { + pub entries: &'a [BindGroupLayoutEntryDesc], +} + pub struct GraphicsPipelineLayout<'a> { pub color_attachment_formats: &'a [TextureFormat], pub depth_attachment_format: Option, @@ -160,6 +187,7 @@ pub struct GraphicsPipelineLayout<'a> { pub struct GraphicsPipelineDesc<'a> { pub vertex_shader: ShaderDesc<'a>, pub fragment_shader: ShaderDesc<'a>, + pub bind_group_layouts: &'a [BindGroupLayout], pub layout: GraphicsPipelineLayout<'a>, } @@ -225,12 +253,18 @@ pub trait Device { fn create_texture(&self, texture_desc: &TextureDesc) -> Texture; fn create_texture_view(&self, desc: &TextureViewDesc) -> Texture; fn create_sampler(&self, desc: &SamplerDesc) -> Sampler; + fn create_bind_group_layout(&self, desc: &BindGroupLayoutDesc) -> BindGroupLayout; fn create_graphics_pipeline(&self, desc: &GraphicsPipelineDesc) -> Pipeline; fn create_compute_pipeline(&self, desc: &ComputePipelineDesc) -> Pipeline; fn destroy_buffer(&self, frame_token: &FrameToken, buffer: Buffer); fn destroy_texture(&self, frame_token: &FrameToken, texture: Texture); fn destroy_sampler(&self, frame_token: &FrameToken, sampler: Sampler); + fn destroy_bind_group_layout( + &self, + frame_token: &FrameToken, + bind_group_layout: BindGroupLayout, + ); fn destroy_pipeline(&self, frame_token: &FrameToken, pipeline: Pipeline); fn acquire_swapchain( diff --git a/narcissus-gpu/src/vulkan.rs b/narcissus-gpu/src/vulkan.rs index 13e4455..c504f52 100644 --- a/narcissus-gpu/src/vulkan.rs +++ b/narcissus-gpu/src/vulkan.rs @@ -16,10 +16,11 @@ use vk::DeviceFunctions; use vulkan_sys as vk; use crate::{ - Buffer, BufferDesc, BufferUsageFlags, ClearValue, CommandBufferToken, ComputePipelineDesc, - Device, FrameToken, GpuConcurrent, GraphicsPipelineDesc, LoadOp, MemoryLocation, Pipeline, - Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc, SamplerFilter, Texture, - TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken, + BindGroupLayout, BindGroupLayoutDesc, BindingType, Buffer, BufferDesc, BufferUsageFlags, + ClearValue, CommandBufferToken, ComputePipelineDesc, Device, FrameToken, GpuConcurrent, + GraphicsPipelineDesc, LoadOp, MemoryLocation, Pipeline, Sampler, SamplerAddressMode, + SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderStageFlags, Texture, TextureDesc, + TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken, }; const NUM_FRAMES: usize = 2; @@ -89,6 +90,33 @@ fn vulkan_clear_value(clear_value: ClearValue) -> vk::ClearValue { } } +#[must_use] +fn vulkan_shader_stage_flags(stage_flags: ShaderStageFlags) -> vk::ShaderStageFlags { + let mut flags = vk::ShaderStageFlags::default(); + if stage_flags.contains(ShaderStageFlags::COMPUTE) { + flags |= vk::ShaderStageFlags::COMPUTE; + } + if stage_flags.contains(ShaderStageFlags::FRAGMENT) { + flags |= vk::ShaderStageFlags::FRAGMENT; + } + if stage_flags.contains(ShaderStageFlags::VERTEX) { + flags |= vk::ShaderStageFlags::VERTEX; + } + flags +} + +#[must_use] +fn vulkan_descriptor_type(binding_type: BindingType) -> vk::DescriptorType { + match binding_type { + BindingType::Sampler => vk::DescriptorType::Sampler, + BindingType::Texture => vk::DescriptorType::SampledImage, + BindingType::UniformBuffer => vk::DescriptorType::UniformBuffer, + BindingType::StorageBuffer => vk::DescriptorType::StorageBuffer, + BindingType::DynamicUniformBuffer => vk::DescriptorType::UniformBufferDynamic, + BindingType::DynamicStorageBuffer => vk::DescriptorType::StorageBufferDynamic, + } +} + struct DelayQueue { delay: u64, counter: u64, @@ -165,6 +193,8 @@ enum VulkanTextureHolder { struct VulkanSampler(vk::Sampler); +struct VulkanBindGroupLayout(vk::DescriptorSetLayout); + struct VulkanPipeline { pipeline: vk::Pipeline, pipeline_bind_point: vk::PipelineBindPoint, @@ -218,6 +248,7 @@ struct VulkanPools { textures: Pool, buffers: Pool, samplers: Pool, + bind_group_layouts: Pool, pipelines: Pool, } @@ -303,6 +334,7 @@ struct VulkanFrame { destroyed_images: Mutex>, destroyed_image_views: Mutex>, destroyed_samplers: Mutex>, + destroyed_descriptor_set_layouts: Mutex>, destroyed_pipelines: Mutex>, recycled_semaphores: Mutex>, @@ -581,15 +613,16 @@ impl<'app> VulkanDevice<'app> { UnsafeCell::new(VulkanFrame { command_buffer_pools: cmd_buffer_pools, universal_queue_fence: AtomicU64::new(universal_queue_fence), - present_swapchains: Mutex::new(HashMap::new()), - destroyed_buffers: Mutex::new(VecDeque::new()), - destroyed_buffer_views: Mutex::new(VecDeque::new()), - destroyed_images: Mutex::new(VecDeque::new()), - destroyed_image_views: Mutex::new(VecDeque::new()), - destroyed_samplers: Mutex::new(VecDeque::new()), - destroyed_allocations: Mutex::new(VecDeque::new()), - destroyed_pipelines: Mutex::new(VecDeque::new()), - recycled_semaphores: Mutex::new(VecDeque::new()), + present_swapchains: Default::default(), + destroyed_allocations: Default::default(), + destroyed_buffers: Default::default(), + destroyed_buffer_views: Default::default(), + destroyed_images: Default::default(), + destroyed_image_views: Default::default(), + destroyed_samplers: Default::default(), + destroyed_descriptor_set_layouts: Default::default(), + destroyed_pipelines: Default::default(), + recycled_semaphores: Default::default(), }) })); @@ -616,6 +649,7 @@ impl<'app> VulkanDevice<'app> { textures: Pool::new(), buffers: Pool::new(), samplers: Pool::new(), + bind_group_layouts: Pool::new(), pipelines: Pool::new(), }), @@ -770,6 +804,9 @@ impl<'app> VulkanDevice<'app> { for pipeline in frame.destroyed_pipelines.get_mut().drain(..) { unsafe { device_fn.destroy_pipeline(device, pipeline, None) } } + for descriptor_set_layout in frame.destroyed_descriptor_set_layouts.get_mut().drain(..) { + unsafe { device_fn.destroy_descriptor_set_layout(device, descriptor_set_layout, None) } + } for sampler in frame.destroyed_samplers.get_mut().drain(..) { unsafe { device_fn.destroy_sampler(device, sampler, None) } } @@ -1139,9 +1176,46 @@ impl<'driver> Device for VulkanDevice<'driver> { Sampler(handle) } + fn create_bind_group_layout(&self, desc: &BindGroupLayoutDesc) -> BindGroupLayout { + let layout_bindings = desc + .entries + .iter() + .map(|x| vk::DescriptorSetLayoutBinding { + binding: x.slot, + descriptor_type: vulkan_descriptor_type(x.binding_type), + descriptor_count: x.count, + stage_flags: vulkan_shader_stage_flags(x.stages), + immutable_samplers: std::ptr::null(), + }) + .collect::>(); + + let create_info = &vk::DescriptorSetLayoutCreateInfo { + bindings: layout_bindings.as_slice().into(), + ..default() + }; + let mut set_layout = vk::DescriptorSetLayout::null(); + vk_check!(self.device_fn.create_descriptor_set_layout( + self.device, + create_info, + None, + &mut set_layout, + )); + + let bind_group_layout = self + .pools + .lock() + .bind_group_layouts + .insert(VulkanBindGroupLayout(set_layout)); + + BindGroupLayout(bind_group_layout) + } + fn create_graphics_pipeline(&self, desc: &GraphicsPipelineDesc) -> Pipeline { let layout = { - let create_info = vk::PipelineLayoutCreateInfo::default(); + let create_info = vk::PipelineLayoutCreateInfo { + //set_layouts: set_layouts.as_slice().into(), + ..default() + }; let mut pipeline_layout = vk::PipelineLayout::null(); vk_check!(self.device_fn.create_pipeline_layout( self.device, @@ -1340,6 +1414,24 @@ impl<'driver> Device for VulkanDevice<'driver> { } } + fn destroy_bind_group_layout( + &self, + frame_token: &FrameToken, + bind_group_layout: BindGroupLayout, + ) { + if let Some(bind_group_layout) = self + .pools + .lock() + .bind_group_layouts + .remove(bind_group_layout.0) + { + self.frame(frame_token) + .destroyed_descriptor_set_layouts + .lock() + .push_back(bind_group_layout.0) + } + } + fn destroy_pipeline(&self, frame_token: &FrameToken, pipeline: Pipeline) { if let Some(pipeline) = self.pools.lock().pipelines.remove(pipeline.0) { self.frame(frame_token) @@ -1365,6 +1457,7 @@ impl<'driver> Device for VulkanDevice<'driver> { textures, buffers: _, samplers: _, + bind_group_layouts: _, pipelines: _, } = pools.deref_mut(); @@ -1477,6 +1570,7 @@ impl<'driver> Device for VulkanDevice<'driver> { textures, buffers: _, samplers: _, + bind_group_layouts: _, pipelines: _, } = pools.deref_mut(); @@ -2229,6 +2323,12 @@ impl<'app> Drop for VulkanDevice<'app> { unsafe { device_fn.destroy_pipeline(device, pipeline.pipeline, None) } } + for descriptor_set_layout in self.pools.get_mut().bind_group_layouts.values() { + unsafe { + device_fn.destroy_descriptor_set_layout(device, descriptor_set_layout.0, None) + } + } + unsafe { device_fn.destroy_device(device, None) } unsafe { self.instance_fn.destroy_instance(self.instance, None) }; } diff --git a/narcissus/src/main.rs b/narcissus/src/main.rs index b6e601f..1a8175c 100644 --- a/narcissus/src/main.rs +++ b/narcissus/src/main.rs @@ -1,10 +1,11 @@ use narcissus_app::{create_app, Event, Window, WindowDesc}; use narcissus_core::{cstr, obj, slice, Image}; use narcissus_gpu::{ - create_vulkan_device, ClearValue, Device, FrameToken, GraphicsPipelineDesc, - GraphicsPipelineLayout, LoadOp, MemoryLocation, Pipeline, RenderingAttachment, RenderingDesc, - Scissor, ShaderDesc, StoreOp, TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, - TextureViewDesc, ThreadToken, Viewport, + create_vulkan_device, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, ClearValue, + Device, FrameToken, GraphicsPipelineDesc, GraphicsPipelineLayout, LoadOp, MemoryLocation, + Pipeline, RenderingAttachment, RenderingDesc, Scissor, ShaderDesc, ShaderStageFlags, StoreOp, + TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken, + Viewport, }; use narcissus_maths::{Vec2, Vec3}; @@ -83,6 +84,49 @@ pub fn main() { let vert_shader_spv = Spirv(*include_bytes!("shaders/triangle.vert.spv")); let frag_shader_spv = Spirv(*include_bytes!("shaders/triangle.frag.spv")); + let global_layout = device.create_bind_group_layout(&BindGroupLayoutDesc { + entries: &[ + // Global uniforms. + BindGroupLayoutEntryDesc { + slot: 0, + stages: ShaderStageFlags::ALL, + binding_type: BindingType::UniformBuffer, + count: 1, + }, + ], + }); + + let per_material_layout = device.create_bind_group_layout(&BindGroupLayoutDesc { + entries: &[ + // Per-material uniforms. + BindGroupLayoutEntryDesc { + slot: 0, + stages: ShaderStageFlags::ALL, + binding_type: BindingType::UniformBuffer, + count: 1, + }, + // Per-material textures. + BindGroupLayoutEntryDesc { + slot: 1, + stages: ShaderStageFlags::ALL, + binding_type: BindingType::Texture, + count: 1, + }, + ], + }); + + let per_draw_layout = device.create_bind_group_layout(&BindGroupLayoutDesc { + entries: &[ + // Per-draw Uniforms + BindGroupLayoutEntryDesc { + slot: 0, + stages: ShaderStageFlags::ALL, + binding_type: BindingType::DynamicUniformBuffer, + count: 1, + }, + ], + }); + let pipeline = device.create_graphics_pipeline(&GraphicsPipelineDesc { vertex_shader: ShaderDesc { entrypoint_name: cstr!("main"), @@ -92,6 +136,14 @@ pub fn main() { entrypoint_name: cstr!("main"), code: &frag_shader_spv.0, }, + bind_group_layouts: &[ + // Set 0 + global_layout, + // Set 1 + per_material_layout, + // Set 2 + per_draw_layout, + ], layout: GraphicsPipelineLayout { color_attachment_formats: &[TextureFormat::BGRA8_SRGB], depth_attachment_format: None, -- 2.49.0