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
64pub struct Example {
65 pipeline: wgpu::RenderPipeline,
66}
67impl crate::framework::Example for Example {
68 fn init(
69 config: &wgpu::SurfaceConfiguration,
70 adapter: &wgpu::Adapter,
71 device: &wgpu::Device,
72 _queue: &wgpu::Queue,
73 ) -> Self {
74 let (ts, ms, fs) = if adapter.get_info().backend == wgpu::Backend::Vulkan {
75 (
76 compile_glsl(device, "task"),
77 compile_glsl(device, "mesh"),
78 compile_glsl(device, "frag"),
79 )
80 } else if adapter.get_info().backend == wgpu::Backend::Dx12 {
81 (
82 compile_hlsl(device, "Task", "as"),
83 compile_hlsl(device, "Mesh", "ms"),
84 compile_hlsl(device, "Frag", "ps"),
85 )
86 } else {
87 panic!("Example can only run on vulkan or dx12");
88 };
89 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
90 label: None,
91 bind_group_layouts: &[],
92 push_constant_ranges: &[],
93 });
94 let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor {
95 label: None,
96 layout: Some(&pipeline_layout),
97 task: Some(wgpu::TaskState {
98 module: &ts,
99 entry_point: Some("main"),
100 compilation_options: Default::default(),
101 }),
102 mesh: wgpu::MeshState {
103 module: &ms,
104 entry_point: Some("main"),
105 compilation_options: Default::default(),
106 },
107 fragment: Some(wgpu::FragmentState {
108 module: &fs,
109 entry_point: Some("main"),
110 compilation_options: Default::default(),
111 targets: &[Some(config.view_formats[0].into())],
112 }),
113 primitive: wgpu::PrimitiveState {
114 cull_mode: Some(wgpu::Face::Back),
115 ..Default::default()
116 },
117 depth_stencil: None,
118 multisample: Default::default(),
119 multiview: None,
120 cache: None,
121 });
122 Self { pipeline }
123 }
124 fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
125 let mut encoder =
126 device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
127 {
128 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
129 label: None,
130 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
131 view,
132 resolve_target: None,
133 ops: wgpu::Operations {
134 load: wgpu::LoadOp::Clear(wgpu::Color {
135 r: 0.1,
136 g: 0.2,
137 b: 0.3,
138 a: 1.0,
139 }),
140 store: wgpu::StoreOp::Store,
141 },
142 depth_slice: None,
143 })],
144 depth_stencil_attachment: None,
145 timestamp_writes: None,
146 occlusion_query_set: None,
147 });
148 rpass.push_debug_group("Prepare data for draw.");
149 rpass.set_pipeline(&self.pipeline);
150 rpass.pop_debug_group();
151 rpass.insert_debug_marker("Draw!");
152 rpass.draw_mesh_tasks(1, 1, 1);
153 }
154 queue.submit(Some(encoder.finish()));
155 }
156 fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
157 Default::default()
158 }
159 fn required_features() -> wgpu::Features {
160 wgpu::Features::EXPERIMENTAL_MESH_SHADER | wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS
161 }
162 fn required_limits() -> wgpu::Limits {
163 wgpu::Limits::defaults().using_recommended_minimum_mesh_shader_values()
164 }
165 fn resize(
166 &mut self,
167 _config: &wgpu::SurfaceConfiguration,
168 _device: &wgpu::Device,
169 _queue: &wgpu::Queue,
170 ) {
171 }
173 fn update(&mut self, _event: winit::event::WindowEvent) {
174 }
176}
177
178pub fn main() {
179 crate::framework::run::<Example>("mesh_shader");
180}