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}