diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8df0d6f200..01c5f55c63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added ⭐
+- [PR#1036](https://github.com/EmbarkStudios/rust-gpu/pull/1036) added a `--force-spirv-passthru` flag to `example-runner-wgpu`, to bypass Naga (`wgpu`'s shader translator),
+ used it to test `debugPrintf` for `wgpu`, and updated `ShaderPanicStrategy::DebugPrintfThenExit` docs to reflect what "enabling `debugPrintf`" looks like for `wgpu`
+ (e.g. `VK_LOADER_LAYERS_ENABLE=VK_LAYER_KHRONOS_validation VK_LAYER_ENABLES=VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT DEBUG_PRINTF_TO_STDOUT=1`)
- [PR#1080](https://github.com/EmbarkStudios/rust-gpu/pull/1080) added `debugPrintf`-based
panic reporting, with the desired behavior selected via `spirv_builder::ShaderPanicStrategy`
(see its documentation for more details about each available panic handling strategy)
diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs
index 4fc502f083..430650b482 100644
--- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs
+++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs
@@ -2648,7 +2648,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
// HACK(eddyb) redirect any possible panic call to an abort, to avoid
// needing to materialize `&core::panic::Location` or `format_args!`.
// FIXME(eddyb) find a way to extract the original message.
- self.abort_with_message("panic!(...)".into());
+ self.abort_with_message("panicked: ".into());
self.undef(result_type)
} else if let Some(mode) = buffer_load_intrinsic {
self.codegen_buffer_load_intrinsic(result_type, args, mode)
diff --git a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs
index 9f05f03cdb..b94475c5dc 100644
--- a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs
+++ b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs
@@ -339,7 +339,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
fn abort(&mut self) {
- self.abort_with_message("intrinsics::abort()".into());
+ self.abort_with_message("aborted: intrinsics::abort() called".into());
}
fn assume(&mut self, _val: Self::Value) {
diff --git a/crates/rustc_codegen_spirv/src/linker/spirt_passes/controlflow.rs b/crates/rustc_codegen_spirv/src/linker/spirt_passes/controlflow.rs
index 5ced285a19..e22c94beab 100644
--- a/crates/rustc_codegen_spirv/src/linker/spirt_passes/controlflow.rs
+++ b/crates/rustc_codegen_spirv/src/linker/spirt_passes/controlflow.rs
@@ -330,12 +330,16 @@ pub fn convert_custom_aborts_to_unstructured_returns_in_entry_points(
let mut fmt = String::new();
// HACK(eddyb) this improves readability w/ very verbose Vulkan loggers.
- fmt += "\n ";
+ fmt += "\n";
+ fmt += "[RUST-GPU] ";
+ fmt += &cx[const_str(message)].replace('%', "%%");
+
+ // FIXME(eddyb) deduplicate with "called at" form below
+ // (not trivial becasue both closures would borrow `fmt`).
if let Some((file, line, col)) = current_debug_src_loc.take() {
- fmt += &format!("{file}:{line}:{col}: ").replace('%', "%%");
+ fmt += &format!("\n at {file}:{line}:{col}").replace('%', "%%");
}
- fmt += &cx[const_str(message)].replace('%', "%%");
let mut innermost = true;
let mut append_call = |callsite_debug_src_loc, callee: &str| {
@@ -360,6 +364,8 @@ pub fn convert_custom_aborts_to_unstructured_returns_in_entry_points(
}
append_call(None, &debug_printf_context_fmt_str);
+ fmt += "\n";
+
let abort_inst_def = &mut func_def_body.data_insts[abort_inst];
abort_inst_def.kind = DataInstKind::SpvExtInst {
ext_set: cx.intern("NonSemantic.DebugPrintf"),
diff --git a/crates/spirv-builder/src/lib.rs b/crates/spirv-builder/src/lib.rs
index ed17a22ae0..5f516e3edd 100644
--- a/crates/spirv-builder/src/lib.rs
+++ b/crates/spirv-builder/src/lib.rs
@@ -193,15 +193,40 @@ pub enum ShaderPanicStrategy {
/// If you have multiple entry-points, you *may* need to also enable the
/// `multimodule` node (see ).
///
- /// **Note**: actually obtaining the `debugPrintf` output requires enabling:
- /// * `VK_KHR_shader_non_semantic_info` Vulkan *Device* extension
- /// * Vulkan Validation Layers (which contain the `debugPrintf` implementation)
- /// * `VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT` in Validation Layers,
- /// either by using `VkValidationFeaturesEXT` during instance creating,
- /// setting the `VK_LAYER_ENABLES` environment variable to that value,
- /// or adding it to `khronos_validation.enables` in `vk_layer_settings.txt`
- ///
- /// See also: .
+ /// **Note**: actually obtaining the `debugPrintf` output requires:
+ /// * Vulkan Validation Layers (from e.g. the Vulkan SDK)
+ /// * (they contain the `debugPrintf` implementation, a SPIR-V -> SPIR-V translation)
+ /// * **set the `VK_LOADER_LAYERS_ENABLE=VK_LAYER_KHRONOS_validation`
+ /// environment variable** to easily enable them without any code changes
+ /// * alternatively, `"VK_LAYER_KHRONOS_validation"` can be passed during
+ /// instance creation, to enable them programmatically
+ /// * Validation Layers' `debugPrintf` support:
+ /// * **set the `VK_LAYER_ENABLES=VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT`
+ /// environment variable** to easily enable the `debugPrintf` support
+ /// * alternatively, `VkValidationFeaturesEXT` during instance creation,
+ /// or the `khronos_validation.enables` field in `vk_layer_settings.txt`,
+ /// can be used to enable `VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT`
+ /// (see also )
+ /// * for outputting the `debugPrintf` messages sent back from the GPU:
+ /// * **set the `DEBUG_PRINTF_TO_STDOUT=1` environment variable** if you don't
+ /// plan on customizing the reporting (see below for alternatives)
+ /// * for `wgpu`:
+ /// * **required**: `wgpu::Features::SPIRV_SHADER_PASSTHROUGH` (Naga lacks `debugPrintf`)
+ /// * *optional*: building in debug mode (and/or with debug-assertions enabled),
+ /// to enable `wgpu` logging/debug support
+ /// * (the debug assertions requirement may be lifted in future `wgpu` versions)
+ /// * this uses `VK_EXT_debug_utils` internally, and is a better-integrated
+ /// alternative to just setting `DEBUG_PRINTF_TO_STDOUT=1`
+ /// * `RUST_LOG=wgpu_hal::vulkan=info` (or equivalent) will enable said
+ /// output (as `debugPrintf` messages have the "info" level)
+ /// * `RUST_LOG` controls `env_logger`, which isn't itself required,
+ /// but *some* `log`/`tracing` subscriber is needed to get any output
+ /// * for Vulkan (e.g. via `ash`):
+ /// * **required**: enabling the `VK_KHR_shader_non_semantic_info` Vulkan *Device* extension
+ /// * *optional*: as described above, enabling the Validation Layers and
+ /// their `debugPrintf` support can be done during instance creation
+ /// * *optional*: integrating [`VK_EXT_debug_utils`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html)
+ /// allows more reporting flexibility than `DEBUG_PRINTF_TO_STDOUT=1`)
DebugPrintfThenExit {
/// Whether to also print the entry-point inputs (excluding buffers/resources),
/// which should uniquely identify the panicking shader invocation.
diff --git a/examples/runners/ash/src/main.rs b/examples/runners/ash/src/main.rs
index 8e65bd6a26..9ff4430b60 100644
--- a/examples/runners/ash/src/main.rs
+++ b/examples/runners/ash/src/main.rs
@@ -211,8 +211,6 @@ pub fn compile_shaders() -> Vec {
SpirvBuilder::new("examples/shaders/sky-shader", "spirv-unknown-vulkan1.1")
.print_metadata(MetadataPrintout::None)
- // HACK(eddyb) having the `ash` runner do this is the easiest way I found
- // to test this `panic!` feature with actual `debugPrintf` support.
.shader_panic_strategy(spirv_builder::ShaderPanicStrategy::DebugPrintfThenExit {
print_inputs: true,
print_backtrace: true,
@@ -380,10 +378,10 @@ impl RenderBase {
};
let device: ash::Device = {
- let mut device_extension_names_raw = vec![khr::Swapchain::name().as_ptr()];
- if options.debug_layer {
- device_extension_names_raw.push(vk::KhrShaderNonSemanticInfoFn::name().as_ptr());
- }
+ let device_extension_names_raw = [
+ khr::Swapchain::name().as_ptr(),
+ vk::KhrShaderNonSemanticInfoFn::name().as_ptr(),
+ ];
let features = vk::PhysicalDeviceFeatures {
shader_clip_distance: 1,
..Default::default()
diff --git a/examples/runners/wgpu/src/compute.rs b/examples/runners/wgpu/src/compute.rs
index 6736642003..8e706e0bc5 100644
--- a/examples/runners/wgpu/src/compute.rs
+++ b/examples/runners/wgpu/src/compute.rs
@@ -1,20 +1,17 @@
-use wgpu::util::DeviceExt;
+use crate::{maybe_watch, CompiledShaderModules, Options};
-use super::Options;
use std::{convert::TryInto, time::Duration};
+use wgpu::util::DeviceExt;
pub fn start(options: &Options) {
env_logger::init();
- let shader_binary = crate::maybe_watch(options.shader, None);
+ let compiled_shader_modules = maybe_watch(options, None);
- futures::executor::block_on(start_internal(options, shader_binary));
+ futures::executor::block_on(start_internal(options, compiled_shader_modules));
}
-pub async fn start_internal(
- _options: &Options,
- shader_binary: wgpu::ShaderModuleDescriptor<'static>,
-) {
+async fn start_internal(options: &Options, compiled_shader_modules: CompiledShaderModules) {
let backends = wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::PRIMARY);
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends,
@@ -24,11 +21,17 @@ pub async fn start_internal(
.await
.expect("Failed to find an appropriate adapter");
+ let mut features =
+ wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES;
+ if options.force_spirv_passthru {
+ features |= wgpu::Features::SPIRV_SHADER_PASSTHROUGH;
+ }
+
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
- features: wgpu::Features::TIMESTAMP_QUERY,
+ features,
limits: wgpu::Limits::default(),
},
None,
@@ -40,8 +43,19 @@ pub async fn start_internal(
let timestamp_period = queue.get_timestamp_period();
- // Load the shaders from disk
- let module = device.create_shader_module(shader_binary);
+ let entry_point = "main_cs";
+
+ // FIXME(eddyb) automate this decision by default.
+ let module = compiled_shader_modules.spv_module_for_entry_point(entry_point);
+ let module = if options.force_spirv_passthru {
+ unsafe { device.create_shader_module_spirv(&module) }
+ } else {
+ let wgpu::ShaderModuleDescriptorSpirV { label, source } = module;
+ device.create_shader_module(wgpu::ShaderModuleDescriptor {
+ label,
+ source: wgpu::ShaderSource::SpirV(source),
+ })
+ };
let top = 2u32.pow(20);
let src_range = 1..top;
@@ -75,7 +89,7 @@ pub async fn start_internal(
label: None,
layout: Some(&pipeline_layout),
module: &module,
- entry_point: "main_cs",
+ entry_point,
});
let readback_buffer = device.create_buffer(&wgpu::BufferDescriptor {
@@ -97,10 +111,17 @@ pub async fn start_internal(
let timestamp_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Timestamps buffer"),
size: 16,
+ usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
+ mapped_at_creation: false,
+ });
+
+ let timestamp_readback_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+ label: None,
+ size: 16,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: true,
});
- timestamp_buffer.unmap();
+ timestamp_readback_buffer.unmap();
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
@@ -137,10 +158,17 @@ pub async fn start_internal(
src.len() as wgpu::BufferAddress,
);
encoder.resolve_query_set(&queries, 0..2, ×tamp_buffer, 0);
+ encoder.copy_buffer_to_buffer(
+ ×tamp_buffer,
+ 0,
+ ×tamp_readback_buffer,
+ 0,
+ timestamp_buffer.size(),
+ );
queue.submit(Some(encoder.finish()));
let buffer_slice = readback_buffer.slice(..);
- let timestamp_slice = timestamp_buffer.slice(..);
+ let timestamp_slice = timestamp_readback_buffer.slice(..);
timestamp_slice.map_async(wgpu::MapMode::Read, |r| r.unwrap());
buffer_slice.map_async(wgpu::MapMode::Read, |r| r.unwrap());
// NOTE(eddyb) `poll` should return only after the above callbacks fire
@@ -160,7 +188,7 @@ pub async fn start_internal(
drop(data);
readback_buffer.unmap();
drop(timing_data);
- timestamp_buffer.unmap();
+ timestamp_readback_buffer.unmap();
let mut max = 0;
for (src, out) in src_range.zip(result.iter().copied()) {
if out == u32::MAX {
diff --git a/examples/runners/wgpu/src/graphics.rs b/examples/runners/wgpu/src/graphics.rs
index 9a5bd43263..5d1cb202e5 100644
--- a/examples/runners/wgpu/src/graphics.rs
+++ b/examples/runners/wgpu/src/graphics.rs
@@ -1,6 +1,5 @@
-use crate::maybe_watch;
+use crate::{maybe_watch, CompiledShaderModules, Options};
-use super::Options;
use shared::ShaderConstants;
use winit::{
event::{ElementState, Event, KeyboardInput, MouseButton, VirtualKeyCode, WindowEvent},
@@ -33,9 +32,10 @@ fn mouse_button_index(button: MouseButton) -> usize {
}
async fn run(
- event_loop: EventLoop>,
+ options: Options,
+ event_loop: EventLoop,
window: Window,
- shader_binary: wgpu::ShaderModuleDescriptor<'static>,
+ compiled_shader_modules: CompiledShaderModules,
) {
let backends = wgpu::util::backend_bits_from_env()
.unwrap_or(wgpu::Backends::VULKAN | wgpu::Backends::METAL);
@@ -69,7 +69,10 @@ async fn run(
.await
.expect("Failed to find an appropriate adapter");
- let features = wgpu::Features::PUSH_CONSTANTS;
+ let mut features = wgpu::Features::PUSH_CONSTANTS;
+ if options.force_spirv_passthru {
+ features |= wgpu::Features::SPIRV_SHADER_PASSTHROUGH;
+ }
let limits = wgpu::Limits {
max_push_constant_size: 128,
..Default::default()
@@ -124,13 +127,14 @@ async fn run(
});
let mut render_pipeline = create_pipeline(
+ &options,
&device,
&pipeline_layout,
surface_with_config.as_ref().map_or_else(
|pending| pending.preferred_format,
|(_, surface_config)| surface_config.format,
),
- shader_binary,
+ compiled_shader_modules,
);
let start = std::time::Instant::now();
@@ -309,6 +313,7 @@ async fn run(
}
Event::UserEvent(new_module) => {
*render_pipeline = create_pipeline(
+ &options,
&device,
&pipeline_layout,
surface_with_config.as_ref().map_or_else(
@@ -326,18 +331,49 @@ async fn run(
}
fn create_pipeline(
+ options: &Options,
device: &wgpu::Device,
pipeline_layout: &wgpu::PipelineLayout,
surface_format: wgpu::TextureFormat,
- shader_binary: wgpu::ShaderModuleDescriptor<'_>,
+ compiled_shader_modules: CompiledShaderModules,
) -> wgpu::RenderPipeline {
- let module = device.create_shader_module(shader_binary);
+ // FIXME(eddyb) automate this decision by default.
+ let create_module = |module| {
+ if options.force_spirv_passthru {
+ unsafe { device.create_shader_module_spirv(&module) }
+ } else {
+ let wgpu::ShaderModuleDescriptorSpirV { label, source } = module;
+ device.create_shader_module(wgpu::ShaderModuleDescriptor {
+ label,
+ source: wgpu::ShaderSource::SpirV(source),
+ })
+ }
+ };
+
+ let vs_entry_point = shaders::main_vs;
+ let fs_entry_point = shaders::main_fs;
+
+ let vs_module_descr = compiled_shader_modules.spv_module_for_entry_point(vs_entry_point);
+ let fs_module_descr = compiled_shader_modules.spv_module_for_entry_point(fs_entry_point);
+
+ // HACK(eddyb) avoid calling `device.create_shader_module` twice unnecessarily.
+ let vs_fs_same_module = std::ptr::eq(&vs_module_descr.source[..], &fs_module_descr.source[..]);
+
+ let vs_module = &create_module(vs_module_descr);
+ let fs_module;
+ let fs_module = if vs_fs_same_module {
+ vs_module
+ } else {
+ fs_module = create_module(fs_module_descr);
+ &fs_module
+ };
+
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(pipeline_layout),
vertex: wgpu::VertexState {
- module: &module,
- entry_point: shaders::main_vs,
+ module: vs_module,
+ entry_point: vs_entry_point,
buffers: &[],
},
primitive: wgpu::PrimitiveState {
@@ -356,8 +392,8 @@ fn create_pipeline(
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
- module: &module,
- entry_point: shaders::main_fs,
+ module: fs_module,
+ entry_point: fs_entry_point,
targets: &[Some(wgpu::ColorTargetState {
format: surface_format,
blend: None,
@@ -394,7 +430,7 @@ pub fn start(
// Build the shader before we pop open a window, since it might take a while.
let initial_shader = maybe_watch(
- options.shader,
+ options,
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
{
let proxy = event_loop.create_proxy();
@@ -425,16 +461,17 @@ pub fn start(
})
.expect("couldn't append canvas to document body");
wasm_bindgen_futures::spawn_local(run(
+ options.clone(),
event_loop,
window,
initial_shader,
));
} else {
futures::executor::block_on(run(
+ options.clone(),
event_loop,
window,
initial_shader,
-
));
}
}
diff --git a/examples/runners/wgpu/src/lib.rs b/examples/runners/wgpu/src/lib.rs
index 4bdb7cd9cc..2fe3fa7bcf 100644
--- a/examples/runners/wgpu/src/lib.rs
+++ b/examples/runners/wgpu/src/lib.rs
@@ -70,6 +70,7 @@
// crate-specific exceptions:
// #![allow()]
+use std::borrow::Cow;
use structopt::StructOpt;
use strum::{Display, EnumString};
@@ -87,16 +88,45 @@ pub enum RustGPUShader {
Mouse,
}
+struct CompiledShaderModules {
+ named_spv_modules: Vec<(Option, wgpu::ShaderModuleDescriptorSpirV<'static>)>,
+}
+
+impl CompiledShaderModules {
+ fn spv_module_for_entry_point<'a>(
+ &'a self,
+ wanted_entry: &str,
+ ) -> wgpu::ShaderModuleDescriptorSpirV<'a> {
+ for (name, spv_module) in &self.named_spv_modules {
+ match name {
+ Some(name) if name != wanted_entry => continue,
+ _ => {
+ return wgpu::ShaderModuleDescriptorSpirV {
+ label: name.as_deref(),
+ source: Cow::Borrowed(&spv_module.source),
+ };
+ }
+ }
+ }
+ unreachable!(
+ "{wanted_entry:?} not found in modules {:?}",
+ self.named_spv_modules
+ .iter()
+ .map(|(name, _)| name)
+ .collect::>()
+ );
+ }
+}
+
fn maybe_watch(
- shader: RustGPUShader,
+ options: &Options,
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))] on_watch: Option<
- Box) + Send + 'static>,
+ Box,
>,
-) -> wgpu::ShaderModuleDescriptor<'static> {
+) -> CompiledShaderModules {
#[cfg(not(any(target_os = "android", target_arch = "wasm32")))]
{
use spirv_builder::{CompileResult, MetadataPrintout, SpirvBuilder};
- use std::borrow::Cow;
use std::path::PathBuf;
// Hack: spirv_builder builds into a custom directory if running under cargo, to not
// deadlock, and the default target directory if not. However, packages like `proc-macro2`
@@ -106,7 +136,7 @@ fn maybe_watch(
// under cargo by setting these environment variables.
std::env::set_var("OUT_DIR", env!("OUT_DIR"));
std::env::set_var("PROFILE", env!("PROFILE"));
- let crate_name = match shader {
+ let crate_name = match options.shader {
RustGPUShader::Simplest => "simplest-shader",
RustGPUShader::Sky => "sky-shader",
RustGPUShader::Compute => "compute-shader",
@@ -117,8 +147,22 @@ fn maybe_watch(
.iter()
.copied()
.collect::();
+
+ let has_debug_printf = options.force_spirv_passthru;
+
let builder = SpirvBuilder::new(crate_path, "spirv-unknown-vulkan1.1")
- .print_metadata(MetadataPrintout::None);
+ .print_metadata(MetadataPrintout::None)
+ .shader_panic_strategy(if has_debug_printf {
+ spirv_builder::ShaderPanicStrategy::DebugPrintfThenExit {
+ print_inputs: true,
+ print_backtrace: true,
+ }
+ } else {
+ spirv_builder::ShaderPanicStrategy::SilentExit
+ })
+ // HACK(eddyb) needed because of `debugPrintf` instrumentation limitations
+ // (see https://github.com/KhronosGroup/SPIRV-Tools/issues/4892).
+ .multimodule(has_debug_printf);
let initial_result = if let Some(mut f) = on_watch {
builder
.watch(move |compile_result| f(handle_compile_result(compile_result)))
@@ -126,35 +170,55 @@ fn maybe_watch(
} else {
builder.build().unwrap()
};
- fn handle_compile_result(
- compile_result: CompileResult,
- ) -> wgpu::ShaderModuleDescriptor<'static> {
- let module_path = compile_result.module.unwrap_single();
- let data = std::fs::read(module_path).unwrap();
- let spirv = Cow::Owned(wgpu::util::make_spirv_raw(&data).into_owned());
- wgpu::ShaderModuleDescriptor {
- label: None,
- source: wgpu::ShaderSource::SpirV(spirv),
+ fn handle_compile_result(compile_result: CompileResult) -> CompiledShaderModules {
+ let load_spv_module = |path| {
+ let data = std::fs::read(path).unwrap();
+ // FIXME(eddyb) this reallocates all the data pointlessly, there is
+ // not a good reason to use `ShaderModuleDescriptorSpirV` specifically.
+ let spirv = Cow::Owned(wgpu::util::make_spirv_raw(&data).into_owned());
+ wgpu::ShaderModuleDescriptorSpirV {
+ label: None,
+ source: spirv,
+ }
+ };
+ CompiledShaderModules {
+ named_spv_modules: match compile_result.module {
+ spirv_builder::ModuleResult::SingleModule(path) => {
+ vec![(None, load_spv_module(path))]
+ }
+ spirv_builder::ModuleResult::MultiModule(modules) => modules
+ .into_iter()
+ .map(|(name, path)| (Some(name), load_spv_module(path)))
+ .collect(),
+ },
}
}
handle_compile_result(initial_result)
}
#[cfg(any(target_os = "android", target_arch = "wasm32"))]
{
- match shader {
- RustGPUShader::Simplest => wgpu::include_spirv!(env!("simplest_shader.spv")),
- RustGPUShader::Sky => wgpu::include_spirv!(env!("sky_shader.spv")),
- RustGPUShader::Compute => wgpu::include_spirv!(env!("compute_shader.spv")),
- RustGPUShader::Mouse => wgpu::include_spirv!(env!("mouse_shader.spv")),
+ let module = match options.shader {
+ RustGPUShader::Simplest => {
+ wgpu::include_spirv_raw!(env!("simplest_shader.spv"))
+ }
+ RustGPUShader::Sky => wgpu::include_spirv_raw!(env!("sky_shader.spv")),
+ RustGPUShader::Compute => wgpu::include_spirv_raw!(env!("compute_shader.spv")),
+ RustGPUShader::Mouse => wgpu::include_spirv_raw!(env!("mouse_shader.spv")),
+ };
+ CompiledShaderModules {
+ named_spv_modules: vec![(None, module)],
}
}
}
-#[derive(StructOpt)]
+#[derive(StructOpt, Clone)]
#[structopt(name = "example-runner-wgpu")]
pub struct Options {
#[structopt(short, long, default_value = "Sky")]
shader: RustGPUShader,
+
+ #[structopt(long)]
+ force_spirv_passthru: bool,
}
#[cfg_attr(target_os = "android", export_name = "android_main")]