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