Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
0622940
[spv-out] Support for mesh shaders
cwfitzgerald Dec 12, 2025
dc69d86
Update mesh_shader.rs
inner-daemons Dec 13, 2025
dfd360f
Tried one thing
inner-daemons Dec 14, 2025
c1973fe
Updated snapshots
inner-daemons Dec 14, 2025
50c2aa9
Tried another thing
inner-daemons Dec 14, 2025
7749867
Removed per primitive stuff + cull primitive
inner-daemons Dec 14, 2025
ca3f93a
Testing new thing
inner-daemons Dec 14, 2025
4fbfc60
Ahh well I think I'm done for the night
inner-daemons Dec 14, 2025
7551f6a
Slight improvements
inner-daemons Dec 14, 2025
3225030
Fixed another comment
inner-daemons Dec 14, 2025
c34c474
Added note on feature
inner-daemons Dec 14, 2025
18164ee
Preparing for merge as is
inner-daemons Dec 14, 2025
0b9bf09
Ok I'm tired
inner-daemons Dec 14, 2025
da72786
Blah blah blah
inner-daemons Dec 14, 2025
8862d56
Updated loop logic
inner-daemons Dec 14, 2025
0286af7
Tried something else
inner-daemons Dec 14, 2025
7cc51c9
Tried another little fix
inner-daemons Dec 14, 2025
3818efe
Tried something new
inner-daemons Dec 14, 2025
ea50cb0
Told it to skip instead of expect a failure
inner-daemons Dec 14, 2025
8d9a451
Redocumented feature, made tests run on AMD
inner-daemons Dec 15, 2025
3edc37c
Removed obseleted files, updated changelog, updated shaders
inner-daemons Dec 15, 2025
6ffd2d5
Added task shader to the changelog entry
inner-daemons Dec 15, 2025
094a5ac
Enabled debugigng
inner-daemons Dec 15, 2025
6417327
Fixed typo
inner-daemons Dec 15, 2025
2e863e2
Trying with better aligned task payload stuff
inner-daemons Dec 15, 2025
05eada6
Made the tests actually run on LLVMPIPE
inner-daemons Dec 15, 2025
6dc1fd0
Testing on LLVMPIPE if removing task payload reads does anything
inner-daemons Dec 15, 2025
883a443
Undid test that didnt work
inner-daemons Dec 15, 2025
3457ed5
Tried making it write a barrier
inner-daemons Dec 15, 2025
cad1bdf
Wrote another barrier I guess
inner-daemons Dec 15, 2025
4927043
Gonna see if this one does anything
inner-daemons Dec 15, 2025
19af909
Jeez im stupid
inner-daemons Dec 15, 2025
3d7327f
Removed debugging files
inner-daemons Dec 15, 2025
c4574e7
Fixed the example shader sorta
inner-daemons Dec 15, 2025
ff46752
Blah blah blah
inner-daemons Dec 15, 2025
a5324ee
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/spv-w…
inner-daemons Dec 15, 2025
c36f9f8
Final cleanup
inner-daemons Dec 15, 2025
4c29d13
Added new mesh shader tasks
inner-daemons Dec 16, 2025
c08d00a
Fixed test
inner-daemons Dec 16, 2025
7703aef
Fixed some test shenanigans
inner-daemons Dec 16, 2025
90624c8
Seeing if this breaks anything
inner-daemons Dec 16, 2025
307f908
Tried to fix one issue
inner-daemons Dec 16, 2025
69a1d50
Initial setup
inner-daemons Dec 17, 2025
3611377
More scaffolding
inner-daemons Dec 17, 2025
f0235fb
Temporary changes while I pivot
inner-daemons Dec 17, 2025
1e33465
A little more progress made here
inner-daemons Dec 17, 2025
f09af9b
Task shaders are now good I think
inner-daemons Dec 17, 2025
03b46f7
Some more work, it mostly compiles now
inner-daemons Dec 17, 2025
e8188cb
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Dec 17, 2025
12551f4
Fixed warnings
inner-daemons Dec 17, 2025
445382b
Ran taplo fmt, fixed a warning
inner-daemons Dec 17, 2025
1b48851
Many shaders now build properly
inner-daemons Dec 18, 2025
dbdff84
It flippin works!!
inner-daemons Dec 18, 2025
c875a58
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Dec 18, 2025
e31b4f9
Hahaha its valid
inner-daemons Dec 18, 2025
82db42d
Final tweaks
inner-daemons Dec 18, 2025
f47a220
Fixed missing commas in some shaders
inner-daemons Dec 18, 2025
85f65a5
Fixed final test
inner-daemons Dec 18, 2025
2a8396f
Zero initialized stuff
inner-daemons Dec 18, 2025
cced603
Initial commit adding miscellaneous changes from msl-write and hlsl-w…
inner-daemons Dec 18, 2025
61a7e95
Same as previous commit
inner-daemons Dec 18, 2025
5bef12b
Fixed divergence issue
inner-daemons Dec 18, 2025
3b04e7a
Removed some unnecessary barriers
inner-daemons Dec 18, 2025
7576625
Zero init workgroup memory
inner-daemons Dec 18, 2025
ffc0939
Added limits validation
inner-daemons Dec 18, 2025
9435f58
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/spv-f…
inner-daemons Dec 18, 2025
d66e920
Added changelog
inner-daemons Dec 18, 2025
ba8f67b
Handled overflow, removed todo
inner-daemons Dec 18, 2025
cb41edd
Amazing stuff here on display
inner-daemons Dec 18, 2025
28b684d
Undid barrier generation
inner-daemons Dec 19, 2025
b91d24b
Fixed some stuff up
inner-daemons Dec 19, 2025
12158d3
Fixed the thing
inner-daemons Dec 19, 2025
f8f10e6
Lets see if this fixes llvmpipe
inner-daemons Dec 19, 2025
c6e7b48
Also this commit fixes llvmpipe maybe
inner-daemons Dec 19, 2025
9b1ea2c
Unfortunate but not too unexpected at this point
inner-daemons Dec 19, 2025
b2e9657
Pushing changes even though they're broken
inner-daemons Dec 19, 2025
f32c091
Updated feature to say to use ShaderRuntimeChecks::unchecked()
inner-daemons Dec 19, 2025
503038c
Reverted to old method for snapshots
inner-daemons Jan 6, 2026
192120b
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Jan 6, 2026
b012e5e
Updated spirv snapshot
inner-daemons Jan 6, 2026
2ac1478
Refactored nested function outer into its own function
inner-daemons Jan 6, 2026
2793e15
Tweaked a comment
inner-daemons Jan 6, 2026
ed1f75c
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/spv-f…
inner-daemons Jan 6, 2026
49c98a1
Updated snapshots and took some changes from the hlsl writer
inner-daemons Jan 6, 2026
3ef0910
Snapshots
inner-daemons Jan 6, 2026
3cfc8fe
2 quick tweaks
inner-daemons Jan 6, 2026
fe58d2d
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Jan 15, 2026
65cd586
Update naga/src/back/hlsl/writer.rs
inner-daemons Jan 15, 2026
20ff07d
Fixes round 1 (cleaned up small things)
inner-daemons Jan 15, 2026
33066c7
Made mesh shader actually use the payload input
inner-daemons Jan 15, 2026
647afcf
Refactored a bunch of stuff into a new file
inner-daemons Jan 15, 2026
91bc3cb
Made hal require shader model 6.5 to expose support
inner-daemons Jan 15, 2026
ffc6cce
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/spv-f…
inner-daemons Jan 15, 2026
6d4fe94
Merge branch 'mesh-shading/spv-fixes' into mesh-shading/hlsl-write-id
inner-daemons Jan 15, 2026
fc3d916
Made naga check for proper shader model
inner-daemons Jan 15, 2026
526dec4
Updated framework with suggestions by Connor in #8752
inner-daemons Jan 15, 2026
a97cf46
Moved the task runtime limits into naga::back
inner-daemons Jan 15, 2026
ab4625b
Merge branch 'mesh-shading/spv-fixes' into mesh-shading/hlsl-write-id
inner-daemons Jan 15, 2026
7b907ad
Added validation for the task shader's dispatched workgroup count
inner-daemons Jan 15, 2026
34b53fb
Blindly trying to pass limits in
inner-daemons Jan 15, 2026
9367b77
Fixed soem stuff
inner-daemons Jan 15, 2026
2e31c0e
Added limits field
inner-daemons Jan 15, 2026
29f630a
Added changelog entry
inner-daemons Jan 15, 2026
f17979a
Fixed some checks
inner-daemons Jan 15, 2026
189b479
Fixed checks: >= into >
inner-daemons Jan 15, 2026
2d3330b
Cant believe I forgot this
inner-daemons Jan 16, 2026
4b630d9
Removed code using u64 to check stuff
inner-daemons Jan 16, 2026
ce99291
Removed thing with limiting it to 2<<21
inner-daemons Jan 16, 2026
5031bb8
Updated limit
inner-daemons Jan 16, 2026
2177dea
Added new check and undid limit change
inner-daemons Jan 16, 2026
ec3ecfa
Some more work
inner-daemons Jan 17, 2026
cd6341c
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/spv-f…
inner-daemons Jan 30, 2026
ab03a6d
Fixed compiles
inner-daemons Jan 30, 2026
036751b
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Jan 30, 2026
bc69def
Merge branch 'mesh-shading/spv-fixes' into mesh-shading/hlsl-write-id
inner-daemons Jan 30, 2026
35abad7
Fixed compiles for passthrough by re-adding a hash derive to TestingC…
inner-daemons Jan 30, 2026
6d44233
Fixed a warning
inner-daemons Jan 31, 2026
3e65d43
Added new field to spv options
inner-daemons Jan 31, 2026
a6190e4
Merge branch 'mesh-shading/spv-fixes' into mesh-shading/hlsl-write-id
inner-daemons Jan 31, 2026
c8df89b
Updated some stuff to pass around the task runtime limits in more ways
inner-daemons Jan 31, 2026
afe4267
Merge branch 'mesh-shading/spv-fixes' into mesh-shading/hlsl-write-id
inner-daemons Jan 31, 2026
f4f4140
Added new option to hlsl writer
inner-daemons Jan 31, 2026
e88652b
Fixed an issue and removed some unnecessary config
inner-daemons Jan 31, 2026
ebeeda1
Tried to fix another compile error
inner-daemons Jan 31, 2026
e64067a
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Feb 16, 2026
1398c61
Tried to fix some stuff
inner-daemons Feb 16, 2026
2c0eb59
Updated snapshots
inner-daemons Feb 16, 2026
dd08f38
Merge branch 'trunk' into mesh-shading/hlsl-write-id
inner-daemons Feb 18, 2026
29bdebf
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Feb 22, 2026
ff5a2fc
Various quick changes
inner-daemons Feb 22, 2026
8c95e72
Update naga/src/back/hlsl/writer.rs
inner-daemons Feb 22, 2026
2fbfbc0
Update naga/src/back/hlsl/mesh_shader.rs
inner-daemons Feb 22, 2026
1622bb1
Fixed recommendations and updated snapshots
inner-daemons Feb 22, 2026
073286a
Updated the spec
inner-daemons Feb 22, 2026
277a042
Fixed another small commet
inner-daemons Feb 22, 2026
7e1fc43
Split up mesh shader funciton
inner-daemons Feb 22, 2026
c7a42a2
Merge remote-tracking branch 'upstream/trunk' into mesh-shading/hlsl-…
inner-daemons Feb 27, 2026
aefa1f8
Fixed thing
inner-daemons Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ depth_stencil: Some(wgpu::DepthStencilState::stencil(
- Added support for dual-source blending in SPIR-V shaders. By @andyleiserson in [#8865](https://github.com/gfx-rs/wgpu/pull/8865).
- Added `supported_capabilities` to all backends. By @inner-daemons in [#9068](https://github.com/gfx-rs/wgpu/pull/9068).

### General

#### DX12

- Added support for mesh shaders in naga's HLSL writer, completing DX12 support for mesh shaders. By @inner-daemons in #8752.

### Bug Fixes

#### General
Expand Down Expand Up @@ -197,7 +203,7 @@ depth_stencil: Some(wgpu::DepthStencilState::stencil(
- Require that the blend factor is `One` when the blend operation is `Min` or `Max`. The `BlendFactorOnUnsupportedTarget` error is now reported within `ColorStateError` rather than directly in `CreateRenderPipelineError`. By @andyleiserson in [#9110](https://github.com/gfx-rs/wgpu/pull/9110).

#### Vulkan
- Fixed a variety of mesh shader SPIR-V writer issues from the original implementation. By @inner-daemons in [#8756](https://github.com/gfx-rs/wgpu/pull/8756)
- Fixed a variety of mesh shader SPIR-V writer issues from the original implementation. By @inner-daemons in [#8756](https://github.com/gfx-rs/wgpu/pull/8756).

#### GLES

Expand Down
15 changes: 12 additions & 3 deletions docs/api-specs/mesh_shading.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,16 @@ An example of using mesh shaders to render a single triangle can be seen [here](

> **NOTE**: More limits will be added when support is added to `naga`.

* `Limits::max_task_workgroup_total_count` - the maximum total number of workgroups from a `draw_mesh_tasks` command or similar. The dimensions passed must be less than or equal to this limit when multiplied together.
* `Limits::max_task_workgroups_per_dimension` - the maximum for each of the 3 workgroup dimensions in a `draw_mesh_tasks` command. Each dimension passed must be less than or equal to this limit.
* `max_mesh_multiview_count` - The maximum number of views used when multiview rendering with a mesh shader pipeline.
* `Limits::max_task_mesh_workgroup_total_count` - the maximum total number of workgroups from a `draw_mesh_tasks` command or similar. The dimensions passed must be less than or equal to this limit when multiplied together.
* `Limits::max_task_mesh_workgroups_per_dimension` - the maximum for each of the 3 workgroup dimensions in a `draw_mesh_tasks` command. Each dimension passed must be less than or equal to this limit.
* `max_task_invocations_per_workgroup` - The maximum total number of threads in a task shader workgroup, given by `workgroupSize.x * workgroupSize.y * workgroupSize.z`.
* `max_task_invocations_per_dimension` the maximum value for each of `workgroupSize.x`, `workgroupSize.y` and `workgroupSize.z` in task shader workgroups.
* `max_mesh_invocations_per_workgroup` - The maximum total number of threads in a mesh shader workgroup, given by `workgroupSize.x * workgroupSize.y * workgroupSize.z`.
* `max_mesh_invocations_per_dimension` the maximum value for each of `workgroupSize.x`, `workgroupSize.y` and `workgroupSize.z` in mesh shader workgroups.
* `max_task_payload_size` - the size of an `var<task_payload>` variable, in bytes.
* `max_mesh_output_vertices` - the maximum number of vertices that a single mesh shader workgroup may output.
* `max_mesh_output_primitives` - the maximum number of primitives that a single mesh shader workgroup may output.
* `max_mesh_multiview_count` - the maximum number of views used when multiview rendering with a mesh shader pipeline.
Comment on lines 91 to +102
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! But I suppose the "more limits will be added" bit is obsolete now?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the MSL writer PR adds 1 more limit, once we get this merged and I handle the changes from this in the MSL PR then we can remove that.

* `max_mesh_output_layers` - the maximum number of output layers for a mesh shader pipeline.

## Naga implementation
Expand Down Expand Up @@ -132,6 +139,8 @@ A task shader entry point must also have a `@payload(G)` property, where `G` is

A task shader entry point must return a `vec3<u32>` value decorated with `@builtin(mesh_task_size)`. The return value of each workgroup's first invocation (that is, the one whose `local_invocation_index` is `0`) is taken as the size of a **mesh shader grid** to dispatch, measured in workgroups. (If the task shader entry point returns `vec3(0, 0, 0)`, then no mesh shaders are dispatched.) Mesh shader grids are described in the next section.

The output of a task shader is set to zero if it violates either of the limits `max_task_mesh_workgroup_total_count` or `max_task_mesh_workgroups_per_dimension`.

Each task shader workgroup dispatches an independent mesh shader grid: in mesh shader invocations, `@builtin` values like `workgroup_id` and `global_invocation_id` describe the position of the workgroup and invocation within that grid;
and `@builtin(num_workgroups)` matches the task shader workgroup's return value. Mesh shaders dispatched for other task shader workgroups are not included in the count. If it is necessary for a mesh shader to know which task shader workgroup dispatched it, the task shader can include its own workgroup id in the task payload.

Expand Down
41 changes: 1 addition & 40 deletions examples/features/src/mesh_shader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,6 @@ fn compile_wgsl(device: &wgpu::Device) -> wgpu::ShaderModule {
)
}
}
fn compile_hlsl(device: &wgpu::Device, entry: &str, stage_str: &str) -> wgpu::ShaderModule {
let out_path = format!(
"{}/src/mesh_shader/shader.{stage_str}.cso",
env!("CARGO_MANIFEST_DIR")
);
let cmd = std::process::Command::new("dxc")
.args([
"-T",
&format!("{stage_str}_6_5"),
"-E",
entry,
&format!("{}/src/mesh_shader/shader.hlsl", env!("CARGO_MANIFEST_DIR")),
"-Fo",
&out_path,
])
.output()
.unwrap();
if !cmd.status.success() {
panic!("DXC failed:\n{}", String::from_utf8(cmd.stderr).unwrap());
}
let file = std::fs::read(&out_path).unwrap();
std::fs::remove_file(out_path).unwrap();
unsafe {
device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {
label: None,
num_workgroups: (1, 1, 1),
dxil: Some(std::borrow::Cow::Owned(file)),
..Default::default()
})
}
}

fn compile_msl(device: &wgpu::Device) -> wgpu::ShaderModule {
unsafe {
Expand All @@ -67,7 +36,7 @@ fn get_shaders(device: &wgpu::Device, backend: wgpu::Backend) -> Shaders {
// In the case that the platform does support mesh shaders, the dummy
// shader is used to avoid requiring PASSTHROUGH_SHADERS.
match backend {
wgpu::Backend::Vulkan => {
wgpu::Backend::Vulkan | wgpu::Backend::Dx12 => {
let compiled = compile_wgsl(device);
Shaders {
ts: compiled.clone(),
Expand All @@ -78,14 +47,6 @@ fn get_shaders(device: &wgpu::Device, backend: wgpu::Backend) -> Shaders {
fs_name: "fs_main",
}
}
wgpu::Backend::Dx12 => Shaders {
ts: compile_hlsl(device, "Task", "as"),
ms: compile_hlsl(device, "Mesh", "ms"),
fs: compile_hlsl(device, "Frag", "ps"),
ts_name: "main",
ms_name: "main",
fs_name: "main",
},
wgpu::Backend::Metal => {
let compiled = compile_msl(device);
Shaders {
Expand Down
53 changes: 0 additions & 53 deletions examples/features/src/mesh_shader/shader.hlsl

This file was deleted.

2 changes: 2 additions & 0 deletions naga-cli/src/bin/naga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,8 @@ fn run() -> anyhow::Result<()> {

params.spv_out.mesh_shader_primitive_indices_clamp = args.validate_mesh_output;
params.spv_out.task_dispatch_limits = args.task_limits.0;
params.hlsl.mesh_shader_primitive_indices_clamp = args.validate_mesh_output;
params.hlsl.task_dispatch_limits = args.task_limits.0;

if args.bulk_validate {
return bulk_validate(&args, &params);
Expand Down
12 changes: 11 additions & 1 deletion naga/hlsl-snapshots/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub struct Config {
pub vertex: Vec<ConfigItem>,
pub fragment: Vec<ConfigItem>,
pub compute: Vec<ConfigItem>,
pub task: Vec<ConfigItem>,
pub mesh: Vec<ConfigItem>,
}

impl Config {
Expand All @@ -59,6 +61,8 @@ impl Config {
vertex: Default::default(),
fragment: Default::default(),
compute: Default::default(),
task: Default::default(),
mesh: Default::default(),
}
}

Expand All @@ -85,8 +89,14 @@ impl Config {
vertex,
fragment,
compute,
task,
mesh,
} = self;
vertex.is_empty() && fragment.is_empty() && compute.is_empty()
vertex.is_empty()
&& fragment.is_empty()
&& compute.is_empty()
&& task.is_empty()
&& mesh.is_empty()
}
}

Expand Down
14 changes: 9 additions & 5 deletions naga/src/back/hlsl/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,10 @@ impl crate::StorageFormat {
}

impl crate::BuiltIn {
pub(super) fn to_hlsl_str(self) -> Result<&'static str, Error> {
Ok(match self {
/// Returns `None` for "virtual" builtins, i.e. mesh shader builtins that are
/// used by naga but not recognized by HLSL.
pub(super) fn to_hlsl_str(self) -> Result<Option<&'static str>, Error> {
Ok(Some(match self {
Self::Position { .. } => "SV_Position",
// vertex
Self::ClipDistance => "SV_ClipDistance",
Expand Down Expand Up @@ -186,12 +188,14 @@ impl crate::BuiltIn {
return Err(Error::Custom(format!("Unsupported builtin {self:?}")))
}
Self::CullPrimitive => "SV_CullPrimitive",
Self::PointIndex | Self::LineIndices | Self::TriangleIndices => unimplemented!(),
Self::MeshTaskSize
| Self::VertexCount
| Self::PrimitiveCount
| Self::Vertices
| Self::Primitives => unreachable!(),
| Self::Primitives
| Self::PointIndex
| Self::LineIndices
| Self::TriangleIndices => return Ok(None),
Self::RayInvocationId
| Self::NumRayInvocations
| Self::InstanceCustomData
Expand All @@ -205,7 +209,7 @@ impl crate::BuiltIn {
| Self::ObjectToWorld
| Self::WorldToObject
| Self::HitKind => unreachable!(),
})
}))
}
}

Expand Down
Loading
Loading