]> git.nega.tv - josh/narcissus/commitdiff
narcissus-gpu: Plumb through mesh shader calls
authorJoshua Simmons <josh@nega.tv>
Tue, 21 Oct 2025 17:41:55 +0000 (19:41 +0200)
committerJoshua Simmons <josh@nega.tv>
Wed, 22 Oct 2025 21:33:24 +0000 (23:33 +0200)
Additionally, require mesh shaders by default.

engine/narcissus-gpu/src/backend/vulkan/barrier.rs
engine/narcissus-gpu/src/backend/vulkan/convert.rs
engine/narcissus-gpu/src/backend/vulkan/mod.rs
engine/narcissus-gpu/src/backend/vulkan/physical_device_features.rs
engine/narcissus-gpu/src/lib.rs

index d900a8e6a26514882648488c1ddfbd094a01d832..4fab1915546652e8122936bbf92bb655e574cea1 100644 (file)
@@ -53,6 +53,22 @@ pub fn vulkan_access_info(access: Access) -> VulkanAccessInfo {
             layout: vk::ImageLayout::General,
         },
 
+        Access::MeshShaderUniformBufferRead => VulkanAccessInfo {
+            stages: vk::PipelineStageFlags2::MESH_SHADER_EXT,
+            access: vk::AccessFlags2::UNIFORM_READ,
+            layout: vk::ImageLayout::Undefined,
+        },
+        Access::MeshShaderSampledImageRead => VulkanAccessInfo {
+            stages: vk::PipelineStageFlags2::MESH_SHADER_EXT,
+            access: vk::AccessFlags2::SHADER_READ,
+            layout: vk::ImageLayout::ReadOnlyOptimal,
+        },
+        Access::MeshShaderOtherRead => VulkanAccessInfo {
+            stages: vk::PipelineStageFlags2::MESH_SHADER_EXT,
+            access: vk::AccessFlags2::SHADER_READ,
+            layout: vk::ImageLayout::General,
+        },
+
         Access::FragmentShaderUniformBufferRead => VulkanAccessInfo {
             stages: vk::PipelineStageFlags2::FRAGMENT_SHADER,
             access: vk::AccessFlags2::UNIFORM_READ,
@@ -129,6 +145,11 @@ pub fn vulkan_access_info(access: Access) -> VulkanAccessInfo {
             access: vk::AccessFlags2::SHADER_WRITE,
             layout: vk::ImageLayout::General,
         },
+        Access::MeshWrite => VulkanAccessInfo {
+            stages: vk::PipelineStageFlags2::MESH_SHADER_EXT,
+            access: vk::AccessFlags2::SHADER_WRITE,
+            layout: vk::ImageLayout::General,
+        },
         Access::FragmentShaderWrite => VulkanAccessInfo {
             stages: vk::PipelineStageFlags2::FRAGMENT_SHADER,
             access: vk::AccessFlags2::SHADER_WRITE,
index c4d323fc7e4344c6ad60823ccbb3c263a63cec68..90267d7666196a2795d2c5f97523e8ae7dc2e895 100644 (file)
@@ -182,14 +182,17 @@ pub fn vulkan_store_op(store_op: StoreOp) -> vk::AttachmentStoreOp {
 #[must_use]
 pub 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::VERTEX) {
+        flags |= vk::ShaderStageFlags::VERTEX;
+    }
+    if stage_flags.contains(ShaderStageFlags::MESH) {
+        flags |= vk::ShaderStageFlags::MESH_EXT;
     }
     if stage_flags.contains(ShaderStageFlags::FRAGMENT) {
         flags |= vk::ShaderStageFlags::FRAGMENT;
     }
-    if stage_flags.contains(ShaderStageFlags::VERTEX) {
-        flags |= vk::ShaderStageFlags::VERTEX;
+    if stage_flags.contains(ShaderStageFlags::COMPUTE) {
+        flags |= vk::ShaderStageFlags::COMPUTE;
     }
     flags
 }
index 6573e627e82ee712f83d5e281feebab4569ef0a0..175cf0a3bd1ac919f15d68bef6212e57c2083d3e 100644 (file)
@@ -24,9 +24,9 @@ use crate::{
     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, SpecConstant,
+    SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderDesc, ShaderStageFlags, SpecConstant,
     SwapchainConfigurator, SwapchainImage, SwapchainOutOfDateError, ThreadToken, TransientBuffer,
-    TypedBind, frame_counter::FrameCounter, mapped_buffer::TransientBindGroup,
+    TypedBind, VertexOrMeshShader, frame_counter::FrameCounter, mapped_buffer::TransientBindGroup,
 };
 
 mod allocator;
@@ -187,6 +187,91 @@ fn vulkan_shader_module(
     shader_module
 }
 
+fn vulkan_pipeline_shader_stage_create_flags(
+    shader_desc: &ShaderDesc<'_>,
+) -> vk::PipelineShaderStageCreateFlags {
+    let mut flags = default();
+
+    if shader_desc.require_full_subgroups {
+        flags |= vk::PipelineShaderStageCreateFlags::REQUIRE_FULL_SUBGROUPS
+    }
+
+    if shader_desc.allow_varying_subgroup_size {
+        flags |= vk::PipelineShaderStageCreateFlags::ALLOW_VARYING_SUBGROUP_SIZE;
+    }
+
+    flags
+}
+
+fn vulkan_specialization_info<'a>(
+    arena: &'a HybridArena<1024>,
+    spec_constants: &'a [SpecConstant],
+) -> Option<&'a vk::SpecializationInfo<'a>> {
+    if spec_constants.is_empty() {
+        return None;
+    }
+
+    let block_len = spec_constants
+        .iter()
+        .map(|spec_constant| match spec_constant {
+            SpecConstant::Bool { id: _, value: _ }
+            | SpecConstant::U32 { id: _, value: _ }
+            | SpecConstant::I32 { id: _, value: _ }
+            | SpecConstant::F32 { id: _, value: _ } => 4,
+        })
+        .sum::<usize>();
+
+    let block = arena.alloc_slice_fill_copy(block_len, 0u8);
+
+    let mut offset = 0;
+    let map_entries = arena.alloc_slice_fill_iter(spec_constants.iter().map(|spec_constant| {
+        let constant_id;
+        let value_size;
+        match *spec_constant {
+            SpecConstant::Bool { id, value } => {
+                constant_id = id;
+                let value = if value {
+                    vk::Bool32::True
+                } else {
+                    vk::Bool32::False
+                } as u32;
+                value_size = std::mem::size_of_val(&value);
+                block[offset..offset + value_size].copy_from_slice(&value.to_ne_bytes())
+            }
+            SpecConstant::U32 { id, value } => {
+                constant_id = id;
+                value_size = std::mem::size_of_val(&value);
+                block[offset..offset + value_size].copy_from_slice(&value.to_ne_bytes());
+            }
+            SpecConstant::I32 { id, value } => {
+                constant_id = id;
+                value_size = std::mem::size_of_val(&value);
+                block[offset..offset + value_size].copy_from_slice(&value.to_ne_bytes());
+            }
+            SpecConstant::F32 { id, value } => {
+                constant_id = id;
+                value_size = std::mem::size_of_val(&value);
+                block[offset..offset + value_size].copy_from_slice(&value.to_ne_bytes());
+            }
+        }
+
+        let map_entry = vk::SpecializationMapEntry {
+            constant_id,
+            offset: offset as u32,
+            size: value_size,
+        };
+
+        offset += value_size;
+
+        map_entry
+    }));
+
+    Some(arena.alloc(vk::SpecializationInfo {
+        data: block.into(),
+        map_entries: map_entries.into(),
+    }) as &vk::SpecializationInfo)
+}
+
 struct VulkanBuffer {
     memory: VulkanMemory,
     buffer: vk::Buffer,
@@ -395,6 +480,8 @@ pub(crate) struct VulkanDevice {
     instance_fn: vk::InstanceFunctions,
     device_fn: vk::DeviceFunctions,
 
+    mesh_shader_fn: vk::MeshShaderFunctions,
+
     #[cfg(feature = "debug_markers")]
     debug_utils_fn: Option<vk::DebugUtilsFunctions>,
 }
@@ -527,6 +614,7 @@ impl VulkanDevice {
                 }
 
                 physical_device_properties.api_version() >= vk::VERSION_1_3
+                    && physical_device_features.mesh_shader()
                     && physical_device_features.dynamic_rendering()
                     && physical_device_features.subgroup_size_control()
                     && physical_device_features.maintenance4()
@@ -581,7 +669,7 @@ impl VulkanDevice {
                 )
             });
 
