wgpu_examples/mesh_shader/
mod.rs

1use std::process::Stdio;
2
3// Same as in mesh shader tests
4fn compile_glsl(device: &wgpu::Device, shader_stage: &'static str) -> wgpu::ShaderModule {
5    let cmd = std::process::Command::new("glslc")
6        .args([
7            &format!(
8                "{}/src/mesh_shader/shader.{shader_stage}",
9                env!("CARGO_MANIFEST_DIR")
10            ),
11            "-o",
12            "-",
13            "--target-env=vulkan1.2",
14            "--target-spv=spv1.4",
15        ])
16        .stdin(Stdio::piped())
17        .stdout(Stdio::piped())
18        .spawn()
19        .expect("Failed to call glslc");
20    let output = cmd.wait_with_output().expect("Error waiting for glslc");
21    assert!(output.status.success());
22    unsafe {
23        device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {
24            entry_point: "main".into(),
25            label: None,
26            spirv: Some(wgpu::util::make_spirv_raw(&output.stdout)),
27            ..Default::default()
28        })
29    }
30}
31fn compile_hlsl(device: &wgpu::Device, entry: &str, stage_str: &str) -> wgpu::ShaderModule {
32    let out_path = format!(
33        "{}/src/mesh_shader/shader.{stage_str}.cso",
34        env!("CARGO_MANIFEST_DIR")
35    );
36    let cmd = std::process::Command::new("dxc")
37        .args([
38            "-T",
39            &format!("{stage_str}_6_5"),
40            "-E",
41            entry,
42            &format!("{}/src/mesh_shader/shader.hlsl", env!("CARGO_MANIFEST_DIR")),
43            "-Fo",
44            &out_path,
45        ])
46        .output()
47        .unwrap();
48    if !cmd.status.success() {
49        panic!("DXC failed:\n{}", String::from_utf8(cmd.stderr).unwrap());
50    }
51    let file = std::fs::read(&out_path).unwrap();
52    std::fs::remove_file(out_path).unwrap();
53    unsafe {
54        device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {
55            entry_point: entry.to_owned(),
56            label: None,
57            num_workgroups: (1, 1, 1),
58            dxil: Some(std::borrow::Cow::Owned(file)),
59            ..Default::default()
60        })
61    }
62}
63
64fn compile_msl(device: &wgpu::Device, entry: &str) -> wgpu::ShaderModule {
65    unsafe {
66        device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough {
67            entry_point: entry.to_owned(),
68            label: None,
69            msl: Some(std::borrow::Cow::Borrowed(include_str!("shader.metal"))),
70            num_workgroups: (1, 1, 1),
71            ..Default::default()
72        })
73    }
74}
75
76pub struct Example {
77    pipeline: wgpu::RenderPipeline,
78}
79impl crate::framework::Example for Example {
80    fn init(
81        config: &wgpu::SurfaceConfiguration,
82        adapter: &wgpu::Adapter,
83        device: &wgpu::Device,
84        _queue: &wgpu::Queue,
85    ) -> Self {
86        let (ts, ms, fs) = match adapter.get_info().backend {
87            wgpu::Backend::Vulkan => (
88                compile_glsl(device, "task"),
89                compile_glsl(device, "mesh"),
90                compile_glsl(device, "frag"),
91            ),
92            wgpu::Backend::Dx12 => (
93                compile_hlsl(device, "Task", "as"),
94                compile_hlsl(device, "Mesh", "ms"),
95                compile_hlsl(device, "Frag", "ps"),
96            ),
97            wgpu::Backend::Metal => (
98                compile_msl(device, "taskShader"),
99                compile_msl(device, "meshShader"),
100                compile_msl(device, "fragShader"),
101            ),
102            _ => panic!("Example can currently only run on vulkan, dx12 or metal"),
103        };
104        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
105            label: None,
106            bind_group_layouts: &[],
107            push_constant_ranges: &[],
108        });
109        let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {
110            label: None,
111            layout: Some(&pipeline_layout),
112            task: Some(wgpu::TaskState {
113                module: &ts,
114                entry_point: Some("main"),
115                compilation_options: Default::default(),
116            }),
117            mesh: wgpu::MeshState {
118                module: &ms,
119                entry_point: Some("main"),
120                compilation_options: Default::default(),
121            },
122            fragment: Some(wgpu::FragmentState {
123                module: &fs,
124                entry_point: Some("main"),
125                compilation_options: Default::default(),
126                targets: &[Some(config.view_formats[0].into())],
127            }),
128            primitive: wgpu::PrimitiveState {
129                cull_mode: Some(wgpu::Face::Back),
130                ..Default::default()
131            },
132            depth_stencil: None,
133            multisample: Default::default(),
134            multiview: None,
135            cache: None,
136        });
137        Self { pipeline }
138    }
139    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
140        let mut encoder =
141            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
142        {
143            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
144                label: None,
145                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
146                    view,
147                    resolve_target: None,
148                    ops: wgpu::Operations {
149                        load: wgpu::LoadOp::Clear(wgpu::Color {
150                            r: 0.1,
151                            g: 0.2,
152                            b: 0.3,
153                            a: 1.0,
154                        }),
155                        store: wgpu::StoreOp::Store,
156                    },
157                    depth_slice: None,
158                })],
159                depth_stencil_attachment: None,
160                timestamp_writes: None,
161                occlusion_query_set: None,
162                multiview_mask: None,
163            });
164            rpass.push_debug_group("Prepare data for draw.");
165            rpass.set_pipeline(&self.pipeline);
166            rpass.pop_debug_group();
167            rpass.insert_debug_marker("Draw!");
168            rpass.draw_mesh_tasks(1, 1, 1);
169        }
170        queue.submit(Some(encoder.finish()));
171    }
172    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
173        Default::default()
174    }
175    fn required_features() -> wgpu::Features {
176        wgpu::Features::EXPERIMENTAL_MESH_SHADER | wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS
177    }
178    fn required_limits() -> wgpu::Limits {
179        wgpu::Limits::defaults().using_recommended_minimum_mesh_shader_values()
180    }
181    fn resize(
182        &mut self,
183        _config: &wgpu::SurfaceConfiguration,
184        _device: &wgpu::Device,
185        _queue: &wgpu::Queue,
186    ) {
187        // empty
188    }
189    fn update(&mut self, _event: winit::event::WindowEvent) {
190        // empty
191    }
192}
193
194pub fn main() {
195    crate::framework::run::<Example>("mesh_shader");
196}
197
198#[cfg(test)]
199#[wgpu_test::gpu_test]
200pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
201    name: "mesh_shader",
202    image_path: "/examples/features/src/mesh_shader/screenshot.png",
203    width: 1024,
204    height: 768,
205    optional_features: wgpu::Features::default(),
206    base_test_parameters: wgpu_test::TestParameters::default()
207        .features(
208            wgpu::Features::EXPERIMENTAL_MESH_SHADER
209                | wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS,
210        )
211        .limits(wgpu::Limits::defaults().using_recommended_minimum_mesh_shader_values()),
212    comparisons: &[wgpu_test::ComparisonType::Mean(0.01)],
213    _phantom: std::marker::PhantomData::<Example>,
214};