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