From daa15a853f25b3133174e63b531d536dfd28ffeb Mon Sep 17 00:00:00 2001 From: Mikko Lehtonen Date: Thu, 12 Nov 2020 00:07:02 +0200 Subject: [PATCH] [rs] Add labels for some error messages This adds transforms for the ids in the errors to a label, and adds them to the validation error message. This changes the formatting of the validation error to happen when constructing the error, rather than in the unhandled error handler. It also requires some code for all the error variants for extracting the ids. --- wgpu/src/backend/direct.rs | 186 ++++++++++++++--------------- wgpu/src/backend/error.rs | 238 +++++++++++++++++++++++++++++++++++++ wgpu/src/backend/mod.rs | 2 + wgpu/src/lib.rs | 6 +- 4 files changed, 331 insertions(+), 101 deletions(-) create mode 100644 wgpu/src/backend/error.rs diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 63fba37eb8..a8c92c63ea 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -1,7 +1,8 @@ use crate::{ - backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, - CommandEncoderDescriptor, ComputePipelineDescriptor, Features, Limits, LoadOp, MapMode, - Operations, PipelineLayoutDescriptor, RenderBundleEncoderDescriptor, RenderPipelineDescriptor, + backend::error::format_error, backend::native_gpu_future, BindGroupDescriptor, + BindGroupLayoutDescriptor, BindingResource, CommandEncoderDescriptor, + ComputePipelineDescriptor, Features, Limits, LoadOp, MapMode, Operations, + PipelineLayoutDescriptor, RenderBundleEncoderDescriptor, RenderPipelineDescriptor, SamplerDescriptor, ShaderModuleSource, SwapChainStatus, TextureDescriptor, TextureViewDescriptor, }; @@ -19,6 +20,10 @@ use typed_arena::Arena; pub struct Context(wgc::hub::Global); impl Context { + pub(crate) fn global(&self) -> &wgc::hub::Global { + &self.0 + } + pub fn adapter_get_info(&self, id: wgc::id::AdapterId) -> wgc::instance::AdapterInfo { let global = &self.0; wgc::gfx_select!(id => global.adapter_get_info(id)).unwrap_pretty() @@ -651,9 +656,10 @@ impl crate::Context for Context { wgc::gfx_select!( device.id => global.device_create_shader_module(device.id, desc, PhantomData) ) - .map_err(|err| err.with_context("In Device::create_shader_module".to_string())) + .map_err(|err| err.with_context("In Device::create_shader_module", None)) .unwrap_error_sink( &device.error_sink, + &self, || wgc::gfx_select!( device.id => global.shader_module_error(PhantomData)), ) } @@ -670,8 +676,8 @@ impl crate::Context for Context { entries: Borrowed(desc.entries), }, PhantomData) ) - .map_err(|err| err.with_context(format!("In Device::create_bind_group_layout with label {:?}", desc.label))) - .unwrap_error_sink(&device.error_sink, || wgc::gfx_select!( device.id => global.bind_group_layout_error(PhantomData, desc.label))) + .map_err(|err| err.with_context(&format!("In Device::create_bind_group_layout"),desc.label)) + .unwrap_error_sink(&device.error_sink, &self, || wgc::gfx_select!( device.id => global.bind_group_layout_error(PhantomData, desc.label))) } fn device_create_bind_group( @@ -722,14 +728,10 @@ impl crate::Context for Context { }, PhantomData )) - .map_err(|err| { - err.with_context(format!( - "In Device::create_bind_group with label {:?}", - desc.label - )) - }) + .map_err(|err| err.with_context(&format!("In Device::create_bind_group"), desc.label)) .unwrap_error_sink( &device.error_sink, + &self, || wgc::gfx_select!( device.id => global.bind_group_error(PhantomData, desc.label)), ) } @@ -767,13 +769,14 @@ impl crate::Context for Context { PhantomData )) .map_err(|err| { - err.with_context(format!( - "In Device::create_pipeline_layout with label {:?}", + err.with_context(&format!( + "In Device::create_pipeline_layout"), desc.label - )) + ) }) .unwrap_error_sink( &device.error_sink, + &self, || wgc::gfx_select!( device.id => global.pipeline_layout_error(PhantomData, desc.label)), ) } @@ -841,12 +844,12 @@ impl crate::Context for Context { implicit_pipeline_ids )) .map_err(|err| { - err.with_context(format!( - "In Device::create_render_pipeline with label {:?}", + err.with_context(&format!( + "In Device::create_render_pipeline"), desc.label - )) + ) }) - .unwrap_error_sink(&device.error_sink, || { + .unwrap_error_sink(&device.error_sink, &self, || { let err = wgc::gfx_select!( device.id => global.render_pipeline_error(PhantomData, desc.label)); (err, 0u8) }) @@ -883,12 +886,12 @@ impl crate::Context for Context { implicit_pipeline_ids )) .map_err(|err| { - err.with_context(format!( - "In Device::create_compute_pipeline with label {:?}", + err.with_context(&format!( + "In Device::create_compute_pipeline"), desc.label - )) + ) }) - .unwrap_error_sink(&device.error_sink, || { + .unwrap_error_sink(&device.error_sink, &self, || { let err = wgc::gfx_select!( device.id => global.compute_pipeline_error(PhantomData, desc.label)); (err, 0u8) }) @@ -911,14 +914,10 @@ impl crate::Context for Context { }, PhantomData )) - .map_err(|err| { - err.with_context(format!( - "In Device::create_buffer with label {:?}", - desc.label - )) - }) + .map_err(|err| err.with_context(&format!("In Device::create_buffer"), desc.label)) .unwrap_error_sink( &device.error_sink, + &self, || wgc::gfx_select!( device.id => global.buffer_error(PhantomData, desc.label)), ); Buffer { @@ -946,14 +945,10 @@ impl crate::Context for Context { }, PhantomData )) - .map_err(|err| { - err.with_context(format!( - "In Device::create_texture with label {:?}", - desc.label - )) - }) + .map_err(|err| err.with_context(&format!("In Device::create_texture"), desc.label)) .unwrap_error_sink( &device.error_sink, + &self, || wgc::gfx_select!( device.id => global.texture_error(PhantomData, desc.label)), ); Texture { @@ -984,14 +979,10 @@ impl crate::Context for Context { }, PhantomData )) - .map_err(|err| { - err.with_context(format!( - "In Device::create_sampler with label {:?}", - desc.label - )) - }) + .map_err(|err| err.with_context(&format!("In Device::create_sampler"), desc.label)) .unwrap_error_sink( &device.error_sink, + &self, || wgc::gfx_select!( device.id => global.sampler_error(PhantomData, desc.label)), ) } @@ -1009,14 +1000,10 @@ impl crate::Context for Context { }, PhantomData )) - .map_err(|err| { - err.with_context(format!( - "In Device::create_command_encoder with label {:?}", - desc.label - )) - }) + .map_err(|err| err.with_context(&format!("In Device::create_command_encoder"), desc.label)) .unwrap_error_sink( &device.error_sink, + &self, || wgc::gfx_select!( device.id => global.command_encoder_error(PhantomData)), ); CommandEncoder { @@ -1112,8 +1099,8 @@ impl crate::Context for Context { let global = &self.0; wgc::gfx_select!(buffer.id => global.buffer_map_async(buffer.id, range, operation)) - .map_err(|err| err.with_context("In Buffer::map_async".to_string())) - .unwrap_error_sink(&buffer.error_sink, || ()); + .map_err(|err| err.with_context("In Buffer::map_async", None)) + .unwrap_error_sink(&buffer.error_sink, &self, || ()); future } @@ -1153,8 +1140,8 @@ impl crate::Context for Context { fn buffer_unmap(&self, buffer: &Self::BufferId) { let global = &self.0; wgc::gfx_select!(buffer.id => global.buffer_unmap(buffer.id)) - .map_err(|err| err.with_context("In Buffer::get_mapped_range".to_string())) - .unwrap_error_sink(&buffer.error_sink, || ()); + .map_err(|err| err.with_context("In Buffer::get_mapped_range", None)) + .unwrap_error_sink(&buffer.error_sink, &self, || ()); } fn swap_chain_get_current_texture_view( @@ -1204,14 +1191,10 @@ impl crate::Context for Context { wgc::gfx_select!( texture.id => global.texture_create_view(texture.id, &descriptor, PhantomData) ) - .map_err(|err| { - err.with_context(format!( - "In Texture::create_view with label {:?}", - desc.label - )) - }) + .map_err(|err| err.with_context(&format!("In Texture::create_view"), desc.label)) .unwrap_error_sink( &texture.error_sink, + &self, || wgc::gfx_select!( texture.id =>global.texture_view_error(PhantomData, desc.label)), ) } @@ -1318,8 +1301,8 @@ impl crate::Context for Context { destination_offset, copy_size )) - .map_err(|err| err.with_context("In CommandEncoder::copy_buffer_to_buffer".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()); + .map_err(|err| err.with_context("In CommandEncoder::copy_buffer_to_buffer", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()); } fn command_encoder_copy_buffer_to_texture( @@ -1336,8 +1319,8 @@ impl crate::Context for Context { &map_texture_copy_view(destination), ©_size )) - .map_err(|err| err.with_context("In CommandEncoder::copy_buffer_to_texture".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()) + .map_err(|err| err.with_context("In CommandEncoder::copy_buffer_to_texture", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()) } fn command_encoder_copy_texture_to_buffer( @@ -1354,8 +1337,8 @@ impl crate::Context for Context { &map_buffer_copy_view(destination), ©_size )) - .map_err(|err| err.with_context("In CommandEncoder::copy_texture_to_buffer".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()) + .map_err(|err| err.with_context("In CommandEncoder::copy_texture_to_buffer", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()) } fn command_encoder_copy_texture_to_texture( @@ -1372,8 +1355,8 @@ impl crate::Context for Context { &map_texture_copy_view(destination), ©_size )) - .map_err(|err| err.with_context("In CommandEncoder::copy_texture_to_texture".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()) + .map_err(|err| err.with_context("In CommandEncoder::copy_texture_to_texture", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()) } fn command_encoder_begin_compute_pass( @@ -1392,8 +1375,8 @@ impl crate::Context for Context { wgc::gfx_select!( encoder.id => global.command_encoder_run_compute_pass(encoder.id, pass) ) - .map_err(|err| err.with_context("In a ComputePass".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()) + .map_err(|err| err.with_context("In a ComputePass", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()) } fn command_encoder_begin_render_pass<'a>( @@ -1436,17 +1419,18 @@ impl crate::Context for Context { ) { let global = &self.0; wgc::gfx_select!(encoder.id => global.command_encoder_run_render_pass(encoder.id, pass)) - .map_err(|err| err.with_context("In a RenderPass".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()) + .map_err(|err| err.with_context("In a RenderPass", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()) } fn command_encoder_finish(&self, encoder: &Self::CommandEncoderId) -> Self::CommandBufferId { let desc = wgt::CommandBufferDescriptor::default(); let global = &self.0; wgc::gfx_select!(encoder.id => global.command_encoder_finish(encoder.id, &desc)) - .map_err(|err| err.with_context("In a CommandEncoder".to_string())) + .map_err(|err| err.with_context("In a CommandEncoder", None)) .unwrap_error_sink( &encoder.error_sink, + &self, || wgc::gfx_select!( encoder.id => global.command_buffer_error(PhantomData, None)), ) } @@ -1454,20 +1438,20 @@ impl crate::Context for Context { fn command_encoder_insert_debug_marker(&self, encoder: &Self::CommandEncoderId, label: &str) { let global = &self.0; wgc::gfx_select!(encoder.id => global.command_encoder_insert_debug_marker(encoder.id, &label)) - .map_err(|err| err.with_context("In CommandEncoder::insert_debug_marker".to_string())) - .unwrap_error_sink(&encoder.error_sink, ||()) + .map_err(|err| err.with_context("In CommandEncoder::insert_debug_marker", None)) + .unwrap_error_sink(&encoder.error_sink,&self, ||()) } fn command_encoder_push_debug_group(&self, encoder: &Self::CommandEncoderId, label: &str) { let global = &self.0; wgc::gfx_select!(encoder.id => global.command_encoder_push_debug_group(encoder.id, &label)) - .map_err(|err| err.with_context("In CommandEncoder::push_debug_group".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()) + .map_err(|err| err.with_context("In CommandEncoder::push_debug_group", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()) } fn command_encoder_pop_debug_group(&self, encoder: &Self::CommandEncoderId) { let global = &self.0; wgc::gfx_select!(encoder.id => global.command_encoder_pop_debug_group(encoder.id)) - .map_err(|err| err.with_context("In CommandEncoder::pop_debug_group".to_string())) - .unwrap_error_sink(&encoder.error_sink, || ()) + .map_err(|err| err.with_context("In CommandEncoder::pop_debug_group", None)) + .unwrap_error_sink(&encoder.error_sink, &self, || ()) } fn render_bundle_encoder_finish( @@ -1483,7 +1467,7 @@ impl crate::Context for Context { }, PhantomData )) - .map_err(|err| err.with_context("In a RenderBundleEncoder".to_string())) + .map_err(|err| err.with_context("In a RenderBundleEncoder", None)) .unwrap_pretty() } @@ -1540,7 +1524,12 @@ pub(crate) struct SwapChainOutputDetail { trait PrettyResult { fn unwrap_pretty(self) -> T; - fn unwrap_error_sink(self, error_sink: &ErrorSink, fallback: impl FnOnce() -> T) -> T; + fn unwrap_error_sink( + self, + error_sink: &ErrorSink, + context: &Context, + fallback: impl FnOnce() -> T, + ) -> T; } impl PrettyResult for Result @@ -1551,7 +1540,12 @@ where self.unwrap_or_else(|err| panic!("{}", err)) } - fn unwrap_error_sink(self, error_sink: &ErrorSink, fallback: impl FnOnce() -> T) -> T { + fn unwrap_error_sink( + self, + error_sink: &ErrorSink, + context: &Context, + fallback: impl FnOnce() -> T, + ) -> T { self.unwrap_or_else(|err| { let error_sink = error_sink.lock(); @@ -1573,8 +1567,10 @@ where } // Otherwise, it is a validation error + let desc = format_error(&err, context); error_sink.handle_error(crate::Error::ValidationError { source: Box::new(err), + description: desc, }); fallback() }) @@ -1582,33 +1578,35 @@ where } trait WithContextError: Error + Send + Sync + 'static + Sized { - fn with_context(self, string: String) -> ContextError; + fn with_context(self, string: &str, label: Option<&str>) -> ContextError; } impl WithContextError for E { - fn with_context(self, string: String) -> ContextError { + fn with_context(self, string: &str, label: Option<&str>) -> ContextError { ContextError { - string, - cause: self, + string: string.to_string(), + cause: Box::new(self), + label: label.unwrap_or("").to_string(), } } } #[derive(Debug)] -struct ContextError { - string: String, - cause: E, +pub(crate) struct ContextError { + pub string: String, + pub cause: Box, + pub label: String, } -impl Display for ContextError { +impl Display for ContextError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.string) } } -impl Error for ContextError { +impl Error for ContextError { fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.cause) + Some(self.cause.as_ref()) } } @@ -1638,15 +1636,5 @@ impl Debug for ErrorSinkRaw { fn default_error_handler(err: crate::Error) { eprintln!("wgpu error: {}\n", err); - if err.source().is_some() { - eprintln!("Caused by:"); - let mut source_opt = err.source(); - while let Some(source) = source_opt { - eprintln!(" {}", source); - source_opt = source.source(); - } - eprintln!(); - } - panic!("Handling wgpu errors as fatal by default"); } diff --git a/wgpu/src/backend/error.rs b/wgpu/src/backend/error.rs new file mode 100644 index 0000000000..92bd1335a6 --- /dev/null +++ b/wgpu/src/backend/error.rs @@ -0,0 +1,238 @@ +use std::{error::Error, fmt::Display}; + +use super::Context; + +pub(crate) fn format_error(err: &(impl Error + 'static), context: &Context) -> String { + let mut err_descs = Vec::new(); + + err_descs.push(fmt_pretty_any(err, context)); + + let mut source_opt = err.source(); + while let Some(source) = source_opt { + err_descs.push(fmt_pretty_any(source, context)); + source_opt = source.source(); + } + + let desc = format!("Validation Error\n\nCaused by:\n{}", err_descs.join("")); + desc +} + +fn fmt_pretty_any(error: &(dyn Error + 'static), context: &Context) -> String { + if let Some(pretty_err) = error.downcast_ref::() { + return pretty_err.fmt_pretty(context); + } + if let Some(pretty_err) = error.downcast_ref::() { + return pretty_err.fmt_pretty(context); + } + if let Some(pretty_err) = error.downcast_ref::() { + return pretty_err.fmt_pretty(context); + } + if let Some(pretty_err) = error.downcast_ref::() + { + return pretty_err.fmt_pretty(context); + } + if let Some(pretty_err) = error.downcast_ref::() { + return pretty_err.fmt_pretty(context); + } + if let Some(pretty_err) = error.downcast_ref::() { + return pretty_err.fmt_pretty(context); + } + if let Some(pretty_err) = error.downcast_ref::() { + return pretty_err.fmt_pretty(context); + } + if let Some(pretty_err) = error.downcast_ref::() { + return pretty_err.fmt_pretty(context); + } + + // default + format_error_line(error.as_display()) +} + +pub(crate) fn format_error_line(err: &dyn Display) -> String { + format!(" {}\n", err) +} + +pub(crate) fn format_note_line(note: &dyn Display) -> String { + format!(" note: {}\n", note) +} + +pub(crate) fn format_label_line(label_key: &str, label_value: &str) -> String { + if label_key.is_empty() || label_value.is_empty() { + String::new() + } else { + format_note_line(&format!("{} = `{}`", label_key, label_value)) + } +} + +trait AsDisplay { + fn as_display(&self) -> &dyn Display; +} + +impl AsDisplay for T { + fn as_display(&self) -> &dyn Display { + self + } +} + +pub trait PrettyError: Error { + fn fmt_pretty(&self, _context: &Context) -> String { + format_error_line(self.as_display()) + } +} + +impl PrettyError for super::direct::ContextError { + fn fmt_pretty(&self, _context: &Context) -> String { + format_error_line(self.as_display()) + &format_label_line("label", &self.label) + } +} + +impl PrettyError for wgc::command::RenderCommandError { + fn fmt_pretty(&self, context: &Context) -> String { + let global = context.global(); + let mut ret = format_error_line(self); + match *self { + Self::InvalidBindGroup(id) => { + let name = wgc::gfx_select!(id => global.bind_group_label(id)); + ret.push_str(&format_label_line("bind group", &name)); + } + Self::InvalidPipeline(id) => { + let name = wgc::gfx_select!(id => global.render_pipeline_label(id)); + ret.push_str(&format_label_line("render pipeline", &name)); + } + Self::Buffer(id, ..) | Self::DestroyedBuffer(id) => { + let name = wgc::gfx_select!(id => global.buffer_label(id)); + ret.push_str(&format_label_line("buffer", &name)); + } + _ => {} + }; + ret + } +} +impl PrettyError for wgc::binding_model::CreateBindGroupError { + fn fmt_pretty(&self, context: &Context) -> String { + let global = context.global(); + let mut ret = format_error_line(self); + match *self { + Self::InvalidBuffer(id) => { + let name = wgc::gfx_select!(id => global.buffer_label(id)); + ret.push_str(&format_label_line("buffer", &name)); + } + Self::InvalidTextureView(id) => { + let name = wgc::gfx_select!(id => global.texture_view_label(id)); + ret.push_str(&format_label_line("texture view", &name)); + } + Self::InvalidSampler(id) => { + let name = wgc::gfx_select!(id => global.sampler_label(id)); + ret.push_str(&format_label_line("sampler", &name)); + } + _ => {} + }; + ret + } +} + +impl PrettyError for wgc::binding_model::CreatePipelineLayoutError { + fn fmt_pretty(&self, context: &Context) -> String { + let global = context.global(); + let mut ret = format_error_line(self); + match *self { + Self::InvalidBindGroupLayout(id) => { + let name = wgc::gfx_select!(id => global.bind_group_layout_label(id)); + ret.push_str(&format_label_line("bind group layout", &name)); + } + _ => {} + }; + ret + } +} + +impl PrettyError for wgc::command::ExecutionError { + fn fmt_pretty(&self, context: &Context) -> String { + let global = context.global(); + let mut ret = format_error_line(self); + match *self { + Self::DestroyedBuffer(id) => { + let name = wgc::gfx_select!(id => global.buffer_label(id)); + ret.push_str(&format_label_line("buffer", &name)); + } + }; + ret + } +} + +impl PrettyError for wgc::command::RenderPassErrorInner { + fn fmt_pretty(&self, context: &Context) -> String { + let global = context.global(); + let mut ret = format_error_line(self); + match *self { + Self::InvalidAttachment(id) => { + let name = wgc::gfx_select!(id => global.texture_view_label(id)); + ret.push_str(&format_label_line("attachment", &name)); + } + _ => {} + }; + ret + } +} + +impl PrettyError for wgc::command::ComputePassErrorInner { + fn fmt_pretty(&self, context: &Context) -> String { + let global = context.global(); + let mut ret = format_error_line(self); + match *self { + Self::InvalidBindGroup(id) => { + let name = wgc::gfx_select!(id => global.bind_group_label(id)); + ret.push_str(&format_label_line("bind group", &name)); + } + Self::InvalidPipeline(id) => { + let name = wgc::gfx_select!(id => global.compute_pipeline_label(id)); + ret.push_str(&format_label_line("pipeline", &name)); + } + Self::InvalidIndirectBuffer(id) => { + let name = wgc::gfx_select!(id => global.buffer_label(id)); + ret.push_str(&format_label_line("indirect buffer", &name)); + } + _ => {} + }; + ret + } +} + +impl PrettyError for wgc::command::TransferError { + fn fmt_pretty(&self, context: &Context) -> String { + let global = context.global(); + let mut ret = format_error_line(self); + match *self { + Self::InvalidBuffer(id) => { + let name = wgc::gfx_select!(id => global.buffer_label(id)); + ret.push_str(&format_label_line("label", &name)); + } + Self::InvalidTexture(id) => { + let name = wgc::gfx_select!(id => global.texture_label(id)); + ret.push_str(&format_label_line("texture", &name)); + } + // Self::MissingCopySrcUsageFlag(buf_opt, tex_opt) => { + // if let Some(buf) = buf_opt { + // let name = wgc::gfx_select!(buf => global.buffer_label(buf)); + // ret.push_str(&format_label_line("source", &name)); + // } + // if let Some(tex) = tex_opt { + // let name = wgc::gfx_select!(tex => global.texture_label(tex)); + // ret.push_str(&format_label_line("source", &name)); + // } + // } + Self::MissingCopyDstUsageFlag(buf_opt, tex_opt) => { + if let Some(buf) = buf_opt { + let name = wgc::gfx_select!(buf => global.buffer_label(buf)); + ret.push_str(&format_label_line("destination", &name)); + } + if let Some(tex) = tex_opt { + let name = wgc::gfx_select!(tex => global.texture_label(tex)); + ret.push_str(&format_label_line("destination", &name)); + } + } + _ => {} + }; + ret + } +} diff --git a/wgpu/src/backend/mod.rs b/wgpu/src/backend/mod.rs index 854488de91..f8a3ec5628 100644 --- a/wgpu/src/backend/mod.rs +++ b/wgpu/src/backend/mod.rs @@ -5,6 +5,8 @@ pub(crate) use web::Context; #[cfg(not(target_arch = "wasm32"))] mod direct; +#[cfg(not(target_arch = "wasm32"))] +mod error; #[cfg(not(target_arch = "wasm32"))] pub(crate) use direct::Context; diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index ca7490355f..cbeb6aa5df 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -2696,6 +2696,8 @@ pub enum Error { ValidationError { /// source: Box, + /// + description: String, }, } @@ -2703,7 +2705,7 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::OutOfMemoryError { source } => Some(source.as_ref()), - Error::ValidationError { source } => Some(source.as_ref()), + Error::ValidationError { source, .. } => Some(source.as_ref()), } } } @@ -2712,7 +2714,7 @@ impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Error::OutOfMemoryError { .. } => f.write_str("Out of Memory"), - Error::ValidationError { .. } => f.write_str("Validation error"), + Error::ValidationError { description, .. } => f.write_str(description), } } }