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}