From 117d323f71654d062b1fa7f2d4b882496e3a9186 Mon Sep 17 00:00:00 2001 From: Mikko Lehtonen Date: Thu, 3 Sep 2020 00:17:05 +0300 Subject: [PATCH 1/6] [rs] Don't import Error directly --- wgpu/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 6444687ab1..5a21cf7e51 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -12,7 +12,7 @@ mod macros; use std::{ borrow::Cow, - error::Error, + error, fmt::{Debug, Display}, future::Future, marker::PhantomData, @@ -1266,7 +1266,7 @@ impl Display for SwapChainError { } } -impl Error for SwapChainError {} +impl error::Error for SwapChainError {} impl Instance { /// Create an new instance of wgpu. @@ -1562,7 +1562,7 @@ impl Display for RequestDeviceError { } } -impl Error for RequestDeviceError {} +impl error::Error for RequestDeviceError {} /// Error occurred when trying to async map a buffer. #[derive(Clone, PartialEq, Eq, Debug)] @@ -1574,7 +1574,7 @@ impl Display for BufferAsyncError { } } -impl Error for BufferAsyncError {} +impl error::Error for BufferAsyncError {} /// Type of buffer mapping. #[derive(Debug, Clone, Copy, PartialEq)] From f987a82503b93d24d19cc75f7464c6848832f36e Mon Sep 17 00:00:00 2001 From: Mikko Lehtonen Date: Thu, 3 Sep 2020 09:22:58 +0300 Subject: [PATCH 2/6] [rs] Add Device::on_captured_error The implementation is dummy one --- wgpu/src/backend/direct.rs | 8 ++++++++ wgpu/src/backend/web.rs | 8 ++++++++ wgpu/src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 8cd5252b2c..9e99d34d1c 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -930,6 +930,14 @@ impl crate::Context for Context { .unwrap_pretty() } + fn device_on_uncaptured_error( + &self, + _device: &Self::DeviceId, + _handler: impl crate::UncapturedErrorHandler, + ) { + todo!(); + } + fn buffer_map_async( &self, buffer: &Self::BufferId, diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 804abe000c..680b0c81dd 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -1148,6 +1148,14 @@ impl crate::Context for Context { // Device is polled automatically } + fn device_on_uncaptured_error( + &self, + _device: &Self::DeviceId, + _handler: impl crate::UncapturedErrorHandler, + ) { + // TODO: + } + fn buffer_map_async( &self, _buffer: &Self::BufferId, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 5a21cf7e51..34d3e77178 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -262,6 +262,11 @@ trait Context: Debug + Send + Sized + Sync { ) -> Self::RenderBundleEncoderId; fn device_drop(&self, device: &Self::DeviceId); fn device_poll(&self, device: &Self::DeviceId, maintain: Maintain); + fn device_on_uncaptured_error( + &self, + device: &Self::DeviceId, + handler: impl UncapturedErrorHandler, + ); fn buffer_map_async( &self, @@ -1542,6 +1547,11 @@ impl Device { id: Context::device_create_swap_chain(&*self.context, &self.id, &surface.id, desc), } } + + /// Set a callback for errors that are not handled in error scopes. + pub fn on_uncaptured_error(&self, handler: impl UncapturedErrorHandler) { + self.context.device_on_uncaptured_error(&self.id, handler); + } } impl Drop for Device { @@ -2610,3 +2620,27 @@ impl SwapChain { } } } + +/// Type for the callback of uncaptured error handler +pub trait UncapturedErrorHandler: Fn(Error) + Send + Sync + 'static {} +impl UncapturedErrorHandler for T where T: Fn(Error) + Send + Sync + 'static {} + +/// Error type +#[derive(Debug)] +pub enum Error { + /// Out of memory error + OutOfMemoryError, + /// Validation error, signifying a bug in code or data + ValidationError { + /// + source: Box, + }, +} +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 { source } => std::fmt::Display::fmt(source, f), + } + } +} From 506ee7a368b8f6e92b1ba8aebfc919bd61af58b7 Mon Sep 17 00:00:00 2001 From: Mikko Lehtonen Date: Thu, 3 Sep 2020 00:13:34 +0300 Subject: [PATCH 3/6] [rs] direct: Move Device to its own struct --- wgpu/src/backend/direct.rs | 62 +++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 9e99d34d1c..d0905ccd09 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -497,9 +497,14 @@ fn map_pass_channel( } } +#[derive(Debug)] +pub(crate) struct Device { + id: wgc::id::DeviceId, +} + impl crate::Context for Context { type AdapterId = wgc::id::AdapterId; - type DeviceId = wgc::id::DeviceId; + type DeviceId = Device; type QueueId = wgc::id::QueueId; type ShaderModuleId = wgc::id::ShaderModuleId; type BindGroupLayoutId = wgc::id::BindGroupLayoutId; @@ -567,7 +572,8 @@ impl crate::Context for Context { *adapter => global.adapter_request_device(*adapter, desc, trace_dir, PhantomData) ) .unwrap_pretty(); - ready(Ok((device_id, device_id))) + let device = Device { id: device_id }; + ready(Ok((device, device_id))) } fn adapter_features(&self, adapter: &Self::AdapterId) -> Features { @@ -582,12 +588,12 @@ impl crate::Context for Context { fn device_features(&self, device: &Self::DeviceId) -> Features { let global = &self.0; - wgc::gfx_select!(*device => global.device_features(*device)).unwrap_pretty() + wgc::gfx_select!(device.id => global.device_features(device.id)).unwrap_pretty() } fn device_limits(&self, device: &Self::DeviceId) -> Limits { let global = &self.0; - wgc::gfx_select!(*device => global.device_limits(*device)).unwrap_pretty() + wgc::gfx_select!(device.id => global.device_limits(device.id)).unwrap_pretty() } fn device_create_swap_chain( @@ -598,7 +604,7 @@ impl crate::Context for Context { ) -> Self::SwapChainId { let global = &self.0; wgc::gfx_select!( - *device => global.device_create_swap_chain(*device, *surface, desc) + device.id => global.device_create_swap_chain(device.id, *surface, desc) ) .unwrap_pretty() } @@ -614,7 +620,7 @@ impl crate::Context for Context { }; let global = &self.0; wgc::gfx_select!( - *device => global.device_create_shader_module(*device, desc, PhantomData) + device.id => global.device_create_shader_module(device.id, desc, PhantomData) ) .unwrap_pretty() } @@ -626,7 +632,7 @@ impl crate::Context for Context { ) -> Self::BindGroupLayoutId { let global = &self.0; wgc::gfx_select!( - *device => global.device_create_bind_group_layout(*device, &wgc::binding_model::BindGroupLayoutDescriptor { + device.id => global.device_create_bind_group_layout(device.id, &wgc::binding_model::BindGroupLayoutDescriptor { label: desc.label.map(Borrowed), entries: Borrowed(desc.entries), }, PhantomData) @@ -673,8 +679,8 @@ impl crate::Context for Context { .collect::>(); let global = &self.0; - wgc::gfx_select!(*device => global.device_create_bind_group( - *device, + wgc::gfx_select!(device.id => global.device_create_bind_group( + device.id, &bm::BindGroupDescriptor { label: desc.label.as_ref().map(|label| Borrowed(&label[..])), layout: desc.layout.id, @@ -708,8 +714,8 @@ impl crate::Context for Context { .collect::>(); let global = &self.0; - wgc::gfx_select!(*device => global.device_create_pipeline_layout( - *device, + wgc::gfx_select!(device.id => global.device_create_pipeline_layout( + device.id, &wgc::binding_model::PipelineLayoutDescriptor { label: desc.label.map(Borrowed), bind_group_layouts: Borrowed(&temp_layouts), @@ -755,8 +761,8 @@ impl crate::Context for Context { }; let global = &self.0; - wgc::gfx_select!(*device => global.device_create_render_pipeline( - *device, + wgc::gfx_select!(device.id => global.device_create_render_pipeline( + device.id, &pipe::RenderPipelineDescriptor { label: desc.label.map(Borrowed), layout: desc.layout.map(|l| l.id), @@ -786,8 +792,8 @@ impl crate::Context for Context { use wgc::pipeline as pipe; let global = &self.0; - wgc::gfx_select!(*device => global.device_create_compute_pipeline( - *device, + wgc::gfx_select!(device.id => global.device_create_compute_pipeline( + device.id, &pipe::ComputePipelineDescriptor { label: desc.label.map(Borrowed), layout: desc.layout.map(|l| l.id), @@ -809,8 +815,8 @@ impl crate::Context for Context { desc: &crate::BufferDescriptor<'_>, ) -> Self::BufferId { let global = &self.0; - wgc::gfx_select!(*device => global.device_create_buffer( - *device, + wgc::gfx_select!(device.id => global.device_create_buffer( + device.id, &wgt::BufferDescriptor { label: desc.label.map(Borrowed), mapped_at_creation: desc.mapped_at_creation, @@ -828,8 +834,8 @@ impl crate::Context for Context { desc: &TextureDescriptor, ) -> Self::TextureId { let global = &self.0; - wgc::gfx_select!(*device => global.device_create_texture( - *device, + wgc::gfx_select!(device.id => global.device_create_texture( + device.id, &wgt::TextureDescriptor { label: desc.label.map(Borrowed), size: desc.size, @@ -850,8 +856,8 @@ impl crate::Context for Context { desc: &SamplerDescriptor, ) -> Self::SamplerId { let global = &self.0; - wgc::gfx_select!(*device => global.device_create_sampler( - *device, + wgc::gfx_select!(device.id => global.device_create_sampler( + device.id, &wgc::resource::SamplerDescriptor { label: desc.label.map(Borrowed), address_modes: [desc.address_mode_u, desc.address_mode_v, desc.address_mode_w], @@ -875,8 +881,8 @@ impl crate::Context for Context { desc: &CommandEncoderDescriptor, ) -> Self::CommandEncoderId { let global = &self.0; - wgc::gfx_select!(*device => global.device_create_command_encoder( - *device, + wgc::gfx_select!(device.id => global.device_create_command_encoder( + device.id, &wgt::CommandEncoderDescriptor { label: desc.label.map(Borrowed), }, @@ -897,7 +903,7 @@ impl crate::Context for Context { depth_stencil_format: desc.depth_stencil_format, sample_count: desc.sample_count, }, - *device, + device.id, None, ) .unwrap_pretty() @@ -907,21 +913,21 @@ impl crate::Context for Context { #[cfg(not(target_arch = "wasm32"))] { let global = &self.0; - wgc::gfx_select!(*device => global.device_poll(*device, true)).unwrap_pretty() + wgc::gfx_select!(device.id => global.device_poll(device.id, true)).unwrap_pretty() } //TODO: make this work in general #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "metal-auto-capture")] { let global = &self.0; - wgc::gfx_select!(*device => global.device_drop(*device)); + wgc::gfx_select!(device.id => global.device_drop(device.id)); } } fn device_poll(&self, device: &Self::DeviceId, maintain: crate::Maintain) { let global = &self.0; - wgc::gfx_select!(*device => global.device_poll( - *device, + wgc::gfx_select!(device.id => global.device_poll( + device.id, match maintain { crate::Maintain::Poll => false, crate::Maintain::Wait => true, From 03fda219e39a002f31b2ad4e75123b4dc44a0553 Mon Sep 17 00:00:00 2001 From: Mikko Lehtonen Date: Thu, 3 Sep 2020 09:23:21 +0300 Subject: [PATCH 4/6] [rs] direct: Implement on_uncaptured_error Currently the sink is used only in Device::create_shader_module and Device::create_render_pipeline, to be expanded in future commits. --- wgpu/src/backend/direct.rs | 84 ++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index d0905ccd09..22201382eb 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -7,9 +7,13 @@ use crate::{ }; use arrayvec::ArrayVec; +use fmt::Debug; use futures::future::{ready, Ready}; +use parking_lot::Mutex; use smallvec::SmallVec; -use std::{borrow::Cow::Borrowed, error::Error, fmt, marker::PhantomData, ops::Range, slice}; +use std::{ + borrow::Cow::Borrowed, error::Error, fmt, marker::PhantomData, ops::Range, slice, sync::Arc, +}; use typed_arena::Arena; pub struct Context(wgc::hub::Global); @@ -500,6 +504,7 @@ fn map_pass_channel( #[derive(Debug)] pub(crate) struct Device { id: wgc::id::DeviceId, + error_sink: ErrorSink, } impl crate::Context for Context { @@ -572,7 +577,10 @@ impl crate::Context for Context { *adapter => global.adapter_request_device(*adapter, desc, trace_dir, PhantomData) ) .unwrap_pretty(); - let device = Device { id: device_id }; + let device = Device { + id: device_id, + error_sink: Arc::new(Mutex::new(ErrorSinkRaw::new())), + }; ready(Ok((device, device_id))) } @@ -619,10 +627,19 @@ impl crate::Context for Context { ShaderModuleSource::Wgsl(code) => wgc::pipeline::ShaderModuleSource::Wgsl(code), }; let global = &self.0; - wgc::gfx_select!( + let res = wgc::gfx_select!( device.id => global.device_create_shader_module(device.id, desc, PhantomData) - ) - .unwrap_pretty() + ); + + match res { + Ok(val) => val, + + Err(err) => { + let error_sink = device.error_sink.lock(); + error_sink.handle_error(crate::Error::from(err)); + wgc::gfx_select!( device.id => global.shader_module_error(PhantomData)) + } + } } fn device_create_bind_group_layout( @@ -761,7 +778,7 @@ impl crate::Context for Context { }; let global = &self.0; - wgc::gfx_select!(device.id => global.device_create_render_pipeline( + let res = wgc::gfx_select!(device.id => global.device_create_render_pipeline( device.id, &pipe::RenderPipelineDescriptor { label: desc.label.map(Borrowed), @@ -779,9 +796,17 @@ impl crate::Context for Context { }, PhantomData, None - )) - .unwrap_pretty() - .0 + )); + + match res { + Ok(val) => val.0, + + Err(err) => { + let error_sink = device.error_sink.lock(); + error_sink.handle_error(crate::Error::from(err)); + wgc::gfx_select!( device.id => global.render_pipeline_error(PhantomData)) + } + } } fn device_create_compute_pipeline( @@ -938,10 +963,11 @@ impl crate::Context for Context { fn device_on_uncaptured_error( &self, - _device: &Self::DeviceId, - _handler: impl crate::UncapturedErrorHandler, + device: &Self::DeviceId, + handler: impl crate::UncapturedErrorHandler, ) { - todo!(); + let mut error_sink = device.error_sink.lock(); + error_sink.uncaptured_handler = Box::new(handler); } fn buffer_map_async( @@ -1370,3 +1396,37 @@ where self.unwrap_or_else(|err| panic!("{}", err)) } } + +type ErrorSink = Arc>; + +impl From for crate::Error { + fn from(err: E) -> Self { + crate::Error::ValidationError { source: err.into() } + } +} + +struct ErrorSinkRaw { + uncaptured_handler: Box, +} + +impl ErrorSinkRaw { + fn new() -> ErrorSinkRaw { + ErrorSinkRaw { + uncaptured_handler: Box::from(default_error_handler), + } + } + fn handle_error(&self, err: crate::Error) { + (self.uncaptured_handler)(err); + } +} + +impl Debug for ErrorSinkRaw { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ErrorSink") + } +} + +fn default_error_handler(err: crate::Error) { + eprintln!("WGPU Error: {}", err); + panic!("Handling wgpu errors as fatal by default"); +} From 4277bcab2cdc7dda706b4f39dac4f619a181d7bc Mon Sep 17 00:00:00 2001 From: Mikko Lehtonen Date: Thu, 3 Sep 2020 15:32:24 +0300 Subject: [PATCH 5/6] [rs] direct: ErrorSinkify Device methods --- wgpu/src/backend/direct.rs | 87 +++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 22201382eb..b417c2c248 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -627,19 +627,13 @@ impl crate::Context for Context { ShaderModuleSource::Wgsl(code) => wgc::pipeline::ShaderModuleSource::Wgsl(code), }; let global = &self.0; - let res = wgc::gfx_select!( + wgc::gfx_select!( device.id => global.device_create_shader_module(device.id, desc, PhantomData) - ); - - match res { - Ok(val) => val, - - Err(err) => { - let error_sink = device.error_sink.lock(); - error_sink.handle_error(crate::Error::from(err)); - wgc::gfx_select!( device.id => global.shader_module_error(PhantomData)) - } - } + ) + .unwrap_error_sink( + &device.error_sink, + || wgc::gfx_select!( device.id => global.shader_module_error(PhantomData)), + ) } fn device_create_bind_group_layout( @@ -654,7 +648,7 @@ impl crate::Context for Context { entries: Borrowed(desc.entries), }, PhantomData) ) - .unwrap_pretty() + .unwrap_error_sink(&device.error_sink, || wgc::gfx_select!( device.id => global.bind_group_layout_error(PhantomData))) } fn device_create_bind_group( @@ -705,7 +699,10 @@ impl crate::Context for Context { }, PhantomData )) - .unwrap_pretty() + .unwrap_error_sink( + &device.error_sink, + || wgc::gfx_select!( device.id => global.bind_group_error(PhantomData)), + ) } fn device_create_pipeline_layout( @@ -740,7 +737,10 @@ impl crate::Context for Context { }, PhantomData )) - .unwrap_pretty() + .unwrap_error_sink( + &device.error_sink, + || wgc::gfx_select!( device.id => global.pipeline_layout_error(PhantomData)), + ) } fn device_create_render_pipeline( @@ -778,7 +778,7 @@ impl crate::Context for Context { }; let global = &self.0; - let res = wgc::gfx_select!(device.id => global.device_create_render_pipeline( + wgc::gfx_select!(device.id => global.device_create_render_pipeline( device.id, &pipe::RenderPipelineDescriptor { label: desc.label.map(Borrowed), @@ -796,17 +796,12 @@ impl crate::Context for Context { }, PhantomData, None - )); - - match res { - Ok(val) => val.0, - - Err(err) => { - let error_sink = device.error_sink.lock(); - error_sink.handle_error(crate::Error::from(err)); - wgc::gfx_select!( device.id => global.render_pipeline_error(PhantomData)) - } - } + )) + .unwrap_error_sink(&device.error_sink, || { + let err = wgc::gfx_select!( device.id => global.render_pipeline_error(PhantomData)); + (err, 0u8) + }) + .0 } fn device_create_compute_pipeline( @@ -830,7 +825,10 @@ impl crate::Context for Context { PhantomData, None )) - .unwrap_pretty() + .unwrap_error_sink(&device.error_sink, || { + let err = wgc::gfx_select!( device.id => global.compute_pipeline_error(PhantomData)); + (err, 0u8) + }) .0 } @@ -850,7 +848,10 @@ impl crate::Context for Context { }, PhantomData )) - .unwrap_pretty() + .unwrap_error_sink( + &device.error_sink, + || wgc::gfx_select!( device.id => global.buffer_error(PhantomData)), + ) } fn device_create_texture( @@ -872,7 +873,10 @@ impl crate::Context for Context { }, PhantomData )) - .unwrap_pretty() + .unwrap_error_sink( + &device.error_sink, + || wgc::gfx_select!( device.id => global.texture_error(PhantomData)), + ) } fn device_create_sampler( @@ -897,7 +901,10 @@ impl crate::Context for Context { }, PhantomData )) - .unwrap_pretty() + .unwrap_error_sink( + &device.error_sink, + || wgc::gfx_select!( device.id => global.sampler_error(PhantomData)), + ) } fn device_create_command_encoder( @@ -913,7 +920,10 @@ impl crate::Context for Context { }, PhantomData )) - .unwrap_pretty() + .unwrap_error_sink( + &device.error_sink, + || wgc::gfx_select!( device.id => global.command_encoder_error(PhantomData)), + ) } fn device_create_render_bundle_encoder( @@ -931,7 +941,7 @@ impl crate::Context for Context { device.id, None, ) - .unwrap_pretty() + .unwrap_pretty() // TODO: errorsink, but missing render_bundle_error } fn device_drop(&self, device: &Self::DeviceId) { @@ -1386,15 +1396,24 @@ pub(crate) struct SwapChainOutputDetail { trait PrettyResult { fn unwrap_pretty(self) -> T; + fn unwrap_error_sink(self, error_sink: &ErrorSink, fallback: impl FnOnce() -> T) -> T; } impl PrettyResult for Result where - E: Error, + E: Error + Send + Sync + 'static, { fn unwrap_pretty(self) -> T { self.unwrap_or_else(|err| panic!("{}", err)) } + + fn unwrap_error_sink(self, error_sink: &ErrorSink, fallback: impl FnOnce() -> T) -> T { + self.unwrap_or_else(|err| { + let error_sink = error_sink.lock(); + error_sink.handle_error(crate::Error::from(err)); + fallback() + }) + } } type ErrorSink = Arc>; From e28bc9cbbbd17e3f0b0fa8757b58df5622c986a3 Mon Sep 17 00:00:00 2001 From: Mikko Lehtonen Date: Sat, 5 Sep 2020 01:29:03 +0300 Subject: [PATCH 6/6] [rs] impl std error for Error Relies more on the source() of error for informative messages --- wgpu/src/backend/direct.rs | 23 +++++++++++++++-------- wgpu/src/lib.rs | 12 +++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index b417c2c248..7d742fd891 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -1410,7 +1410,9 @@ where fn unwrap_error_sink(self, error_sink: &ErrorSink, fallback: impl FnOnce() -> T) -> T { self.unwrap_or_else(|err| { let error_sink = error_sink.lock(); - error_sink.handle_error(crate::Error::from(err)); + error_sink.handle_error(crate::Error::ValidationError { + source: Box::new(err), + }); fallback() }) } @@ -1418,12 +1420,6 @@ where type ErrorSink = Arc>; -impl From for crate::Error { - fn from(err: E) -> Self { - crate::Error::ValidationError { source: err.into() } - } -} - struct ErrorSinkRaw { uncaptured_handler: Box, } @@ -1446,6 +1442,17 @@ impl Debug for ErrorSinkRaw { } fn default_error_handler(err: crate::Error) { - eprintln!("WGPU Error: {}", err); + 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/lib.rs b/wgpu/src/lib.rs index 34d3e77178..e745a0bca3 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -2636,11 +2636,21 @@ pub enum Error { source: Box, }, } + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Error::OutOfMemoryError => None, + Error::ValidationError { source } => Some(source.as_ref()), + } + } +} + 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 { source } => std::fmt::Display::fmt(source, f), + Error::ValidationError { .. } => f.write_str("Validation error"), } } }