Skip to content

Add mesh shading api to wgpu & wgpu-core #7345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 28 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
df296da
Initial changes
SupaMaggie70Incorporated Mar 7, 2025
798a3af
Merge branch 'trunk' into mesh-shading/naga-shader-stages
SupaMaggie70Incorporated Mar 7, 2025
193ad7e
Fixed metal backend in wgpu-hal, ran tests
SupaMaggie70Incorporated Mar 7, 2025
e496ecb
Initial wgpu api, still no tests/examples(coming up)
SupaMaggie70Incorporated Mar 7, 2025
f4445e9
Added examples, tests
SupaMaggie70Incorporated Mar 7, 2025
97e9ead
Merge branch 'trunk' into mesh-shading/wgpu-api
SupaMaggie70Incorporated Mar 15, 2025
a370333
Merge branch 'trunk' into mesh-shading/wgpu-api
SupaMaggie70Incorporated Mar 15, 2025
93a66ba
Merge thing
SupaMaggie70Incorporated Mar 15, 2025
ddc3277
Fixed typo
SupaMaggie70Incorporated Mar 15, 2025
e4422ab
Hopefully unbroke player(probably not). Major refactor though
SupaMaggie70Incorporated Mar 15, 2025
31d732d
Fixed JMS suggestion, replaced shaders with glsl source + asm, and re…
SupaMaggie70Incorporated Mar 16, 2025
2f07ee3
Updated changelog
SupaMaggie70Incorporated Mar 16, 2025
b505624
Combined stuff in wgpu-hal, this will break dx12 and metal. Vulkan is…
SupaMaggie70Incorporated Mar 16, 2025
12a11f7
Attempted to fix metal & dx12
SupaMaggie70Incorporated Mar 16, 2025
32b6cac
Attempted to fix metal again
SupaMaggie70Incorporated Mar 16, 2025
fa1c212
Hopefully fixed metal & dx12 for real this time
SupaMaggie70Incorporated Mar 16, 2025
238d626
Maybe actually fixed vulkan this time
SupaMaggie70Incorporated Mar 16, 2025
2c66dbd
Added validation + limits for mesh size limit
SupaMaggie70Incorporated Mar 16, 2025
4527f78
Accidentally pushed an error
SupaMaggie70Incorporated Mar 16, 2025
1b1b121
Tired & sloppy
SupaMaggie70Incorporated Mar 16, 2025
16d4dbe
Ok this is embarassing
SupaMaggie70Incorporated Mar 16, 2025
118e186
Added more validation
SupaMaggie70Incorporated Mar 16, 2025
f61859a
Added limits to webgpu backend to fix errors
SupaMaggie70Incorporated Mar 16, 2025
639a34e
Fixed name, fixed comment with code
SupaMaggie70Incorporated Mar 16, 2025
5c26d35
Hopefully fixed linux, doctest
SupaMaggie70Incorporated Mar 16, 2025
d9267a8
Attempted to fix doctest error with limits
SupaMaggie70Incorporated Mar 16, 2025
df7bfb4
Fixed mesh shader tests(hopefully)
SupaMaggie70Incorporated Mar 16, 2025
6750508
Updated limits
SupaMaggie70Incorporated Mar 16, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ By @wumpf in [#7144](https://github.com/gfx-rs/wgpu/pull/7144)
- If you use Binding Arrays in a bind group, you may not use Dynamic Offset Buffers or Uniform Buffers in that bind group. By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811)
- Rename `instance_id` and `instance_custom_index` to `instance_index` and `instance_custom_data` by @Vecvec in
[#6780](https://github.com/gfx-rs/wgpu/pull/6780)
- Add mesh shader support to `wgpu` (currently vulkan + spirv-passthrough only). By @SupaMaggie70Incorporated in [#7345](https://github.com/gfx-rs/wgpu/pull/7345)


#### Naga
Expand Down
1 change: 1 addition & 0 deletions examples/features/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod hello_synchronization;
pub mod hello_triangle;
pub mod hello_windows;
pub mod hello_workgroups;
pub mod mesh_shader;
pub mod mipmap;
pub mod msaa_line;
pub mod multiple_render_targets;
Expand Down
6 changes: 6 additions & 0 deletions examples/features/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ const EXAMPLES: &[ExampleDesc] = &[
webgl: false, // No Ray-tracing extensions
webgpu: false, // No Ray-tracing extensions (yet)
},
ExampleDesc {
name: "mesh_shader",
function: wgpu_examples::mesh_shader::main,
webgl: false,
webgpu: false,
},
];

fn get_example_name() -> Option<String> {
Expand Down
9 changes: 9 additions & 0 deletions examples/features/src/mesh_shader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# mesh_shader

This example renders a triangle to a window with mesh shaders, while showcasing most mesh shader related features(task shaders, payloads, per primitive data).

## To Run

```
cargo run --bin wgpu-examples mesh_shader
```
130 changes: 130 additions & 0 deletions examples/features/src/mesh_shader/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::{io::Write, process::Stdio};

// Same as in mesh shader tests
fn compile_spv_asm(device: &wgpu::Device, data: &[u8]) -> wgpu::ShaderModule {
let cmd = std::process::Command::new("spirv-as")
.args(["-", "-o", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to call spirv-as");
cmd.stdin.as_ref().unwrap().write_all(data).unwrap();
let output = cmd.wait_with_output().expect("Error waiting for spirv-as");
assert!(output.status.success());
unsafe {
device.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
label: None,
source: wgpu::util::make_spirv_raw(&output.stdout),
})
}
}

pub struct Example {
pipeline: wgpu::RenderPipeline,
}
impl crate::framework::Example for Example {
fn init(
config: &wgpu::SurfaceConfiguration,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
_queue: &wgpu::Queue,
) -> Self {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let (ts, ms, fs) = (
compile_spv_asm(device, include_bytes!("shader.task.spv.asm")),
compile_spv_asm(device, include_bytes!("shader.mesh.spv.asm")),
compile_spv_asm(device, include_bytes!("shader.frag.spv.asm")),
);
let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
task: Some(wgpu::TaskState {
module: &ts,
entry_point: Some("main"),
compilation_options: Default::default(),
}),
mesh: wgpu::MeshState {
module: &ms,
entry_point: Some("main"),
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &fs,
entry_point: Some("main"),
compilation_options: Default::default(),
targets: &[Some(config.view_formats[0].into())],
}),
primitive: wgpu::PrimitiveState {
cull_mode: Some(wgpu::Face::Back),
..Default::default()
},
depth_stencil: None,
multisample: Default::default(),
multiview: None,
cache: None,
});
Self { pipeline }
}
fn optional_features() -> wgpu::Features {
wgpu::Features::empty()
}
fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
rpass.push_debug_group("Prepare data for draw.");
rpass.set_pipeline(&self.pipeline);
rpass.pop_debug_group();
rpass.insert_debug_marker("Draw!");
rpass.draw_mesh_tasks(1, 1, 1);
}
queue.submit(Some(encoder.finish()));
}
fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
Default::default()
}
fn required_features() -> wgpu::Features {
wgpu::Features::EXPERIMENTAL_MESH_SHADER | wgpu::Features::SPIRV_SHADER_PASSTHROUGH
}
fn required_limits() -> wgpu::Limits {
Default::default()
}
fn resize(
&mut self,
_config: &wgpu::SurfaceConfiguration,
_device: &wgpu::Device,
_queue: &wgpu::Queue,
) {
// empty
}
fn update(&mut self, _event: winit::event::WindowEvent) {
// empty
}
}

