Skip to content

Commit 24f42f2

Browse files
committed
example-runner-wgpu: enable debugPrintf panic support via --force-spirv-passthru.
1 parent 9808cd0 commit 24f42f2

File tree

4 files changed

+124
-52
lines changed

4 files changed

+124
-52
lines changed

examples/runners/ash/src/main.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,6 @@ pub fn compile_shaders() -> Vec<SpvFile> {
211211

212212
SpirvBuilder::new("examples/shaders/sky-shader", "spirv-unknown-vulkan1.1")
213213
.print_metadata(MetadataPrintout::None)
214-
// HACK(eddyb) having the `ash` runner do this is the easiest way I found
215-
// to test this `panic!` feature with actual `debugPrintf` support.
216214
.shader_panic_strategy(spirv_builder::ShaderPanicStrategy::DebugPrintfThenExit {
217215
print_inputs: true,
218216
print_backtrace: true,
@@ -380,10 +378,10 @@ impl RenderBase {
380378
};
381379

382380
let device: ash::Device = {
383-
let mut device_extension_names_raw = vec![khr::Swapchain::name().as_ptr()];
384-
if options.debug_layer {
385-
device_extension_names_raw.push(vk::KhrShaderNonSemanticInfoFn::name().as_ptr());
386-
}
381+
let device_extension_names_raw = [
382+
khr::Swapchain::name().as_ptr(),
383+
vk::KhrShaderNonSemanticInfoFn::name().as_ptr(),
384+
];
387385
let features = vk::PhysicalDeviceFeatures {
388386
shader_clip_distance: 1,
389387
..Default::default()

examples/runners/wgpu/src/compute.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
use wgpu::util::DeviceExt;
1+
use crate::{maybe_watch, CompiledShaderModules, Options};
22

3-
use super::Options;
43
use std::{convert::TryInto, time::Duration};
4+
use wgpu::util::DeviceExt;
55

66
pub fn start(options: &Options) {
77
env_logger::init();
88

9-
let shader_binary = crate::maybe_watch(options.shader, None);
9+
let compiled_shader_modules = maybe_watch(options, None);
1010

11-
futures::executor::block_on(start_internal(options, shader_binary));
11+
futures::executor::block_on(start_internal(options, compiled_shader_modules));
1212
}
1313

14-
pub async fn start_internal(
15-
options: &Options,
16-
shader_binary: wgpu::ShaderModuleDescriptorSpirV<'static>,
17-
) {
14+
async fn start_internal(options: &Options, compiled_shader_modules: CompiledShaderModules) {
1815
let backends = wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::PRIMARY);
1916
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
2017
backends,
@@ -46,11 +43,14 @@ pub async fn start_internal(
4643

4744
let timestamp_period = queue.get_timestamp_period();
4845

46+
let entry_point = "main_cs";
47+
4948
// FIXME(eddyb) automate this decision by default.
49+
let module = compiled_shader_modules.spv_module_for_entry_point(entry_point);
5050
let module = if options.force_spirv_passthru {
51-
unsafe { device.create_shader_module_spirv(&shader_binary) }
51+
unsafe { device.create_shader_module_spirv(&module) }
5252
} else {
53-
let wgpu::ShaderModuleDescriptorSpirV { label, source } = shader_binary;
53+
let wgpu::ShaderModuleDescriptorSpirV { label, source } = module;
5454
device.create_shader_module(wgpu::ShaderModuleDescriptor {
5555
label,
5656
source: wgpu::ShaderSource::SpirV(source),
@@ -89,7 +89,7 @@ pub async fn start_internal(
8989
label: None,
9090
layout: Some(&pipeline_layout),
9191
module: &module,
92-
entry_point: "main_cs",
92+
entry_point,
9393
});
9494

9595
let readback_buffer = device.create_buffer(&wgpu::BufferDescriptor {

examples/runners/wgpu/src/graphics.rs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use crate::maybe_watch;
1+
use crate::{maybe_watch, CompiledShaderModules, Options};
22

3-
use super::Options;
43
use shared::ShaderConstants;
54
use winit::{
65
event::{ElementState, Event, KeyboardInput, MouseButton, VirtualKeyCode, WindowEvent},
@@ -34,9 +33,9 @@ fn mouse_button_index(button: MouseButton) -> usize {
3433

3534
async fn run(
3635
options: Options,
37-
event_loop: EventLoop<wgpu::ShaderModuleDescriptorSpirV<'static>>,
36+
event_loop: EventLoop<CompiledShaderModules>,
3837
window: Window,
39-
shader_binary: wgpu::ShaderModuleDescriptorSpirV<'static>,
38+
compiled_shader_modules: CompiledShaderModules,
4039
) {
4140
let backends = wgpu::util::backend_bits_from_env()
4241
.unwrap_or(wgpu::Backends::VULKAN | wgpu::Backends::METAL);
@@ -135,7 +134,7 @@ async fn run(
135134
|pending| pending.preferred_format,
136135
|(_, surface_config)| surface_config.format,
137136
),
138-
shader_binary,
137+
compiled_shader_modules,
139138
);
140139

141140
let start = std::time::Instant::now();
@@ -336,24 +335,45 @@ fn create_pipeline(
336335
device: &wgpu::Device,
337336
pipeline_layout: &wgpu::PipelineLayout,
338337
surface_format: wgpu::TextureFormat,
339-
shader_binary: wgpu::ShaderModuleDescriptorSpirV<'_>,
338+
compiled_shader_modules: CompiledShaderModules,
340339
) -> wgpu::RenderPipeline {
341340
// FIXME(eddyb) automate this decision by default.
342-
let module = if options.force_spirv_passthru {
343-
unsafe { device.create_shader_module_spirv(&shader_binary) }
341+
let create_module = |module| {
342+
if options.force_spirv_passthru {
343+
unsafe { device.create_shader_module_spirv(&module) }
344+
} else {
345+
let wgpu::ShaderModuleDescriptorSpirV { label, source } = module;
346+
device.create_shader_module(wgpu::ShaderModuleDescriptor {
347+
label,
348+
source: wgpu::ShaderSource::SpirV(source),
349+
})
350+
}
351+
};
352+
353+
let vs_entry_point = shaders::main_vs;
354+
let fs_entry_point = shaders::main_fs;
355+
356+
let vs_module_descr = compiled_shader_modules.spv_module_for_entry_point(vs_entry_point);
357+
let fs_module_descr = compiled_shader_modules.spv_module_for_entry_point(fs_entry_point);
358+
359+
// HACK(eddyb) avoid calling `device.create_shader_module` twice unnecessarily.
360+
let vs_fs_same_module = std::ptr::eq(&vs_module_descr.source[..], &fs_module_descr.source[..]);
361+
362+
let vs_module = &create_module(vs_module_descr);
363+
let fs_module;
364+
let fs_module = if vs_fs_same_module {
365+
vs_module
344366
} else {
345-
let wgpu::ShaderModuleDescriptorSpirV { label, source } = shader_binary;
346-
device.create_shader_module(wgpu::ShaderModuleDescriptor {
347-
label,
348-
source: wgpu::ShaderSource::SpirV(source),
349-
})
367+
fs_module = create_module(fs_module_descr);
368+
&fs_module
350369
};
370+
351371
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
352372
label: None,
353373
layout: Some(pipeline_layout),
354374
vertex: wgpu::VertexState {
355-
module: &module,
356-
entry_point: shaders::main_vs,
375+
module: vs_module,
376+
entry_point: vs_entry_point,
357377
buffers: &[],
358378
},
359379
primitive: wgpu::PrimitiveState {
@@ -372,8 +392,8 @@ fn create_pipeline(
372392
alpha_to_coverage_enabled: false,
373393
},
374394
fragment: Some(wgpu::FragmentState {
375-
module: &module,
376-
entry_point: shaders::main_fs,
395+
module: fs_module,
396+
entry_point: fs_entry_point,
377397
targets: &[Some(wgpu::ColorTargetState {
378398
format: surface_format,
379399
blend: None,
@@ -410,7 +430,7 @@ pub fn start(
410430

411431
// Build the shader before we pop open a window, since it might take a while.
412432
let initial_shader = maybe_watch(
413-
options.shader,
433+
options,
414434
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
415435
{
416436
let proxy = event_loop.create_proxy();

examples/runners/wgpu/src/lib.rs

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
// crate-specific exceptions:
7171
// #![allow()]
7272

73+
use std::borrow::Cow;
7374
use structopt::StructOpt;
7475
use strum::{Display, EnumString};
7576

@@ -87,16 +88,45 @@ pub enum RustGPUShader {
8788
Mouse,
8889
}
8990

91+
struct CompiledShaderModules {
92+
named_spv_modules: Vec<(Option<String>, wgpu::ShaderModuleDescriptorSpirV<'static>)>,
93+
}
94+
95+
impl CompiledShaderModules {
96+
fn spv_module_for_entry_point<'a>(
97+
&'a self,
98+
wanted_entry: &str,
99+
) -> wgpu::ShaderModuleDescriptorSpirV<'a> {
100+
for (name, spv_module) in &self.named_spv_modules {
101+
match name {
102+
Some(name) if name != wanted_entry => continue,
103+
_ => {
104+
return wgpu::ShaderModuleDescriptorSpirV {
105+
label: name.as_deref(),
106+
source: Cow::Borrowed(&spv_module.source),
107+
};
108+
}
109+
}
110+
}
111+
unreachable!(
112+
"{wanted_entry:?} not found in modules {:?}",
113+
self.named_spv_modules
114+
.iter()
115+
.map(|(name, _)| name)
116+
.collect::<Vec<_>>()
117+
);
118+
}
119+
}
120+
90121
fn maybe_watch(
91-
shader: RustGPUShader,
122+
options: &Options,
92123
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))] on_watch: Option<
93-
Box<dyn FnMut(wgpu::ShaderModuleDescriptorSpirV<'static>) + Send + 'static>,
124+
Box<dyn FnMut(CompiledShaderModules) + Send + 'static>,
94125
>,
95-
) -> wgpu::ShaderModuleDescriptorSpirV<'static> {
126+
) -> CompiledShaderModules {
96127
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
97128
{
98129
use spirv_builder::{CompileResult, MetadataPrintout, SpirvBuilder};
99-
use std::borrow::Cow;
100130
use std::path::PathBuf;
101131
// Hack: spirv_builder builds into a custom directory if running under cargo, to not
102132
// deadlock, and the default target directory if not. However, packages like `proc-macro2`
@@ -106,7 +136,7 @@ fn maybe_watch(
106136
// under cargo by setting these environment variables.
107137
std::env::set_var("OUT_DIR", env!("OUT_DIR"));
108138
std::env::set_var("PROFILE", env!("PROFILE"));
109-
let crate_name = match shader {
139+
let crate_name = match options.shader {
110140
RustGPUShader::Simplest => "simplest-shader",
111141
RustGPUShader::Sky => "sky-shader",
112142
RustGPUShader::Compute => "compute-shader",
@@ -117,26 +147,50 @@ fn maybe_watch(
117147
.iter()
118148
.copied()
119149
.collect::<PathBuf>();
150+
151+
let has_debug_printf = options.force_spirv_passthru;
152+
120153
let builder = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.1")
121-
.print_metadata(MetadataPrintout::None);
154+
.print_metadata(MetadataPrintout::None)
155+
.shader_panic_strategy(if has_debug_printf {
156+
spirv_builder::ShaderPanicStrategy::DebugPrintfThenExit {
157+
print_inputs: true,
158+
print_backtrace: true,
159+
}
160+
} else {
161+
spirv_builder::ShaderPanicStrategy::SilentExit
162+
})
163+
// HACK(eddyb) needed because of `debugPrintf` instrumentation limitations
164+
// (see https://github.com/KhronosGroup/SPIRV-Tools/issues/4892).
165+
.multimodule(has_debug_printf);
122166
let initial_result = if let Some(mut f) = on_watch {
123167
builder
124168
.watch(move |compile_result| f(handle_compile_result(compile_result)))
125169
.expect("Configuration is correct for watching")
126170
} else {
127171
builder.build().unwrap()
128172
};
129-
fn handle_compile_result(
130-
compile_result: CompileResult,
131-
) -> wgpu::ShaderModuleDescriptorSpirV<'static> {
132-
let module_path = compile_result.module.unwrap_single();
133-
let data = std::fs::read(module_path).unwrap();
134-
// FIXME(eddyb) this reallocates all the data pointlessly, there is
135-
// not a good reason to use `ShaderModuleDescriptorSpirV` specifically.
136-
let spirv = Cow::Owned(wgpu::util::make_spirv_raw(&data).into_owned());
137-
wgpu::ShaderModuleDescriptorSpirV {
138-
label: None,
139-
source: spirv,
173+
fn handle_compile_result(compile_result: CompileResult) -> CompiledShaderModules {
174+
let load_spv_module = |path| {
175+
let data = std::fs::read(path).unwrap();
176+
// FIXME(eddyb) this reallocates all the data pointlessly, there is
177+
// not a good reason to use `ShaderModuleDescriptorSpirV` specifically.
178+
let spirv = Cow::Owned(wgpu::util::make_spirv_raw(&data).into_owned());
179+
wgpu::ShaderModuleDescriptorSpirV {
180+
label: None,
181+
source: spirv,
182+
}
183+
};
184+
CompiledShaderModules {
185+
named_spv_modules: match compile_result.module {
186+
spirv_builder::ModuleResult::SingleModule(path) => {
187+
vec![(None, load_spv_module(path))]
188+
}
189+
spirv_builder::ModuleResult::MultiModule(modules) => modules
190+
.into_iter()
191+
.map(|(name, path)| (Some(name), load_spv_module(path)))
192+
.collect(),
193+
},
140194
}
141195
}
142196
handle_compile_result(initial_result)

0 commit comments

Comments
 (0)