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