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}