pub fn main() {
crate::framework::run::<Example>("mesh_shader");
}
11 changes: 11 additions & 0 deletions examples/features/src/mesh_shader/shader.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#version 450
#extension GL_EXT_mesh_shader : require

in VertexInput { layout(location = 0) vec4 color; }
vertexInput;
layout(location = 1) perprimitiveEXT in PrimitiveInput { vec4 colorMask; }
primitiveInput;

layout(location = 0) out vec4 fragColor;

void main() { fragColor = vertexInput.color * primitiveInput.colorMask; }
53 changes: 53 additions & 0 deletions examples/features/src/mesh_shader/shader.frag.spv.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
; SPIR-V
; Version: 1.5
; Generator: Khronos Glslang Reference Front End; 11
; Bound: 24
; Schema: 0
OpCapability Shader
OpCapability MeshShadingEXT
OpExtension "SPV_EXT_mesh_shader"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %fragColor %vertexInput %primitiveInput
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpSourceExtension "GL_EXT_mesh_shader"
OpName %main "main"
OpName %fragColor "fragColor"
OpName %VertexInput "VertexInput"
OpMemberName %VertexInput 0 "color"
OpName %vertexInput "vertexInput"
OpName %PrimitiveInput "PrimitiveInput"
OpMemberName %PrimitiveInput 0 "colorMask"
OpName %primitiveInput "primitiveInput"
OpDecorate %fragColor Location 0
OpDecorate %VertexInput Block
OpMemberDecorate %VertexInput 0 Location 0
OpDecorate %PrimitiveInput Block
OpMemberDecorate %PrimitiveInput 0 PerPrimitiveEXT
OpDecorate %primitiveInput Location 1
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%fragColor = OpVariable %_ptr_Output_v4float Output
%VertexInput = OpTypeStruct %v4float
%_ptr_Input_VertexInput = OpTypePointer Input %VertexInput
%vertexInput = OpVariable %_ptr_Input_VertexInput Input
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Input_v4float = OpTypePointer Input %v4float
%PrimitiveInput = OpTypeStruct %v4float
%_ptr_Input_PrimitiveInput = OpTypePointer Input %PrimitiveInput
%primitiveInput = OpVariable %_ptr_Input_PrimitiveInput Input
%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpAccessChain %_ptr_Input_v4float %vertexInput %int_0
%17 = OpLoad %v4float %16
%21 = OpAccessChain %_ptr_Input_v4float %primitiveInput %int_0
%22 = OpLoad %v4float %21
%23 = OpFMul %v4float %17 %22
OpStore %fragColor %23
OpReturn
OpFunctionEnd
36 changes: 36 additions & 0 deletions examples/features/src/mesh_shader/shader.mesh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#version 450
#extension GL_EXT_mesh_shader : require

