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 an empty [`CommandEncoder`].
206 #[must_use]
207 pub fn create_command_encoder(&self, desc: &CommandEncoderDescriptor<'_>) -> CommandEncoder {
208 let encoder = self.inner.create_command_encoder(desc);
209 // Each encoder starts with its own deferred-action store that travels
210 // with the CommandBuffer produced by finish().
211 CommandEncoder {
212 inner: encoder,
213 actions: Default::default(),
214 }
215 }
216
217 /// Creates an empty [`RenderBundleEncoder`].
218 #[must_use]
219 pub fn create_render_bundle_encoder<'a>(
220 &self,
221 desc: &RenderBundleEncoderDescriptor<'_>,
222 ) -> RenderBundleEncoder<'a> {
223 let encoder = self.inner.create_render_bundle_encoder(desc);
224 RenderBundleEncoder {
225 inner: encoder,
226 _p: PhantomData,
227 }
228 }
229
230 /// Creates a new [`BindGroup`].
231 #[must_use]
232 pub fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> BindGroup {
233 let group = self.inner.create_bind_group(desc);
234 BindGroup { inner: group }
235 }
236
237 /// Creates a [`BindGroupLayout`].
238 #[must_use]
239 pub fn create_bind_group_layout(
240 &self,
241 desc: &BindGroupLayoutDescriptor<'_>,
242 ) -> BindGroupLayout {
243 let layout = self.inner.create_bind_group_layout(desc);
244 BindGroupLayout { inner: layout }
245 }
246
247 /// Creates a [`PipelineLayout`].
248 #[must_use]
249 pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDescriptor<'_>) -> PipelineLayout {
250 let layout = self.inner.create_pipeline_layout(desc);
251 PipelineLayout { inner: layout }
252 }
253
254 /// Creates a [`RenderPipeline`].
255 #[must_use]
256 pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor<'_>) -> RenderPipeline {
257 let pipeline = self.inner.create_render_pipeline(desc);
258 RenderPipeline { inner: pipeline }
259 }
260
261 /// Creates a mesh shader based [`RenderPipeline`].
262 #[must_use]
263 pub fn create_mesh_pipeline(&self, desc: &MeshPipelineDescriptor<'_>) -> RenderPipeline {
264 let pipeline = self.inner.create_mesh_pipeline(desc);
265 RenderPipeline { inner: pipeline }
266 }
267
268 /// Creates a [`ComputePipeline`].
269 #[must_use]
270 pub fn create_compute_pipeline(&self, desc: &ComputePipelineDescriptor<'_>) -> ComputePipeline {
271 let pipeline = self.inner.create_compute_pipeline(desc);
272 ComputePipeline { inner: pipeline }
273 }
274
275 /// Creates a [`Buffer`].
276 #[must_use]
277 pub fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> Buffer {
278 let map_context = MapContext::new(desc.mapped_at_creation.then_some(0..desc.size));
279
280 let buffer = self.inner.create_buffer(desc);
281
282 Buffer {
283 inner: buffer,
284 map_context: Arc::new(Mutex::new(map_context)),
285 size: desc.size,
286 usage: desc.usage,
287 }
288 }
289
290 /// Creates a new [`Texture`].
291 ///
292 /// `desc` specifies the general format of the texture.
293 #[must_use]
294 pub fn create_texture(&self, desc: &TextureDescriptor<'_>) -> Texture {
295 let texture = self.inner.create_texture(desc);
296
297 Texture {
298 inner: texture,
299 descriptor: TextureDescriptor {
300 label: None,
301 view_formats: &[],
302 ..desc.clone()
303 },
304 }
305 }
306
307 /// Creates a [`Texture`] from a wgpu-hal Texture.
308 ///
309 /// # Types
310 ///
311 /// The type of `A::Texture` depends on the backend:
312 ///
313 #[doc = crate::macros::hal_type_vulkan!("Texture")]
314 #[doc = crate::macros::hal_type_metal!("Texture")]
315 #[doc = crate::macros::hal_type_dx12!("Texture")]
316 #[doc = crate::macros::hal_type_gles!("Texture")]
317 ///
318 /// On [`Backend::BrowserWebGpu`], use `Device::create_texture_from_webgpu_handle()` instead.
319 ///
320 /// # `initial_state`
321 ///
322 /// If the resource has already been initialized, `initial_state` should be
323 /// set to the [`wgt::TextureUses`] state of the wrapped resource. It will
324 /// be used as the source state (`oldLayout` / `StateBefore`) of the first
325 /// barrier emitted on the texture.
326 ///
327 /// If the resource has not been initialized (or if the existing contents
328 /// may be discarded), `initial_state` may be set to
329 /// `TextureUses::UNINITIALIZED`.
330 ///
331 /// # Safety
332 ///
333 /// - `hal_texture` must be created from this device internal handle
334 /// - `hal_texture` must be created respecting `desc`
335 /// - `hal_texture` must be initialized
336 /// - `initial_state`, if it is not `TextureUses::UNINITIALIZED`, must
337 /// match the actual driver-side layout/state of the wrapped resource at
338 /// the moment of wrap.
339 #[cfg(wgpu_core)]
340 #[must_use]
341 pub unsafe fn create_texture_from_hal<A: hal::Api>(
342 &self,
343 hal_texture: A::Texture,
344 desc: &TextureDescriptor<'_>,
345 initial_state: wgt::TextureUses,
346 ) -> Texture {
347 let texture = unsafe {
348 let core_device = self.inner.as_core();
349 core_device.context.create_texture_from_hal::<A>(
350 hal_texture,
351 core_device,
352 desc,
353 initial_state,
354 )
355 };
356 Texture {
357 inner: texture.into(),
358 descriptor: TextureDescriptor {
359 label: None,
360 view_formats: &[],
361 ..desc.clone()
362 },
363 }
364 }
365
366 /// Wraps a foreign [`webgpu::GpuTexture`] (e.g. a canvas `getCurrentTexture()` result)
367 /// as a [`Texture`] without any copy.
368 ///
369 /// The wrapped texture is *external*: dropping the returned `Texture` (or
370 /// calling [`Texture::destroy`] on it) does **not** call `GpuTexture.destroy()`
371 /// on the underlying handle - its lifetime is the caller's responsibility.
372 ///
373 /// If `drop_callback` is `Some`, it fires when wgpu releases its last
374 /// reference to the wrapped handle. wgpu never calls `GpuTexture.destroy()`
375 /// itself on a wrapped texture; to hand the handle's lifetime to wgpu,
376 /// supply a callback that calls `GpuTexture.destroy()`. The callback can
377 /// also be used to free a pool slot or notify dependent code that wgpu is
378 /// done with the handle. Pass `None` if the caller manages the handle's
379 /// lifetime entirely on their own.
380 ///
381 /// This is the WebGPU counterpart of [`Self::create_texture_from_hal`].
382 /// A `Some` `drop_callback` plays the same role as `wgpu_hal::DropCallback`
383 /// does on the Vulkan backend. The `None` case differs: here the texture is
384 /// always external and wgpu never destroys it, whereas on Vulkan a `None`
385 /// callback means wgpu takes ownership of the image and destroys it.
386 ///
387 /// The caller must guarantee:
388 ///
389 /// 1. `texture` was produced by the same underlying `GpuDevice` that this `Device` wraps.
390 /// 2. `desc.format`, `desc.size`, `desc.usage`, `desc.dimension`,
391 /// `desc.mip_level_count`, and `desc.sample_count` match the actual
392 /// `GPUTexture`'s reflected values. wgpu stores these verbatim and
393 /// returns them from [`Texture::size`], [`Texture::format`], etc.
394 /// without re-checking the handle; a mismatch yields silently incorrect
395 /// metadata and, downstream, `GPUValidationError`s rather than memory
396 /// unsafety (the browser bounds every access).
397 /// 3. The underlying `GpuTexture` must remain alive for as long as wgpu
398 /// may use it (e.g. until any submitted command buffer that references
399 /// it has finished executing). If `drop_callback` is `Some`, it is
400 /// sufficient to keep the handle alive until the callback fires.
401 #[cfg(webgpu)]
402 #[must_use]
403 pub fn create_texture_from_webgpu_handle(
404 &self,
405 texture: webgpu::GpuTexture,
406 desc: &TextureDescriptor<'_>,
407 drop_callback: Option<webgpu::DropCallback>,
408 ) -> Texture {
409 let inner = self
410 .inner
411 .as_webgpu()
412 .wrap_external_texture(texture, drop_callback);
413 Texture {
414 inner,
415 descriptor: TextureDescriptor {
416 label: None,
417 view_formats: &[],
418 ..desc.clone()
419 },
420 }
421 }
422
423 /// Returns the underlying [`webgpu::GpuDevice`] handle if this `Device`
424 /// is on the WebGPU backend, otherwise `None`.
425 #[cfg(webgpu)]
426 pub fn as_webgpu(&self) -> Option<&webgpu::GpuDevice> {
427 self.inner.as_webgpu_opt().map(|wd| &wd.inner)
428 }
429
430 /// Creates a new [`ExternalTexture`].
431 #[must_use]
432 pub fn create_external_texture(
433 &self,
434 desc: &ExternalTextureDescriptor<'_>,
435 planes: &[&TextureView],
436 ) -> ExternalTexture {
437 let external_texture = self.inner.create_external_texture(desc, planes);
438
439 ExternalTexture {
440 inner: external_texture,
441 }
442 }
443
444 /// Creates a [`Buffer`] from a wgpu-hal Buffer.
445 ///
446 /// # Types
447 ///
448 /// The type of `A::Buffer` depends on the backend:
449 ///
450 #[doc = crate::macros::hal_type_vulkan!("Buffer")]
451 #[doc = crate::macros::hal_type_metal!("Buffer")]
452 #[doc = crate::macros::hal_type_dx12!("Buffer")]
453 #[doc = crate::macros::hal_type_gles!("Buffer")]
454 ///
455 /// # Safety
456 ///
457 /// - `hal_buffer` must be created from this device internal handle
458 /// - `hal_buffer` must be created respecting `desc`
459 /// - `hal_buffer` must be initialized
460 /// - `hal_buffer` must not have zero size
461 #[cfg(wgpu_core)]
462 #[must_use]
463 pub unsafe fn create_buffer_from_hal<A: hal::Api>(
464 &self,
465 hal_buffer: A::Buffer,
466 desc: &BufferDescriptor<'_>,
467 ) -> Buffer {
468 let map_context = MapContext::new(desc.mapped_at_creation.then_some(0..desc.size));
469
470 let buffer = unsafe {
471 let core_device = self.inner.as_core();
472 core_device
473 .context
474 .create_buffer_from_hal::<A>(hal_buffer, core_device, desc)
475 };
476
477 Buffer {
478 inner: buffer.into(),
479 map_context: Arc::new(Mutex::new(map_context)),
480 size: desc.size,
481 usage: desc.usage,
482 }
483 }
484
485 /// Creates a new [`Sampler`].
486 ///
487 /// `desc` specifies the behavior of the sampler.
488 #[must_use]
489 pub fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> Sampler {
490 let sampler = self.inner.create_sampler(desc);
491 Sampler { inner: sampler }
492 }
493
494 /// Creates a new [`QuerySet`].
495 #[must_use]
496 pub fn create_query_set(&self, desc: &QuerySetDescriptor<'_>) -> QuerySet {
497 let query_set = self.inner.create_query_set(desc);
498 QuerySet {
499 inner: query_set,
500 ty: desc.ty,
501 count: desc.count,
502 }
503 }
504
505 /// Set a callback which will be called for all errors that are not handled in error scopes.
506 pub fn on_uncaptured_error(&self, handler: Arc<dyn UncapturedErrorHandler>) {
507 self.inner.on_uncaptured_error(handler)
508 }
509
510 /// Push an error scope on this device's thread-local error scope
511 /// stack. All operations on this device, or on resources created
512 /// from this device, will have their errors captured by this scope
513 /// until the scope is popped.
514 ///
515 /// Scopes must be popped in reverse order to their creation. If
516 /// a guard is dropped without being `pop()`ped, the scope will be
517 /// popped, and the captured errors will be dropped.
518 ///
519 /// Multiple error scopes may be active at one time, forming a stack.
520 /// Each error will be reported to the inner-most scope that matches
521 /// its filter.
522 ///
523 /// With the `std` feature enabled, this stack is **thread-local**.
524 /// Without, this is **global** to all threads.
525 ///
526 /// ```rust
527 /// # async move {
528 /// # let device: wgpu::Device = unreachable!();
529 /// let error_scope = device.push_error_scope(wgpu::ErrorFilter::Validation);
530 ///
531 /// // ...
532 /// // do work that may produce validation errors
533 /// // ...
534 ///
535 /// // pop the error scope and get a future for the result
536 /// let error_future = error_scope.pop();
537 ///
538 /// // await the future to get the error, if any
539 /// let error = error_future.await;
540 /// # };
541 /// ```
542 pub fn push_error_scope(&self, filter: ErrorFilter) -> ErrorScopeGuard {
543 let index = self.inner.push_error_scope(filter);
544 ErrorScopeGuard {
545 device: self.inner.clone(),
546 index,
547 popped: false,
548 _phantom: PhantomData,
549 }
550 }
551
552 /// Starts a capture in the attached graphics debugger.
553 ///
554 /// This behaves differently depending on which graphics debugger is attached:
555 ///
556 /// - Renderdoc: Calls [`StartFrameCapture(device, NULL)`][rd].
557 /// - Xcode: Creates a capture with [`MTLCaptureManager`][xcode].
558 /// - None: No action is taken.
559 ///
560 /// # Safety
561 ///
562 /// - There should not be any other captures currently active.
563 /// - All other safety rules are defined by the graphics debugger, see the
564 /// documentation for the specific debugger.
565 /// - In general, graphics debuggers can easily cause crashes, so this isn't
566 /// ever guaranteed to be sound.
567 ///
568 /// # Tips
569 ///
570 /// - Debuggers need to capture both the recording of the commands and the
571 /// submission of the commands to the GPU. Try to wrap all of your
572 /// gpu work in a capture.
573 /// - If you encounter issues, try waiting for the GPU to finish all work
574 /// before stopping the capture.
575 ///
576 /// [rd]: https://renderdoc.org/docs/in_application_api.html#_CPPv417StartFrameCapture23RENDERDOC_DevicePointer22RENDERDOC_WindowHandle
577 /// [xcode]: https://developer.apple.com/documentation/metal/mtlcapturemanager
578 #[doc(alias = "start_renderdoc_capture")]
579 #[doc(alias = "start_xcode_capture")]
580 pub unsafe fn start_graphics_debugger_capture(&self) {
581 unsafe { self.inner.start_graphics_debugger_capture() }
582 }
583
584 /// Stops the current capture in the attached graphics debugger.
585 ///
586 /// This behaves differently depending on which graphics debugger is attached:
587 ///
588 /// - Renderdoc: Calls [`EndFrameCapture(device, NULL)`][rd].
589 /// - Xcode: Stops the capture with [`MTLCaptureManager`][xcode].
590 /// - None: No action is taken.
591 ///
592 /// # Safety
593 ///
594 /// - There should be a capture currently active.
595 /// - All other safety rules are defined by the graphics debugger, see the
596 /// documentation for the specific debugger.
597 /// - In general, graphics debuggers can easily cause crashes, so this isn't
598 /// ever guaranteed to be sound.
599 ///
600 /// # Tips
601 ///
602 /// - If you encounter issues, try to submit all work to the GPU, and waiting
603 /// for that work to finish before stopping the capture.
604 ///
605 /// [rd]: https://renderdoc.org/docs/in_application_api.html#_CPPv415EndFrameCapture23RENDERDOC_DevicePointer22RENDERDOC_WindowHandle
606 /// [xcode]: https://developer.apple.com/documentation/metal/mtlcapturemanager
607 #[doc(alias = "stop_renderdoc_capture")]
608 #[doc(alias = "stop_xcode_capture")]
609 pub unsafe fn stop_graphics_debugger_capture(&self) {
610 unsafe { self.inner.stop_graphics_debugger_capture() }
611 }
612
613 /// Query internal counters from the native backend for debugging purposes.
614 ///
615 /// Some backends may not set all counters, or may not set any counter at all.
616 /// The `counters` cargo feature must be enabled for any counter to be set.
617 ///
618 /// If a counter is not set, its contains its default value (zero).
619 #[must_use]
620 pub fn get_internal_counters(&self) -> wgt::InternalCounters {
621 self.inner.get_internal_counters()
622 }
623
624 /// Generate an GPU memory allocation report if the underlying backend supports it.
625 ///
626 /// Backends that do not support producing these reports return `None`. A backend may
627 /// Support it and still return `None` if it is not using performing sub-allocation,
628 /// for example as a workaround for driver issues.
629 #[must_use]
630 pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
631 self.inner.generate_allocator_report()
632 }
633
634 /// Get the [`wgpu_hal`] device from this `Device`.
635 ///
636 /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],
637 /// and pass that struct to the to the `A` type parameter.
638 ///
639 /// Returns a guard that dereferences to the type of the hal backend
640 /// which implements [`A::Device`].
641 ///
642 /// # Types
643 ///
644 /// The returned type depends on the backend:
645 ///
646 #[doc = crate::macros::hal_type_vulkan!("Device")]
647 #[doc = crate::macros::hal_type_metal!("Device")]
648 #[doc = crate::macros::hal_type_dx12!("Device")]
649 #[doc = crate::macros::hal_type_gles!("Device")]
650 ///
651 /// # Errors
652 ///
653 /// This method will return None if:
654 /// - The device is not from the backend specified by `A`.
655 /// - The device is from the `webgpu` or `custom` backend.
656 ///
657 /// On the `webgpu` backend, use `as_webgpu` instead.
658 ///
659 /// # Safety
660 ///
661 /// - The returned resource must not be destroyed unless the guard
662 /// is the last reference to it and it is not in use by the GPU.
663 /// The guard and handle may be dropped at any time however.
664 /// - All the safety requirements of wgpu-hal must be upheld.
665 ///
666 /// [`A::Device`]: hal::Api::Device
667 #[cfg(wgpu_core)]
668 pub unsafe fn as_hal<A: hal::Api>(
669 &self,
670 ) -> Option<impl Deref<Target = A::Device> + WasmNotSendSync> {
671 let device = self.inner.as_core_opt()?;
672 unsafe { device.context.device_as_hal::<A>(device) }
673 }
674
675 /// Destroy this device.
676 pub fn destroy(&self) {
677 self.inner.destroy()
678 }
679
680 /// Set a DeviceLostCallback on this device.
681 pub fn set_device_lost_callback(
682 &self,
683 callback: impl Fn(DeviceLostReason, String) + Send + 'static,
684 ) {
685 self.inner.set_device_lost_callback(Box::new(callback))
686 }
687
688 /// Create a [`PipelineCache`] with initial data
689 ///
690 /// This can be passed to [`Device::create_compute_pipeline`]
691 /// and [`Device::create_render_pipeline`] to either accelerate these
692 /// or add the cache results from those.
693 ///
694 /// # Safety
695 ///
696 /// If the `data` field of `desc` is set, it must have previously been returned from a call
697 /// to [`PipelineCache::get_data`][^saving]. This `data` will only be used if it came
698 /// from an adapter with the same [`util::pipeline_cache_key`].
699 /// This *is* compatible across wgpu versions, as any data format change will
700 /// be accounted for.
701 ///
702 /// It is *not* supported to bring caches from previous direct uses of backend APIs
703 /// into this method.
704 ///
705 /// # Errors
706 ///
707 /// Returns an error value if:
708 /// * the [`PIPELINE_CACHE`](wgt::Features::PIPELINE_CACHE) feature is not enabled
709 /// * this device is invalid; or
710 /// * the device is out of memory
711 ///
712 /// This method also returns an error value if:
713 /// * The `fallback` field on `desc` is false; and
714 /// * the `data` provided would not be used[^data_not_used]
715 ///
716 /// If an error value is used in subsequent calls, default caching will be used.
717 ///
718 /// [^saving]: We do recognise that saving this data to disk means this condition
719 /// is impossible to fully prove. Consider the risks for your own application in this case.
720 ///
721 /// [^data_not_used]: This data may be not used if: the data was produced by a prior
722 /// version of wgpu; or was created for an incompatible adapter, or there was a GPU driver
723 /// update. In some cases, the data might not be used and a real value is returned,
724 /// this is left to the discretion of GPU drivers.
725 #[must_use]
726 pub unsafe fn create_pipeline_cache(
727 &self,
728 desc: &PipelineCacheDescriptor<'_>,
729 ) -> PipelineCache {
730 let cache = unsafe { self.inner.create_pipeline_cache(desc) };
731 PipelineCache { inner: cache }
732 }
733}
734
735/// [`Features::EXPERIMENTAL_RAY_QUERY`] must be enabled on the device in order to call these functions.
736impl Device {
737 /// Create a bottom level acceleration structure, used inside a top level acceleration structure for ray tracing.
738 /// - `desc`: The descriptor of the acceleration structure.
739 /// - `sizes`: Size descriptor limiting what can be built into the acceleration structure.
740 ///
741 /// # Validation
742 /// If any of the following is not satisfied a validation error is generated
743 ///
744 /// The device ***must*** have [`Features::EXPERIMENTAL_RAY_QUERY`] enabled.
745 /// if `sizes` is [`BlasGeometrySizeDescriptors::Triangles`] then the following must be satisfied
746 /// - For every geometry descriptor (for the purposes this is called `geo_desc`) of `sizes.descriptors` the following must be satisfied:
747 /// - `geo_desc.vertex_format` must be within allowed formats (allowed formats for a given feature set
748 /// may be queried with [`Features::allowed_vertex_formats_for_blas`]).
749 /// - Both or neither of `geo_desc.index_format` and `geo_desc.index_count` must be provided.
750 ///
751 /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY
752 /// [`Features::allowed_vertex_formats_for_blas`]: wgt::Features::allowed_vertex_formats_for_blas
753 #[must_use]
754 pub fn create_blas(
755 &self,
756 desc: &CreateBlasDescriptor<'_>,
757 sizes: BlasGeometrySizeDescriptors,
758 ) -> Blas {
759 let (handle, blas) = self.inner.create_blas(desc, sizes);
760
761 Blas {
762 inner: blas,
763 handle,
764 }
765 }
766
767 /// Create a top level acceleration structure, used for ray tracing.
768 /// - `desc`: The descriptor of the acceleration structure.
769 ///
770 /// # Validation
771 /// If any of the following is not satisfied a validation error is generated
772 ///
773 /// The device ***must*** have [`Features::EXPERIMENTAL_RAY_QUERY`] enabled.
774 ///
775 /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY
776 #[must_use]
777 pub fn create_tlas(&self, desc: &CreateTlasDescriptor<'_>) -> Tlas {
778 let tlas = self.inner.create_tlas(desc);
779
780 Tlas {
781 inner: tlas,
782 instances: vec![None; desc.max_instances as usize],
783 lowest_unmodified: 0,
784 }
785 }
786}
787
788/// Requesting a device from an [`Adapter`] failed.
789#[derive(Clone, Debug)]
790pub struct RequestDeviceError {
791 pub(crate) inner: RequestDeviceErrorKind,
792}
793
794impl RequestDeviceError {
795 /// Construct an error from a custom backend message. This is mainly useful for custom backends.
796 #[cfg(custom)]
797 pub fn from_message(message: String) -> Self {
798 RequestDeviceError {
799 inner: RequestDeviceErrorKind::Custom(message),
800 }
801 }
802}
803
804#[derive(Clone, Debug)]
805pub(crate) enum RequestDeviceErrorKind {
806 /// Error from [`wgpu_core`].
807 // must match dependency cfg
808 #[cfg(wgpu_core)]
809 Core(wgc::instance::RequestDeviceError),
810
811 /// Error from web API that was called by `wgpu` to request a device.
812 ///
813 /// (This is currently never used by the webgl backend, but it could be.)
814 #[cfg(webgpu)]
815 WebGpu(String),
816
817 /// Error from a custom backend.
818 #[cfg(custom)]
819 Custom(String),
820}
821
822static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);
823
824impl fmt::Display for RequestDeviceError {
825 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
826 match &self.inner {
827 #[cfg(wgpu_core)]
828 RequestDeviceErrorKind::Core(error) => error.fmt(_f),
829 #[cfg(webgpu)]
830 RequestDeviceErrorKind::WebGpu(error) => {
831 write!(_f, "{error}")
832 }
833 #[cfg(custom)]
834 RequestDeviceErrorKind::Custom(msg) => write!(_f, "{msg}"),
835 #[cfg(not(any(webgpu, wgpu_core)))]
836 _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
837 }
838 }
839}
840
841impl error::Error for RequestDeviceError {
842 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
843 match &self.inner {
844 #[cfg(wgpu_core)]
845 RequestDeviceErrorKind::Core(error) => error.source(),
846 #[cfg(webgpu)]
847 RequestDeviceErrorKind::WebGpu(_) => None,
848 #[cfg(custom)]
849 RequestDeviceErrorKind::Custom(_) => None,
850 #[cfg(not(any(webgpu, wgpu_core)))]
851 _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
852 }
853 }
854}
855
856#[cfg(wgpu_core)]
857impl From<wgc::instance::RequestDeviceError> for RequestDeviceError {
858 fn from(error: wgc::instance::RequestDeviceError) -> Self {
859 Self {
860 inner: RequestDeviceErrorKind::Core(error),
861 }
862 }
863}
864
865/// The callback of [`Device::on_uncaptured_error()`].
866///
867/// It must be a function with this signature.
868pub trait UncapturedErrorHandler: Fn(Error) + Send + Sync + 'static {}
869impl<T> UncapturedErrorHandler for T where T: Fn(Error) + Send + Sync + 'static {}
870
871/// Kinds of [`Error`]s a [`Device::push_error_scope()`] may be configured to catch.
872#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
873pub enum ErrorFilter {
874 /// Catch only out-of-memory errors.
875 OutOfMemory,
876 /// Catch only validation errors.
877 Validation,
878 /// Catch only internal errors.
879 Internal,
880}
881static_assertions::assert_impl_all!(ErrorFilter: Send, Sync);
882
883/// Lower level source of the error.
884///
885/// `Send + Sync` varies depending on configuration.
886#[cfg(send_sync)]
887#[cfg_attr(docsrs, doc(cfg(all())))]
888pub type ErrorSource = Box<dyn error::Error + Send + Sync + 'static>;
889/// Lower level source of the error.
890///
891/// `Send + Sync` varies depending on configuration.
892#[cfg(not(send_sync))]
893#[cfg_attr(docsrs, doc(cfg(all())))]
894pub type ErrorSource = Box<dyn error::Error + 'static>;
895
896/// Errors resulting from usage of GPU APIs.
897///
898/// By default, errors translate into panics. Depending on the backend and circumstances,
899/// errors may occur synchronously or asynchronously. When errors need to be handled, use
900/// [`Device::push_error_scope()`] or [`Device::on_uncaptured_error()`].
901#[derive(Debug)]
902pub enum Error {
903 /// Out of memory.
904 OutOfMemory {
905 /// Lower level source of the error.
906 source: ErrorSource,
907 },
908 /// Validation error, signifying a bug in code or data provided to `wgpu`.
909 Validation {
910 /// Lower level source of the error.
911 source: ErrorSource,
912 /// Description of the validation error.
913 description: String,
914 },
915 /// Internal error. Used for signalling any failures not explicitly expected by WebGPU.
916 ///
917 /// These could be due to internal implementation or system limits being reached.
918 Internal {
919 /// Lower level source of the error.
920 source: ErrorSource,
921 /// Description of the internal GPU error.
922 description: String,
923 },
924}
925#[cfg(send_sync)]
926static_assertions::assert_impl_all!(Error: Send, Sync);
927
928impl error::Error for Error {
929 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
930 match self {
931 Error::OutOfMemory { source } => Some(source.as_ref()),
932 Error::Validation { source, .. } => Some(source.as_ref()),
933 Error::Internal { source, .. } => Some(source.as_ref()),
934 }
935 }
936}
937
938impl fmt::Display for Error {
939 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
940 match self {
941 Error::OutOfMemory { .. } => f.write_str("Out of Memory"),
942 Error::Validation { description, .. } => f.write_str(description),
943 Error::Internal { description, .. } => f.write_str(description),
944 }
945 }
946}
947
948/// Guard for an error scope pushed with [`Device::push_error_scope()`].
949///
950/// Call [`pop()`] to pop the scope and get a future for the result. If
951/// the guard is dropped without being popped explicitly, the scope will still be popped,
952/// and the captured errors will be dropped.
953///
954/// This guard is neither `Send` nor `Sync`, as error scopes are handled
955/// on a per-thread basis when the `std` feature is enabled.
956///
957/// [`pop()`]: ErrorScopeGuard::pop
958#[must_use = "Error scopes must be explicitly popped to retrieve errors they catch"]
959pub struct ErrorScopeGuard {
960 device: dispatch::DispatchDevice,
961 index: u32,
962 popped: bool,
963 // Ensure the guard is !Send and !Sync
964 _phantom: PhantomData<*mut ()>,
965}
966
967static_assertions::assert_not_impl_any!(ErrorScopeGuard: Send, Sync);
968
969impl ErrorScopeGuard {
970 /// Pops the error scope.
971 ///
972 /// Returns a future which resolves to the error captured by this scope, if any.
973 /// The pop takes effect immediately; the future does not need to be awaited before doing work that is outside of this error scope.
974 pub fn pop(mut self) -> impl Future<Output = Option<Error>> + WasmNotSend {
975 self.popped = true;
976 self.device.pop_error_scope(self.index)
977 }
978}
979
980impl Drop for ErrorScopeGuard {
981 fn drop(&mut self) {
982 if !self.popped {
983 drop(self.device.pop_error_scope(self.index));
984 }
985 }
986}
987
988impl fmt::Debug for ErrorScopeGuard {
989 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
990 let ErrorScopeGuard {
991 device,
992 index,
993 popped,
994 _phantom: _,
995 } = self;
996 f.debug_struct("ErrorScopeGuard")
997 .field("device", device)
998 .field("index", index)
999 .field("popped", popped)
1000 .finish()
1001 }
1002}