wgpu/api/device.rs
1use std::{error, fmt, future::Future, sync::Arc};
2
3use parking_lot::Mutex;
4
5use crate::api::blas::{Blas, BlasGeometrySizeDescriptors, CreateBlasDescriptor};
6use crate::api::tlas::{CreateTlasDescriptor, Tlas};
7use crate::*;
8
9/// Open connection to a graphics and/or compute device.
10///
11/// Responsible for the creation of most rendering and compute resources.
12/// These are then used in commands, which are submitted to a [`Queue`].
13///
14/// A device may be requested from an adapter with [`Adapter::request_device`].
15///
16/// Corresponds to [WebGPU `GPUDevice`](https://gpuweb.github.io/gpuweb/#gpu-device).
17#[derive(Debug, Clone)]
18pub struct Device {
19 pub(crate) inner: dispatch::DispatchDevice,
20}
21#[cfg(send_sync)]
22static_assertions::assert_impl_all!(Device: Send, Sync);
23
24crate::cmp::impl_eq_ord_hash_proxy!(Device => .inner);
25
26/// Describes a [`Device`].
27///
28/// For use with [`Adapter::request_device`].
29///
30/// Corresponds to [WebGPU `GPUDeviceDescriptor`](
31/// https://gpuweb.github.io/gpuweb/#dictdef-gpudevicedescriptor).
32pub type DeviceDescriptor<'a> = wgt::DeviceDescriptor<Label<'a>>;
33static_assertions::assert_impl_all!(DeviceDescriptor<'_>: Send, Sync);
34
35impl Device {
36 /// Check for resource cleanups and mapping callbacks. Will block if [`Maintain::Wait`] is passed.
37 ///
38 /// Return `true` if the queue is empty, or `false` if there are more queue
39 /// submissions still in flight. (Note that, unless access to the [`Queue`] is
40 /// coordinated somehow, this information could be out of date by the time
41 /// the caller receives it. `Queue`s can be shared between threads, so
42 /// other threads could submit new work at any time.)
43 ///
44 /// When running on WebGPU, this is a no-op. `Device`s are automatically polled.
45 pub fn poll(&self, maintain: Maintain) -> MaintainResult {
46 self.inner.poll(maintain)
47 }
48
49 /// The features which can be used on this device.
50 ///
51 /// No additional features can be used, even if the underlying adapter can support them.
52 #[must_use]
53 pub fn features(&self) -> Features {
54 self.inner.features()
55 }
56
57 /// The limits which can be used on this device.
58 ///
59 /// No better limits can be used, even if the underlying adapter can support them.
60 #[must_use]
61 pub fn limits(&self) -> Limits {
62 self.inner.limits()
63 }
64
65 /// Creates a shader module.
66 ///
67 /// <div class="warning">
68 // NOTE: Keep this in sync with `naga::front::wgsl::parse_str`!
69 // NOTE: Keep this in sync with `wgpu_core::Global::device_create_shader_module`!
70 ///
71 /// This function may consume a lot of stack space. Compiler-enforced limits for parsing
72 /// recursion exist; if shader compilation runs into them, it will return an error gracefully.
73 /// However, on some build profiles and platforms, the default stack size for a thread may be
74 /// exceeded before this limit is reached during parsing. Callers should ensure that there is
75 /// enough stack space for this, particularly if calls to this method are exposed to user
76 /// input.
77 ///
78 /// </div>
79 #[must_use]
80 pub fn create_shader_module(&self, desc: ShaderModuleDescriptor<'_>) -> ShaderModule {
81 let module = self
82 .inner
83 .create_shader_module(desc, wgt::ShaderRuntimeChecks::checked());
84 ShaderModule { inner: module }
85 }
86
87 /// Deprecated: Use [`create_shader_module_trusted`][csmt] instead.
88 ///
89 /// # Safety
90 ///
91 /// See [`create_shader_module_trusted`][csmt].
92 ///
93 /// [csmt]: Self::create_shader_module_trusted
94 #[deprecated(
95 since = "24.0.0",
96 note = "Use `Device::create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())` instead."
97 )]
98 #[must_use]
99 pub unsafe fn create_shader_module_unchecked(
100 &self,
101 desc: ShaderModuleDescriptor<'_>,
102 ) -> ShaderModule {
103 unsafe { self.create_shader_module_trusted(desc, crate::ShaderRuntimeChecks::unchecked()) }
104 }
105
106 /// Creates a shader module with flags to dictate runtime checks.
107 ///
108 /// When running on WebGPU, this will merely call [`create_shader_module`][csm].
109 ///
110 /// # Safety
111 ///
112 /// In contrast with [`create_shader_module`][csm] this function
113 /// creates a shader module with user-customizable runtime checks which allows shaders to
114 /// perform operations which can lead to undefined behavior like indexing out of bounds,
115 /// thus it's the caller responsibility to pass a shader which doesn't perform any of this
116 /// operations.
117 ///
118 /// See the documentation for [`ShaderRuntimeChecks`][src] for more information about specific checks.
119 ///
120 /// [csm]: Self::create_shader_module
121 /// [src]: crate::ShaderRuntimeChecks
122 #[must_use]
123 pub unsafe fn create_shader_module_trusted(
124 &self,
125 desc: ShaderModuleDescriptor<'_>,
126 runtime_checks: crate::ShaderRuntimeChecks,
127 ) -> ShaderModule {
128 let module = self.inner.create_shader_module(desc, runtime_checks);
129 ShaderModule { inner: module }
130 }
131
132 /// Creates a shader module from SPIR-V binary directly.
133 ///
134 /// # Safety
135 ///
136 /// This function passes binary data to the backend as-is and can potentially result in a
137 /// driver crash or bogus behaviour. No attempt is made to ensure that data is valid SPIR-V.
138 ///
139 /// See also [`include_spirv_raw!`] and [`util::make_spirv_raw`].
140 #[must_use]
141 pub unsafe fn create_shader_module_spirv(
142 &self,
143 desc: &ShaderModuleDescriptorSpirV<'_>,
144 ) -> ShaderModule {
145 let module = unsafe { self.inner.create_shader_module_spirv(desc) };
146 ShaderModule { inner: module }
147 }
148
149 /// Creates an empty [`CommandEncoder`].
150 #[must_use]
151 pub fn create_command_encoder(&self, desc: &CommandEncoderDescriptor<'_>) -> CommandEncoder {
152 let encoder = self.inner.create_command_encoder(desc);
153 CommandEncoder { inner: encoder }
154 }
155
156 /// Creates an empty [`RenderBundleEncoder`].
157 #[must_use]
158 pub fn create_render_bundle_encoder<'a>(
159 &self,
160 desc: &RenderBundleEncoderDescriptor<'_>,
161 ) -> RenderBundleEncoder<'a> {
162 let encoder = self.inner.create_render_bundle_encoder(desc);
163 RenderBundleEncoder {
164 inner: encoder,
165 _p: std::marker::PhantomData,
166 }
167 }
168
169 /// Creates a new [`BindGroup`].
170 #[must_use]
171 pub fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> BindGroup {
172 let group = self.inner.create_bind_group(desc);
173 BindGroup { inner: group }
174 }
175
176 /// Creates a [`BindGroupLayout`].
177 #[must_use]
178 pub fn create_bind_group_layout(
179 &self,
180 desc: &BindGroupLayoutDescriptor<'_>,
181 ) -> BindGroupLayout {
182 let layout = self.inner.create_bind_group_layout(desc);
183 BindGroupLayout { inner: layout }
184 }
185
186 /// Creates a [`PipelineLayout`].
187 #[must_use]
188 pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDescriptor<'_>) -> PipelineLayout {
189 let layout = self.inner.create_pipeline_layout(desc);
190 PipelineLayout { inner: layout }
191 }
192
193 /// Creates a [`RenderPipeline`].
194 #[must_use]
195 pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor<'_>) -> RenderPipeline {
196 let pipeline = self.inner.create_render_pipeline(desc);
197 RenderPipeline { inner: pipeline }
198 }
199
200 /// Creates a [`ComputePipeline`].
201 #[must_use]
202 pub fn create_compute_pipeline(&self, desc: &ComputePipelineDescriptor<'_>) -> ComputePipeline {
203 let pipeline = self.inner.create_compute_pipeline(desc);
204 ComputePipeline { inner: pipeline }
205 }
206
207 /// Creates a [`Buffer`].
208 #[must_use]
209 pub fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> Buffer {
210 let mut map_context = MapContext::new(desc.size);
211 if desc.mapped_at_creation {
212 map_context.initial_range = 0..desc.size;
213 }
214
215 let buffer = self.inner.create_buffer(desc);
216
217 Buffer {
218 inner: buffer,
219 map_context: Arc::new(Mutex::new(map_context)),
220 size: desc.size,
221 usage: desc.usage,
222 }
223 }
224
225 /// Creates a new [`Texture`].
226 ///
227 /// `desc` specifies the general format of the texture.
228 #[must_use]
229 pub fn create_texture(&self, desc: &TextureDescriptor<'_>) -> Texture {
230 let texture = self.inner.create_texture(desc);
231
232 Texture {
233 inner: texture,
234 descriptor: TextureDescriptor {
235 label: None,
236 view_formats: &[],
237 ..desc.clone()
238 },
239 }
240 }
241
242 /// Creates a [`Texture`] from a wgpu-hal Texture.
243 ///
244 /// # Safety
245 ///
246 /// - `hal_texture` must be created from this device internal handle
247 /// - `hal_texture` must be created respecting `desc`
248 /// - `hal_texture` must be initialized
249 #[cfg(wgpu_core)]
250 #[must_use]
251 pub unsafe fn create_texture_from_hal<A: wgc::hal_api::HalApi>(
252 &self,
253 hal_texture: A::Texture,
254 desc: &TextureDescriptor<'_>,
255 ) -> Texture {
256 let texture = unsafe {
257 let core_device = self.inner.as_core();
258 core_device
259 .context
260 .create_texture_from_hal::<A>(hal_texture, core_device, desc)
261 };
262 Texture {
263 inner: texture.into(),
264 descriptor: TextureDescriptor {
265 label: None,
266 view_formats: &[],
267 ..desc.clone()
268 },
269 }
270 }
271
272 /// Creates a [`Buffer`] from a wgpu-hal Buffer.
273 ///
274 /// # Safety
275 ///
276 /// - `hal_buffer` must be created from this device internal handle
277 /// - `hal_buffer` must be created respecting `desc`
278 /// - `hal_buffer` must be initialized
279 #[cfg(wgpu_core)]
280 #[must_use]
281 pub unsafe fn create_buffer_from_hal<A: wgc::hal_api::HalApi>(
282 &self,
283 hal_buffer: A::Buffer,
284 desc: &BufferDescriptor<'_>,
285 ) -> Buffer {
286 let mut map_context = MapContext::new(desc.size);
287 if desc.mapped_at_creation {
288 map_context.initial_range = 0..desc.size;
289 }
290
291 let buffer = unsafe {
292 let core_device = self.inner.as_core();
293 core_device
294 .context
295 .create_buffer_from_hal::<A>(hal_buffer, core_device, desc)
296 };
297
298 Buffer {
299 inner: buffer.into(),
300 map_context: Arc::new(Mutex::new(map_context)),
301 size: desc.size,
302 usage: desc.usage,
303 }
304 }
305
306 /// Creates a new [`Sampler`].
307 ///
308 /// `desc` specifies the behavior of the sampler.
309 #[must_use]
310 pub fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> Sampler {
311 let sampler = self.inner.create_sampler(desc);
312 Sampler { inner: sampler }
313 }
314
315 /// Creates a new [`QuerySet`].
316 #[must_use]
317 pub fn create_query_set(&self, desc: &QuerySetDescriptor<'_>) -> QuerySet {
318 let query_set = self.inner.create_query_set(desc);
319 QuerySet { inner: query_set }
320 }
321
322 /// Set a callback for errors that are not handled in error scopes.
323 pub fn on_uncaptured_error(&self, handler: Box<dyn UncapturedErrorHandler>) {
324 self.inner.on_uncaptured_error(handler)
325 }
326
327 /// Push an error scope.
328 pub fn push_error_scope(&self, filter: ErrorFilter) {
329 self.inner.push_error_scope(filter)
330 }
331
332 /// Pop an error scope.
333 pub fn pop_error_scope(&self) -> impl Future<Output = Option<Error>> + WasmNotSend {
334 self.inner.pop_error_scope()
335 }
336
337 /// Starts frame capture.
338 pub fn start_capture(&self) {
339 self.inner.start_capture()
340 }
341
342 /// Stops frame capture.
343 pub fn stop_capture(&self) {
344 self.inner.stop_capture()
345 }
346
347 /// Query internal counters from the native backend for debugging purposes.
348 ///
349 /// Some backends may not set all counters, or may not set any counter at all.
350 /// The `counters` cargo feature must be enabled for any counter to be set.
351 ///
352 /// If a counter is not set, its contains its default value (zero).
353 #[must_use]
354 pub fn get_internal_counters(&self) -> wgt::InternalCounters {
355 self.inner.get_internal_counters()
356 }
357
358 /// Generate an GPU memory allocation report if the underlying backend supports it.
359 ///
360 /// Backends that do not support producing these reports return `None`. A backend may
361 /// Support it and still return `None` if it is not using performing sub-allocation,
362 /// for example as a workaround for driver issues.
363 #[must_use]
364 pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
365 self.inner.generate_allocator_report()
366 }
367
368 /// Apply a callback to this `Device`'s underlying backend device.
369 ///
370 /// If this `Device` is implemented by the backend API given by `A` (Vulkan,
371 /// Dx12, etc.), then apply `hal_device_callback` to `Some(&device)`, where
372 /// `device` is the underlying backend device type, [`A::Device`].
373 ///
374 /// If this `Device` uses a different backend, apply `hal_device_callback`
375 /// to `None`.
376 ///
377 /// The device is locked for reading while `hal_device_callback` runs. If
378 /// the callback attempts to perform any `wgpu` operations that require
379 /// write access to the device (destroying a buffer, say), deadlock will
380 /// occur. The locks are automatically released when the callback returns.
381 ///
382 /// # Safety
383 ///
384 /// - The raw handle passed to the callback must not be manually destroyed.
385 ///
386 /// [`A::Device`]: hal::Api::Device
387 #[cfg(wgpu_core)]
388 pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
389 &self,
390 hal_device_callback: F,
391 ) -> R {
392 if let Some(core_device) = self.inner.as_core_opt() {
393 unsafe {
394 core_device
395 .context
396 .device_as_hal::<A, F, R>(core_device, hal_device_callback)
397 }
398 } else {
399 hal_device_callback(None)
400 }
401 }
402
403 /// Destroy this device.
404 pub fn destroy(&self) {
405 self.inner.destroy()
406 }
407
408 /// Set a DeviceLostCallback on this device.
409 pub fn set_device_lost_callback(
410 &self,
411 callback: impl Fn(DeviceLostReason, String) + Send + 'static,
412 ) {
413 self.inner.set_device_lost_callback(Box::new(callback))
414 }
415
416 /// Create a [`PipelineCache`] with initial data
417 ///
418 /// This can be passed to [`Device::create_compute_pipeline`]
419 /// and [`Device::create_render_pipeline`] to either accelerate these
420 /// or add the cache results from those.
421 ///
422 /// # Safety
423 ///
424 /// If the `data` field of `desc` is set, it must have previously been returned from a call
425 /// to [`PipelineCache::get_data`][^saving]. This `data` will only be used if it came
426 /// from an adapter with the same [`util::pipeline_cache_key`].
427 /// This *is* compatible across wgpu versions, as any data format change will
428 /// be accounted for.
429 ///
430 /// It is *not* supported to bring caches from previous direct uses of backend APIs
431 /// into this method.
432 ///
433 /// # Errors
434 ///
435 /// Returns an error value if:
436 /// * the [`PIPELINE_CACHE`](wgt::Features::PIPELINE_CACHE) feature is not enabled
437 /// * this device is invalid; or
438 /// * the device is out of memory
439 ///
440 /// This method also returns an error value if:
441 /// * The `fallback` field on `desc` is false; and
442 /// * the `data` provided would not be used[^data_not_used]
443 ///
444 /// If an error value is used in subsequent calls, default caching will be used.
445 ///
446 /// [^saving]: We do recognise that saving this data to disk means this condition
447 /// is impossible to fully prove. Consider the risks for your own application in this case.
448 ///
449 /// [^data_not_used]: This data may be not used if: the data was produced by a prior
450 /// version of wgpu; or was created for an incompatible adapter, or there was a GPU driver
451 /// update. In some cases, the data might not be used and a real value is returned,
452 /// this is left to the discretion of GPU drivers.
453 #[must_use]
454 pub unsafe fn create_pipeline_cache(
455 &self,
456 desc: &PipelineCacheDescriptor<'_>,
457 ) -> PipelineCache {
458 let cache = unsafe { self.inner.create_pipeline_cache(desc) };
459 PipelineCache { inner: cache }
460 }
461}
462
463/// [`Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE`] must be enabled on the device in order to call these functions.
464impl Device {
465 /// Create a bottom level acceleration structure, used inside a top level acceleration structure for ray tracing.
466 /// - `desc`: The descriptor of the acceleration structure.
467 /// - `sizes`: Size descriptor limiting what can be built into the acceleration structure.
468 ///
469 /// # Validation
470 /// If any of the following is not satisfied a validation error is generated
471 ///
472 /// The device ***must*** have [Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE] enabled.
473 /// if `sizes` is [BlasGeometrySizeDescriptors::Triangles] then the following must be satisfied
474 /// - For every geometry descriptor (for the purposes this is called `geo_desc`) of `sizes.descriptors` the following must be satisfied:
475 /// - `geo_desc.vertex_format` must be within allowed formats (allowed formats for a given feature set
476 /// may be queried with [Features::allowed_vertex_formats_for_blas]).
477 /// - Both or neither of `geo_desc.index_format` and `geo_desc.index_count` must be provided.
478 ///
479 /// [Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE]: wgt::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE
480 /// [Features::allowed_vertex_formats_for_blas]: wgt::Features::allowed_vertex_formats_for_blas
481 #[must_use]
482 pub fn create_blas(
483 &self,
484 desc: &CreateBlasDescriptor<'_>,
485 sizes: BlasGeometrySizeDescriptors,
486 ) -> Blas {
487 let (handle, blas) = self.inner.create_blas(desc, sizes);
488
489 Blas {
490 inner: blas,
491 handle,
492 }
493 }
494
495 /// Create a top level acceleration structure, used for ray tracing.
496 /// - `desc`: The descriptor of the acceleration structure.
497 ///
498 /// # Validation
499 /// If any of the following is not satisfied a validation error is generated
500 ///
501 /// The device ***must*** have [Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE] enabled.
502 ///
503 /// [Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE]: wgt::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE
504 #[must_use]
505 pub fn create_tlas(&self, desc: &CreateTlasDescriptor<'_>) -> Tlas {
506 let tlas = self.inner.create_tlas(desc);
507
508 Tlas {
509 shared: Arc::new(TlasShared {
510 inner: tlas,
511 max_instances: desc.max_instances,
512 }),
513 }
514 }
515}
516
517/// Requesting a device from an [`Adapter`] failed.
518#[derive(Clone, Debug)]
519pub struct RequestDeviceError {
520 pub(crate) inner: RequestDeviceErrorKind,
521}
522#[derive(Clone, Debug)]
523pub(crate) enum RequestDeviceErrorKind {
524 /// Error from [`wgpu_core`].
525 // must match dependency cfg
526 #[cfg(wgpu_core)]
527 Core(wgc::instance::RequestDeviceError),
528
529 /// Error from web API that was called by `wgpu` to request a device.
530 ///
531 /// (This is currently never used by the webgl backend, but it could be.)
532 #[cfg(webgpu)]
533 WebGpu(wasm_bindgen::JsValue),
534}
535
536#[cfg(send_sync)]
537unsafe impl Send for RequestDeviceErrorKind {}
538#[cfg(send_sync)]
539unsafe impl Sync for RequestDeviceErrorKind {}
540
541#[cfg(send_sync)]
542static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);
543
544impl fmt::Display for RequestDeviceError {
545 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
546 match &self.inner {
547 #[cfg(wgpu_core)]
548 RequestDeviceErrorKind::Core(error) => error.fmt(_f),
549 #[cfg(webgpu)]
550 RequestDeviceErrorKind::WebGpu(error_js_value) => {
551 // wasm-bindgen provides a reasonable error stringification via `Debug` impl
552 write!(_f, "{error_js_value:?}")
553 }
554 #[cfg(not(any(webgpu, wgpu_core)))]
555 _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
556 }
557 }
558}
559
560impl error::Error for RequestDeviceError {
561 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
562 match &self.inner {
563 #[cfg(wgpu_core)]
564 RequestDeviceErrorKind::Core(error) => error.source(),
565 #[cfg(webgpu)]
566 RequestDeviceErrorKind::WebGpu(_) => None,
567 #[cfg(not(any(webgpu, wgpu_core)))]
568 _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
569 }
570 }
571}
572
573#[cfg(wgpu_core)]
574impl From<wgc::instance::RequestDeviceError> for RequestDeviceError {
575 fn from(error: wgc::instance::RequestDeviceError) -> Self {
576 Self {
577 inner: RequestDeviceErrorKind::Core(error),
578 }
579 }
580}
581
582/// Type for the callback of uncaptured error handler
583pub trait UncapturedErrorHandler: Fn(Error) + Send + 'static {}
584impl<T> UncapturedErrorHandler for T where T: Fn(Error) + Send + 'static {}
585
586/// Filter for error scopes.
587#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
588pub enum ErrorFilter {
589 /// Catch only out-of-memory errors.
590 OutOfMemory,
591 /// Catch only validation errors.
592 Validation,
593 /// Catch only internal errors.
594 Internal,
595}
596static_assertions::assert_impl_all!(ErrorFilter: Send, Sync);
597
598/// Lower level source of the error.
599///
600/// `Send + Sync` varies depending on configuration.
601#[cfg(send_sync)]
602#[cfg_attr(docsrs, doc(cfg(all())))]
603pub type ErrorSource = Box<dyn error::Error + Send + Sync + 'static>;
604/// Lower level source of the error.
605///
606/// `Send + Sync` varies depending on configuration.
607#[cfg(not(send_sync))]
608#[cfg_attr(docsrs, doc(cfg(all())))]
609pub type ErrorSource = Box<dyn error::Error + 'static>;
610
611/// Error type
612#[derive(Debug)]
613pub enum Error {
614 /// Out of memory error
615 OutOfMemory {
616 /// Lower level source of the error.
617 source: ErrorSource,
618 },
619 /// Validation error, signifying a bug in code or data
620 Validation {
621 /// Lower level source of the error.
622 source: ErrorSource,
623 /// Description of the validation error.
624 description: String,
625 },
626 /// Internal error. Used for signalling any failures not explicitly expected by WebGPU.
627 ///
628 /// These could be due to internal implementation or system limits being reached.
629 Internal {
630 /// Lower level source of the error.
631 source: ErrorSource,
632 /// Description of the internal GPU error.
633 description: String,
634 },
635}
636#[cfg(send_sync)]
637static_assertions::assert_impl_all!(Error: Send, Sync);
638
639impl error::Error for Error {
640 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
641 match self {
642 Error::OutOfMemory { source } => Some(source.as_ref()),
643 Error::Validation { source, .. } => Some(source.as_ref()),
644 Error::Internal { source, .. } => Some(source.as_ref()),
645 }
646 }
647}
648
649impl fmt::Display for Error {
650 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
651 match self {
652 Error::OutOfMemory { .. } => f.write_str("Out of Memory"),
653 Error::Validation { description, .. } => f.write_str(description),
654 Error::Internal { description, .. } => f.write_str(description),
655 }
656 }
657}