const vec4[3] positions = {vec4(0., 1.0, 0., 1.0), vec4(-1.0, -1.0, 0., 1.0),
vec4(1.0, -1.0, 0., 1.0)};
const vec4[3] colors = {vec4(0., 1., 0., 1.), vec4(0., 0., 1., 1.),
vec4(1., 0., 0., 1.)};

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
struct PayloadData {
vec4 colorMask;
bool visible;
};
taskPayloadSharedEXT PayloadData payloadData;

out VertexOutput { layout(location = 0) vec4 color; }
vertexOutput[];
layout(location = 1) perprimitiveEXT out PrimitiveOutput { vec4 colorMask; }
primitiveOutput[];

shared uint sharedData;

layout(triangles, max_vertices = 3, max_primitives = 1) out;
void main() {
sharedData = 5;
SetMeshOutputsEXT(3, 1);
gl_MeshVerticesEXT[0].gl_Position = positions[0];
gl_MeshVerticesEXT[1].gl_Position = positions[1];
gl_MeshVerticesEXT[2].gl_Position = positions[2];
vertexOutput[0].color = colors[0] * payloadData.colorMask;
vertexOutput[1].color = colors[1] * payloadData.colorMask;
vertexOutput[2].color = colors[2] * payloadData.colorMask;
gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);
primitiveOutput[0].colorMask = vec4(1.0, 0.0, 1.0, 1.0);
gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = !payloadData.visible;
}
Loading
Loading