]> git.nega.tv - josh/narcissus/commitdiff
narcissus-gpu: Add support for immutable samplers
authorJosh Simmons <josh@nega.tv>
Sat, 8 Jun 2024 19:27:11 +0000 (21:27 +0200)
committerJosh Simmons <josh@nega.tv>
Sat, 8 Jun 2024 19:27:11 +0000 (21:27 +0200)
engine/narcissus-gpu/src/backend/vulkan/mod.rs
engine/narcissus-gpu/src/lib.rs

index dd97e7f34febbba48ede2047a6d7cfbe5d0f9e95..f0944c6106df3decbcffc17aab7d1d65ca4c50ec 100644 (file)
@@ -18,13 +18,14 @@ use narcissus_core::{
 use vulkan_sys as vk;
 
 use crate::{
-    frame_counter::FrameCounter, Bind, BindDesc, BindGroupLayout, Buffer, BufferArg, BufferDesc,
-    BufferImageCopy, BufferUsageFlags, CmdEncoder, ComputePipelineDesc, Device, Extent2d, Extent3d,
-    Frame, GlobalBarrier, GpuConcurrent, GraphicsPipelineDesc, Image, ImageBarrier, ImageBlit,
-    ImageDesc, ImageDimension, ImageLayout, ImageTiling, ImageViewDesc, IndexType, MemoryLocation,
-    Offset2d, Offset3d, PersistentBuffer, Pipeline, PipelineLayout, Sampler, SamplerAddressMode,
-    SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderStageFlags, SwapchainConfigurator,
-    SwapchainImage, SwapchainOutOfDateError, ThreadToken, TransientBuffer, TypedBind,
+    frame_counter::FrameCounter, Bind, BindDesc, BindGroupLayout, BindingType, Buffer, BufferArg,
+    BufferDesc, BufferImageCopy, BufferUsageFlags, CmdEncoder, ComputePipelineDesc, Device,
+    Extent2d, Extent3d, Frame, GlobalBarrier, GpuConcurrent, GraphicsPipelineDesc, Image,
+    ImageBarrier, ImageBlit, ImageDesc, ImageDimension, ImageLayout, ImageTiling, ImageViewDesc,
+    IndexType, MemoryLocation, Offset2d, Offset3d, PersistentBuffer, Pipeline, PipelineLayout,
+    Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderStageFlags,
+    SwapchainConfigurator, SwapchainImage, SwapchainOutOfDateError, ThreadToken, TransientBuffer,
+    TypedBind,
 };
 
 mod allocator;
@@ -1239,30 +1240,64 @@ impl Device for VulkanDevice {
         Sampler(handle)
     }
 
-    fn create_bind_group_layout(&self, bindings_desc: &[BindDesc]) -> BindGroupLayout {
+    fn create_bind_group_layout(&self, binds_desc: &[BindDesc]) -> BindGroupLayout {
+        let arena = HybridArena::<256>::new();
         let mut hasher = blake3_smol::Hasher::new();
-        for binding in bindings_desc {
-            hasher.update(&binding.slot.to_le_bytes());
-            hasher.update(&binding.stages.as_raw().to_le_bytes());
-            hasher.update(&(binding.binding_type as u32).to_le_bytes());
-            hasher.update(&binding.count.to_le_bytes());
+
+        for bind_desc in binds_desc {
+            hasher.update(&bind_desc.slot.to_le_bytes());
+            hasher.update(&bind_desc.stages.as_raw().to_le_bytes());
+            hasher.update(&(bind_desc.binding_type as u32).to_le_bytes());
+            hasher.update(&bind_desc.count.to_le_bytes());
         }
-        let hash = hasher.finalize();
 
-        let arena = HybridArena::<256>::new();
-        let layout_bindings = arena.alloc_slice_fill_iter(bindings_desc.iter().enumerate().map(
-            |(i, binding_desc)| vk::DescriptorSetLayoutBinding {
-                binding: if binding_desc.slot != !0 {
-                    binding_desc.slot
+        let layout_bindings =
+            arena.alloc_slice_fill_iter(binds_desc.iter().enumerate().map(|(i, bind_desc)| {
+                let immutable_samplers = if !bind_desc.immutable_samplers.is_empty() {
+                    assert_eq!(
+                        bind_desc.binding_type,
+                        BindingType::Sampler,
+                        "can only use immutable samplers with sampler binds"
+                    );
+                    assert_eq!(
+                        bind_desc.count as usize,
+                        bind_desc.immutable_samplers.len(),
+                        "number of immutable samplers must match bind count"
+                    );
+
+                    let sampler_pool = self.sampler_pool.lock();
+
+                    let immutable_samplers = arena.alloc_slice_fill_iter(
+                        bind_desc.immutable_samplers.iter().map(|sampler| {
+                            let sampler = sampler_pool
+                                .get(sampler.0)
+                                .expect("trying to set an invalid immutable sampler");
+
+                            // We need to make sure we include immutable samplers in the hash calculation.
+                            hasher.update(&sampler.0.as_raw().to_le_bytes());
+
+                            sampler.0
+                        }),
+                    );
+
+                    // This pointer can safely escape the block as its lifetime is bound to the arena.
+                    immutable_samplers.as_ptr()
                 } else {
-                    i as u32
-                },
-                descriptor_type: vulkan_descriptor_type(binding_desc.binding_type),
-                descriptor_count: binding_desc.count,
-                stage_flags: vulkan_shader_stage_flags(binding_desc.stages),
-                immutable_samplers: std::ptr::null(),
-            },
-        ));
+                    std::ptr::null()
+                };
+
+                vk::DescriptorSetLayoutBinding {
+                    binding: if bind_desc.slot != !0 {
+                        bind_desc.slot
+                    } else {
+                        i as u32
+                    },
+                    descriptor_type: vulkan_descriptor_type(bind_desc.binding_type),
+                    descriptor_count: bind_desc.count,
+                    stage_flags: vulkan_shader_stage_flags(bind_desc.stages),
+                    immutable_samplers,
+                }
+            }));
         let create_info = &vk::DescriptorSetLayoutCreateInfo {
             bindings: layout_bindings.into(),
             ..default()
@@ -1274,6 +1309,8 @@ impl Device for VulkanDevice {
             None,
             &mut descriptor_set_layout,
         ));
+
+        let hash = hasher.finalize();
         let bind_group_layout = self
             .bind_group_layout_pool
             .lock()
index a35a7e26b96fe16f9b3a34d13be806c4ab85691a..cc73295f9c81f15f77d35bd14f29e9b2eb7877bc 100644 (file)
@@ -492,20 +492,35 @@ pub enum BindingType {
     DynamicStorageBuffer,
 }
 
-pub struct BindDesc {
+pub struct BindDesc<'a> {
     pub slot: u32,
     pub stages: ShaderStageFlags,
     pub binding_type: BindingType,
     pub count: u32,
+    pub immutable_samplers: &'a [Sampler],
 }
 
-impl BindDesc {
-    pub const fn new(stages: ShaderStageFlags, binding_type: BindingType) -> BindDesc {
+impl<'a> BindDesc<'a> {
+    pub const fn new(stages: ShaderStageFlags, binding_type: BindingType) -> BindDesc<'static> {
         BindDesc {
             slot: !0,
             stages,
             binding_type,
             count: 1,
+            immutable_samplers: &[],
+        }
+    }
+
+    pub const fn with_immutable_samplers(
+        stages: ShaderStageFlags,
+        immutable_samplers: &[Sampler],
+    ) -> BindDesc {
+        BindDesc {
+            slot: !0,
+            stages,
+            binding_type: BindingType::Sampler,
+            count: immutable_samplers.len() as u32,
+            immutable_samplers,
         }
     }
 }