diff --git a/Cargo.lock b/Cargo.lock index 2f4ed4ed20..1c3a5b28c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -983,9 +983,8 @@ dependencies = [ [[package]] name = "naga" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "032288733590c094012e5f1dec15848be90015bb9b3fd16743d84ea582c4154a" +version = "0.7.1" +source = "git+https://github.com/gfx-rs/naga?rev=323999f#323999fcb9eb8e8c525d75d2878fe1e2e5a99aad" dependencies = [ "bit-set", "bitflags", @@ -1831,6 +1830,7 @@ dependencies = [ "arrayvec", "bitflags", "cfg_aliases", + "codespan-reporting", "copyless", "fxhash", "log", diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index a62051822d..9c09c3ebce 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -23,6 +23,7 @@ serial-pass = ["serde", "wgt/serde", "arrayvec/serde"] [dependencies] arrayvec = "0.7" bitflags = "1.0" +codespan-reporting = "0.11" copyless = "0.1" fxhash = "0.2" log = "0.4" @@ -35,10 +36,10 @@ smallvec = "1" thiserror = "1" [dependencies.naga] -#git = "https://github.com/gfx-rs/naga" -#rev = "9d2b357" -version = "0.7" -features = ["validate", "wgsl-in"] +git = "https://github.com/gfx-rs/naga" +rev = "323999f" +#version = "0.7" +features = ["span", "validate", "wgsl-in"] [dependencies.wgt] path = "../wgpu-types" diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index dae24ceede..9929703acd 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -967,25 +967,19 @@ impl Device { desc: &pipeline::ShaderModuleDescriptor<'a>, source: pipeline::ShaderModuleSource<'a>, ) -> Result, pipeline::CreateShaderModuleError> { - let module = match source { + let (module, source) = match source { pipeline::ShaderModuleSource::Wgsl(code) => { profiling::scope!("naga::wgsl::parse_str"); - // TODO: refactor the corresponding Naga error to be owned, and then - // display it instead of unwrapping - match naga::front::wgsl::parse_str(&code) { - Ok(module) => module, - Err(err) => { - log::error!("Failed to parse WGSL code for {:?}: {}", desc.label, err); - return Err(pipeline::CreateShaderModuleError::Parsing( - pipeline::NagaParseError { - shader_source: code.to_string(), - error: err, - }, - )); - } - } + let module = naga::front::wgsl::parse_str(&code).map_err(|inner| { + pipeline::CreateShaderModuleError::Parsing(pipeline::ShaderError { + source: code.to_string(), + label: desc.label.as_ref().map(|l| l.to_string()), + inner, + }) + })?; + (module, code.into_owned()) } - pipeline::ShaderModuleSource::Naga(module) => module, + pipeline::ShaderModuleSource::Naga(module) => (module, String::new()), }; use naga::valid::Capabilities as Caps; @@ -1006,7 +1000,14 @@ impl Device { .contains(wgt::Features::SHADER_PRIMITIVE_INDEX), ); let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) - .validate(&module)?; + .validate(&module) + .map_err(|inner| { + pipeline::CreateShaderModuleError::Validation(pipeline::ShaderError { + source, + label: desc.label.as_ref().map(|l| l.to_string()), + inner, + }) + })?; let interface = validation::Interface::new(&module, &info, self.features); let hal_shader = hal::ShaderInput::Naga(hal::NagaShader { module, info }); diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index 12195d85e1..36b55dc20b 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -5,9 +5,10 @@ use crate::{ id::{DeviceId, PipelineLayoutId, ShaderModuleId}, validation, Label, LifeGuard, Stored, }; -use std::borrow::Cow; +use std::{borrow::Cow, fmt}; use thiserror::Error; +#[allow(clippy::large_enum_variant)] pub enum ShaderModuleSource<'a> { Wgsl(Cow<'a, str>), Naga(naga::Module), @@ -46,30 +47,62 @@ impl Resource for ShaderModule { } #[derive(Clone, Debug, Error)] -pub struct NagaParseError { - pub shader_source: String, - pub error: naga::front::wgsl::ParseError, +pub struct ShaderError { + pub source: String, + pub label: Option, + #[source] + pub inner: E, } -impl std::fmt::Display for NagaParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for ShaderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let label = self.label.as_deref().unwrap_or_default(); + let string = self.inner.emit_to_string(&self.source); + write!(f, "\nShader '{}' parsing {}", label, string) + } +} +impl fmt::Display for ShaderError> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use codespan_reporting::{ + diagnostic::{Diagnostic, Label}, + files::SimpleFile, + term, + }; + + let label = self.label.as_deref().unwrap_or_default(); + let files = SimpleFile::new(label, &self.source); + let config = term::Config::default(); + let mut writer = term::termcolor::Ansi::new(Vec::new()); + + let diagnostic = Diagnostic::error().with_labels( + self.inner + .spans() + .map(|&(span, ref desc)| { + Label::primary((), span.to_range().unwrap()).with_message(desc.to_owned()) + }) + .collect(), + ); + + term::emit(&mut writer, &config, &files, &diagnostic).expect("cannot write error"); + write!( f, - "\nShader error:\n{}", - self.error.emit_to_string(&self.shader_source) + "\nShader validation {}", + String::from_utf8_lossy(&writer.into_inner()) ) } } -#[derive(Clone, Debug, Error)] +//Note: `Clone` would require `WithSpan: Clone`. +#[derive(Debug, Error)] pub enum CreateShaderModuleError { - #[error("Failed to parse a shader")] - Parsing(#[from] NagaParseError), + #[error(transparent)] + Parsing(#[from] ShaderError), #[error("Failed to generate the backend-specific code")] Generation, #[error(transparent)] Device(#[from] DeviceError), #[error(transparent)] - Validation(#[from] naga::valid::ValidationError), + Validation(#[from] ShaderError>), #[error(transparent)] MissingFeatures(#[from] MissingFeatures), } diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index dfe53beda7..5363c75e2d 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -74,14 +74,14 @@ web-sys = { version = "0.3", features = ["Window", "HtmlCanvasElement", "WebGl2R js-sys = { version = "0.3" } [dependencies.naga] -#git = "https://github.com/gfx-rs/naga" -#rev = "9d2b357" -version = "0.7" +git = "https://github.com/gfx-rs/naga" +rev = "323999f" +#version = "0.7" [dev-dependencies.naga] -#git = "https://github.com/gfx-rs/naga" -#rev = "9d2b357" -version = "0.7" +git = "https://github.com/gfx-rs/naga" +rev = "323999f" +#version = "0.7" features = ["wgsl-in"] [dev-dependencies] diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 0112abf7b4..a04dd92814 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -917,6 +917,7 @@ impl fmt::Debug for NagaShader { } /// Shader input. +#[allow(clippy::large_enum_variant)] pub enum ShaderInput<'a> { Naga(NagaShader), SpirV(&'a [u32]), diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index bd132cc8dc..c9f5909da0 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -134,20 +134,22 @@ pollster = "0.2" env_logger = "0.8" [dependencies.naga] -#git = "https://github.com/gfx-rs/naga" -#rev = "9d2b357" -version = "0.7" +git = "https://github.com/gfx-rs/naga" +rev = "323999f" +#version = "0.7" optional = true # used to test all the example shaders [dev-dependencies.naga] -#git = "https://github.com/gfx-rs/naga" -#rev = "9d2b357" -version = "0.7" +git = "https://github.com/gfx-rs/naga" +rev = "323999f" +#version = "0.7" features = ["wgsl-in"] [target.'cfg(target_arch = "wasm32")'.dependencies.naga] -version = "0.7" +git = "https://github.com/gfx-rs/naga" +rev = "323999f" +#version = "0.7" features = ["wgsl-out"] [target.'cfg(target_arch = "wasm32")'.dependencies]