-            let mut enabled_extensions = vec![];
+            let mut enabled_extensions = vec![c"VK_EXT_mesh_shader"];
 
             VulkanWsi::check_device_extensions(
                 &extension_properties,
@@ -596,6 +684,7 @@ impl VulkanDevice {
 
             let mut enabled_features: Box<VulkanPhysicalDeviceFeatures> = default();
 
+            enabled_features.set_mesh_shader(true);
             enabled_features.set_buffer_device_address(true);
             enabled_features.set_compute_full_subgroups(true);
             enabled_features.set_descriptor_binding_partially_bound(true);
@@ -634,6 +723,8 @@ impl VulkanDevice {
 
         let device_fn = vk::DeviceFunctions::new(&instance_fn, device, vk::VERSION_1_3);
 
+        let mesh_shader_fn = vk::MeshShaderFunctions::new(&instance_fn, device);
+
         #[cfg(feature = "debug_markers")]
         let debug_utils_fn = if has_debug_utils {
             Some(vk::DebugUtilsFunctions::new(
@@ -761,6 +852,8 @@ impl VulkanDevice {
             instance_fn,
             device_fn,
 
+            mesh_shader_fn,
+
             #[cfg(feature = "debug_markers")]
             debug_utils_fn,
         }
@@ -1303,11 +1396,38 @@ impl Device for VulkanDevice {
 
         let arena = HybridArena::<1024>::new();
 
-        let vertex_module = vulkan_shader_module(
+        let vertex_or_mesh_shader_desc = match &pipeline_desc.vertex_or_mesh_shader {
+            VertexOrMeshShader::Vertex(shader_desc) => {
+                assert_eq!(
+                    shader_desc.required_subgroup_size, None,
+                    "cannot use subgroup size control on vertex shaders"
+                );
+                shader_desc
+            }
+            VertexOrMeshShader::Mesh(shader_desc) => shader_desc,
+        };
+
+        let vertex_or_mesh_module = vulkan_shader_module(
             &self.device_fn,
             self.device,
-            pipeline_desc.vertex_shader.code,
+            vertex_or_mesh_shader_desc.code,
         );
+
+        let vertex_or_mesh_stage_flags = match &pipeline_desc.vertex_or_mesh_shader {
+            VertexOrMeshShader::Vertex(_) => ShaderStageFlags::VERTEX,
+            VertexOrMeshShader::Mesh(_) => ShaderStageFlags::MESH,
+        };
+
+        let vertex_or_mesh_shader_stage_create_flags =
+            vulkan_pipeline_shader_stage_create_flags(&pipeline_desc.fragment_shader);
+
+        let vertex_or_mesh_specialization_info = match &pipeline_desc.vertex_or_mesh_shader {
+            VertexOrMeshShader::Vertex(_shader_desc) => None,
+            VertexOrMeshShader::Mesh(shader_desc) => {
+                vulkan_specialization_info(&arena, shader_desc.spec_constants)
+            }
+        };
+
         let fragment_module = vulkan_shader_module(
             &self.device_fn,
             self.device,
@@ -1315,30 +1435,61 @@ impl Device for VulkanDevice {
         );
 
         assert!(
-            !(pipeline_desc.vertex_shader.required_subgroup_size.is_some()
-                || pipeline_desc
-                    .fragment_shader
-                    .required_subgroup_size
-                    .is_some()
-                || pipeline_desc.vertex_shader.allow_varying_subgroup_size
+            !(pipeline_desc
+                .fragment_shader
+                .required_subgroup_size
+                .is_some()
                 || pipeline_desc.fragment_shader.allow_varying_subgroup_size),
-            "subgroup size control features not implemented for graphics shader stages"
+            "subgroup size control features not implemented for fragment shaders"
         );
 
-        let stages = &[
+        let fragment_shader_stage_create_flags =
+            vulkan_pipeline_shader_stage_create_flags(&pipeline_desc.fragment_shader);
+
+        let fragment_specialization_info =
+            vulkan_specialization_info(&arena, pipeline_desc.fragment_shader.spec_constants);
+
+        let stages = arena.alloc([
             vk::PipelineShaderStageCreateInfo {
-                stage: vk::ShaderStageFlags::VERTEX,
-                name: pipeline_desc.vertex_shader.entry.as_ptr(),
-                module: vertex_module,
+                stage: vulkan_shader_stage_flags(vertex_or_mesh_stage_flags),
+                name: vertex_or_mesh_shader_desc.entry.as_ptr(),
+                module: vertex_or_mesh_module,
+                flags: vertex_or_mesh_shader_stage_create_flags,
+                specialization_info: vertex_or_mesh_specialization_info,
                 ..default()
             },
             vk::PipelineShaderStageCreateInfo {
                 stage: vk::ShaderStageFlags::FRAGMENT,
                 name: pipeline_desc.fragment_shader.entry.as_ptr(),
                 module: fragment_module,
+                flags: fragment_shader_stage_create_flags,
+                specialization_info: fragment_specialization_info,
                 ..default()
             },
-        ];
+        ]);
+
+        if let Some(required_subgroup_size) = vertex_or_mesh_shader_desc.required_subgroup_size {
+            assert!(
+                self.physical_device_properties
+                    .required_subgroup_size_stages()
+                    .contains(vk::ShaderStageFlags::MESH_EXT)
+            );
+            assert!(
+                required_subgroup_size >= self.physical_device_properties.min_subgroup_size()
+                    && required_subgroup_size
+                        <= self.physical_device_properties.max_subgroup_size()
+            );
+
+            let shader_stage_required_subgroup_size_create_info =
+                arena.alloc(vk::PipelineShaderStageRequiredSubgroupSizeCreateInfo {
+                    required_subgroup_size,
+                    ..default()
+                });
+
+            // SAFETY: Both allocated from the same arena.
+            stages[0]._next =
+                shader_stage_required_subgroup_size_create_info as *const _ as *const _;
+        }
 
         let topology = vulkan_primitive_topology(pipeline_desc.topology);
         let primitive_restart_enable = vulkan_bool32(pipeline_desc.primitive_restart);
@@ -1421,7 +1572,7 @@ impl Device for VulkanDevice {
                 .copied()
                 .map(vulkan_format),
         );
-        let pipeline_rendering_create_info = vk::PipelineRenderingCreateInfo {
+        let pipeline_rendering_create_info = arena.alloc(vk::PipelineRenderingCreateInfo {
             view_mask: 0,
             color_attachment_formats: color_attachment_formats.into(),
             depth_attachment_format: pipeline_desc
@@ -1433,10 +1584,10 @@ impl Device for VulkanDevice {
                 .stencil_attachment_format
                 .map_or(vk::Format::Undefined, vulkan_format),
             ..default()
-        };
+        });
 
-        let create_infos = &mut [vk::GraphicsPipelineCreateInfo {
-            _next: &pipeline_rendering_create_info as *const vk::PipelineRenderingCreateInfo
+        let graphics_pipeline_create_info = vk::GraphicsPipelineCreateInfo {
+            _next: pipeline_rendering_create_info as *const vk::PipelineRenderingCreateInfo
                 as *const _,
             stages: stages.into(),
             vertex_input_state: Some(&vertex_input_state),
@@ -1450,7 +1601,9 @@ impl Device for VulkanDevice {
             dynamic_state: Some(&dynamic_state),
             layout: pipeline_layout.pipeline_layout,
             ..default()
-        }];
+        };
+
+        let create_infos = &[graphics_pipeline_create_info];
         let mut pipelines = [vk::Pipeline::null()];
         vk_check!(unsafe {
             self.device_fn.create_graphics_pipelines(
@@ -1464,7 +1617,7 @@ impl Device for VulkanDevice {
 
         unsafe {
             self.device_fn
-                .destroy_shader_module(self.device, vertex_module, None)
+                .destroy_shader_module(self.device, vertex_or_mesh_module, None)
         };
         unsafe {
             self.device_fn
@@ -1486,91 +1639,10 @@ impl Device for VulkanDevice {
         let pipeline_layout = self.cache_pipeline_layout(&pipeline_desc.layout);
 
         let module = vulkan_shader_module(&self.device_fn, self.device, pipeline_desc.shader.code);
+        let flags = vulkan_pipeline_shader_stage_create_flags(&pipeline_desc.shader);
 
-        let mut shader_stage_create_flags = default();
-
-        if pipeline_desc.shader.require_full_subgroups {
-            shader_stage_create_flags |= vk::PipelineShaderStageCreateFlags::REQUIRE_FULL_SUBGROUPS
-        }
-
-        if pipeline_desc.shader.allow_varying_subgroup_size {
-            shader_stage_create_flags |=
-                vk::PipelineShaderStageCreateFlags::ALLOW_VARYING_SUBGROUP_SIZE;
-        }
-
-        let specialization_info: Option<&vk::SpecializationInfo> =
-            if !pipeline_desc.shader.spec_constants.is_empty() {
-                let block_len = pipeline_desc
-                    .shader
-                    .spec_constants
-                    .iter()
-                    .map(|spec_constant| match spec_constant {
-                        SpecConstant::Bool { id: _, value: _ }
-                        | SpecConstant::U32 { id: _, value: _ }
-                        | SpecConstant::I32 { id: _, value: _ }
-                        | SpecConstant::F32 { id: _, value: _ } => 4,
-                    })
-                    .sum::<usize>();
-
-                let block = arena.alloc_slice_fill_copy(block_len, 0u8);
-
-                let mut offset = 0;
-                let map_entries =
-                    arena.alloc_slice_fill_iter(pipeline_desc.shader.spec_constants.iter().map(
-                        |spec_constant| {
-                            let constant_id;
-                            let value_size;
-                            match *spec_constant {
-                                SpecConstant::Bool { id, value } => {
-                                    constant_id = id;
-                                    let value = if value {
-                                        vk::Bool32::True
-                                    } else {
-                                        vk::Bool32::False
-                                    } as u32;
-                                    value_size = std::mem::size_of_val(&value);
-                                    block[offset..offset + value_size]
-                                        .copy_from_slice(&value.to_ne_bytes())
-                                }
-                                SpecConstant::U32 { id, value } => {
-                                    constant_id = id;
-                                    value_size = std::mem::size_of_val(&value);
-                                    block[offset..offset + value_size]
-                                        .copy_from_slice(&value.to_ne_bytes());
-                                }
-                                SpecConstant::I32 { id, value } => {
-                                    constant_id = id;
-                                    value_size = std::mem::size_of_val(&value);
-                                    block[offset..offset + value_size]
-                                        .copy_from_slice(&value.to_ne_bytes());
-                                }
-                                SpecConstant::F32 { id, value } => {
-                                    constant_id = id;
-                                    value_size = std::mem::size_of_val(&value);
-                                    block[offset..offset + value_size]
-                                        .copy_from_slice(&value.to_ne_bytes());
-                                }
-                            }
-
-                            let map_entry = vk::SpecializationMapEntry {
-                                constant_id,
-                                offset: offset as u32,
-                                size: value_size,
-                            };
-
-                            offset += value_size;
-
-                            map_entry
-                        },
-                    ));
-
-                Some(arena.alloc(vk::SpecializationInfo {
-                    data: block.into(),
-                    map_entries: map_entries.into(),
-                }))
-            } else {
-                None
-            };
+        let specialization_info =
+            vulkan_specialization_info(&arena, pipeline_desc.shader.spec_constants);
 
         let compute_pipeline_create_info = arena.alloc(vk::ComputePipelineCreateInfo {
             layout: pipeline_layout.pipeline_layout,
@@ -1578,7 +1650,7 @@ impl Device for VulkanDevice {
                 stage: vk::ShaderStageFlags::COMPUTE,
                 name: pipeline_desc.shader.entry.as_ptr(),
                 module,
-                flags: shader_stage_create_flags,
+                flags,
                 specialization_info,
                 ..default()
             },
@@ -2670,6 +2742,71 @@ impl Device for VulkanDevice {
         }
     }
 
+    fn cmd_draw_mesh_tasks(
+        &self,
+        cmd_encoder: &mut CmdEncoder,
+        group_count_x: u32,
+        group_count_y: u32,
+        group_count_z: u32,
+    ) {
+        let command_buffer = self.cmd_encoder_mut(cmd_encoder).command_buffer;
+        unsafe {
+            self.mesh_shader_fn.cmd_draw_mesh_tasks_ext(
+                command_buffer,
+                group_count_x,
+                group_count_y,
+                group_count_z,
+            )
+        }
+    }
+
+    fn cmd_draw_mesh_tasks_indirect(
+        &self,
+        cmd_encoder: &mut CmdEncoder,
+        buffer: BufferArg,
+        offset: u64,
+        draw_count: u32,
+        stride: u32,
+    ) {
+        let (buffer, base_offset, _range) = self.unwrap_buffer_arg(&buffer);
+        let command_buffer = self.cmd_encoder_mut(cmd_encoder).command_buffer;
+        unsafe {
+            self.mesh_shader_fn.cmd_draw_mesh_tasks_indirect_ext(
+                command_buffer,
+                buffer,
+                base_offset + offset,
+                draw_count,
+                stride,
+            )
+        }
+    }
+
+    fn cmd_draw_mesh_tasks_indirect_count(
+        &self,
+        cmd_encoder: &mut CmdEncoder,
+        buffer: BufferArg,
+        offset: u64,
+        count_buffer: BufferArg,
+        count_buffer_offset: u64,
+        max_draw_count: u32,
+        stride: u32,
+    ) {
+        let (buffer, base_offset, _range) = self.unwrap_buffer_arg(&buffer);
+        let (count_buffer, count_base_offset, _range) = self.unwrap_buffer_arg(&count_buffer);
+        let command_buffer = self.cmd_encoder_mut(cmd_encoder).command_buffer;
+        unsafe {
+            self.mesh_shader_fn.cmd_draw_mesh_tasks_indirect_count_ext(
+                command_buffer,
+                buffer,
+                base_offset + offset,
+                count_buffer,
+                count_base_offset + count_buffer_offset,
+                max_draw_count,
+                stride,
+            )
+        }
+    }
+
     fn cmd_dispatch(
         &self,
         cmd_encoder: &mut CmdEncoder,
index e8d5d5b992d43d3c5ba4edf12f96774812228fb9..fe6d9dde06e78217d57eaa62e4deca4d3fa9d51d 100644 (file)
@@ -7,9 +7,14 @@ pub struct VulkanPhysicalDeviceFeatures {
     features_12: vk::PhysicalDeviceVulkan12Features,
     features_13: vk::PhysicalDeviceVulkan13Features,
     features_swapchain_maintenance1: vk::PhysicalDeviceSwapchainMaintenance1FeaturesEXT,
+    features_mesh_shader: vk::PhysicalDeviceMeshShaderFeaturesExt,
 }
 
 impl VulkanPhysicalDeviceFeatures {
+    pub fn mesh_shader(&self) -> bool {
+        self.features_mesh_shader.mesh_shader == vk::Bool32::True
+    }
+
     pub fn compute_full_subgroups(&self) -> bool {
         self.features_13.compute_full_subgroups == vk::Bool32::True
     }
@@ -62,6 +67,10 @@ impl VulkanPhysicalDeviceFeatures {
         self.features_12.scalar_block_layout == vk::Bool32::True
     }
 
+    pub fn set_mesh_shader(&mut self, arg: bool) {
+        self.features_mesh_shader.mesh_shader = vk::Bool32::from(arg)
+    }
+
     pub fn set_buffer_device_address(&mut self, arg: bool) {
         self.features_12.buffer_device_address = vk::Bool32::from(arg)
     }
@@ -139,6 +148,8 @@ impl VulkanPhysicalDeviceFeatures {
     }
 
     pub fn link(&mut self) -> &mut vk::PhysicalDeviceFeatures2 {
+        self.features_swapchain_maintenance1._next =
+            &mut self.features_mesh_shader as *mut _ as *mut _;
         self.features_13._next = &mut self.features_swapchain_maintenance1 as *mut _ as *mut _;
         self.features_12._next = &mut self.features_13 as *mut _ as *mut _;
         self.features_11._next = &mut self.features_12 as *mut _ as *mut _;
index 477a284f7e6914345452fb9a87595a81e18c59a6..12c1ef2c7464d728f85d42efc086280d1169b37b 100644 (file)
@@ -129,9 +129,10 @@ pub enum PipelineBindPoint {
 flags_def!(ShaderStageFlags);
 impl ShaderStageFlags {
     pub const VERTEX: Self = Self(1 << 0);
-    pub const FRAGMENT: Self = Self(1 << 1);
-    pub const COMPUTE: Self = Self(1 << 2);
-    pub const ALL: Self = Self(0b111); /* Self::VERTEX | Self::FRAGMENT | Self::COMPUTE */
+    pub const MESH: Self = Self(1 << 1);
+    pub const FRAGMENT: Self = Self(1 << 2);
+    pub const COMPUTE: Self = Self(1 << 3);
+    pub const ALL: Self = Self(0b1111); /* Self::VERTEX | Self::MESH | Self::FRAGMENT | Self::COMPUTE */
 }
 
 #[derive(Clone, Copy, PartialEq, Eq)]
@@ -448,8 +449,13 @@ pub struct GraphicsPipelineAttachments<'a> {
     pub stencil_attachment_format: Option<ImageFormat>,
 }
 
+pub enum VertexOrMeshShader<'a> {
+    Vertex(ShaderDesc<'a>),
+    Mesh(ShaderDesc<'a>),
+}
+
 pub struct GraphicsPipelineDesc<'a> {
-    pub vertex_shader: ShaderDesc<'a>,
+    pub vertex_or_mesh_shader: VertexOrMeshShader<'a>,
     pub fragment_shader: ShaderDesc<'a>,
     pub layout: PipelineLayout<'a>,
     pub attachments: GraphicsPipelineAttachments<'a>,
@@ -632,6 +638,13 @@ pub enum Access {
     /// Read as any other resource in a vertex shader.
     VertexShaderOtherRead,
 
+    /// Read as a uniform buffer in a mesh shader.
+    MeshShaderUniformBufferRead,
+    /// Read as a sampled image or uniform texel buffer in a mesh shader.
+    MeshShaderSampledImageRead,
+    /// Read as any other resource in a mesh shader.
+    MeshShaderOtherRead,
+
     /// Read as a uniform buffer in a fragment shader.
     FragmentShaderUniformBufferRead,
     /// Read as a sampled image or uniform texel buffer in a fragment shader.
@@ -674,6 +687,9 @@ pub enum Access {
     /// Written as a depth-stencil attachment during rendering.
     DepthStencilAttachmentWrite,
 
+    /// Written as a resource in a mesh shader.
+    MeshWrite,
+
     /// Written as a resource in a compute shader.
     ComputeWrite,
 
@@ -705,6 +721,9 @@ impl Access {
             Access::VertexShaderUniformBufferRead => true,
             Access::VertexShaderSampledImageRead => true,
             Access::VertexShaderOtherRead => true,
+            Access::MeshShaderUniformBufferRead => true,
+            Access::MeshShaderSampledImageRead => true,
+            Access::MeshShaderOtherRead => true,
             Access::FragmentShaderUniformBufferRead => true,
             Access::FragmentShaderSampledImageRead => true,
             Access::FragmentShaderOtherRead => true,
@@ -722,6 +741,7 @@ impl Access {
             Access::FragmentShaderWrite => false,
             Access::ColorAttachmentWrite => false,
             Access::DepthStencilAttachmentWrite => false,
+            Access::MeshWrite => false,
             Access::ComputeWrite => false,
             Access::ShaderWrite => false,
             Access::TransferWrite => false,
@@ -1086,6 +1106,34 @@ pub trait Device {
         first_instance: u32,
     );
 
+    fn cmd_draw_mesh_tasks(
+        &self,
+        cmd_encoder: &mut CmdEncoder,
+        group_count_x: u32,
+        group_count_y: u32,
+        group_count_z: u32,
+    );
+
+    fn cmd_draw_mesh_tasks_indirect(
+        &self,
+        cmd_encoder: &mut CmdEncoder,
+        buffer: BufferArg,
+        offset: u64,
+        draw_count: u32,
+        stride: u32,
+    );
+
+    fn cmd_draw_mesh_tasks_indirect_count(
+        &self,
+        cmd_encoder: &mut CmdEncoder,
+        buffer: BufferArg,
+        offset: u64,
+        count_buffer: BufferArg,
+        count_buffer_offset: u64,
+        max_draw_count: u32,
+        stride: u32,
+    );
+
     fn cmd_dispatch(
         &self,
         cmd_encoder: &mut CmdEncoder,