Skip to content

Commit

Permalink
Merge #1518
Browse files Browse the repository at this point in the history
1518: Make spirv an optional feature r=cwfitzgerald a=kvark

**Connections**
gfx-rs/naga#940 shows how much SPIR-V parsing can be a pain.

**Description**
Keep it supported natively, but put it behind a feature flag. This allows to skip compilation of parts of Naga as well as dependencies like `petgraph`.
On my machine, compiling `wgpu-core` time is reduced from 40.87s to 35.36s, which is about 13% improvement.

**Testing**
Just compiling


Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
  • Loading branch information
bors[bot] and kvark authored Jun 22, 2021
2 parents 56d584a + 4c03d28 commit 5f2df9a
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 45 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 6 additions & 8 deletions player/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,17 +239,15 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
self.bind_group_drop::<A>(id);
}
Action::CreateShaderModule { id, desc, data } => {
log::info!("Creating shader from {}", data);
let code = fs::read_to_string(dir.join(&data)).unwrap();
let source = if data.ends_with(".wgsl") {
let code = fs::read_to_string(dir.join(data)).unwrap();
wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code))
} else if data.ends_with(".ron") {
let module = ron::de::from_str(&code).unwrap();
wgc::pipeline::ShaderModuleSource::Naga(module)
} else {
let byte_vec = fs::read(dir.join(&data))
.unwrap_or_else(|e| panic!("Unable to open '{}': {:?}", data, e));
let spv = byte_vec
.chunks(4)
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect::<Vec<_>>();
wgc::pipeline::ShaderModuleSource::SpirV(Cow::Owned(spv))
panic!("Unknown shader {}", data);
};
let (_, error) = self.device_create_shader_module::<A>(device, &desc, source, id);
if let Some(e) = error {
Expand Down
6 changes: 3 additions & 3 deletions wgpu-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ license = "MIT OR Apache-2.0"
[features]
default = []
# Enable API tracing
trace = ["ron", "serde", "wgt/trace", "arrayvec/serde"]
trace = ["ron", "serde", "wgt/trace", "arrayvec/serde", "naga/serialize"]
# Enable API replaying
replay = ["serde", "wgt/replay", "arrayvec/serde"]
replay = ["serde", "wgt/replay", "arrayvec/serde", "naga/deserialize"]
# Enable serializable compute/render passes, and bundle encoders.
serial-pass = ["serde", "wgt/serde", "arrayvec/serde"]

Expand All @@ -37,7 +37,7 @@ thiserror = "1"
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
tag = "gfx-26"
features = ["spv-in", "wgsl-in"]
features = ["wgsl-in"]

[dependencies.wgt]
path = "../wgpu-types"
Expand Down
34 changes: 5 additions & 29 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -838,27 +838,6 @@ impl<A: HalApi> Device<A> {
source: pipeline::ShaderModuleSource<'a>,
) -> Result<pipeline::ShaderModule<A>, pipeline::CreateShaderModuleError> {
let module = match source {
pipeline::ShaderModuleSource::SpirV(spv) => {
profiling::scope!("naga::spv::parse");
// Parse the given shader code and store its representation.
let options = naga::front::spv::Options {
adjust_coordinate_space: false, // we require NDC_Y_UP feature
strict_capabilities: true,
flow_graph_dump_prefix: None,
};
let parser = naga::front::spv::Parser::new(spv.iter().cloned(), &options);
match parser.parse() {
Ok(module) => module,
Err(err) => {
log::warn!(
"Failed to parse shader SPIR-V code for {:?}: {:?}",
desc.label,
err
);
return Err(pipeline::CreateShaderModuleError::Parsing);
}
}
}
pipeline::ShaderModuleSource::Wgsl(code) => {
profiling::scope!("naga::wgsl::parse_str");
// TODO: refactor the corresponding Naga error to be owned, and then
Expand Down Expand Up @@ -3476,17 +3455,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if let Some(ref trace) = device.trace {
let mut trace = trace.lock();
let data = match source {
pipeline::ShaderModuleSource::SpirV(ref spv) => {
trace.make_binary("spv", unsafe {
std::slice::from_raw_parts(spv.as_ptr() as *const u8, spv.len() * 4)
})
}
pipeline::ShaderModuleSource::Wgsl(ref code) => {
trace.make_binary("wgsl", code.as_bytes())
}
pipeline::ShaderModuleSource::Naga(_) => {
// we don't want to enable Naga serialization just for this alone
trace.make_binary("ron", &[])
pipeline::ShaderModuleSource::Naga(ref module) => {
let string =
ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default())
.unwrap();
trace.make_binary("ron", string.as_bytes())
}
};
trace.add(trace::Action::CreateShaderModule {
Expand Down
1 change: 0 additions & 1 deletion wgpu-core/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use std::borrow::Cow;
use thiserror::Error;

pub enum ShaderModuleSource<'a> {
SpirV(Cow<'a, [u32]>),
Wgsl(Cow<'a, str>),
Naga(naga::Module),
}
Expand Down
11 changes: 10 additions & 1 deletion wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ all-features = true

[features]
default = []
spirv = ["naga/spv-in"]
trace = ["serde", "wgc/trace"]
replay = ["serde", "wgc/replay"]
webgl = ["wgc"]
Expand Down Expand Up @@ -64,6 +65,11 @@ async-executor = "1.0"
pollster = "0.2"
env_logger = "0.8"

[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
tag = "gfx-26"
optional = true

# used to test all the example shaders
[dev-dependencies.naga]
git = "https://github.com/gfx-rs/naga"
Expand All @@ -78,9 +84,12 @@ features = ["wgsl-in", "spv-out"]

[[example]]
name="hello-compute"
path="examples/hello-compute/main.rs"
test = true

[[example]]
name="texture-arrays"
required-features = ["spirv"]

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2.73" # remember to change version in wiki as well
web-sys = { version = "=0.3.50", features = [
Expand Down
11 changes: 11 additions & 0 deletions wgpu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ The following environment variables can be used to configure how the framework e

See [wiki article](https://github.com/gfx-rs/wgpu-rs/wiki/Running-on-the-Web-with-WebGPU-and-WebGL).

## Shaders

[WGSL](https://gpuweb.github.io/gpuweb/wgsl/) is the main shading language of WebGPU.

Users can run the [naga](https://github.com/gfx-rs/naga) binary in the following way to convert their SPIR-V shaders to WGSL:
```bash
cargo run -- <input.spv> <output.wgsl>
```

In addition, SPIR-V can be used by enabling the `spirv` feature, and the cost of slightly increased build times.

## Development

If you need to test local fixes to gfx or other dependencies, the simplest way is to add a Cargo patch. For example, when working on DX12 backend on Windows, you can check out the latest release branch in the [gfx-hal repository](https://github.com/gfx-rs/gfx) (e.g. currently `hal-0.8`) and add this patch to the end of `Cargo.toml`:
Expand Down
14 changes: 13 additions & 1 deletion wgpu/src/backend/direct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,19 @@ impl crate::Context for Context {
label: desc.label.map(Borrowed),
};
let source = match desc.source {
ShaderSource::SpirV(ref spv) => wgc::pipeline::ShaderModuleSource::SpirV(Borrowed(spv)),
#[cfg(feature = "spirv")]
ShaderSource::SpirV(ref spv) => {
profiling::scope!("naga::spv::parse");
// Parse the given shader code and store its representation.
let options = naga::front::spv::Options {
adjust_coordinate_space: false, // we require NDC_Y_UP feature
strict_capabilities: true,
flow_graph_dump_prefix: None,
};
let parser = naga::front::spv::Parser::new(spv.iter().cloned(), &options);
let module = parser.parse().unwrap();
wgc::pipeline::ShaderModuleSource::Naga(module)
}
ShaderSource::Wgsl(ref code) => wgc::pipeline::ShaderModuleSource::Wgsl(Borrowed(code)),
};
let (id, error) = wgc::gfx_select!(
Expand Down
1 change: 1 addition & 0 deletions wgpu/src/backend/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@ impl crate::Context for Context {
desc: &crate::ShaderModuleDescriptor,
) -> Self::ShaderModuleId {
let mut descriptor = match desc.source {
#[cfg(feature = "spirv")]
crate::ShaderSource::SpirV(ref spv) => {
web_sys::GpuShaderModuleDescriptor::new(&js_sys::Uint32Array::from(&**spv))
}
Expand Down
1 change: 1 addition & 0 deletions wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ pub enum ShaderSource<'a> {
///
/// wgpu will attempt to parse and validate it, but the original binary
/// is passed to `gfx-rs` and `spirv_cross` for translation.
#[cfg(feature = "spirv")]
SpirV(Cow<'a, [u32]>),
/// WGSL module as a string slice.
///
Expand Down
6 changes: 4 additions & 2 deletions wgpu/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ mod belt;
mod device;
mod encoder;

use std::future::Future;
#[cfg(feature = "spirv")]
use std::{
borrow::Cow,
future::Future,
mem::{align_of, size_of},
ptr::copy_nonoverlapping,
};
Expand All @@ -24,15 +25,16 @@ pub use encoder::RenderEncoder;
/// - Input length isn't multiple of 4
/// - Input is longer than [`usize::max_value`]
/// - SPIR-V magic number is missing from beginning of stream
#[cfg(feature = "spirv")]
pub fn make_spirv(data: &[u8]) -> super::ShaderSource {
super::ShaderSource::SpirV(make_spirv_raw(data))
}

/// Version of [`make_spirv`] intended for use with [`Device::create_shader_module_spirv`].
/// Returns raw slice instead of ShaderSource.
#[cfg(feature = "spirv")]
pub fn make_spirv_raw(data: &[u8]) -> Cow<[u32]> {
const MAGIC_NUMBER: u32 = 0x0723_0203;

assert_eq!(
data.len() % size_of::<u32>(),
0,
Expand Down

0 comments on commit 5f2df9a

Please sign in to comment.