wgpu_examples/mesh_shader/
mod.rs1use std::process::Stdio;
2
3fn 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 }
189 fn update(&mut self, _event: winit::event::WindowEvent) {
190 }
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};