Skip to content

Commit

Permalink
Add get_compilation_info API
Browse files Browse the repository at this point in the history
  • Loading branch information
stefnotch committed Apr 27, 2024
1 parent a2cd2b9 commit 66cc3e4
Show file tree
Hide file tree
Showing 17 changed files with 666 additions and 168 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ By @atlv24 and @cwfitzgerald in [#5154](https://github.com/gfx-rs/wgpu/pull/5154
```
- Breaking change: [`wgpu_core::pipeline::ProgrammableStageDescriptor`](https://docs.rs/wgpu-core/latest/wgpu_core/pipeline/struct.ProgrammableStageDescriptor.html#structfield.entry_point) is now optional. By @ErichDonGubler in [#5305](https://github.com/gfx-rs/wgpu/pull/5305).
- `Features::downlevel{_webgl2,}_features` was made const by @MultisampledNight in [#5343](https://github.com/gfx-rs/wgpu/pull/5343)
- Breaking change: [`wgpu_core::pipeline::ShaderError`](https://docs.rs/wgpu-core/latest/wgpu_core/pipeline/struct.ShaderError.html) has been moved to `naga`. By @stefnotch in [#5410](https://github.com/gfx-rs/wgpu/pull/5410)
- Add a `get_compilation_info` method to `wgpu` to get shader compilation errors. By @stefnotch in [#5410](https://github.com/gfx-rs/wgpu/pull/5410)
- More as_hal methods and improvements by @JMS55 in [#5452](https://github.com/gfx-rs/wgpu/pull/5452)
- Added `wgpu::CommandEncoder::as_hal_mut`
- Added `wgpu::TextureView::as_hal`
Expand Down
1 change: 0 additions & 1 deletion Cargo.lock

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

74 changes: 74 additions & 0 deletions naga/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use std::{error::Error, fmt};

#[derive(Clone, Debug)]
pub struct ShaderError<E> {
/// The source code of the shader.
pub source: String,
pub label: Option<String>,
pub inner: Box<E>,
}

#[cfg(feature = "wgsl-in")]
impl fmt::Display for ShaderError<crate::front::wgsl::ParseError> {
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 '{label}' parsing {string}")
}
}
#[cfg(feature = "glsl-in")]
impl fmt::Display for ShaderError<crate::front::glsl::ParseError> {
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 '{label}' parsing {string}")
}
}
#[cfg(feature = "spv-in")]
impl fmt::Display for ShaderError<crate::front::spv::Error> {
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 '{label}' parsing {string}")
}
}
impl fmt::Display for ShaderError<crate::WithSpan<crate::valid::ValidationError>> {
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::NoColor::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 validation {}",
String::from_utf8_lossy(&writer.into_inner())
)
}
}
impl<E> Error for ShaderError<E>
where
ShaderError<E>: fmt::Display,
E: Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.inner)
}
}
9 changes: 9 additions & 0 deletions naga/src/front/glsl/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::token::TokenValue;
use crate::SourceLocation;
use crate::{proc::ConstantEvaluatorError, Span};
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files::SimpleFile;
Expand Down Expand Up @@ -137,6 +138,14 @@ pub struct Error {
pub meta: Span,
}

impl Error {
/// Returns a [`SourceLocation`] for the error message.
pub fn location(&self, source: &str) -> Option<SourceLocation> {
Some(self.meta.location(source))
}
}

// TODO: Rename to ParseErrors?
/// A collection of errors returned during shader parsing.
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
Expand Down
1 change: 1 addition & 0 deletions naga/src/front/wgsl/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use thiserror::Error;
#[derive(Clone, Debug)]
pub struct ParseError {
message: String,
// TODO: Document that the first span should be the primary span, and the other ones should be complementary.
labels: Vec<(Span, Cow<'static, str>)>,
notes: Vec<String>,
}
Expand Down
1 change: 1 addition & 0 deletions naga/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ pub mod back;
mod block;
#[cfg(feature = "compact")]
pub mod compact;
pub mod error;
pub mod front;
pub mod keywords;
pub mod proc;
Expand Down
2 changes: 2 additions & 0 deletions tests/tests/shader/compilation_messages/error_shader.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/*🐈🐈🐈🐈🐈🐈🐈*/?
// Expected Error: invalid character found
49 changes: 49 additions & 0 deletions tests/tests/shader/compilation_messages/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use wgpu::include_wgsl;

use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters};

#[gpu_test]
static SHADER_COMPILE_SUCCESS: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_async(|ctx| async move {
let sm = ctx
.device
.create_shader_module(include_wgsl!("successful_shader.wgsl"));

let compilation_info = sm.get_compilation_info().await;
for message in compilation_info.messages.iter() {
assert!(message.message_type != wgpu::CompilationMessageType::Error);
}
});

#[gpu_test]
static SHADER_COMPILE_ERROR: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_async(|ctx| async move {
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
let sm = ctx
.device
.create_shader_module(include_wgsl!("error_shader.wgsl"));
assert!(pollster::block_on(ctx.device.pop_error_scope()).is_some());

let compilation_info = sm.get_compilation_info().await;
let error_message = compilation_info
.messages
.iter()
.find(|message| message.message_type == wgpu::CompilationMessageType::Error)
.expect("Expected error message not found");
let span = error_message.location.expect("Expected span not found");
assert_eq!(
span.offset, 32,
"Expected the offset to be 32, because we're counting UTF-8 bytes"
);
assert_eq!(span.length, 1, "Expected length to roughly be 1"); // Could be relaxed, depending on the parser requirements.
assert_eq!(
span.line_number, 1,
"Expected the line number to be 1, because we're counting lines from 1"
);
assert_eq!(
span.line_position, 33,
"Expected the column number to be 33, because we're counting lines from 1"
);
});
31 changes: 31 additions & 0 deletions tests/tests/shader/compilation_messages/successful_shader.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const array_size = 512u;

struct WStruct {
arr: array<u32, array_size>,
atom: atomic<u32>
}

var<workgroup> w_mem: WStruct;

@group(0) @binding(0)
var<storage, read_write> output: array<u32>;

@compute @workgroup_size(1)
fn read(@builtin(workgroup_id) wgid: vec3<u32>, @builtin(num_workgroups) num_workgroups: vec3<u32>) {
var is_zero = true;
for(var i = 0u; i < array_size; i++) {
is_zero &= w_mem.arr[i] == 0u;
}
is_zero &= atomicLoad(&w_mem.atom) == 0u;

let idx = wgid.x + (wgid.y * num_workgroups.x) + (wgid.z * num_workgroups.x * num_workgroups.y);
output[idx] = u32(!is_zero);
}

@compute @workgroup_size(1)
fn write() {
for(var i = 0u; i < array_size; i++) {
w_mem.arr[i] = i;
}
atomicStore(&w_mem.atom, 3u);
}
1 change: 1 addition & 0 deletions tests/tests/shader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use wgpu::{

use wgpu_test::TestingContext;

pub mod compilation_messages;
pub mod numeric_builtins;
pub mod struct_layout;
pub mod zero_init_workgroup_mem;
Expand Down
1 change: 0 additions & 1 deletion wgpu-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ arrayvec = "0.7"
bit-vec = "0.6"
bitflags = "2"
bytemuck = { version = "1.14", optional = true }
codespan-reporting = "0.11"
document-features.workspace = true
indexmap = "2"
log = "0.4"
Expand Down
10 changes: 5 additions & 5 deletions wgpu-core/src/device/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
},
instance::Adapter,
lock::{rank, Mutex, MutexGuard, RwLock},
pipeline,
pipeline::{self},
pool::ResourcePool,
registry::Registry,
resource::{
Expand Down Expand Up @@ -1430,7 +1430,7 @@ impl<A: HalApi> Device<A> {
pipeline::ShaderModuleSource::Wgsl(code) => {
profiling::scope!("naga::front::wgsl::parse_str");
let module = naga::front::wgsl::parse_str(&code).map_err(|inner| {
pipeline::CreateShaderModuleError::Parsing(pipeline::ShaderError {
pipeline::CreateShaderModuleError::Parsing(naga::error::ShaderError {
source: code.to_string(),
label: desc.label.as_ref().map(|l| l.to_string()),
inner: Box::new(inner),
Expand All @@ -1443,7 +1443,7 @@ impl<A: HalApi> Device<A> {
let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);
profiling::scope!("naga::front::spv::Frontend");
let module = parser.parse().map_err(|inner| {
pipeline::CreateShaderModuleError::ParsingSpirV(pipeline::ShaderError {
pipeline::CreateShaderModuleError::ParsingSpirV(naga::error::ShaderError {
source: String::new(),
label: desc.label.as_ref().map(|l| l.to_string()),
inner: Box::new(inner),
Expand All @@ -1456,7 +1456,7 @@ impl<A: HalApi> Device<A> {
let mut parser = naga::front::glsl::Frontend::default();
profiling::scope!("naga::front::glsl::Frontend.parse");
let module = parser.parse(&options, &code).map_err(|inner| {
pipeline::CreateShaderModuleError::ParsingGlsl(pipeline::ShaderError {
pipeline::CreateShaderModuleError::ParsingGlsl(naga::error::ShaderError {
source: code.to_string(),
label: desc.label.as_ref().map(|l| l.to_string()),
inner: Box::new(inner),
Expand Down Expand Up @@ -1499,7 +1499,7 @@ impl<A: HalApi> Device<A> {
.create_validator(naga::valid::ValidationFlags::all())
.validate(&module)
.map_err(|inner| {
pipeline::CreateShaderModuleError::Validation(pipeline::ShaderError {
pipeline::CreateShaderModuleError::Validation(naga::error::ShaderError {
source,
label: desc.label.as_ref().map(|l| l.to_string()),
inner: Box::new(inner),
Expand Down
85 changes: 2 additions & 83 deletions wgpu-core/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::{
resource_log, validation, Label,
};
use arrayvec::ArrayVec;
use std::{borrow::Cow, error::Error, fmt, marker::PhantomData, num::NonZeroU32, sync::Arc};
use naga::error::ShaderError;
use std::{borrow::Cow, marker::PhantomData, num::NonZeroU32, sync::Arc};
use thiserror::Error;

/// Information about buffer bindings, which
Expand Down Expand Up @@ -107,77 +108,6 @@ impl<A: HalApi> ShaderModule<A> {
}
}

#[derive(Clone, Debug)]
pub struct ShaderError<E> {
pub source: String,
pub label: Option<String>,
pub inner: Box<E>,
}
#[cfg(feature = "wgsl")]
impl fmt::Display for ShaderError<naga::front::wgsl::ParseError> {
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 '{label}' parsing {string}")
}
}
#[cfg(feature = "glsl")]
impl fmt::Display for ShaderError<naga::front::glsl::ParseError> {
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 '{label}' parsing {string}")
}
}
#[cfg(feature = "spirv")]
impl fmt::Display for ShaderError<naga::front::spv::Error> {
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 '{label}' parsing {string}")
}
}
impl fmt::Display for ShaderError<naga::WithSpan<naga::valid::ValidationError>> {
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::NoColor::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 validation {}",
String::from_utf8_lossy(&writer.into_inner())
)
}
}
impl<E> Error for ShaderError<E>
where
ShaderError<E>: fmt::Display,
E: Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.inner)
}
}

//Note: `Clone` would require `WithSpan: Clone`.
#[derive(Debug, Error)]
#[non_exhaustive]
Expand Down Expand Up @@ -209,17 +139,6 @@ pub enum CreateShaderModuleError {
},
}

impl CreateShaderModuleError {
pub fn location(&self, source: &str) -> Option<naga::SourceLocation> {
match *self {
#[cfg(feature = "wgsl")]
CreateShaderModuleError::Parsing(ref err) => err.inner.location(source),
CreateShaderModuleError::Validation(ref err) => err.inner.location(source),
_ => None,
}
}
}

/// Describes a programmable pipeline stage.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down
Loading

0 comments on commit 66cc3e4

Please sign in to comment.