Skip to main content

wgpu/api/
device.rs

1use alloc::{boxed::Box, string::String, sync::Arc, vec};
2#[cfg(wgpu_core)]
3use core::ops::Deref;
4use core::{error, fmt, future::Future, marker::PhantomData};
5
6use crate::api::blas::{Blas, BlasGeometrySizeDescriptors, CreateBlasDescriptor};
7use crate::api::tlas::{CreateTlasDescriptor, Tlas};
8use crate::util::Mutex;
9use crate::*;
10
11/// Open connection to a graphics and/or compute device.
12///
13/// Responsible for the creation of most rendering and compute resources.
14/// These are then used in commands, which are submitted to a [`Queue`].
15///
16/// A device may be requested from an adapter with [`Adapter::request_device`].
17///
18/// Corresponds to [WebGPU `GPUDevice`](https://gpuweb.github.io/gpuweb/#gpu-device).
19#[derive(Debug, Clone)]
20pub struct Device {
21    pub(crate) inner: dispatch::DispatchDevice,
22}
23#[cfg(send_sync)]
24static_assertions::assert_impl_all!(Device: Send, Sync);
25
26crate::cmp::impl_eq_ord_hash_proxy!(Device => .inner);
27
28/// Describes a [`Device`].
29///
30/// For use with [`Adapter::request_device`].
31///
32/// Corresponds to [WebGPU `GPUDeviceDescriptor`](
33/// https://gpuweb.github.io/gpuweb/#dictdef-gpudevicedescriptor).
34pub type DeviceDescriptor<'a> = wgt::DeviceDescriptor<Label<'a>>;
35static_assertions::assert_impl_all!(DeviceDescriptor<'_>: Send, Sync);
36
37impl Device {
38    #[cfg(custom)]
39    /// Returns custom implementation of Device (if custom backend and is internally T)
40    pub fn as_custom<T: custom::DeviceInterface>(&self) -> Option<&T> {
41        self.inner.as_custom()
42    }
43
44    #[cfg(custom)]
45    /// Creates Device from custom implementation
46    pub fn from_custom<T: custom::DeviceInterface>(device: T) -> Self {
47        Self {
48            inner: dispatch::DispatchDevice::custom(device),
49        }
50    }
51
52    /// Constructs a stub device for testing using [`Backend::Noop`].
53    ///
54    /// This is a convenience function which avoids the configuration, `async`, and fallibility
55    /// aspects of constructing a device through `Instance`.
56    #[cfg(feature = "noop")]
57    pub fn noop(desc: &DeviceDescriptor<'_>) -> (Device, Queue) {
58        use core::future::Future as _;
59        use core::pin::pin;
60        use core::task;
61        let ctx = &mut task::Context::from_waker(task::Waker::noop());
62
63        let instance = Instance::new(InstanceDescriptor {
64            backends: Backends::NOOP,
65            backend_options: BackendOptions {
66                noop: NoopBackendOptions::enabled(),
67                ..Default::default()
68            },
69            ..InstanceDescriptor::new_without_display_handle()
70        });
71
72        // Both of these futures are trivial and should complete instantaneously,
73        // so we do not need an executor and can just poll them once.
74        let task::Poll::Ready(Ok(adapter)) =
75            pin!(instance.request_adapter(&RequestAdapterOptions::default())).poll(ctx)
76        else {
77            unreachable!()
78        };
79        let task::Poll::Ready(Ok(device_and_queue)) = pin!(adapter.request_device(desc)).poll(ctx)
80        else {
81            unreachable!()
82        };
83        device_and_queue
84    }
85
86    /// Check for resource cleanups and mapping callbacks. Will block if [`PollType::Wait`] is passed.
87    ///
88    /// Return `true` if the queue is empty, or `false` if there are more queue
89    /// submissions still in flight. (Note that, unless access to the [`Queue`] is
90    /// coordinated somehow, this information could be out of date by the time
91    /// the caller receives it. `Queue`s can be shared between threads, so
92    /// other threads could submit new work at any time.)
93    ///
94    /// When running on WebGPU, this is a no-op. `Device`s are automatically polled.
95    pub fn poll(&self, poll_type: PollType) -> Result<crate::PollStatus, crate::PollError> {
96        self.inner.poll(poll_type.map_index(|s| s.index))
97    }
98
99    /// The [features][Features] which can be used on this device.
100    ///
101    /// This will be equal to the [`required_features`][DeviceDescriptor::required_features]
102    /// specified when creating the device.
103    /// No additional features can be used, even if the underlying adapter can support them.
104    #[must_use]
105    pub fn features(&self) -> Features {
106        self.inner.features()
107    }
108
109    /// The limits which can be used on this device.
110    ///
111    /// This will be equal to the [`required_limits`][DeviceDescriptor::required_limits]
112    /// specified when creating the device.
113    /// No better limits can be used, even if the underlying adapter can support them.
114    #[must_use]
115    pub fn limits(&self) -> Limits {
116        self.inner.limits()
117    }
118
119    /// Get info about the adapter that this device was created from.
120    pub fn adapter_info(&self) -> AdapterInfo {
121        self.inner.adapter_info()
122    }
123
124    /// Creates a shader module.
125    ///
126    /// <div class="warning">
127    // NOTE: Keep this in sync with `naga::front::wgsl::parse_str`!
128    // NOTE: Keep this in sync with `wgpu_core::Global::device_create_shader_module`!
129    ///
130    /// This function may consume a lot of stack space. Compiler-enforced limits for parsing
131    /// recursion exist; if shader compilation runs into them, it will return an error gracefully.
132    /// However, on some build profiles and platforms, the default stack size for a thread may be
133    /// exceeded before this limit is reached during parsing. Callers should ensure that there is
134    /// enough stack space for this, particularly if calls to this method are exposed to user
135    /// input.
136    ///
137    /// </div>
138    #[must_use]
139    pub fn create_shader_module(&self, desc: ShaderModuleDescriptor<'_>) -> ShaderModule {
140        let module = self
141            .inner
142            .create_shader_module(desc, wgt::ShaderRuntimeChecks::checked());
143        ShaderModule { inner: module }
144    }
145
146    /// Deprecated: Use [`create_shader_module_trusted`][csmt] instead.
147    ///
148    /// # Safety
149    ///
150    /// See [`create_shader_module_trusted`][csmt].
151    ///
152    /// [csmt]: Self::create_shader_module_trusted
153    #[deprecated(
154        since = "24.0.0",
155        note = "Use `Device::create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())` instead."
156    )]
157    #[must_use]
158    pub unsafe fn create_shader_module_unchecked(
159        &self,
160        desc: ShaderModuleDescriptor<'_>,
161    ) -> ShaderModule {
162        unsafe { self.create_shader_module_trusted(desc, crate::ShaderRuntimeChecks::unchecked()) }
163    }
164
165    /// Creates a shader module with flags to dictate runtime checks.
166    ///
167    /// When running on WebGPU, this will merely call [`create_shader_module`][csm].
168    ///
169    /// # Safety
170    ///
171    /// In contrast with [`create_shader_module`][csm] this function
172    /// creates a shader module with user-customizable runtime checks which allows shaders to
173    /// perform operations which can lead to undefined behavior like indexing out of bounds,
174    /// thus it's the caller responsibility to pass a shader which doesn't perform any of this
175    /// operations.
176    ///
177    /// See the documentation for [`ShaderRuntimeChecks`] for more information about specific checks.
178    ///
179    /// [csm]: Self::create_shader_module
180    #[must_use]
181    pub unsafe fn create_shader_module_trusted(
182        &self,
183        desc: ShaderModuleDescriptor<'_>,
184        runtime_checks: crate::ShaderRuntimeChecks,
185    ) -> ShaderModule {
186        let module = self.inner.create_shader_module(desc, runtime_checks);
187        ShaderModule { inner: module }
188    }
189
190    /// Creates a shader module which will bypass wgpu's shader tooling and validation and be used directly by the backend.
191    ///
192    /// # Safety
193    ///
194    /// This function passes data to the backend as-is and can potentially result in a
195    /// driver crash or bogus behaviour. No attempt is made to ensure that data is valid.
196    #[must_use]
197    pub unsafe fn create_shader_module_passthrough(
198        &self,
199        desc: ShaderModuleDescriptorPassthrough<'_>,
200    ) -> ShaderModule {
201        let module = unsafe { self.inner.create_shader_module_passthrough(&desc) };
202        ShaderModule { inner: module }
203    }
204
205    /// Creates a shader module from GLSL ES templates, one per entry point,
206    /// on a WebGL device; `None` on a device of another backend.
207    ///
208    /// A pipeline's constants are defined as the templates' macros, and the
209    /// browser compiles the GLSL: no naga runs. The browser checks the GLSL,
210    /// so unlike [`Device::create_shader_module_passthrough`] this is safe.
211    #[cfg(webgl)]
212    #[must_use]
213    pub fn create_shader_module_glsl_templates(
214        &self,
215        label: Label<'_>,
216        templates: &'static [hal::GlslTemplate],
217    ) -> Option<ShaderModule> {
218        let device = self.inner.as_core_opt()?;
219        Some(ShaderModule {
220            inner: device.create_shader_module_glsl_templates(label, templates),
221        })
222    }
223
224    /// Creates an empty [`CommandEncoder`].
225    #[must_use]
226    pub fn create_command_encoder(&self, desc: &CommandEncoderDescriptor<'_>) -> CommandEncoder {
227        let encoder = self.inner.create_command_encoder(desc);
228        // Each encoder starts with its own deferred-action store that travels
229        // with the CommandBuffer produced by finish().
230        CommandEncoder {
231            inner: encoder,
232            actions: Default::default(),
233        }
234    }
235
236    /// Creates an empty [`RenderBundleEncoder`].
237    #[must_use]
238    pub fn create_render_bundle_encoder<'a>(
239        &self,
240        desc: &RenderBundleEncoderDescriptor<'_>,
241    ) -> RenderBundleEncoder<'a> {
242        let encoder = self.inner.create_render_bundle_encoder(desc);
243        RenderBundleEncoder {
244            inner: encoder,
245            _p: PhantomData,
246        }
247    }
248
249    /// Creates a new [`BindGroup`].
250    #[must_use]
251    pub fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> BindGroup {
252        let group = self.inner.create_bind_group(desc);
253        BindGroup { inner: group }
254    }
255
256    /// Creates a [`BindGroupLayout`].
257    #[must_use]
258    pub fn create_bind_group_layout(
259        &self,
260        desc: &BindGroupLayoutDescriptor<'_>,
261    ) -> BindGroupLayout {
262        let layout = self.inner.create_bind_group_layout(desc);
263        BindGroupLayout { inner: layout }
264    }
265
266    /// Creates a [`PipelineLayout`].
267    #[must_use]
268    pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDescriptor<'_>) -> PipelineLayout {
269        let layout = self.inner.create_pipeline_layout(desc);
270        PipelineLayout { inner: layout }
271    }
272
273    /// Creates a [`RenderPipeline`].
274    #[must_use]
275    pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor<'_>) -> RenderPipeline {
276        let pipeline = self.inner.create_render_pipeline(desc);
277        RenderPipeline { inner: pipeline }
278    }
279
280    /// Creates a mesh shader based [`RenderPipeline`].
281    #[must_use]
282    pub fn create_mesh_pipeline(&self, desc: &MeshPipelineDescriptor<'_>) -> RenderPipeline {
283        let pipeline = self.inner.create_mesh_pipeline(desc);
284        RenderPipeline { inner: pipeline }
285    }
286
287    /// Creates a [`ComputePipeline`].
288    #[must_use]
289    pub fn create_compute_pipeline(&self, desc: &ComputePipelineDescriptor<'_>) -> ComputePipeline {
290        let pipeline = self.inner.create_compute_pipeline(desc);
291        ComputePipeline { inner: pipeline }
292    }
293
294    /// Creates a [`Buffer`].
295    #[must_use]
296    pub fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> Buffer {
297        let map_context = MapContext::new(desc.mapped_at_creation.then_some(0..desc.size));
298
299        let buffer = self.inner.create_buffer(desc);
300
301        Buffer {
302            inner: buffer,
303            map_context: Arc::new(Mutex::new(map_context)),
304            size: desc.size,
305            usage: desc.usage,
306        }
307    }
308
309    /// Creates a new [`Texture`].
310    ///
311    /// `desc` specifies the general format of the texture.
312    #[must_use]
313    pub fn create_texture(&self, desc: &TextureDescriptor<'_>) -> Texture {
314        let texture = self.inner.create_texture(desc);
315
316        Texture {
317            inner: texture,
318            descriptor: TextureDescriptor {
319                label: None,
320                view_formats: &[],
321                ..desc.clone()
322            },
323        }
324    }
325
326    /// Creates a [`Texture`] from a wgpu-hal Texture.
327    ///
328    /// # Types
329    ///
330    /// The type of `A::Texture` depends on the backend:
331    ///
332    #[doc = crate::macros::hal_type_vulkan!("Texture")]
333    #[doc = crate::macros::hal_type_metal!("Texture")]
334    #[doc = crate::macros::hal_type_dx12!("Texture")]
335    #[doc = crate::macros::hal_type_gles!("Texture")]
336    ///
337    /// On [`Backend::BrowserWebGpu`], use `Device::create_texture_from_webgpu_handle()` instead.
338    ///
339    /// # `initial_state`
340    ///
341    /// If the resource has already been initialized, `initial_state` should be
342    /// set to the [`wgt::TextureUses`] state of the wrapped resource.  It will
343    /// be used as the source state (`oldLayout` / `StateBefore`) of the first
344    /// barrier emitted on the texture.
345    ///
346    /// If the resource has not been initialized (or if the existing contents
347    /// may be discarded), `initial_state` may be set to
348    /// `TextureUses::UNINITIALIZED`.
349    ///
350    /// # Safety
351    ///
352    /// - `hal_texture` must be created from this device internal handle
353    /// - `hal_texture` must be created respecting `desc`
354    /// - `hal_texture` must be initialized
355    /// - `initial_state`, if it is not `TextureUses::UNINITIALIZED`, must
356    ///   match the actual driver-side layout/state of the wrapped resource at
357    ///   the moment of wrap.
358    #[cfg(wgpu_core)]
359    #[must_use]
360    pub unsafe fn create_texture_from_hal<A: hal::Api>(
361        &self,
362        hal_texture: A::Texture,
363        desc: &TextureDescriptor<'_>,
364        initial_state: wgt::TextureUses,
365    ) -> Texture {
366        let texture = unsafe {
367            let core_device = self.inner.as_core();
368            core_device.context.create_texture_from_hal::<A>(
369                hal_texture,
370                core_device,
371                desc,
372                initial_state,
373            )
374        };
375        Texture {
376            inner: texture.into(),
377            descriptor: TextureDescriptor {
378                label: None,
379                view_formats: &[],
380                ..desc.clone()
381            },
382        }
383    }
384
385    /// Wraps a foreign [`webgpu::GpuTexture`] (e.g. a canvas `getCurrentTexture()` result)
386    /// as a [`Texture`] without any copy.
387    ///
388    /// The wrapped texture is *external*: dropping the returned `Texture` (or
389    /// calling [`Texture::destroy`] on it) does **not** call `GpuTexture.destroy()`
390    /// on the underlying handle - its lifetime is the caller's responsibility.
391    ///
392    /// If `drop_callback` is `Some`, it fires when wgpu releases its last
393    /// reference to the wrapped handle. wgpu never calls `GpuTexture.destroy()`
394    /// itself on a wrapped texture; to hand the handle's lifetime to wgpu,
395    /// supply a callback that calls `GpuTexture.destroy()`. The callback can
396    /// also be used to free a pool slot or notify dependent code that wgpu is
397    /// done with the handle. Pass `None` if the caller manages the handle's
398    /// lifetime entirely on their own.
399    ///
400    /// This is the WebGPU counterpart of [`Self::create_texture_from_hal`].
401    /// A `Some` `drop_callback` plays the same role as `wgpu_hal::DropCallback`
402    /// does on the Vulkan backend. The `None` case differs: here the texture is
403    /// always external and wgpu never destroys it, whereas on Vulkan a `None`
404    /// callback means wgpu takes ownership of the image and destroys it.
405    ///
406    /// The caller must guarantee:
407    ///
408    /// 1. `texture` was produced by the same underlying `GpuDevice` that this `Device` wraps.
409    /// 2. `desc.format`, `desc.size`, `desc.usage`, `desc.dimension`,
410    ///    `desc.mip_level_count`, and `desc.sample_count` match the actual
411    ///    `GPUTexture`'s reflected values. wgpu stores these verbatim and
412    ///    returns them from [`Texture::size`], [`Texture::format`], etc.
413    ///    without re-checking the handle; a mismatch yields silently incorrect
414    ///    metadata and, downstream, `GPUValidationError`s rather than memory
415    ///    unsafety (the browser bounds every access).
416    /// 3. The underlying `GpuTexture` must remain alive for as long as wgpu
417    ///    may use it (e.g. until any submitted command buffer that references
418    ///    it has finished executing). If `drop_callback` is `Some`, it is
419    ///    sufficient to keep the handle alive until the callback fires.
420    #[cfg(webgpu)]
421    #[must_use]
422    pub fn create_texture_from_webgpu_handle(
423        &self,
424        texture: webgpu::GpuTexture,
425        desc: &TextureDescriptor<'_>,
426        drop_callback: Option<webgpu::DropCallback>,
427    ) -> Texture {
428        let inner = self
429            .inner
430            .as_webgpu()
431            .wrap_external_texture(texture, drop_callback);
432        Texture {
433            inner,
434            descriptor: TextureDescriptor {
435                label: None,
436                view_formats: &[],
437                ..desc.clone()
438            },
439        }
440    }
441
442    /// Starts pipeline discovery when this `Device` is on the WebGPU backend,
443    /// and returns whether it did. Until [`Self::end_pipeline_discovery`],
444    /// every render pipeline it creates compiles through the browser's
445    /// `createRenderPipelineAsync`, in the background and beside the others
446    /// instead of one after another, and every encoder begun meanwhile
447    /// records no draw. A pipeline created during discovery never draws.
448    ///
449    /// A renderer draws a frame this way to learn the pipelines the frame
450    /// needs, drops what it drew, and draws the frame again once the future
451    /// [`Self::end_pipeline_discovery`] returns has resolved: creating the
452    /// same pipelines again then takes the ones the browser compiled.
453    #[cfg(webgpu)]
454    pub fn begin_pipeline_discovery(&self) -> bool {
455        self.inner
456            .as_webgpu_opt()
457            .map(crate::backend::webgpu::WebDevice::begin_pipeline_discovery)
458            .is_some()
459    }
460
461    /// Ends pipeline discovery. The future resolves once every pipeline
462    /// created during it compiled or failed, to those pipelines: keep them
463    /// until the same pipelines are created again, so the browser still has
464    /// them cached.
465    #[cfg(webgpu)]
466    pub fn end_pipeline_discovery(
467        &self,
468    ) -> impl core::future::Future<Output = Option<webgpu::DiscoveredPipelines>> + 'static {
469        let settled = self
470            .inner
471            .as_webgpu_opt()
472            .map(crate::backend::webgpu::WebDevice::end_pipeline_discovery);
473        async move {
474            match settled {
475                Some(settled) => Some(settled.await),
476                None => None,
477            }
478        }
479    }
480
481    /// Returns the underlying [`webgpu::GpuDevice`] handle if this `Device`
482    /// is on the WebGPU backend, otherwise `None`.
483    #[cfg(webgpu)]
484    pub fn as_webgpu(&self) -> Option<&webgpu::GpuDevice> {
485        self.inner.as_webgpu_opt().map(|wd| &wd.inner)
486    }
487
488    /// Creates a new [`ExternalTexture`].
489    #[must_use]
490    pub fn create_external_texture(
491        &self,
492        desc: &ExternalTextureDescriptor<'_>,
493        planes: &[&TextureView],
494    ) -> ExternalTexture {
495        let external_texture = self.inner.create_external_texture(desc, planes);
496
497        ExternalTexture {
498            inner: external_texture,
499        }
500    }
501
502    /// Creates a [`Buffer`] from a wgpu-hal Buffer.
503    ///
504    /// # Types
505    ///
506    /// The type of `A::Buffer` depends on the backend:
507    ///
508    #[doc = crate::macros::hal_type_vulkan!("Buffer")]
509    #[doc = crate::macros::hal_type_metal!("Buffer")]
510    #[doc = crate::macros::hal_type_dx12!("Buffer")]
511    #[doc = crate::macros::hal_type_gles!("Buffer")]
512    ///
513    /// # Safety
514    ///
515    /// - `hal_buffer` must be created from this device internal handle
516    /// - `hal_buffer` must be created respecting `desc`
517    /// - `hal_buffer` must be initialized
518    /// - `hal_buffer` must not have zero size
519    #[cfg(wgpu_core)]
520    #[must_use]
521    pub unsafe fn create_buffer_from_hal<A: hal::Api>(
522        &self,
523        hal_buffer: A::Buffer,
524        desc: &BufferDescriptor<'_>,
525    ) -> Buffer {
526        let map_context = MapContext::new(desc.mapped_at_creation.then_some(0..desc.size));
527
528        let buffer = unsafe {
529            let core_device = self.inner.as_core();
530            core_device
531                .context
532                .create_buffer_from_hal::<A>(hal_buffer, core_device, desc)
533        };
534
535        Buffer {
536            inner: buffer.into(),
537            map_context: Arc::new(Mutex::new(map_context)),
538            size: desc.size,
539            usage: desc.usage,
540        }
541    }
542
543    /// Creates a new [`Sampler`].
544    ///
545    /// `desc` specifies the behavior of the sampler.
546    #[must_use]
547    pub fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> Sampler {
548        let sampler = self.inner.create_sampler(desc);
549        Sampler { inner: sampler }
550    }
551
552    /// Creates a new [`QuerySet`].
553    #[must_use]
554    pub fn create_query_set(&self, desc: &QuerySetDescriptor<'_>) -> QuerySet {
555        let query_set = self.inner.create_query_set(desc);
556        QuerySet {
557            inner: query_set,
558            ty: desc.ty,
559            count: desc.count,
560        }
561    }
562
563    /// Set a callback which will be called for all errors that are not handled in error scopes.
564    pub fn on_uncaptured_error(&self, handler: Arc<dyn UncapturedErrorHandler>) {
565        self.inner.on_uncaptured_error(handler)
566    }
567
568    /// Push an error scope on this device's thread-local error scope
569    /// stack. All operations on this device, or on resources created
570    /// from this device, will have their errors captured by this scope
571    /// until the scope is popped.
572    ///
573    /// Scopes must be popped in reverse order to their creation. If
574    /// a guard is dropped without being `pop()`ped, the scope will be
575    /// popped, and the captured errors will be dropped.
576    ///
577    /// Multiple error scopes may be active at one time, forming a stack.
578    /// Each error will be reported to the inner-most scope that matches
579    /// its filter.
580    ///
581    /// With the `std` feature enabled, this stack is **thread-local**.
582    /// Without, this is **global** to all threads.
583    ///
584    /// ```rust
585    /// # async move {
586    /// # let device: wgpu::Device = unreachable!();
587    /// let error_scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
588    ///
589    /// // ...
590    /// // do work that may produce validation errors
591    /// // ...
592    ///
593    /// // pop the error scope and get a future for the result
594    /// let error_future = error_scope.pop();
595    ///
596    /// // await the future to get the error, if any
597    /// let error = error_future.await;
598    /// # };
599    /// ```
600    pub fn push_error_scope(&self, filter: ErrorFilter) -> ErrorScopeGuard {
601        let index = self.inner.push_error_scope(filter);
602        ErrorScopeGuard {
603            device: self.inner.clone(),
604            index,
605            popped: false,
606            _phantom: PhantomData,
607        }
608    }
609
610    /// Starts a capture in the attached graphics debugger.
611    ///
612    /// This behaves differently depending on which graphics debugger is attached:
613    ///
614    /// - Renderdoc: Calls [`StartFrameCapture(device, NULL)`][rd].
615    /// - Xcode: Creates a capture with [`MTLCaptureManager`][xcode].
616    /// - None: No action is taken.
617    ///
618    /// # Safety
619    ///
620    /// - There should not be any other captures currently active.
621    /// - All other safety rules are defined by the graphics debugger, see the
622    ///   documentation for the specific debugger.
623    /// - In general, graphics debuggers can easily cause crashes, so this isn't
624    ///   ever guaranteed to be sound.
625    ///
626    /// # Tips
627    ///
628    /// - Debuggers need to capture both the recording of the commands and the
629    ///   submission of the commands to the GPU. Try to wrap all of your
630    ///   gpu work in a capture.
631    /// - If you encounter issues, try waiting for the GPU to finish all work
632    ///   before stopping the capture.
633    ///
634    /// [rd]: https://renderdoc.org/docs/in_application_api.html#_CPPv417StartFrameCapture23RENDERDOC_DevicePointer22RENDERDOC_WindowHandle
635    /// [xcode]: https://developer.apple.com/documentation/metal/mtlcapturemanager
636    #[doc(alias = "start_renderdoc_capture")]
637    #[doc(alias = "start_xcode_capture")]
638    pub unsafe fn start_graphics_debugger_capture(&self) {
639        unsafe { self.inner.start_graphics_debugger_capture() }
640    }
641
642    /// Stops the current capture in the attached graphics debugger.
643    ///
644    /// This behaves differently depending on which graphics debugger is attached:
645    ///
646    /// - Renderdoc: Calls [`EndFrameCapture(device, NULL)`][rd].
647    /// - Xcode: Stops the capture with [`MTLCaptureManager`][xcode].
648    /// - None: No action is taken.
649    ///
650    /// # Safety
651    ///
652    /// - There should be a capture currently active.
653    /// - All other safety rules are defined by the graphics debugger, see the
654    ///   documentation for the specific debugger.
655    /// - In general, graphics debuggers can easily cause crashes, so this isn't
656    ///   ever guaranteed to be sound.
657    ///
658    /// # Tips
659    ///
660    /// - If you encounter issues, try to submit all work to the GPU, and waiting
661    ///   for that work to finish before stopping the capture.
662    ///
663    /// [rd]: https://renderdoc.org/docs/in_application_api.html#_CPPv415EndFrameCapture23RENDERDOC_DevicePointer22RENDERDOC_WindowHandle
664    /// [xcode]: https://developer.apple.com/documentation/metal/mtlcapturemanager
665    #[doc(alias = "stop_renderdoc_capture")]
666    #[doc(alias = "stop_xcode_capture")]
667    pub unsafe fn stop_graphics_debugger_capture(&self) {
668        unsafe { self.inner.stop_graphics_debugger_capture() }
669    }
670
671    /// Query internal counters from the native backend for debugging purposes.
672    ///
673    /// Some backends may not set all counters, or may not set any counter at all.
674    /// The `counters` cargo feature must be enabled for any counter to be set.
675    ///
676    /// If a counter is not set, its contains its default value (zero).
677    #[must_use]
678    pub fn get_internal_counters(&self) -> wgt::InternalCounters {
679        self.inner.get_internal_counters()
680    }
681
682    /// Generate an GPU memory allocation report if the underlying backend supports it.
683    ///
684    /// Backends that do not support producing these reports return `None`. A backend may
685    /// Support it and still return `None` if it is not using performing sub-allocation,
686    /// for example as a workaround for driver issues.
687    #[must_use]
688    pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
689        self.inner.generate_allocator_report()
690    }
691
692    /// Get the [`wgpu_hal`] device from this `Device`.
693    ///
694    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],
695    /// and pass that struct to the to the `A` type parameter.
696    ///
697    /// Returns a guard that dereferences to the type of the hal backend
698    /// which implements [`A::Device`].
699    ///
700    /// # Types
701    ///
702    /// The returned type depends on the backend:
703    ///
704    #[doc = crate::macros::hal_type_vulkan!("Device")]
705    #[doc = crate::macros::hal_type_metal!("Device")]
706    #[doc = crate::macros::hal_type_dx12!("Device")]
707    #[doc = crate::macros::hal_type_gles!("Device")]
708    ///
709    /// # Errors
710    ///
711    /// This method will return None if:
712    /// - The device is not from the backend specified by `A`.
713    /// - The device is from the `webgpu` or `custom` backend.
714    ///
715    /// On the `webgpu` backend, use `as_webgpu` instead.
716    ///
717    /// # Safety
718    ///
719    /// - The returned resource must not be destroyed unless the guard
720    ///   is the last reference to it and it is not in use by the GPU.
721    ///   The guard and handle may be dropped at any time however.
722    /// - All the safety requirements of wgpu-hal must be upheld.
723    ///
724    /// [`A::Device`]: hal::Api::Device
725    #[cfg(wgpu_core)]
726    pub unsafe fn as_hal<A: hal::Api>(
727        &self,
728    ) -> Option<impl Deref<Target = A::Device> + WasmNotSendSync> {
729        let device = self.inner.as_core_opt()?;
730        unsafe { device.context.device_as_hal::<A>(device) }
731    }
732
733    /// Destroy this device.
734    pub fn destroy(&self) {
735        self.inner.destroy()
736    }
737
738    /// Set a DeviceLostCallback on this device.
739    pub fn set_device_lost_callback(
740        &self,
741        callback: impl Fn(DeviceLostReason, String) + Send + 'static,
742    ) {
743        self.inner.set_device_lost_callback(Box::new(callback))
744    }
745
746    /// Create a [`PipelineCache`] with initial data
747    ///
748    /// This can be passed to [`Device::create_compute_pipeline`]
749    /// and [`Device::create_render_pipeline`] to either accelerate these
750    /// or add the cache results from those.
751    ///
752    /// # Safety
753    ///
754    /// If the `data` field of `desc` is set, it must have previously been returned from a call
755    /// to [`PipelineCache::get_data`][^saving]. This `data` will only be used if it came
756    /// from an adapter with the same [`util::pipeline_cache_key`].
757    /// This *is* compatible across wgpu versions, as any data format change will
758    /// be accounted for.
759    ///
760    /// It is *not* supported to bring caches from previous direct uses of backend APIs
761    /// into this method.
762    ///
763    /// # Errors
764    ///
765    /// Returns an error value if:
766    ///  * the [`PIPELINE_CACHE`](wgt::Features::PIPELINE_CACHE) feature is not enabled
767    ///  * this device is invalid; or
768    ///  * the device is out of memory
769    ///
770    /// This method also returns an error value if:
771    ///  * The `fallback` field on `desc` is false; and
772    ///  * the `data` provided would not be used[^data_not_used]
773    ///
774    /// If an error value is used in subsequent calls, default caching will be used.
775    ///
776    /// [^saving]: We do recognise that saving this data to disk means this condition
777    /// is impossible to fully prove. Consider the risks for your own application in this case.
778    ///
779    /// [^data_not_used]: This data may be not used if: the data was produced by a prior
780    /// version of wgpu; or was created for an incompatible adapter, or there was a GPU driver
781    /// update. In some cases, the data might not be used and a real value is returned,
782    /// this is left to the discretion of GPU drivers.
783    #[must_use]
784    pub unsafe fn create_pipeline_cache(
785        &self,
786        desc: &PipelineCacheDescriptor<'_>,
787    ) -> PipelineCache {
788        let cache = unsafe { self.inner.create_pipeline_cache(desc) };
789        PipelineCache { inner: cache }
790    }
791}
792
793/// [`Features::EXPERIMENTAL_RAY_QUERY`] must be enabled on the device in order to call these functions.
794impl Device {
795    /// Create a bottom level acceleration structure, used inside a top level acceleration structure for ray tracing.
796    /// - `desc`: The descriptor of the acceleration structure.
797    /// - `sizes`: Size descriptor limiting what can be built into the acceleration structure.
798    ///
799    /// # Validation
800    /// If any of the following is not satisfied a validation error is generated
801    ///
802    /// The device ***must*** have [`Features::EXPERIMENTAL_RAY_QUERY`] enabled.
803    /// if `sizes` is [`BlasGeometrySizeDescriptors::Triangles`] then the following must be satisfied
804    /// - For every geometry descriptor (for the purposes this is called `geo_desc`) of `sizes.descriptors` the following must be satisfied:
805    ///     - `geo_desc.vertex_format` must be within allowed formats (allowed formats for a given feature set
806    ///       may be queried with [`Features::allowed_vertex_formats_for_blas`]).
807    ///     - Both or neither of `geo_desc.index_format` and `geo_desc.index_count` must be provided.
808    ///
809    /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY
810    /// [`Features::allowed_vertex_formats_for_blas`]: wgt::Features::allowed_vertex_formats_for_blas
811    #[must_use]
812    pub fn create_blas(
813        &self,
814        desc: &CreateBlasDescriptor<'_>,
815        sizes: BlasGeometrySizeDescriptors,
816    ) -> Blas {
817        let (handle, blas) = self.inner.create_blas(desc, sizes);
818
819        Blas {
820            inner: blas,
821            handle,
822        }
823    }
824
825    /// Create a top level acceleration structure, used for ray tracing.
826    /// - `desc`: The descriptor of the acceleration structure.
827    ///
828    /// # Validation
829    /// If any of the following is not satisfied a validation error is generated
830    ///
831    /// The device ***must*** have [`Features::EXPERIMENTAL_RAY_QUERY`] enabled.
832    ///
833    /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY
834    #[must_use]
835    pub fn create_tlas(&self, desc: &CreateTlasDescriptor<'_>) -> Tlas {
836        let tlas = self.inner.create_tlas(desc);
837
838        Tlas {
839            inner: tlas,
840            instances: vec![None; desc.max_instances as usize],
841            lowest_unmodified: 0,
842        }
843    }
844}
845
846/// Requesting a device from an [`Adapter`] failed.
847#[derive(Clone, Debug)]
848pub struct RequestDeviceError {
849    pub(crate) inner: RequestDeviceErrorKind,
850}
851
852impl RequestDeviceError {
853    /// Construct an error from a custom backend message. This is mainly useful for custom backends.
854    #[cfg(custom)]
855    pub fn from_message(message: String) -> Self {
856        RequestDeviceError {
857            inner: RequestDeviceErrorKind::Custom(message),
858        }
859    }
860}
861
862#[derive(Clone, Debug)]
863pub(crate) enum RequestDeviceErrorKind {
864    /// Error from [`wgpu_core`].
865    // must match dependency cfg
866    #[cfg(wgpu_core)]
867    Core(wgc::instance::RequestDeviceError),
868
869    /// Error from web API that was called by `wgpu` to request a device.
870    ///
871    /// (This is currently never used by the webgl backend, but it could be.)
872    #[cfg(webgpu)]
873    WebGpu(String),
874
875    /// Error from a custom backend.
876    #[cfg(custom)]
877    Custom(String),
878}
879
880static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);
881
882impl fmt::Display for RequestDeviceError {
883    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
884        match &self.inner {
885            #[cfg(wgpu_core)]
886            RequestDeviceErrorKind::Core(error) => error.fmt(_f),
887            #[cfg(webgpu)]
888            RequestDeviceErrorKind::WebGpu(error) => {
889                write!(_f, "{error}")
890            }
891            #[cfg(custom)]
892            RequestDeviceErrorKind::Custom(msg) => write!(_f, "{msg}"),
893            #[cfg(not(any(webgpu, wgpu_core)))]
894            _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
895        }
896    }
897}
898
899impl error::Error for RequestDeviceError {
900    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
901        match &self.inner {
902            #[cfg(wgpu_core)]
903            RequestDeviceErrorKind::Core(error) => error.source(),
904            #[cfg(webgpu)]
905            RequestDeviceErrorKind::WebGpu(_) => None,
906            #[cfg(custom)]
907            RequestDeviceErrorKind::Custom(_) => None,
908            #[cfg(not(any(webgpu, wgpu_core)))]
909            _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
910        }
911    }
912}
913
914#[cfg(wgpu_core)]
915impl From<wgc::instance::RequestDeviceError> for RequestDeviceError {
916    fn from(error: wgc::instance::RequestDeviceError) -> Self {
917        Self {
918            inner: RequestDeviceErrorKind::Core(error),
919        }
920    }
921}
922
923/// The callback of [`Device::on_uncaptured_error()`].
924///
925/// It must be a function with this signature.
926pub trait UncapturedErrorHandler: Fn(Error) + Send + Sync + 'static {}
927impl<T> UncapturedErrorHandler for T where T: Fn(Error) + Send + Sync + 'static {}
928
929/// Kinds of [`Error`]s a [`Device::push_error_scope()`] may be configured to catch.
930#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
931pub enum ErrorFilter {
932    /// Catch only out-of-memory errors.
933    OutOfMemory,
934    /// Catch only validation errors.
935    Validation,
936    /// Catch only internal errors.
937    Internal,
938}
939static_assertions::assert_impl_all!(ErrorFilter: Send, Sync);
940
941/// Lower level source of the error.
942///
943/// `Send + Sync` varies depending on configuration.
944#[cfg(send_sync)]
945#[cfg_attr(docsrs, doc(cfg(all())))]
946pub type ErrorSource = Box<dyn error::Error + Send + Sync + 'static>;
947/// Lower level source of the error.
948///
949/// `Send + Sync` varies depending on configuration.
950#[cfg(not(send_sync))]
951#[cfg_attr(docsrs, doc(cfg(all())))]
952pub type ErrorSource = Box<dyn error::Error + 'static>;
953
954/// Errors resulting from usage of GPU APIs.
955///
956/// By default, errors translate into panics. Depending on the backend and circumstances,
957/// errors may occur synchronously or asynchronously. When errors need to be handled, use
958/// [`Device::push_error_scope()`] or [`Device::on_uncaptured_error()`].
959#[derive(Debug)]
960pub enum Error {
961    /// Out of memory.
962    OutOfMemory {
963        /// Lower level source of the error.
964        source: ErrorSource,
965    },
966    /// Validation error, signifying a bug in code or data provided to `wgpu`.
967    Validation {
968        /// Lower level source of the error.
969        source: ErrorSource,
970        /// Description of the validation error.
971        description: String,
972    },
973    /// Internal error. Used for signalling any failures not explicitly expected by WebGPU.
974    ///
975    /// These could be due to internal implementation or system limits being reached.
976    Internal {
977        /// Lower level source of the error.
978        source: ErrorSource,
979        /// Description of the internal GPU error.
980        description: String,
981    },
982}
983#[cfg(send_sync)]
984static_assertions::assert_impl_all!(Error: Send, Sync);
985
986impl error::Error for Error {
987    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
988        match self {
989            Error::OutOfMemory { source } => Some(source.as_ref()),
990            Error::Validation { source, .. } => Some(source.as_ref()),
991            Error::Internal { source, .. } => Some(source.as_ref()),
992        }
993    }
994}
995
996impl fmt::Display for Error {
997    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
998        match self {
999            Error::OutOfMemory { .. } => f.write_str("Out of Memory"),
1000            Error::Validation { description, .. } => f.write_str(description),
1001            Error::Internal { description, .. } => f.write_str(description),
1002        }
1003    }
1004}
1005
1006/// Guard for an error scope pushed with [`Device::push_error_scope()`].
1007///
1008/// Call [`pop()`] to pop the scope and get a future for the result. If
1009/// the guard is dropped without being popped explicitly, the scope will still be popped,
1010/// and the captured errors will be dropped.
1011///
1012/// This guard is neither `Send` nor `Sync`, as error scopes are handled
1013/// on a per-thread basis when the `std` feature is enabled.
1014///
1015/// [`pop()`]: ErrorScopeGuard::pop
1016#[must_use = "Error scopes must be explicitly popped to retrieve errors they catch"]
1017pub struct ErrorScopeGuard {
1018    device: dispatch::DispatchDevice,
1019    index: u32,
1020    popped: bool,
1021    // Ensure the guard is !Send and !Sync
1022    _phantom: PhantomData<*mut ()>,
1023}
1024
1025static_assertions::assert_not_impl_any!(ErrorScopeGuard: Send, Sync);
1026
1027impl ErrorScopeGuard {
1028    /// Pops the error scope.
1029    ///
1030    /// Returns a future which resolves to the error captured by this scope, if any.
1031    /// The pop takes effect immediately; the future does not need to be awaited before doing work that is outside of this error scope.
1032    pub fn pop(mut self) -> impl Future<Output = Option<Error>> + WasmNotSend {
1033        self.popped = true;
1034        self.device.pop_error_scope(self.index)
1035    }
1036}
1037
1038impl Drop for ErrorScopeGuard {
1039    fn drop(&mut self) {
1040        if !self.popped {
1041            drop(self.device.pop_error_scope(self.index));
1042        }
1043    }
1044}
1045
1046impl fmt::Debug for ErrorScopeGuard {
1047    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1048        let ErrorScopeGuard {
1049            device,
1050            index,
1051            popped,
1052            _phantom: _,
1053        } = self;
1054        f.debug_struct("ErrorScopeGuard")
1055            .field("device", device)
1056            .field("index", index)
1057            .field("popped", popped)
1058            .finish()
1059    }
1060}