]> git.nega.tv - josh/narcissus/commitdiff
Add `BindGroupLayout`
authorJoshua Simmons <josh@nega.tv>
Sat, 29 Oct 2022 14:14:54 +0000 (16:14 +0200)
committerJoshua Simmons <josh@nega.tv>
Sat, 29 Oct 2022 14:14:54 +0000 (16:14 +0200)
narcissus-gpu/src/lib.rs
narcissus-gpu/src/vulkan.rs
narcissus/src/main.rs

index e78813eb34bd5b849d3312c7420787f19abb0976..d1dd1f168e08edd7615d8aba31753ec8f0cbb13c 100644 (file)
@@ -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<TextureFormat>,
@@ -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(
index 13e4455d2ff5c5ab416c177be3a02d26fae34763..c504f52e4cc979887f122421e02dc10fcba045f6 100644 (file)
@@ -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<T> {
     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<VulkanTextureHolder>,
     buffers: Pool<VulkanBuffer>,
     samplers: Pool<VulkanSampler>,
+    bind_group_layouts: Pool<VulkanBindGroupLayout>,
     pipelines: Pool<VulkanPipeline>,
 }
 
@@ -303,6 +334,7 @@ struct VulkanFrame {
     destroyed_images: Mutex<VecDeque<vk::Image>>,
     destroyed_image_views: Mutex<VecDeque<vk::ImageView>>,
     destroyed_samplers: Mutex<VecDeque<vk::Sampler>>,
+    destroyed_descriptor_set_layouts: Mutex<VecDeque<vk::DescriptorSetLayout>>,
     destroyed_pipelines: Mutex<VecDeque<vk::Pipeline>>,
 
     recycled_semaphores: Mutex<VecDeque<vk::Semaphore>>,
@@ -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::<Vec<_>>();
+
+        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) };
     }
index b6e601f5f6f563efd96273c291503d79eb1789ff..1a8175c6ec6a2f263254d96b08f10252e1f4aaff 100644 (file)
@@ -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,