Skip to content

Add mesh shading info to naga IR - #8104

Merged
cwfitzgerald merged 72 commits into
gfx-rs:trunkfrom
inner-daemons:mesh-shading/naga-ir
Oct 29, 2025
Merged

Add mesh shading info to naga IR#8104
cwfitzgerald merged 72 commits into
gfx-rs:trunkfrom
inner-daemons:mesh-shading/naga-ir

Conversation

@inner-daemons

@inner-daemons inner-daemons commented Aug 14, 2025

Copy link
Copy Markdown
Collaborator

Connections
Mostly split off from #7930
Works towards #7197

Description
This PR adds mesh shading info to naga IR so that parsers and writers have an interface to use.

Testing
No testing yet (coming in later PRs, the code here has been tested in #7930)

Squash or Rebase?

Squash

Checklist

  • Run cargo fmt.
  • Run taplo format.
  • Run cargo clippy --tests. If applicable, add:
    • --target wasm32-unknown-unknown
  • Run cargo xtask test to run tests.
  • If this contains user-facing changes, add a CHANGELOG.md entry.

@inner-daemons
inner-daemons requested a review from a team as a code owner August 14, 2025 17:55
@inner-daemons inner-daemons mentioned this pull request Aug 14, 2025
37 tasks
@inner-daemons

Copy link
Copy Markdown
Collaborator Author

I'm unsure why the MSRV minimal versions thing is failing, it doesn't look related to this PR since it happens in another crate and this PR doesn't touch anything cargo.

@cwfitzgerald

cwfitzgerald commented Aug 17, 2025

Copy link
Copy Markdown
Member

Should be fixed with #8112, thanks @andyleiserson!

Comment thread naga/src/ir/mod.rs
@inner-daemons

Copy link
Copy Markdown
Collaborator Author

Going to ping you again @jimblandy just to make sure that this doesn't get forgotten about until the next meeting :)

@jimblandy

Copy link
Copy Markdown
Member

@SupaMaggie70Incorporated I'll try to get to this tonight, but it may be Saturday.

@jimblandy

Copy link
Copy Markdown
Member

@SupaMaggie70Incorporated Are these comments at the top of mesh_shading.md still accurate?

Currently naga has no support for mesh shaders beyond recognizing the additional shader stages. For this reason, all shaders must be created with Device::create_shader_module_passthrough.

@inner-daemons

Copy link
Copy Markdown
Collaborator Author

@SupaMaggie70Incorporated Are these comments at the top of mesh_shading.md still accurate?

Currently naga has no support for mesh shaders beyond recognizing the additional shader stages. For this reason, all shaders must be created with Device::create_shader_module_passthrough.

I've updated the top of mesh_shading.md. Until further PRs it remains true that you will need to use passthrough shaders but it now has more "processing" ability so the statement wasn't strictly true.

@jimblandy

jimblandy commented Aug 25, 2025

Copy link
Copy Markdown
Member

(This is not a review comment on this PR, and not directed at SupaMaggie, just commenting on the pre-existing state of the docs.)

This is not a thorough explanation of mesh shading and how it works. Those wishing to understand mesh shading more broadly should look elsewhere first.

This is just not adequate. If we're going to add syntax to Naga that is not covered by the WGSL specification, we need to document that syntax. Knowing how mesh shading works in Vulkan does not magically explain the syntax for mesh shaders in WGSL, or how to invoke them in wgpu.

@inner-daemons

Copy link
Copy Markdown
Collaborator Author

(This is not a review comment on this PR, and not directed at SupaMaggie, just commenting on the pre-existing state of the docs.)

This is not a thorough explanation of mesh shading and how it works. Those wishing to understand mesh shading more broadly should look elsewhere first.

This is just not adequate. If we're going to add syntax to Naga that is not covered by the WGSL specification, we need to document that syntax. Knowing how mesh shading works in Vulkan does not magically explain the syntax for mesh shaders in WGSL, or how to invoke them in wgpu.

It wouldn't be part of this PR, but if you think that having a writeup somewhere to describe mesh shaders would be useful, I'm happy to do that separately. However, I have never written a comprehensive GPU API spec before, so I don't exactly think it would be of comparable quality to e.g. the webgpu spec :)

Also, that specific disclaimer was copy-pasted directly from the RT spec.

@jimblandy

Copy link
Copy Markdown
Member

It wouldn't be part of this PR, but if you think that having a writeup somewhere to describe mesh shaders would be useful, I'm happy to do that separately. However, I have never written a comprehensive GPU API spec before, so I don't exactly think it would be of comparable quality to e.g. the webgpu spec :)

Well, the alternative is just saying "read the code and the examples and figure it out". You want people to actually use your work, right?

I just pushed some docs; do they look correct?

@inner-daemons

Copy link
Copy Markdown
Collaborator Author

Just put up #8370, which is the WGSL parsing PR. The WGSL parsing and validation passes there, so this PR's validation seems to not be disallowing valid shaders yet.

@inner-daemons

Copy link
Copy Markdown
Collaborator Author

@jimblandy Me and @cwfitzgerald agreed that mesh shader outputs should always be buffered, so I'm gonna remove references in the spec to having to call setMeshOutputs exactly once before calling setVertex/setPrimitive and stuff like that.

@jimblandy

Copy link
Copy Markdown
Member

@jimblandy Me and @cwfitzgerald agreed that mesh shader outputs should always be buffered, so I'm gonna remove references in the spec to having to call setMeshOutputs exactly once before calling setVertex/setPrimitive and stuff like that.

Does this mean we could also potentially have a design where the vertex and primitive arrays and counts are returned by the mesh shader? It seemed to me like much of this stuff would work just as well as new @builtin kinds:

struct MeshOutput {
    @builtin(vertex_count) num_vertices: u32,
    @builtin(primitive_count) num_primitives: u32,
    @builtin(vertices) vertices: array<Vertex, 10>,
    @builtin(primitives) primitives: array<Primitive, 15>,
}

@mesh @workgroup_size(15, 1, 1)
fn mesh_shader(
    @builtin(local_invocation_index) invocation: u32,
    @builtin(task_payload) payload: Payload
) -> MeshOutput {
    ...
}

@inner-daemons

inner-daemons commented Oct 17, 2025

Copy link
Copy Markdown
Collaborator Author

@jimblandy Me and @cwfitzgerald agreed that mesh shader outputs should always be buffered, so I'm gonna remove references in the spec to having to call setMeshOutputs exactly once before calling setVertex/setPrimitive and stuff like that.

Does this mean we could also potentially have a design where the vertex and primitive arrays and counts are returned by the mesh shader? It seemed to me like much of this stuff would work just as well as new @builtin kinds:

struct MeshOutput {

    @builtin(vertex_count) num_vertices: u32,

    @builtin(primitive_count) num_primitives: u32,

    @builtin(vertices) vertices: array<Vertex, 10>,

    @builtin(primitives) primitives: array<Primitive, 15>,

}



@mesh @workgroup_size(15, 1, 1)

fn mesh_shader(

    @builtin(local_invocation_index) invocation: u32,

    @builtin(task_payload) payload: Payload

) -> MeshOutput {

    ...

}

Err, that's actually really clever. I do think that this isn't right though, since the thing output is per workgroup and this would have each thread return a giant structure, that would in theory be the same for all threads anyway. I'm not opposed to something closer to this though, like a global thread group variable that holds the buffered outputs and then the mesh shader can declare that it uses that global variable.

@inner-daemons

inner-daemons commented Oct 17, 2025

Copy link
Copy Markdown
Collaborator Author

Ok, I've thought about it some more. I think that we can't really avoid buffering for another reason: the output format isn't guaranteed to match whatever the naga struct is. SPIR-V for example has arcane rules on which values must be in different output arrays. HLSL is very similar. I still don't think we should have the task payload/mesh outputs be per-thread, but a small tweak to your proposal would be as follows:

struct MeshOutput {
    @builtin(vertex_count) num_vertices: u32,
    @builtin(primitive_count) num_primitives: u32,
    @builtin(vertices) vertices: array<Vertex, 10>,
    @builtin(primitives) primitives: array<Primitive, 15>,
}

var<mesh_output> output: MeshOutput;
var<task_payload> task_payload: Payload;

@mesh(output) @workgroup_size(15, 1, 1)
fn mesh_shader(
    @builtin(local_invocation_index) invocation: u32,
) {
    ...
}

Then we would just need to allow these @builtin attributes on fields in a mesh_output variable. This variable would then probably function basically the same as task_payload does in task shaders, where only 1 can be used by a task shader and it must match what was declared, and other stages can't access such a global variable.

@jimblandy

jimblandy commented Oct 18, 2025

Copy link
Copy Markdown
Member

@inner-daemons Have the commits I've pushed been looking okay?

@inner-daemons

Copy link
Copy Markdown
Collaborator Author

@jimblandy They look great! I very much appreciate them.

@inner-daemons

Copy link
Copy Markdown
Collaborator Author

@jimblandy Do you have opinions about what we should go with? We could completely switch it up for the new option, which would probably mean less work on each of the backends. On the other hand, I would have to redo most of the work for mesh shaders, and there is a lot of work to redo.

I'm of the opinion that unless there's some problem with the new option I haven't seen we should go with that, but it would be kind of heartbreaking to restart after writing 7k lines and being blocked for 2 months on this PR.

@cwfitzgerald

Copy link
Copy Markdown
Member

Been meaning to sit down with this and figure it out, but ended up getting sick over the weekend ;(

@jimblandy

Copy link
Copy Markdown
Member

Writing down what we said in our meeting today:

@cwfitzgerald cwfitzgerald left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm tired of looking at this, you're tired of looking at this, I don't see any immediate problems, lets land and iterate!

@cwfitzgerald
cwfitzgerald merged commit bf9f752 into gfx-rs:trunk Oct 29, 2025
41 checks passed
@LukasKalbertodt

Copy link
Copy Markdown

Sorry for the spam, but I just want to say: thank you all for the work on this! I know this is not the first nor last step in the mesh shader journey, but as a usually quiet bystander I felt I should express my gratitude here :)

@inner-daemons
inner-daemons deleted the mesh-shading/naga-ir branch November 15, 2025 04:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants