]> git.nega.tv - josh/narcissus/commitdiff
narcissus-gpu: Cache pipeline layouts
authorJosh Simmons <josh@nega.tv>
Sat, 25 May 2024 13:26:21 +0000 (15:26 +0200)
committerJosh Simmons <josh@nega.tv>
Sat, 25 May 2024 13:26:21 +0000 (15:26 +0200)
Cargo.lock
engine/narcissus-gpu/Cargo.toml
engine/narcissus-gpu/src/backend/vulkan/mod.rs
engine/narcissus-gpu/src/lib.rs
title/shark/src/pipelines/basic.rs
title/shark/src/pipelines/display_transform.rs
title/shark/src/pipelines/primitive_2d.rs

index e4fb89e92ca2706f521bdb2dbbeed93778e314ea..8b2c2c0b1c18b407f34f4f1b0b6c3363efb31fce 100644 (file)
@@ -91,6 +91,7 @@ dependencies = [
 name = "narcissus-gpu"
 version = "0.1.0"
 dependencies = [
+ "blake3-smol",
  "narcissus-core",
  "vulkan-sys",
 ]
index 0f6543d684484d8c5f9e57d216eb09e6b8f6f36b..eb8d22d8e67c44480a9c7f9a9983df65889dc165 100644 (file)
@@ -7,4 +7,5 @@ edition = "2021"
 
 [dependencies]
 narcissus-core = { path = "../narcissus-core" }
+blake3-smol = { path = "../../external/blake3-smol" }
 vulkan-sys = { path = "../../external/vulkan-sys" }
\ No newline at end of file
index c8f2da5ec5d7b3ce47500b5982c5462e0c9dfbf0..c5ec0d002c0474d5cef379840070b525c853a18a 100644 (file)
@@ -9,8 +9,10 @@ use std::{
 };
 
 use narcissus_core::{
-    box_assume_init, default, is_aligned_to, manual_arc, manual_arc::ManualArc,
-    raw_window::AsRawWindow, zeroed_box, Arena, HybridArena, Mutex, PhantomUnsend, Pool, Widen,
+    box_assume_init, default, is_aligned_to,
+    manual_arc::{self, ManualArc},
+    raw_window::AsRawWindow,
+    zeroed_box, Arc, Arena, HybridArena, Mutex, PhantomUnsend, Pool, Widen,
 };
 
 use vulkan_sys as vk;
@@ -20,9 +22,9 @@ use crate::{
     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, Sampler, SamplerAddressMode, SamplerCompareOp,
-    SamplerDesc, SamplerFilter, ShaderStageFlags, SwapchainConfigurator, SwapchainImage,
-    SwapchainOutOfDateError, ThreadToken, TransientBuffer, TypedBind,
+    Offset2d, Offset3d, PersistentBuffer, Pipeline, PipelineLayout, Sampler, SamplerAddressMode,
+    SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderStageFlags, SwapchainConfigurator,
+    SwapchainImage, SwapchainOutOfDateError, ThreadToken, TransientBuffer, TypedBind,
 };
 
 mod allocator;
@@ -232,11 +234,18 @@ impl VulkanImageHolder {
 
 struct VulkanSampler(vk::Sampler);
 
-struct VulkanBindGroupLayout(vk::DescriptorSetLayout);
+struct VulkanBindGroupLayout {
+    hash: blake3_smol::Hash,
+    descriptor_set_layout: vk::DescriptorSetLayout,
+}
+
+struct VulkanPipelineLayout {
+    pipeline_layout: vk::PipelineLayout,
+}
 
 struct VulkanPipeline {
     pipeline: vk::Pipeline,
-    pipeline_layout: vk::PipelineLayout,
+    pipeline_layout: Arc<VulkanPipelineLayout>,
     pipeline_bind_point: vk::PipelineBindPoint,
 }
 
@@ -321,7 +330,6 @@ pub(crate) struct VulkanFrame {
     destroyed_image_views: Mutex<VecDeque<vk::ImageView>>,
     destroyed_samplers: Mutex<VecDeque<vk::Sampler>>,
     destroyed_descriptor_set_layouts: Mutex<VecDeque<vk::DescriptorSetLayout>>,
-    destroyed_pipeline_layouts: Mutex<VecDeque<vk::PipelineLayout>>,
     destroyed_pipelines: Mutex<VecDeque<vk::Pipeline>>,
 
     recycled_semaphores: Mutex<VecDeque<vk::Semaphore>>,
@@ -361,6 +369,8 @@ pub(crate) struct VulkanDevice {
     bind_group_layout_pool: Mutex<Pool<VulkanBindGroupLayout>>,
     pipeline_pool: Mutex<Pool<VulkanPipeline>>,
 
+    pipeline_layout_cache: Mutex<HashMap<blake3_smol::Hash, Arc<VulkanPipelineLayout>>>,
+
     recycled_fences: Mutex<VecDeque<vk::Fence>>,
     recycled_semaphores: Mutex<VecDeque<vk::Semaphore>>,
     recycled_descriptor_pools: Mutex<VecDeque<vk::DescriptorPool>>,
@@ -704,7 +714,6 @@ impl VulkanDevice {
                 destroyed_image_views: default(),
                 destroyed_samplers: default(),
                 destroyed_descriptor_set_layouts: default(),
-                destroyed_pipeline_layouts: default(),
                 destroyed_pipelines: default(),
                 recycled_semaphores: default(),
                 recycled_descriptor_pools: default(),
@@ -749,6 +758,8 @@ impl VulkanDevice {
             bind_group_layout_pool: default(),
             pipeline_pool: default(),
 
+            pipeline_layout_cache: default(),
+
             recycled_fences: default(),
             recycled_semaphores: default(),
             recycled_descriptor_pools: default(),
@@ -870,9 +881,6 @@ impl VulkanDevice {
         device: vk::Device,
         frame: &mut VulkanFrame,
     ) {
-        for pipeline_layout in frame.destroyed_pipeline_layouts.get_mut().drain(..) {
-            unsafe { device_fn.destroy_pipeline_layout(device, pipeline_layout, None) }
-        }
         for pipeline in frame.destroyed_pipelines.get_mut().drain(..) {
             unsafe { device_fn.destroy_pipeline(device, pipeline, None) }
         }
@@ -1169,6 +1177,15 @@ impl Device for VulkanDevice {
     }
 
     fn create_bind_group_layout(&self, bindings_desc: &[BindDesc]) -> BindGroupLayout {
+        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());
+        }
+        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 {
@@ -1187,44 +1204,28 @@ impl Device for VulkanDevice {
             bindings: layout_bindings.into(),
             ..default()
         };
-        let mut set_layout = vk::DescriptorSetLayout::null();
+        let mut descriptor_set_layout = vk::DescriptorSetLayout::null();
         vk_check!(self.device_fn.create_descriptor_set_layout(
             self.device,
             create_info,
             None,
-            &mut set_layout,
+            &mut descriptor_set_layout,
         ));
         let bind_group_layout = self
             .bind_group_layout_pool
             .lock()
-            .insert(VulkanBindGroupLayout(set_layout));
+            .insert(VulkanBindGroupLayout {
+                hash,
+                descriptor_set_layout,
+            });
 
         BindGroupLayout(bind_group_layout)
     }
 
     fn create_graphics_pipeline(&self, pipeline_desc: &GraphicsPipelineDesc) -> Pipeline {
-        let arena = HybridArena::<1024>::new();
-        let bind_group_layout_pool = self.bind_group_layout_pool.lock();
-        let set_layouts_iter = pipeline_desc
-            .bind_group_layouts
-            .iter()
-            .map(|bind_group_layout| bind_group_layout_pool.get(bind_group_layout.0).unwrap().0);
-        let set_layouts = arena.alloc_slice_fill_iter(set_layouts_iter);
+        let pipeline_layout = self.cache_pipeline_layout(pipeline_desc.layout);
 
-        let layout = {
-            let create_info = vk::PipelineLayoutCreateInfo {
-                set_layouts: set_layouts.into(),
-                ..default()
-            };
-            let mut pipeline_layout = vk::PipelineLayout::null();
-            vk_check!(self.device_fn.create_pipeline_layout(
-                self.device,
-                &create_info,
-                None,
-                &mut pipeline_layout,
-            ));
-            pipeline_layout
-        };
+        let arena = HybridArena::<1024>::new();
 
         let vertex_module = vulkan_shader_module(
             &self.device_fn,
@@ -1327,7 +1328,7 @@ impl Device for VulkanDevice {
         };
         let color_attachment_formats = arena.alloc_slice_fill_iter(
             pipeline_desc
-                .layout
+                .attachments
                 .color_attachment_formats
                 .iter()
                 .copied()
@@ -1337,11 +1338,11 @@ impl Device for VulkanDevice {
             view_mask: 0,
             color_attachment_formats: color_attachment_formats.into(),
             depth_attachment_format: pipeline_desc
-                .layout
+                .attachments
                 .depth_attachment_format
                 .map_or(vk::Format::Undefined, vulkan_format),
             stencil_attachment_format: pipeline_desc
-                .layout
+                .attachments
                 .stencil_attachment_format
                 .map_or(vk::Format::Undefined, vulkan_format),
             ..default()
@@ -1360,7 +1361,7 @@ impl Device for VulkanDevice {
             depth_stencil_state: Some(&depth_stencil_state),
             color_blend_state: Some(&color_blend_state),
             dynamic_state: Some(&dynamic_state),
-            layout,
+            layout: pipeline_layout.pipeline_layout,
             ..default()
         }];
         let mut pipelines = [vk::Pipeline::null()];
@@ -1383,7 +1384,7 @@ impl Device for VulkanDevice {
 
         let handle = self.pipeline_pool.lock().insert(VulkanPipeline {
             pipeline: pipelines[0],
-            pipeline_layout: layout,
+            pipeline_layout,
             pipeline_bind_point: vk::PipelineBindPoint::Graphics,
         });
 
@@ -1391,28 +1392,7 @@ impl Device for VulkanDevice {
     }
 
     fn create_compute_pipeline(&self, pipeline_desc: &ComputePipelineDesc) -> Pipeline {
-        let arena = HybridArena::<1024>::new();
-        let bind_group_layout_pool = self.bind_group_layout_pool.lock();
-        let set_layouts_iter = pipeline_desc
-            .bind_group_layouts
-            .iter()
-            .map(|bind_group_layout| bind_group_layout_pool.get(bind_group_layout.0).unwrap().0);
-        let set_layouts = arena.alloc_slice_fill_iter(set_layouts_iter);
-
-        let layout = {
-            let create_info = vk::PipelineLayoutCreateInfo {
-                set_layouts: set_layouts.into(),
-                ..default()
-            };
-            let mut pipeline_layout = vk::PipelineLayout::null();
-            vk_check!(self.device_fn.create_pipeline_layout(
-                self.device,
-                &create_info,
-                None,
-                &mut pipeline_layout,
-            ));
-            pipeline_layout
-        };
+        let pipeline_layout = self.cache_pipeline_layout(pipeline_desc.layout);
 
         let module = vulkan_shader_module(&self.device_fn, self.device, pipeline_desc.shader.code);
 
@@ -1424,7 +1404,7 @@ impl Device for VulkanDevice {
         };
 
         let create_infos = &[vk::ComputePipelineCreateInfo {
-            layout,
+            layout: pipeline_layout.pipeline_layout,
             stage,
             ..default()
         }];
@@ -1445,7 +1425,7 @@ impl Device for VulkanDevice {
 
         let handle = self.pipeline_pool.lock().insert(VulkanPipeline {
             pipeline: pipelines[0],
-            pipeline_layout: layout,
+            pipeline_layout,
             pipeline_bind_point: vk::PipelineBindPoint::Compute,
         });
 
@@ -1512,17 +1492,13 @@ impl Device for VulkanDevice {
             self.frame(frame)
                 .destroyed_descriptor_set_layouts
                 .lock()
-                .push_back(bind_group_layout.0)
+                .push_back(bind_group_layout.descriptor_set_layout)
         }
     }
 
     fn destroy_pipeline(&self, frame: &Frame, pipeline: Pipeline) {
         if let Some(pipeline) = self.pipeline_pool.lock().remove(pipeline.0) {
             let frame = self.frame(frame);
-            frame
-                .destroyed_pipeline_layouts
-                .lock()
-                .push_back(pipeline.pipeline_layout);
             frame
                 .destroyed_pipelines
                 .lock()
@@ -1830,7 +1806,12 @@ impl Device for VulkanDevice {
     ) {
         let arena = HybridArena::<4096>::new();
 
-        let descriptor_set_layout = self.bind_group_layout_pool.lock().get(layout.0).unwrap().0;
+        let descriptor_set_layout = self
+            .bind_group_layout_pool
+            .lock()
+            .get(layout.0)
+            .unwrap()
+            .descriptor_set_layout;
 
         let frame = self.frame(frame);
         let per_thread = frame.per_thread.get(cmd_encoder.thread_token);
@@ -2032,11 +2013,16 @@ impl Device for VulkanDevice {
     fn cmd_set_pipeline(&self, cmd_encoder: &mut CmdEncoder, pipeline: Pipeline) {
         let cmd_encoder = self.cmd_encoder_mut(cmd_encoder);
 
-        let VulkanPipeline {
-            pipeline,
-            pipeline_layout,
-            pipeline_bind_point,
-        } = *self.pipeline_pool.lock().get(pipeline.0).unwrap();
+        let vk_pipeline;
+        let pipeline_layout;
+        let pipeline_bind_point;
+        {
+            let pipeline_pool = self.pipeline_pool.lock();
+            let pipeline = pipeline_pool.get(pipeline.0).unwrap();
+            vk_pipeline = pipeline.pipeline;
+            pipeline_layout = pipeline.pipeline_layout.pipeline_layout;
+            pipeline_bind_point = pipeline.pipeline_bind_point;
+        }
 
         cmd_encoder.bound_pipeline = Some(VulkanBoundPipeline {
             pipeline_layout,
@@ -2047,7 +2033,7 @@ impl Device for VulkanDevice {
 
         unsafe {
             self.device_fn
-                .cmd_bind_pipeline(command_buffer, pipeline_bind_point, pipeline)
+                .cmd_bind_pipeline(command_buffer, pipeline_bind_point, vk_pipeline)
         };
     }
 
@@ -2692,6 +2678,66 @@ impl VulkanDevice {
             ),
         }
     }
+
+    fn cache_pipeline_layout(&self, pipeline_layout: &PipelineLayout) -> Arc<VulkanPipelineLayout> {
+        let hash = {
+            let mut hasher = blake3_smol::Hasher::new();
+            let bind_group_layout_pool = self.bind_group_layout_pool.lock();
+            for bind_group_layout in pipeline_layout.bind_group_layouts {
+                hasher.update(
+                    bind_group_layout_pool
+                        .get(bind_group_layout.0)
+                        .unwrap()
+                        .hash
+                        .as_bytes(),
+                );
+            }
+            hasher.finalize()
+        };
+
+        let mut cache = self.pipeline_layout_cache.lock();
+        let entry = cache.entry(hash);
+
+        entry
+            .or_insert_with(
+                #[cold]
+                || {
+                    let arena = HybridArena::<1024>::new();
+
+                    let bind_group_layout_pool = self.bind_group_layout_pool.lock();
+
+                    let set_layouts_iter =
+                        pipeline_layout
+                            .bind_group_layouts
+                            .iter()
+                            .map(|bind_group_layout| {
+                                bind_group_layout_pool
+                                    .get(bind_group_layout.0)
+                                    .unwrap()
+                                    .descriptor_set_layout
+                            });
+                    let set_layouts = arena.alloc_slice_fill_iter(set_layouts_iter);
+
+                    let pipeline_layout = {
+                        let create_info = vk::PipelineLayoutCreateInfo {
+                            set_layouts: set_layouts.into(),
+                            ..default()
+                        };
+                        let mut pipeline_layout = vk::PipelineLayout::null();
+                        vk_check!(self.device_fn.create_pipeline_layout(
+                            self.device,
+                            &create_info,
+                            None,
+                            &mut pipeline_layout,
+                        ));
+                        pipeline_layout
+                    };
+
+                    Arc::new(VulkanPipelineLayout { pipeline_layout })
+                },
+            )
+            .clone()
+    }
 }
 
 impl Drop for VulkanDevice {
@@ -2788,20 +2834,29 @@ impl Drop for VulkanDevice {
         }
 
         for pipeline in self.pipeline_pool.get_mut().values() {
-            unsafe {
-                self.device_fn
-                    .destroy_pipeline_layout(self.device, pipeline.pipeline_layout, None)
-            };
             unsafe {
                 self.device_fn
                     .destroy_pipeline(device, pipeline.pipeline, None)
             }
         }
 
-        for descriptor_set_layout in self.bind_group_layout_pool.get_mut().values() {
+        for pipeline_layout in self.pipeline_layout_cache.get_mut().values() {
             unsafe {
-                self.device_fn
-                    .destroy_descriptor_set_layout(device, descriptor_set_layout.0, None)
+                self.device_fn.destroy_pipeline_layout(
+                    device,
+                    pipeline_layout.pipeline_layout,
+                    None,
+                );
+            }
+        }
+
+        for bind_group_layout in self.bind_group_layout_pool.get_mut().values() {
+            unsafe {
+                self.device_fn.destroy_descriptor_set_layout(
+                    device,
+                    bind_group_layout.descriptor_set_layout,
+                    None,
+                )
             }
         }
 
index def3cad65b818574f438bd716caedda51b6358cc..c44aaac1cfede3b2b9e82820f83b0e8a05c2c156 100644 (file)
@@ -394,7 +394,12 @@ pub struct DepthBias {
     pub slope_factor: f32,
 }
 
-pub struct GraphicsPipelineLayout<'a> {
+#[derive(PartialEq, Eq, Hash)]
+pub struct PipelineLayout<'a> {
+    pub bind_group_layouts: &'a [BindGroupLayout],
+}
+
+pub struct GraphicsPipelineAttachments<'a> {
     pub color_attachment_formats: &'a [ImageFormat],
     pub depth_attachment_format: Option<ImageFormat>,
     pub stencil_attachment_format: Option<ImageFormat>,
@@ -403,8 +408,8 @@ 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>,
+    pub layout: &'a PipelineLayout<'a>,
+    pub attachments: GraphicsPipelineAttachments<'a>,
     pub topology: Topology,
     pub primitive_restart: bool,
     pub polygon_mode: PolygonMode,
@@ -422,7 +427,7 @@ pub struct GraphicsPipelineDesc<'a> {
 
 pub struct ComputePipelineDesc<'a> {
     pub shader: ShaderDesc<'a>,
-    pub bind_group_layouts: &'a [BindGroupLayout],
+    pub layout: &'a PipelineLayout<'a>,
 }
 
 #[derive(Clone, Copy, Debug)]
index 64bfaa91f0d24bb3d8018a86384d3f708e28bc54..1fa174a285c5294cdd2c786ecf7dffd74f6ccdf9 100644 (file)
@@ -1,8 +1,8 @@
 use narcissus_core::default;
 use narcissus_gpu::{
     BindDesc, BindGroupLayout, BindingType, BlendMode, CompareOp, CullingMode, FrontFace,
-    GraphicsPipelineDesc, GraphicsPipelineLayout, ImageFormat, Pipeline, PolygonMode, ShaderDesc,
-    ShaderStageFlags, Topology,
+    GraphicsPipelineAttachments, GraphicsPipelineDesc, ImageFormat, Pipeline, PipelineLayout,
+    PolygonMode, ShaderDesc, ShaderStageFlags, Topology,
 };
 use narcissus_maths::Mat4;
 
@@ -46,6 +46,10 @@ impl BasicPipeline {
             BindDesc::new(ShaderStageFlags::ALL, BindingType::SampledImage),
         ]);
 
+        let layout = &PipelineLayout {
+            bind_group_layouts: &[uniforms_bind_group_layout, storage_bind_group_layout],
+        };
+
         let pipeline = gpu.create_graphics_pipeline(&GraphicsPipelineDesc {
             vertex_shader: ShaderDesc {
                 entry: c"main",
@@ -55,8 +59,8 @@ impl BasicPipeline {
                 entry: c"main",
                 code: shark_shaders::BASIC_FRAG_SPV,
             },
-            bind_group_layouts: &[uniforms_bind_group_layout, storage_bind_group_layout],
-            layout: GraphicsPipelineLayout {
+            layout,
+            attachments: GraphicsPipelineAttachments {
                 color_attachment_formats: &[ImageFormat::RGBA16_FLOAT],
                 depth_attachment_format: Some(ImageFormat::DEPTH_F32),
                 stencil_attachment_format: None,
index 09f34e3e86b8aa746799a869aee1f9ba554f0abe..9ba4ec9152f5578fd74fcfb4c4c1c358827ec056 100644 (file)
@@ -1,6 +1,6 @@
 use narcissus_gpu::{
-    BindDesc, BindGroupLayout, BindingType, ComputePipelineDesc, Pipeline, ShaderDesc,
-    ShaderStageFlags,
+    BindDesc, BindGroupLayout, BindingType, ComputePipelineDesc, Pipeline, PipelineLayout,
+    ShaderDesc, ShaderStageFlags,
 };
 
 use crate::Gpu;
@@ -25,12 +25,16 @@ impl DisplayTransformPipeline {
             BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
         ]);
 
+        let layout = &PipelineLayout {
+            bind_group_layouts: &[bind_group_layout],
+        };
+
         let pipeline = gpu.create_compute_pipeline(&ComputePipelineDesc {
             shader: ShaderDesc {
                 entry: c"main",
                 code: shark_shaders::DISPLAY_TRANSFORM_COMP_SPV,
             },
-            bind_group_layouts: &[bind_group_layout],
+            layout,
         });
 
         Self {
index c91b00d0c305fbbd622ba96cd16104335b4f923f..01cc83363863fbdf9b2998ad3e7de21939d907ea 100644 (file)
@@ -1,7 +1,7 @@
 use narcissus_font::TouchedGlyphIndex;
 use narcissus_gpu::{
-    BindDesc, BindGroupLayout, BindingType, ComputePipelineDesc, Pipeline, ShaderDesc,
-    ShaderStageFlags,
+    BindDesc, BindGroupLayout, BindingType, ComputePipelineDesc, Pipeline, PipelineLayout,
+    ShaderDesc, ShaderStageFlags,
 };
 
 use crate::Gpu;
@@ -72,12 +72,16 @@ impl Primitive2dPipeline {
             BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
         ]);
 
+        let layout = &PipelineLayout {
+            bind_group_layouts: &[bind_group_layout],
+        };
+
         let coarse_bin_pipeline = gpu.create_compute_pipeline(&ComputePipelineDesc {
             shader: ShaderDesc {
                 entry: c"main",
                 code: shark_shaders::PRIMITIVE_2D_BIN_COARSE_COMP_SPV,
             },
-            bind_group_layouts: &[bind_group_layout],
+            layout,
         });
 
         let fine_bin_pipeline = gpu.create_compute_pipeline(&ComputePipelineDesc {
@@ -85,7 +89,7 @@ impl Primitive2dPipeline {
                 entry: c"main",
                 code: shark_shaders::PRIMITIVE_2D_BIN_FINE_COMP_SPV,
             },
-            bind_group_layouts: &[bind_group_layout],
+            layout,
         });
 
         let rasterize_pipeline = gpu.create_compute_pipeline(&ComputePipelineDesc {
@@ -93,7 +97,7 @@ impl Primitive2dPipeline {
                 entry: c"main",
                 code: shark_shaders::PRIMITIVE_2D_RASTERIZE_COMP_SPV,
             },
-            bind_group_layouts: &[bind_group_layout],
+            layout,
         });
 
         Self {