wgpu/api/device.rs
1use std::{error, fmt, future::Future, sync::Arc, thread};
2
3use parking_lot::Mutex;
4
5use crate::context::DynContext;
6use crate::*;
7
8/// Open connection to a graphics and/or compute device.
9///
10/// Responsible for the creation of most rendering and compute resources.
11/// These are then used in commands, which are submitted to a [`Queue`].
12///
13/// A device may be requested from an adapter with [`Adapter::request_device`].
14///
15/// Corresponds to [WebGPU `GPUDevice`](https://gpuweb.github.io/gpuweb/#gpu-device).
16#[derive(Debug)]
17pub struct Device {
18 pub(crate) context: Arc<C>,
19 pub(crate) data: Box<Data>,
20}
21#[cfg(send_sync)]
22static_assertions::assert_impl_all!(Device: Send, Sync);
23
24/// Describes a [`Device`].
25///
26/// For use with [`Adapter::request_device`].
27///
28/// Corresponds to [WebGPU `GPUDeviceDescriptor`](
29/// https://gpuweb.github.io/gpuweb/#dictdef-gpudevicedescriptor).
30pub type DeviceDescriptor<'a> = wgt::DeviceDescriptor<Label<'a>>;
31static_assertions::assert_impl_all!(DeviceDescriptor<'_>: Send, Sync);
32
33impl Device {
34 /// Check for resource cleanups and mapping callbacks. Will block if [`Maintain::Wait`] is passed.
35 ///
36 /// Return `true` if the queue is empty, or `false` if there are more queue
37 /// submissions still in flight. (Note that, unless access to the [`Queue`] is
38 /// coordinated somehow, this information could be out of date by the time
39 /// the caller receives it. `Queue`s can be shared between threads, so
40 /// other threads could submit new work at any time.)
41 ///
42 /// When running on WebGPU, this is a no-op. `Device`s are automatically polled.
43 pub fn poll(&self, maintain: Maintain) -> MaintainResult {
44 DynContext::device_poll(&*self.context, self.data.as_ref(), maintain)
45 }
46
47 /// The features which can be used on this device.
48 ///
49 /// No additional features can be used, even if the underlying adapter can support them.
50 #[must_use]
51 pub fn features(&self) -> Features {
52 DynContext::device_features(&*self.context, self.data.as_ref())
53 }
54
55 /// The limits which can be used on this device.
56 ///
57 /// No better limits can be used, even if the underlying adapter can support them.
58 #[must_use]
59 pub fn limits(&self) -> Limits {
60 DynContext::device_limits(&*self.context, self.data.as_ref())
61 }
62
63 /// Creates a shader module from either SPIR-V or WGSL source code.
64 ///
65 /// <div class="warning">
66 // NOTE: Keep this in sync with `naga::front::wgsl::parse_str`!
67 // NOTE: Keep this in sync with `wgpu_core::Global::device_create_shader_module`!
68 ///
69 /// This function may consume a lot of stack space. Compiler-enforced limits for parsing
70 /// recursion exist; if shader compilation runs into them, it will return an error gracefully.
71 /// However, on some build profiles and platforms, the default stack size for a thread may be
72 /// exceeded before this limit is reached during parsing. Callers should ensure that there is
73 /// enough stack space for this, particularly if calls to this method are exposed to user
74 /// input.
75 ///
76 /// </div>
77 #[must_use]
78 pub fn create_shader_module(&self, desc: ShaderModuleDescriptor<'_>) -> ShaderModule {
79 let data = DynContext::device_create_shader_module(
80 &*self.context,
81 self.data.as_ref(),
82 desc,
83 wgt::ShaderBoundChecks::new(),
84 );
85 ShaderModule {
86 context: Arc::clone(&self.context),
87 data,
88 }
89 }
90
91 /// Creates a shader module from either SPIR-V or WGSL source code without runtime checks.
92 ///
93 /// # Safety
94 /// In contrast with [`create_shader_module`](Self::create_shader_module) this function
95 /// creates a shader module without runtime checks which allows shaders to perform
96 /// operations which can lead to undefined behavior like indexing out of bounds, thus it's
97 /// the caller responsibility to pass a shader which doesn't perform any of this
98 /// operations.
99 ///
100 /// This has no effect on web.
101 #[must_use]
102 pub unsafe fn create_shader_module_unchecked(
103 &self,
104 desc: ShaderModuleDescriptor<'_>,
105 ) -> ShaderModule {
106 let data = DynContext::device_create_shader_module(
107 &*self.context,
108 self.data.as_ref(),
109 desc,
110 unsafe { wgt::ShaderBoundChecks::unchecked() },
111 );
112 ShaderModule {
113 context: Arc::clone(&self.context),
114 data,
115 }
116 }
117
118 /// Creates a shader module from SPIR-V binary directly.
119 ///
120 /// # Safety
121 ///
122 /// This function passes binary data to the backend as-is and can potentially result in a
123 /// driver crash or bogus behaviour. No attempt is made to ensure that data is valid SPIR-V.
124 ///
125 /// See also [`include_spirv_raw!`] and [`util::make_spirv_raw`].
126 #[must_use]
127 pub unsafe fn create_shader_module_spirv(
128 &self,
129 desc: &ShaderModuleDescriptorSpirV<'_>,
130 ) -> ShaderModule {
131 let data = unsafe {
132 DynContext::device_create_shader_module_spirv(&*self.context, self.data.as_ref(), desc)
133 };
134 ShaderModule {
135 context: Arc::clone(&self.context),
136 data,
137 }
138 }
139
140 /// Creates an empty [`CommandEncoder`].
141 #[must_use]
142 pub fn create_command_encoder(&self, desc: &CommandEncoderDescriptor<'_>) -> CommandEncoder {
143 let data =
144 DynContext::device_create_command_encoder(&*self.context, self.data.as_ref(), desc);
145 CommandEncoder {
146 context: Arc::clone(&self.context),
147 data,
148 }
149 }
150
151 /// Creates an empty [`RenderBundleEncoder`].
152 #[must_use]
153 pub fn create_render_bundle_encoder(
154 &self,
155 desc: &RenderBundleEncoderDescriptor<'_>,
156 ) -> RenderBundleEncoder<'_> {
157 let data = DynContext::device_create_render_bundle_encoder(
158 &*self.context,
159 self.data.as_ref(),
160 desc,
161 );
162 RenderBundleEncoder {
163 context: Arc::clone(&self.context),
164 data,
165 parent: self,
166 _p: Default::default(),
167 }
168 }
169
170 /// Creates a new [`BindGroup`].
171 #[must_use]
172 pub fn create_bind_group(&self, desc: &BindGroupDescriptor<'_>) -> BindGroup {
173 let data = DynContext::device_create_bind_group(&*self.context, self.data.as_ref(), desc);
174 BindGroup {
175 context: Arc::clone(&self.context),
176 data,
177 }
178 }
179
180 /// Creates a [`BindGroupLayout`].
181 #[must_use]
182 pub fn create_bind_group_layout(
183 &self,
184 desc: &BindGroupLayoutDescriptor<'_>,
185 ) -> BindGroupLayout {
186 let data =
187 DynContext::device_create_bind_group_layout(&*self.context, self.data.as_ref(), desc);
188 BindGroupLayout {
189 context: Arc::clone(&self.context),
190 data,
191 }
192 }
193
194 /// Creates a [`PipelineLayout`].
195 #[must_use]
196 pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDescriptor<'_>) -> PipelineLayout {
197 let data =
198 DynContext::device_create_pipeline_layout(&*self.context, self.data.as_ref(), desc);
199 PipelineLayout {
200 context: Arc::clone(&self.context),
201 data,
202 }
203 }
204
205 /// Creates a [`RenderPipeline`].
206 #[must_use]
207 pub fn create_render_pipeline(&self, desc: &RenderPipelineDescriptor<'_>) -> RenderPipeline {
208 let data =
209 DynContext::device_create_render_pipeline(&*self.context, self.data.as_ref(), desc);
210 RenderPipeline {
211 context: Arc::clone(&self.context),
212 data,
213 }
214 }
215
216 /// Creates a [`ComputePipeline`].
217 #[must_use]
218 pub fn create_compute_pipeline(&self, desc: &ComputePipelineDescriptor<'_>) -> ComputePipeline {
219 let data =
220 DynContext::device_create_compute_pipeline(&*self.context, self.data.as_ref(), desc);
221 ComputePipeline {
222 context: Arc::clone(&self.context),
223 data,
224 }
225 }
226
227 /// Creates a [`Buffer`].
228 #[must_use]
229 pub fn create_buffer(&self, desc: &BufferDescriptor<'_>) -> Buffer {
230 let mut map_context = MapContext::new(desc.size);
231 if desc.mapped_at_creation {
232 map_context.initial_range = 0..desc.size;
233 }
234
235 let data = DynContext::device_create_buffer(&*self.context, self.data.as_ref(), desc);
236
237 Buffer {
238 context: Arc::clone(&self.context),
239 data,
240 map_context: Mutex::new(map_context),
241 size: desc.size,
242 usage: desc.usage,
243 }
244 }
245
246 /// Creates a new [`Texture`].
247 ///
248 /// `desc` specifies the general format of the texture.
249 #[must_use]
250 pub fn create_texture(&self, desc: &TextureDescriptor<'_>) -> Texture {
251 let data = DynContext::device_create_texture(&*self.context, self.data.as_ref(), desc);
252 Texture {
253 context: Arc::clone(&self.context),
254 data,
255 descriptor: TextureDescriptor {
256 label: None,
257 view_formats: &[],
258 ..desc.clone()
259 },
260 }
261 }
262
263 /// Creates a [`Texture`] from a wgpu-hal Texture.
264 ///
265 /// # Safety
266 ///
267 /// - `hal_texture` must be created from this device internal handle
268 /// - `hal_texture` must be created respecting `desc`
269 /// - `hal_texture` must be initialized
270 #[cfg(wgpu_core)]
271 #[must_use]
272 pub unsafe fn create_texture_from_hal<A: wgc::hal_api::HalApi>(
273 &self,
274 hal_texture: A::Texture,
275 desc: &TextureDescriptor<'_>,
276 ) -> Texture {
277 let texture = unsafe {
278 self.context
279 .as_any()
280 .downcast_ref::<crate::backend::ContextWgpuCore>()
281 // Part of the safety requirements is that the texture was generated from the same hal device.
282 // Therefore, unwrap is fine here since only WgpuCoreContext has the ability to create hal textures.
283 .unwrap()
284 .create_texture_from_hal::<A>(
285 hal_texture,
286 crate::context::downcast_ref(self.data.as_ref()),
287 desc,
288 )
289 };
290 Texture {
291 context: Arc::clone(&self.context),
292 data: Box::new(texture),
293 descriptor: TextureDescriptor {
294 label: None,
295 view_formats: &[],
296 ..desc.clone()
297 },
298 }
299 }
300
301 /// Creates a [`Buffer`] from a wgpu-hal Buffer.
302 ///
303 /// # Safety
304 ///
305 /// - `hal_buffer` must be created from this device internal handle
306 /// - `hal_buffer` must be created respecting `desc`
307 /// - `hal_buffer` must be initialized
308 #[cfg(wgpu_core)]
309 #[must_use]
310 pub unsafe fn create_buffer_from_hal<A: wgc::hal_api::HalApi>(
311 &self,
312 hal_buffer: A::Buffer,
313 desc: &BufferDescriptor<'_>,
314 ) -> Buffer {
315 let mut map_context = MapContext::new(desc.size);
316 if desc.mapped_at_creation {
317 map_context.initial_range = 0..desc.size;
318 }
319
320 let buffer = unsafe {
321 self.context
322 .as_any()
323 .downcast_ref::<crate::backend::ContextWgpuCore>()
324 // Part of the safety requirements is that the buffer was generated from the same hal device.
325 // Therefore, unwrap is fine here since only WgpuCoreContext has the ability to create hal buffers.
326 .unwrap()
327 .create_buffer_from_hal::<A>(
328 hal_buffer,
329 crate::context::downcast_ref(self.data.as_ref()),
330 desc,
331 )
332 };
333
334 Buffer {
335 context: Arc::clone(&self.context),
336 data: Box::new(buffer),
337 map_context: Mutex::new(map_context),
338 size: desc.size,
339 usage: desc.usage,
340 }
341 }
342
343 /// Creates a new [`Sampler`].
344 ///
345 /// `desc` specifies the behavior of the sampler.
346 #[must_use]
347 pub fn create_sampler(&self, desc: &SamplerDescriptor<'_>) -> Sampler {
348 let data = DynContext::device_create_sampler(&*self.context, self.data.as_ref(), desc);
349 Sampler {
350 context: Arc::clone(&self.context),
351 data,
352 }
353 }
354
355 /// Creates a new [`QuerySet`].
356 #[must_use]
357 pub fn create_query_set(&self, desc: &QuerySetDescriptor<'_>) -> QuerySet {
358 let data = DynContext::device_create_query_set(&*self.context, self.data.as_ref(), desc);
359 QuerySet {
360 context: Arc::clone(&self.context),
361 data,
362 }
363 }
364
365 /// Set a callback for errors that are not handled in error scopes.
366 pub fn on_uncaptured_error(&self, handler: Box<dyn UncapturedErrorHandler>) {
367 self.context
368 .device_on_uncaptured_error(self.data.as_ref(), handler);
369 }
370
371 /// Push an error scope.
372 pub fn push_error_scope(&self, filter: ErrorFilter) {
373 self.context
374 .device_push_error_scope(self.data.as_ref(), filter);
375 }
376
377 /// Pop an error scope.
378 pub fn pop_error_scope(&self) -> impl Future<Output = Option<Error>> + WasmNotSend {
379 self.context.device_pop_error_scope(self.data.as_ref())
380 }
381
382 /// Starts frame capture.
383 pub fn start_capture(&self) {
384 DynContext::device_start_capture(&*self.context, self.data.as_ref())
385 }
386
387 /// Stops frame capture.
388 pub fn stop_capture(&self) {
389 DynContext::device_stop_capture(&*self.context, self.data.as_ref())
390 }
391
392 /// Query internal counters from the native backend for debugging purposes.
393 ///
394 /// Some backends may not set all counters, or may not set any counter at all.
395 /// The `counters` cargo feature must be enabled for any counter to be set.
396 ///
397 /// If a counter is not set, its contains its default value (zero).
398 #[must_use]
399 pub fn get_internal_counters(&self) -> wgt::InternalCounters {
400 DynContext::device_get_internal_counters(&*self.context, self.data.as_ref())
401 }
402
403 /// Generate an GPU memory allocation report if the underlying backend supports it.
404 ///
405 /// Backends that do not support producing these reports return `None`. A backend may
406 /// Support it and still return `None` if it is not using performing sub-allocation,
407 /// for example as a workaround for driver issues.
408 #[must_use]
409 pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
410 DynContext::generate_allocator_report(&*self.context, self.data.as_ref())
411 }
412
413 /// Apply a callback to this `Device`'s underlying backend device.
414 ///
415 /// If this `Device` is implemented by the backend API given by `A` (Vulkan,
416 /// Dx12, etc.), then apply `hal_device_callback` to `Some(&device)`, where
417 /// `device` is the underlying backend device type, [`A::Device`].
418 ///
419 /// If this `Device` uses a different backend, apply `hal_device_callback`
420 /// to `None`.
421 ///
422 /// The device is locked for reading while `hal_device_callback` runs. If
423 /// the callback attempts to perform any `wgpu` operations that require
424 /// write access to the device (destroying a buffer, say), deadlock will
425 /// occur. The locks are automatically released when the callback returns.
426 ///
427 /// # Safety
428 ///
429 /// - The raw handle passed to the callback must not be manually destroyed.
430 ///
431 /// [`A::Device`]: hal::Api::Device
432 #[cfg(wgpu_core)]
433 pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
434 &self,
435 hal_device_callback: F,
436 ) -> Option<R> {
437 self.context
438 .as_any()
439 .downcast_ref::<crate::backend::ContextWgpuCore>()
440 .map(|ctx| unsafe {
441 ctx.device_as_hal::<A, F, R>(
442 crate::context::downcast_ref(self.data.as_ref()),
443 hal_device_callback,
444 )
445 })
446 }
447
448 /// Destroy this device.
449 pub fn destroy(&self) {
450 DynContext::device_destroy(&*self.context, self.data.as_ref())
451 }
452
453 /// Set a DeviceLostCallback on this device.
454 pub fn set_device_lost_callback(
455 &self,
456 callback: impl Fn(DeviceLostReason, String) + Send + 'static,
457 ) {
458 DynContext::device_set_device_lost_callback(
459 &*self.context,
460 self.data.as_ref(),
461 Box::new(callback),
462 )
463 }
464
465 /// Create a [`PipelineCache`] with initial data
466 ///
467 /// This can be passed to [`Device::create_compute_pipeline`]
468 /// and [`Device::create_render_pipeline`] to either accelerate these
469 /// or add the cache results from those.
470 ///
471 /// # Safety
472 ///
473 /// If the `data` field of `desc` is set, it must have previously been returned from a call
474 /// to [`PipelineCache::get_data`][^saving]. This `data` will only be used if it came
475 /// from an adapter with the same [`util::pipeline_cache_key`].
476 /// This *is* compatible across wgpu versions, as any data format change will
477 /// be accounted for.
478 ///
479 /// It is *not* supported to bring caches from previous direct uses of backend APIs
480 /// into this method.
481 ///
482 /// # Errors
483 ///
484 /// Returns an error value if:
485 /// * the [`PIPELINE_CACHE`](wgt::Features::PIPELINE_CACHE) feature is not enabled
486 /// * this device is invalid; or
487 /// * the device is out of memory
488 ///
489 /// This method also returns an error value if:
490 /// * The `fallback` field on `desc` is false; and
491 /// * the `data` provided would not be used[^data_not_used]
492 ///
493 /// If an error value is used in subsequent calls, default caching will be used.
494 ///
495 /// [^saving]: We do recognise that saving this data to disk means this condition
496 /// is impossible to fully prove. Consider the risks for your own application in this case.
497 ///
498 /// [^data_not_used]: This data may be not used if: the data was produced by a prior
499 /// version of wgpu; or was created for an incompatible adapter, or there was a GPU driver
500 /// update. In some cases, the data might not be used and a real value is returned,
501 /// this is left to the discretion of GPU drivers.
502 #[must_use]
503 pub unsafe fn create_pipeline_cache(
504 &self,
505 desc: &PipelineCacheDescriptor<'_>,
506 ) -> PipelineCache {
507 let data = unsafe {
508 DynContext::device_create_pipeline_cache(&*self.context, self.data.as_ref(), desc)
509 };
510 PipelineCache {
511 context: Arc::clone(&self.context),
512 data,
513 }
514 }
515}
516
517impl Drop for Device {
518 fn drop(&mut self) {
519 if !thread::panicking() {
520 self.context.device_drop(self.data.as_ref());
521 }
522 }
523}
524
525/// Requesting a device from an [`Adapter`] failed.
526#[derive(Clone, Debug)]
527pub struct RequestDeviceError {
528 pub(crate) inner: RequestDeviceErrorKind,
529}
530#[derive(Clone, Debug)]
531pub(crate) enum RequestDeviceErrorKind {
532 /// Error from [`wgpu_core`].
533 // must match dependency cfg
534 #[cfg(wgpu_core)]
535 Core(wgc::instance::RequestDeviceError),
536
537 /// Error from web API that was called by `wgpu` to request a device.
538 ///
539 /// (This is currently never used by the webgl backend, but it could be.)
540 #[cfg(webgpu)]
541 WebGpu(wasm_bindgen::JsValue),
542}
543
544#[cfg(send_sync)]
545unsafe impl Send for RequestDeviceErrorKind {}
546#[cfg(send_sync)]
547unsafe impl Sync for RequestDeviceErrorKind {}
548
549#[cfg(send_sync)]
550static_assertions::assert_impl_all!(RequestDeviceError: Send, Sync);
551
552impl fmt::Display for RequestDeviceError {
553 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
554 match &self.inner {
555 #[cfg(wgpu_core)]
556 RequestDeviceErrorKind::Core(error) => error.fmt(_f),
557 #[cfg(webgpu)]
558 RequestDeviceErrorKind::WebGpu(error_js_value) => {
559 // wasm-bindgen provides a reasonable error stringification via `Debug` impl
560 write!(_f, "{error_js_value:?}")
561 }
562 #[cfg(not(any(webgpu, wgpu_core)))]
563 _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
564 }
565 }
566}
567
568impl error::Error for RequestDeviceError {
569 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
570 match &self.inner {
571 #[cfg(wgpu_core)]
572 RequestDeviceErrorKind::Core(error) => error.source(),
573 #[cfg(webgpu)]
574 RequestDeviceErrorKind::WebGpu(_) => None,
575 #[cfg(not(any(webgpu, wgpu_core)))]
576 _ => unimplemented!("unknown `RequestDeviceErrorKind`"),
577 }
578 }
579}
580
581#[cfg(wgpu_core)]
582impl From<wgc::instance::RequestDeviceError> for RequestDeviceError {
583 fn from(error: wgc::instance::RequestDeviceError) -> Self {
584 Self {
585 inner: RequestDeviceErrorKind::Core(error),
586 }
587 }
588}
589
590/// Type for the callback of uncaptured error handler
591pub trait UncapturedErrorHandler: Fn(Error) + Send + 'static {}
592impl<T> UncapturedErrorHandler for T where T: Fn(Error) + Send + 'static {}
593
594/// Filter for error scopes.
595#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
596pub enum ErrorFilter {
597 /// Catch only out-of-memory errors.
598 OutOfMemory,
599 /// Catch only validation errors.
600 Validation,
601 /// Catch only internal errors.
602 Internal,
603}
604static_assertions::assert_impl_all!(ErrorFilter: Send, Sync);
605
606/// Lower level source of the error.
607///
608/// `Send + Sync` varies depending on configuration.
609#[cfg(send_sync)]
610#[cfg_attr(docsrs, doc(cfg(all())))]
611pub type ErrorSource = Box<dyn error::Error + Send + Sync + 'static>;
612/// Lower level source of the error.
613///
614/// `Send + Sync` varies depending on configuration.
615#[cfg(not(send_sync))]
616#[cfg_attr(docsrs, doc(cfg(all())))]
617pub type ErrorSource = Box<dyn error::Error + 'static>;
618
619/// Error type
620#[derive(Debug)]
621pub enum Error {
622 /// Out of memory error
623 OutOfMemory {
624 /// Lower level source of the error.
625 source: ErrorSource,
626 },
627 /// Validation error, signifying a bug in code or data
628 Validation {
629 /// Lower level source of the error.
630 source: ErrorSource,
631 /// Description of the validation error.
632 description: String,
633 },
634 /// Internal error. Used for signalling any failures not explicitly expected by WebGPU.
635 ///
636 /// These could be due to internal implementation or system limits being reached.
637 Internal {
638 /// Lower level source of the error.
639 source: ErrorSource,
640 /// Description of the internal GPU error.
641 description: String,
642 },
643}
644#[cfg(send_sync)]
645static_assertions::assert_impl_all!(Error: Send, Sync);
646
647impl error::Error for Error {
648 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
649 match self {
650 Error::OutOfMemory { source } => Some(source.as_ref()),
651 Error::Validation { source, .. } => Some(source.as_ref()),
652 Error::Internal { source, .. } => Some(source.as_ref()),
653 }
654 }
655}
656
657impl fmt::Display for Error {
658 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659 match self {
660 Error::OutOfMemory { .. } => f.write_str("Out of Memory"),
661 Error::Validation { description, .. } => f.write_str(description),
662 Error::Internal { description, .. } => f.write_str(description),
663 }
664 }
665}