Skip to main content

wgpu_core/
instance.rs

1use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec, vec::Vec};
2use core::{
3    fmt,
4    sync::atomic::{AtomicBool, Ordering},
5};
6
7use hashbrown::HashMap;
8use thiserror::Error;
9
10use crate::{
11    api_log, api_log_debug,
12    device::{
13        queue::Queue, resource::Device, DeviceDescriptor, DeviceError, UserClosures, WaitIdleError,
14    },
15    id::markers,
16    limits::{self, check_limits, FailedLimit},
17    lock::{rank, Mutex, MutexGuard},
18    present::{ConfigureSurfaceError, Presentation},
19    resource::ResourceType,
20    resource_log,
21    timestamp_normalization::TimestampNormalizerInitError,
22    weak_vec::WeakVec,
23    DOWNLEVEL_WARNING_MESSAGE,
24};
25
26use wgt::{Backend, Backends, InstanceFlags, PowerPreference};
27
28#[derive(Debug)]
29pub(crate) struct InstanceDevices(Mutex<WeakVec<Device>>);
30
31impl Default for InstanceDevices {
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37impl InstanceDevices {
38    pub(crate) fn new() -> Self {
39        Self(Mutex::new(rank::INSTANCE_DEVICES, WeakVec::new()))
40    }
41
42    pub(crate) fn push(&self, device: &Arc<Device>) {
43        self.0.lock().push(Arc::downgrade(device));
44    }
45
46    /// Poll all devices stored in this instance.
47    ///
48    /// If `force_wait` is true, block until all buffer mappings are done.
49    ///
50    /// Return `all_queue_empty` indicating whether there are more queue
51    /// submissions still in flight.
52    fn poll_all_devices(
53        &self,
54        force_wait: bool,
55        closure_list: &mut UserClosures,
56    ) -> Result<bool, WaitIdleError> {
57        let mut all_queue_empty = true;
58        {
59            let device_guard = self.0.lock();
60
61            for device in device_guard.iter().filter_map(|device| device.upgrade()) {
62                let poll_type = if force_wait {
63                    // TODO(#8286): Should expose timeout to poll_all.
64                    wgt::PollType::wait_indefinitely()
65                } else {
66                    wgt::PollType::Poll
67                };
68
69                let (closures, result) = device.poll_and_return_closures(poll_type);
70
71                let is_queue_empty = matches!(result, Ok(wgt::PollStatus::QueueEmpty));
72
73                all_queue_empty &= is_queue_empty;
74
75                closure_list.extend(closures);
76            }
77        }
78
79        Ok(all_queue_empty)
80    }
81}
82
83#[derive(Default)]
84pub struct Instance {
85    _name: String,
86
87    /// List of instances per `wgpu-hal` backend.
88    ///
89    /// The ordering in this list implies prioritization and needs to be preserved.
90    instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,
91
92    /// The backends that were requested by the user.
93    requested_backends: Backends,
94
95    /// The backends that we could have attempted to obtain from `wgpu-hal` —
96    /// those for which support is compiled in, currently.
97    ///
98    /// The union of this and `requested_backends` is the set of backends that would be used,
99    /// independent of whether accessing the drivers/hardware for them succeeds.
100    /// To obtain the set of backends actually in use by this instance, check
101    /// `instance_per_backend` instead.
102    supported_backends: Backends,
103
104    pub flags: InstanceFlags,
105
106    /// Non-lifetimed [`raw_window_handle::DisplayHandle`], for keepalive and validation purposes in
107    /// [`Self::create_surface()`].
108    ///
109    /// When used with `winit`, callers are expected to pass its `OwnedDisplayHandle` (created from
110    /// the `EventLoop`) here.
111    display: Option<Box<dyn wgt::WgpuHasDisplayHandle>>,
112
113    /// Keeps track of all devices created from this instance, so that they can be polled.
114    devices: InstanceDevices,
115}
116
117impl Instance {
118    pub fn new(
119        name: &str,
120        instance_desc: wgt::InstanceDescriptor,
121        telemetry: Option<hal::Telemetry>,
122    ) -> Arc<Self> {
123        let mut this = InstanceBuilder::new(name, instance_desc, telemetry);
124
125        #[cfg(all(vulkan, not(target_os = "netbsd")))]
126        this.try_add_hal(hal::api::Vulkan);
127        #[cfg(metal)]
128        this.try_add_hal(hal::api::Metal);
129        #[cfg(dx12)]
130        this.try_add_hal(hal::api::Dx12);
131        #[cfg(gles)]
132        this.try_add_hal(hal::api::Gles);
133        #[cfg(feature = "noop")]
134        this.try_add_hal(hal::api::Noop);
135
136        this.build()
137    }
138
139    pub fn from_hal_instance<A: hal::Api>(
140        name: String,
141        hal_instance: <A as hal::Api>::Instance,
142    ) -> Arc<Self> {
143        Arc::new(Self {
144            _name: name,
145            instance_per_backend: vec![(A::VARIANT, Box::new(hal_instance))],
146            requested_backends: A::VARIANT.into(),
147            supported_backends: A::VARIANT.into(),
148            flags: InstanceFlags::default(),
149            display: None, // TODO: Extract display from HAL instance if available?
150            devices: InstanceDevices::new(),
151        })
152    }
153
154    pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynInstance> {
155        self.instance_per_backend
156            .iter()
157            .find_map(|(instance_backend, instance)| {
158                (*instance_backend == backend).then(|| instance.as_ref())
159            })
160    }
161
162    /// # Safety
163    ///
164    /// - The raw instance handle returned must not be manually destroyed.
165    pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {
166        self.raw(A::VARIANT).map(|instance| {
167            instance
168                .as_any()
169                .downcast_ref()
170                // This should be impossible. It would mean that backend instance and enum type are mismatching.
171                .expect("Stored instance is not of the correct type")
172        })
173    }
174
175    /// Creates a new surface targeting the given display/window handles.
176    ///
177    /// Internally attempts to create hal surfaces for all enabled backends.
178    ///
179    /// Fails only if creation for surfaces for all enabled backends fails in which case
180    /// the error for each enabled backend is listed.
181    /// Vice versa, if creation for any backend succeeds, success is returned.
182    /// Surface creation errors are logged to the debug log in any case.
183    ///
184    /// # Safety
185    ///
186    /// - `display_handle` must be a valid object to create a surface upon,
187    ///   falls back to the instance display handle otherwise.
188    /// - `window_handle` must remain valid as long as the returned
189    ///   [`Surface`] is being used.
190    pub unsafe fn create_surface(
191        &self,
192        display_handle: Option<raw_window_handle::RawDisplayHandle>,
193        window_handle: raw_window_handle::RawWindowHandle,
194    ) -> Result<Arc<Surface>, CreateSurfaceError> {
195        profiling::scope!("Instance::create_surface");
196
197        let instance_display_handle = self.display.as_ref().map(|d| {
198            d.display_handle()
199                .expect("Implementation did not provide a DisplayHandle")
200                .as_raw()
201        });
202        let display_handle = match (instance_display_handle, display_handle) {
203            (Some(a), Some(b)) => {
204                if a != b {
205                    return Err(CreateSurfaceError::MismatchingDisplayHandle);
206                }
207                a
208            }
209            (Some(hnd), None) => hnd,
210            (None, Some(hnd)) => hnd,
211            (None, None) => return Err(CreateSurfaceError::MissingDisplayHandle),
212        };
213
214        let mut errors = HashMap::default();
215        let mut surface_per_backend = HashMap::default();
216
217        for (backend, instance) in &self.instance_per_backend {
218            match unsafe {
219                instance
220                    .as_ref()
221                    .create_surface(display_handle, window_handle)
222            } {
223                Ok(raw) => {
224                    surface_per_backend.insert(*backend, raw);
225                }
226                Err(err) => {
227                    log::debug!(
228                        "Instance::create_surface: failed to create surface for {backend:?}: {err:?}"
229                    );
230                    errors.insert(*backend, err);
231                }
232            }
233        }
234
235        if surface_per_backend.is_empty() {
236            Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
237                errors,
238            ))
239        } else {
240            let surface = Arc::new(Surface {
241                presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
242                surface_per_backend,
243            });
244
245            Ok(surface)
246        }
247    }
248
249    /// Creates a new surface from the given drm configuration.
250    ///
251    /// # Safety
252    ///
253    /// - All parameters must point to valid DRM values.
254    ///
255    /// # Platform Support
256    ///
257    /// This function requires the `"drm"` feature. It is only available on
258    /// non-apple Unix-like platforms (Linux, FreeBSD) and currently only works
259    /// with the Vulkan backend.
260    #[cfg(drm)]
261    #[cfg_attr(not(vulkan), expect(unused_variables, unused_mut))]
262    pub unsafe fn create_surface_from_drm(
263        &self,
264        fd: i32,
265        plane: u32,
266        connector_id: u32,
267        width: u32,
268        height: u32,
269        refresh_rate: u32,
270    ) -> Result<Arc<Surface>, CreateSurfaceError> {
271        profiling::scope!("Instance::create_surface_from_drm");
272
273        let mut errors = HashMap::default();
274        let mut surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>> =
275            HashMap::default();
276
277        #[cfg(vulkan)]
278        {
279            let instance = unsafe { self.as_hal::<hal::api::Vulkan>() }
280                .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Vulkan))?;
281
282            // Safety must be upheld by the caller
283            match unsafe {
284                instance.create_surface_from_drm(
285                    fd,
286                    plane,
287                    connector_id,
288                    width,
289                    height,
290                    refresh_rate,
291                )
292            } {
293                Ok(surface) => {
294                    surface_per_backend.insert(Backend::Vulkan, Box::new(surface));
295                }
296                Err(err) => {
297                    errors.insert(Backend::Vulkan, err);
298                }
299            }
300        }
301
302        if surface_per_backend.is_empty() {
303            Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
304                errors,
305            ))
306        } else {
307            let surface = Arc::new(Surface {
308                presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
309                surface_per_backend,
310            });
311
312            Ok(surface)
313        }
314    }
315
316    /// # Safety
317    ///
318    /// `layer` must be a valid pointer.
319    #[cfg(metal)]
320    pub unsafe fn create_surface_metal(
321        &self,
322        layer: *mut core::ffi::c_void,
323    ) -> Result<Arc<Surface>, CreateSurfaceError> {
324        profiling::scope!("Instance::create_surface_metal");
325
326        let instance = unsafe { self.as_hal::<hal::api::Metal>() }
327            .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Metal))?;
328
329        let layer = layer.cast();
330        // SAFETY: We do this cast and deref. (rather than using `metal` to get the
331        // object we want) to avoid direct coupling on the `metal` crate.
332        //
333        // To wit, this pointer…
334        //
335        // - …is properly aligned.
336        // - …is dereferenceable to a `MetalLayerRef` as an invariant of the `metal`
337        //   field.
338        // - …points to an _initialized_ `MetalLayerRef`.
339        // - …is only ever aliased via an immutable reference that lives within this
340        //   lexical scope.
341        let layer = unsafe { &*layer };
342        let raw_surface: Box<dyn hal::DynSurface> =
343            Box::new(instance.create_surface_from_layer(layer));
344
345        let surface = Arc::new(Surface {
346            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
347            surface_per_backend: core::iter::once((Backend::Metal, raw_surface)).collect(),
348        });
349
350        Ok(surface)
351    }
352
353    #[cfg(dx12)]
354    fn create_surface_dx12(
355        &self,
356        create_surface_func: impl FnOnce(&hal::dx12::Instance) -> hal::dx12::Surface,
357    ) -> Result<Arc<Surface>, CreateSurfaceError> {
358        let instance = unsafe { self.as_hal::<hal::api::Dx12>() }
359            .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?;
360        let surface: Box<dyn hal::DynSurface> = Box::new(create_surface_func(instance));
361
362        let surface = Arc::new(Surface {
363            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
364            surface_per_backend: core::iter::once((Backend::Dx12, surface)).collect(),
365        });
366
367        Ok(surface)
368    }
369
370    #[cfg(dx12)]
371    /// # Safety
372    ///
373    /// The visual must be valid and able to be used to make a swapchain with.
374    pub unsafe fn create_surface_from_visual(
375        &self,
376        visual: *mut core::ffi::c_void,
377    ) -> Result<Arc<Surface>, CreateSurfaceError> {
378        profiling::scope!("Instance::instance_create_surface_from_visual");
379        self.create_surface_dx12(|inst| unsafe { inst.create_surface_from_visual(visual) })
380    }
381
382    #[cfg(dx12)]
383    /// # Safety
384    ///
385    /// The surface_handle must be valid and able to be used to make a swapchain with.
386    pub unsafe fn create_surface_from_surface_handle(
387        &self,
388        surface_handle: *mut core::ffi::c_void,
389    ) -> Result<Arc<Surface>, CreateSurfaceError> {
390        profiling::scope!("Instance::instance_create_surface_from_surface_handle");
391        self.create_surface_dx12(|inst| unsafe {
392            inst.create_surface_from_surface_handle(surface_handle)
393        })
394    }
395
396    #[cfg(dx12)]
397    /// # Safety
398    ///
399    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.
400    pub unsafe fn create_surface_from_swap_chain_panel(
401        &self,
402        swap_chain_panel: *mut core::ffi::c_void,
403    ) -> Result<Arc<Surface>, CreateSurfaceError> {
404        profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
405        self.create_surface_dx12(|inst| unsafe {
406            inst.create_surface_from_swap_chain_panel(swap_chain_panel)
407        })
408    }
409
410    fn adapter_allowed(&self, raw: &hal::DynExposedAdapter) -> bool {
411        adapter_allowed(
412            self.flags,
413            &raw.info,
414            &raw.capabilities.limits,
415            &raw.capabilities.downlevel,
416        )
417    }
418
419    pub fn enumerate_adapters(
420        self: &Arc<Self>,
421        backends: Backends,
422        apply_limit_buckets: bool,
423    ) -> Vec<Arc<Adapter>> {
424        profiling::scope!("Instance::enumerate_adapters");
425        api_log!("Instance::enumerate_adapters");
426
427        let mut adapters = Vec::new();
428        for (_backend, instance) in self
429            .instance_per_backend
430            .iter()
431            .filter(|(backend, _)| backends.contains(Backends::from(*backend)))
432        {
433            // NOTE: We might be using `profiling` without any features. The empty backend of this
434            // macro emits no code, so unused code linting changes depending on the backend.
435            profiling::scope!("enumerating", &*alloc::format!("{_backend:?}"));
436
437            let hal_adapters = unsafe { instance.enumerate_adapters(None) };
438
439            adapters.extend(
440                hal_adapters
441                    .into_iter()
442                    .map(|mut raw| {
443                        self.adjust_limits_for_indirect_validation(&mut raw.capabilities.limits);
444                        raw
445                    })
446                    .map(|mut raw| {
447                        filter_features_and_limits(
448                            self.flags,
449                            &mut raw.features,
450                            &mut raw.capabilities.limits,
451                        );
452                        raw
453                    })
454                    .filter(|raw| self.adapter_allowed(raw))
455                    .filter_map(|raw| {
456                        if apply_limit_buckets {
457                            limits::apply_limit_buckets(raw)
458                        } else {
459                            Some(raw)
460                        }
461                    })
462                    .map(|raw| {
463                        let adapter = Adapter::new(raw, self.clone());
464                        api_log_debug!("Adapter {:?}", adapter.raw.info);
465                        adapter
466                    }),
467            );
468        }
469        adapters
470    }
471
472    pub fn request_adapter(
473        self: &Arc<Self>,
474        desc: &wgt::RequestAdapterOptions<&Surface>,
475        backends: Backends,
476    ) -> Result<Arc<Adapter>, wgt::RequestAdapterError> {
477        profiling::scope!("Instance::request_adapter");
478        api_log!("Instance::request_adapter");
479
480        let mut adapters = Vec::new();
481        let mut incompatible_surface_backends = Backends::empty();
482        let mut no_fallback_backends = Backends::empty();
483        let mut no_adapter_backends = Backends::empty();
484
485        for &(backend, ref instance) in self
486            .instance_per_backend
487            .iter()
488            .filter(|&&(backend, _)| backends.contains(Backends::from(backend)))
489        {
490            let compatible_hal_surface = desc
491                .compatible_surface
492                .and_then(|surface| surface.raw(backend));
493
494            let mut backend_adapters =
495                unsafe { instance.enumerate_adapters(compatible_hal_surface) };
496            if backend_adapters.is_empty() {
497                log::debug!("enabled backend `{backend:?}` has no adapters");
498                no_adapter_backends |= Backends::from(backend);
499                // by continuing, we avoid setting the further error bits below
500                continue;
501            }
502
503            if desc.force_fallback_adapter {
504                log::debug!("Filtering `{backend:?}` for `force_fallback_adapter`");
505                backend_adapters.retain(|exposed| {
506                    let keep = exposed.info.device_type == wgt::DeviceType::Cpu;
507                    if !keep {
508                        log::debug!("* Eliminating adapter `{}`", exposed.info.name);
509                    }
510                    keep
511                });
512                if backend_adapters.is_empty() {
513                    log::debug!("* Backend `{backend:?}` has no fallback adapters");
514                    no_fallback_backends |= Backends::from(backend);
515                    continue;
516                }
517            }
518
519            if let Some(surface) = desc.compatible_surface {
520                backend_adapters.retain(|exposed| {
521                    let capabilities = surface.get_capabilities_with_raw(exposed);
522                    if let Err(err) = capabilities {
523                        log::debug!(
524                            "Adapter {:?} not compatible with surface: {}",
525                            exposed.info,
526                            err
527                        );
528                        incompatible_surface_backends |= Backends::from(backend);
529                        false
530                    } else {
531                        true
532                    }
533                });
534                if backend_adapters.is_empty() {
535                    incompatible_surface_backends |= Backends::from(backend);
536                    continue;
537                }
538            }
539
540            let backend_adapters = backend_adapters
541                .into_iter()
542                .map(|mut raw| {
543                    self.adjust_limits_for_indirect_validation(&mut raw.capabilities.limits);
544                    raw
545                })
546                .map(|mut raw| {
547                    filter_features_and_limits(
548                        self.flags,
549                        &mut raw.features,
550                        &mut raw.capabilities.limits,
551                    );
552                    raw
553                })
554                .filter(|raw| self.adapter_allowed(raw));
555
556            if desc.apply_limit_buckets {
557                adapters.extend(backend_adapters.filter_map(limits::apply_limit_buckets));
558            } else {
559                adapters.extend(backend_adapters);
560            }
561        }
562
563        match desc.power_preference {
564            PowerPreference::LowPower => {
565                sort(&mut adapters, true);
566            }
567            PowerPreference::HighPerformance => {
568                sort(&mut adapters, false);
569            }
570            PowerPreference::None => {}
571        };
572
573        fn sort(adapters: &mut [hal::DynExposedAdapter], prefer_integrated_gpu: bool) {
574            adapters
575                .sort_by_key(|adapter| get_order(adapter.info.device_type, prefer_integrated_gpu));
576        }
577
578        fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 {
579            // Since devices of type "Other" might really be "Unknown" and come
580            // from APIs like OpenGL that don't specify device type, Prefer more
581            // Specific types over Other.
582            //
583            // This means that backends which do provide accurate device types
584            // will be preferred if their device type indicates an actual
585            // hardware GPU (integrated or discrete).
586            match device_type {
587                wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2,
588                wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1,
589                wgt::DeviceType::DiscreteGpu => 1,
590                wgt::DeviceType::IntegratedGpu => 2,
591                wgt::DeviceType::Other => 3,
592                wgt::DeviceType::VirtualGpu => 4,
593                wgt::DeviceType::Cpu => 5,
594            }
595        }
596
597        // `request_adapter` can be a bit of a black box.
598        // Shine some light on its decision in debug log.
599        if adapters.is_empty() {
600            log::debug!("Request adapter didn't find compatible adapters.");
601        } else {
602            log::debug!(
603                "Found {} compatible adapters. Sorted by preference:",
604                adapters.len()
605            );
606            for adapter in &adapters {
607                log::debug!("* {:?}", adapter.info);
608            }
609        }
610
611        if let Some(adapter) = adapters.into_iter().next() {
612            api_log_debug!("Request adapter result {:?}", adapter.info);
613            let adapter = Adapter::new(adapter, self.clone());
614            Ok(adapter)
615        } else {
616            Err(wgt::RequestAdapterError::NotFound {
617                supported_backends: self.supported_backends,
618                requested_backends: self.requested_backends,
619                active_backends: self.active_backends(),
620                no_fallback_backends,
621                no_adapter_backends,
622                incompatible_surface_backends,
623            })
624        }
625    }
626
627    /// This is similar to wgpu-hal's `adjust_raw_limits` but tailored to
628    /// wgpu-core's constraints.
629    fn adjust_limits_for_indirect_validation(&self, limits: &mut wgt::Limits) {
630        // Indirect draw validation can't support u64 offsets,
631        // lower max buffer and binding size to fit in an u32.
632        if self.flags.contains(InstanceFlags::VALIDATION_INDIRECT_CALL) {
633            limits.max_buffer_size = limits.max_buffer_size.min(u32::MAX as u64);
634            limits.max_uniform_buffer_binding_size =
635                limits.max_uniform_buffer_binding_size.min(u32::MAX as u64);
636            limits.max_storage_buffer_binding_size = limits
637                .max_storage_buffer_binding_size
638                .min(u32::MAX as u64 & !(wgt::STORAGE_BINDING_SIZE_ALIGNMENT as u64 - 1));
639        }
640    }
641
642    fn active_backends(&self) -> Backends {
643        self.instance_per_backend
644            .iter()
645            .map(|&(backend, _)| Backends::from(backend))
646            .collect()
647    }
648
649    /// Create an adapter from a HAL adapter.
650    ///
651    /// The HAL adapter may be obtained e.g. by calling `enumerate_adapters` on
652    /// the HAL directly.
653    ///
654    /// If [limit bucketing][lt] is desired, [`crate::limits::apply_limit_buckets`]
655    /// should be called with the HAL adapter before calling this function.
656    ///
657    /// # Safety
658    ///
659    /// `hal_adapter` must be created from this global internal instance handle.
660    ///
661    /// [lt]: crate::limits#Limit-bucketing
662    pub unsafe fn create_adapter_from_hal(
663        self: &Arc<Self>,
664        hal_adapter: hal::DynExposedAdapter,
665    ) -> Arc<Adapter> {
666        profiling::scope!("Instance::create_adapter_from_hal");
667
668        let adapter = Adapter::new(hal_adapter, self.clone());
669
670        resource_log!("Created Adapter {:?}", Arc::as_ptr(&adapter));
671        adapter
672    }
673
674    /// Poll all devices on all backends.
675    ///
676    /// This is the implementation of `wgpu::Instance::poll_all`.
677    ///
678    /// Return `all_queue_empty` indicating whether there are more queue
679    /// submissions still in flight.
680    pub fn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {
681        api_log!("poll_all_devices");
682        let mut closures = UserClosures::default();
683        let all_queue_empty = self.devices.poll_all_devices(force_wait, &mut closures)?;
684
685        closures.fire();
686
687        Ok(all_queue_empty)
688    }
689}
690
691pub struct InstanceBuilder {
692    instance: Instance,
693    instance_desc: wgt::InstanceDescriptor,
694    telemetry: Option<hal::Telemetry>,
695}
696
697impl InstanceBuilder {
698    pub fn new(
699        name: &str,
700        mut desc: wgt::InstanceDescriptor,
701        telemetry: Option<hal::Telemetry>,
702    ) -> Self {
703        Self {
704            instance: Instance {
705                _name: name.to_owned(),
706                instance_per_backend: Vec::new(),
707                requested_backends: desc.backends,
708                supported_backends: Backends::empty(),
709                flags: desc.flags,
710                // HACK: We must take ownership of the field here, without being able to pass it into
711                // try_add_hal(). Remove it from the mutable descriptor instead, while try_add_hal()
712                // borrows the handle from `this.display` instead.
713                display: desc.display.take(),
714                devices: InstanceDevices::new(),
715            },
716            instance_desc: desc,
717            telemetry,
718        }
719    }
720
721    pub fn try_add_hal<A: hal::Api>(&mut self, api: A) {
722        use hal::Instance as _;
723        self.try_add_custom_hal(api, |hal_desc| unsafe { A::Instance::init(hal_desc) });
724    }
725
726    pub fn try_add_custom_hal<A: hal::Api>(
727        &mut self,
728        _: A,
729        f: impl FnOnce(&hal::InstanceDescriptor) -> Result<A::Instance, hal::InstanceError>,
730    ) {
731        if self.instance.supported_backends.contains(A::VARIANT.into()) {
732            return;
733        }
734
735        // Whether or not the backend was requested, and whether or not it succeeds,
736        // note that we *could* try it.
737        self.instance.supported_backends |= A::VARIANT.into();
738
739        if !self.instance_desc.backends.contains(A::VARIANT.into()) {
740            log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
741            return;
742        }
743
744        // If this was Some, it was moved into self
745        assert!(self.instance_desc.display.is_none());
746
747        let hal_desc = hal::InstanceDescriptor {
748            name: "wgpu",
749            flags: self.instance.flags,
750            memory_budget_thresholds: self.instance_desc.memory_budget_thresholds,
751            backend_options: self.instance_desc.backend_options.clone(),
752            telemetry: self.telemetry,
753            // Pass a borrow, the core instance here keeps the owned handle alive already
754            // WARNING: Using self here, not instance_desc!
755            display: self.instance.display.as_ref().map(|hdh| {
756                hdh.display_handle()
757                    .expect("Implementation did not provide a DisplayHandle")
758            }),
759        };
760
761        match f(&hal_desc) {
762            Ok(instance) => {
763                log::debug!("Instance::new: created {:?} backend", A::VARIANT);
764                self.instance
765                    .instance_per_backend
766                    .push((A::VARIANT, Box::new(instance)));
767            }
768            Err(err) => {
769                log::debug!(
770                    "Instance::new: failed to create {:?} backend: {:?}",
771                    A::VARIANT,
772                    err
773                );
774            }
775        }
776    }
777
778    pub fn build(self) -> Arc<Instance> {
779        Arc::new(self.instance)
780    }
781}
782
783pub struct Surface {
784    pub(crate) presentation: Mutex<Option<Presentation>>,
785    pub surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>>,
786}
787
788impl ResourceType for Surface {
789    const TYPE: &'static str = "Surface";
790}
791impl crate::storage::StorageItem for Surface {
792    type Marker = markers::Surface;
793}
794
795impl Surface {
796    pub fn get_capabilities(
797        &self,
798        adapter: &Adapter,
799    ) -> Result<wgt::SurfaceCapabilities, GetSurfaceSupportError> {
800        profiling::scope!("Surface::get_capabilities");
801        let mut hal_caps = self.get_hal_capabilities(adapter)?;
802
803        hal_caps
804            .formats
805            .sort_by_key(|fc| !fc.format.has_srgb_suffix());
806
807        let usages = crate::conv::map_texture_usage_from_hal(hal_caps.usage);
808
809        // `SurfaceCapabilities::formats` lists only the formats a
810        // color-space-unaware application can configure via
811        // `SurfaceColorSpace::Auto`, i.e. those for which `Auto` resolves to a
812        // concrete color space. (The full `format_capabilities` still reports
813        // every color space, including HDR ones, for explicit opt-in.)
814        Ok(wgt::SurfaceCapabilities {
815            formats: hal_caps
816                .formats
817                .iter()
818                .filter(|fc| {
819                    crate::device::surface_config::resolve_auto_color_space(
820                        fc.format,
821                        fc.color_spaces,
822                    )
823                    .is_some()
824                })
825                .map(|fc| fc.format)
826                .collect(),
827            format_capabilities: hal_caps.formats,
828            present_modes: hal_caps.present_modes,
829            alpha_modes: hal_caps.composite_alpha_modes,
830            usages,
831        })
832    }
833
834    pub fn get_hal_capabilities(
835        &self,
836        adapter: &Adapter,
837    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
838        self.get_capabilities_with_raw(&adapter.raw)
839    }
840
841    pub fn get_capabilities_with_raw(
842        &self,
843        adapter: &hal::DynExposedAdapter,
844    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
845        let backend = adapter.backend();
846        let suf = self
847            .raw(backend)
848            .ok_or(GetSurfaceSupportError::NotSupportedByBackend(backend))?;
849        profiling::scope!("surface_capabilities");
850        let caps = unsafe { adapter.adapter.surface_capabilities(suf) }
851            .ok_or(GetSurfaceSupportError::FailedToRetrieveSurfaceCapabilitiesForAdapter)?;
852        Ok(caps)
853    }
854
855    /// Returns the HDR / luminance characteristics of the display backing this
856    /// surface on `adapter`.
857    ///
858    /// Falls back to [`wgt::DisplayHdrInfo::default`] (all fields `None`) when the
859    /// surface is not on `adapter`'s backend or the backend reports nothing.
860    pub fn display_hdr_info(&self, adapter: &Adapter) -> wgt::DisplayHdrInfo {
861        profiling::scope!("Surface::display_hdr_info");
862        self.display_hdr_info_with_raw(&adapter.raw)
863    }
864
865    pub fn display_hdr_info_with_raw(
866        &self,
867        adapter: &hal::DynExposedAdapter,
868    ) -> wgt::DisplayHdrInfo {
869        let backend = adapter.backend();
870        let Some(suf) = self.raw(backend) else {
871            return wgt::DisplayHdrInfo::default();
872        };
873        profiling::scope!("surface_display_hdr_info");
874        unsafe { adapter.adapter.surface_display_hdr_info(suf) }.unwrap_or_default()
875    }
876
877    pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynSurface> {
878        self.surface_per_backend
879            .get(&backend)
880            .map(|surface| surface.as_ref())
881    }
882
883    fn unconfigure_inner<'a>(
884        &self,
885        presentation: &mut MutexGuard<'a, Option<Presentation>>,
886    ) -> UserClosures {
887        let mut result = UserClosures::default();
888        if let Some(mut present) = presentation.take() {
889            if let Some(texture) = present.acquired_texture.take() {
890                texture.destroy();
891            }
892
893            let user_callbacks;
894            {
895                // Wait for all work that uses the surface texture to finish
896                let snatch_guard = present.device.snatchable_lock.read();
897
898                let result;
899                (user_callbacks, result) = present
900                    .device
901                    .maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
902                match result {
903                    Ok(_) => {}
904                    Err(WaitIdleError::Device(_)) => {
905                        // we can ignore device lost errors here, since we are just cleaning up
906                    }
907                    Err(WaitIdleError::Timeout) if cfg!(target_family = "wasm") => {
908                        // On wasm, you cannot actually successfully wait for the surface.
909                        // However WebGL does not actually require you do this, so ignoring
910                        // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
911                    }
912                    Err(WaitIdleError::Timeout) => {
913                        unreachable!("wait_indefinitely() should never timeout")
914                    }
915                    Err(WaitIdleError::WrongSubmissionIndex(_, _)) => {
916                        unreachable!("no submission index was provided")
917                    }
918                }
919            }
920            result.extend(user_callbacks);
921
922            for (&backend, surface) in &self.surface_per_backend {
923                if backend == present.device.backend() {
924                    unsafe { surface.unconfigure(present.device.raw()) };
925                }
926            }
927        }
928        result
929    }
930
931    pub fn unconfigure(self: &Arc<Self>) {
932        profiling::scope!("Surface::unconfigure");
933        let user_callbacks;
934        {
935            let mut presentation = self.presentation.lock();
936            user_callbacks = self.unconfigure_inner(&mut presentation);
937        }
938        user_callbacks.fire();
939    }
940
941    pub fn configure(
942        self: &Arc<Self>,
943        device: &Arc<Device>,
944        config: &wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
945    ) -> Result<(), ConfigureSurfaceError> {
946        use ConfigureSurfaceError as E;
947        profiling::scope!("Surface::configure");
948
949        #[cfg(feature = "trace")]
950        if let Some(ref mut trace) = *device.trace.lock() {
951            use crate::device::trace::{Action, IntoTrace};
952
953            trace.add(Action::ConfigureSurface(self.to_trace(), config.clone()));
954        }
955
956        log::debug!("configuring surface with {config:?}");
957
958        let caps = self
959            .get_hal_capabilities(&device.adapter)
960            .map_err(|_| E::UnsupportedQueueFamily)?;
961
962        let mut hal_view_formats = Vec::new();
963        for format in config.view_formats.iter() {
964            if *format == config.format {
965                continue;
966            }
967            if !caps.formats.iter().any(|fc| fc.format == config.format) {
968                return Err(E::UnsupportedFormat {
969                    requested: config.format,
970                    available: caps.texture_formats().collect(),
971                });
972            }
973            if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
974                return Err(E::InvalidViewFormat(*format, config.format));
975            }
976            hal_view_formats.push(*format);
977        }
978
979        if !hal_view_formats.is_empty() {
980            device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)?;
981        }
982
983        let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
984            *caps.maximum_frame_latency.start(),
985            *caps.maximum_frame_latency.end(),
986        );
987        let mut hal_config = hal::SurfaceConfiguration {
988            maximum_frame_latency,
989            present_mode: config.present_mode,
990            composite_alpha_mode: config.alpha_mode,
991            format: config.format,
992            color_space: config.color_space,
993            extent: wgt::Extent3d {
994                width: config.width,
995                height: config.height,
996                depth_or_array_layers: 1,
997            },
998            usage: crate::conv::map_texture_usage(
999                config.usage,
1000                hal::FormatAspects::COLOR,
1001                wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
1002                    | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
1003                    | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1004            ),
1005            view_formats: hal_view_formats,
1006        };
1007
1008        crate::device::surface_config::validate_surface_configuration(
1009            &mut hal_config,
1010            &caps,
1011            device.limits.max_texture_dimension_2d,
1012        )?;
1013
1014        device.check_is_valid()?;
1015
1016        let user_callbacks;
1017        {
1018            // we keep presentation locked for the entire duration of the configure call,
1019            // so that no change can happen in the middle of it.
1020            let mut presentation = self.presentation.lock();
1021
1022            user_callbacks = self.unconfigure_inner(&mut presentation);
1023
1024            let surface_raw = self.raw(device.backend()).unwrap();
1025            match unsafe { surface_raw.configure(device.raw(), &hal_config) } {
1026                Ok(()) => (),
1027                Err(error) => {
1028                    return Err(match error {
1029                        hal::SurfaceError::Outdated
1030                        | hal::SurfaceError::Lost
1031                        | hal::SurfaceError::Occluded
1032                        | hal::SurfaceError::Timeout => E::InvalidSurface,
1033                        hal::SurfaceError::Device(error) => {
1034                            E::Device(device.handle_hal_error(error))
1035                        }
1036                        hal::SurfaceError::Other(message) => {
1037                            log::error!("surface configuration failed: {message}");
1038                            E::InvalidSurface
1039                        }
1040                    });
1041                }
1042            }
1043
1044            *presentation = Some(Presentation {
1045                device: Arc::clone(device),
1046                config: config.clone(),
1047                acquired_texture: None,
1048            });
1049        }
1050        user_callbacks.fire();
1051
1052        Ok(())
1053    }
1054}
1055
1056impl Drop for Surface {
1057    #[allow(trivial_casts)]
1058    fn drop(&mut self) {
1059        profiling::scope!("Surface::drop");
1060
1061        api_log!("Surface::drop {:?}", self as *const _);
1062        let user_closures;
1063        {
1064            let mut presentation = self.presentation.lock();
1065            user_closures = self.unconfigure_inner(&mut presentation);
1066        }
1067        user_closures.fire();
1068    }
1069}
1070
1071pub struct Adapter {
1072    pub(crate) raw: hal::DynExposedAdapter,
1073    pub(crate) instance: Arc<Instance>,
1074    consumed: AtomicBool,
1075}
1076
1077impl Adapter {
1078    pub(crate) fn new(raw: hal::DynExposedAdapter, instance: Arc<Instance>) -> Arc<Self> {
1079        Arc::new(Self {
1080            raw,
1081            instance,
1082            consumed: AtomicBool::new(false),
1083        })
1084    }
1085
1086    /// Returns the backend this adapter is using.
1087    pub fn backend(&self) -> Backend {
1088        self.raw.backend()
1089    }
1090
1091    pub fn is_surface_supported(&self, surface: &Surface) -> bool {
1092        // If get_capabilities returns Err, then the API does not advertise support for the surface.
1093        //
1094        // This could occur if the user is running their app on Wayland but Vulkan does not support
1095        // VK_KHR_wayland_surface.
1096        surface.get_hal_capabilities(self).is_ok()
1097    }
1098
1099    pub fn get_info(&self) -> wgt::AdapterInfo {
1100        self.raw.info.clone()
1101    }
1102
1103    pub fn features(&self) -> wgt::Features {
1104        self.raw.features
1105    }
1106
1107    pub fn limits(&self) -> wgt::Limits {
1108        self.raw.capabilities.limits.clone()
1109    }
1110
1111    pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
1112        self.raw.capabilities.downlevel.clone()
1113    }
1114
1115    pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1116        unsafe { self.raw.adapter.get_presentation_timestamp() }
1117    }
1118
1119    pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1120        self.raw.capabilities.cooperative_matrix_properties.clone()
1121    }
1122
1123    pub fn get_texture_format_features(
1124        &self,
1125        format: wgt::TextureFormat,
1126    ) -> wgt::TextureFormatFeatures {
1127        use hal::TextureFormatCapabilities as Tfc;
1128
1129        let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
1130        let mut allowed_usages = wgt::TextureUsages::empty();
1131
1132        allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
1133        allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
1134        allowed_usages.set(
1135            wgt::TextureUsages::TEXTURE_BINDING,
1136            caps.contains(Tfc::SAMPLED),
1137        );
1138        allowed_usages.set(
1139            wgt::TextureUsages::STORAGE_BINDING,
1140            caps.intersects(
1141                Tfc::STORAGE_WRITE_ONLY
1142                    | Tfc::STORAGE_READ_ONLY
1143                    | Tfc::STORAGE_READ_WRITE
1144                    | Tfc::STORAGE_ATOMIC,
1145            ),
1146        );
1147        allowed_usages.set(
1148            wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT_ATTACHMENT,
1149            caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
1150        );
1151        allowed_usages.set(
1152            wgt::TextureUsages::STORAGE_ATOMIC,
1153            caps.contains(Tfc::STORAGE_ATOMIC),
1154        );
1155
1156        let mut flags = wgt::TextureFormatFeatureFlags::empty();
1157        flags.set(
1158            wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
1159            caps.contains(Tfc::STORAGE_READ_ONLY),
1160        );
1161        flags.set(
1162            wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
1163            caps.contains(Tfc::STORAGE_WRITE_ONLY),
1164        );
1165        flags.set(
1166            wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1167            caps.contains(Tfc::STORAGE_READ_WRITE),
1168        );
1169
1170        flags.set(
1171            wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
1172            caps.contains(Tfc::STORAGE_ATOMIC),
1173        );
1174
1175        flags.set(
1176            wgt::TextureFormatFeatureFlags::FILTERABLE,
1177            caps.contains(Tfc::SAMPLED_LINEAR),
1178        );
1179
1180        flags.set(
1181            wgt::TextureFormatFeatureFlags::BLENDABLE,
1182            caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
1183        );
1184
1185        flags.set(
1186            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
1187            caps.contains(Tfc::MULTISAMPLE_X2),
1188        );
1189        flags.set(
1190            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
1191            caps.contains(Tfc::MULTISAMPLE_X4),
1192        );
1193        flags.set(
1194            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
1195            caps.contains(Tfc::MULTISAMPLE_X8),
1196        );
1197        flags.set(
1198            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1199            caps.contains(Tfc::MULTISAMPLE_X16),
1200        );
1201
1202        flags.set(
1203            wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
1204            caps.contains(Tfc::MULTISAMPLE_RESOLVE),
1205        );
1206
1207        wgt::TextureFormatFeatures {
1208            allowed_usages,
1209            flags,
1210        }
1211    }
1212
1213    /// # Safety
1214    ///
1215    /// - `hal_device` must be created from this adapter.
1216    /// - `desc` must be a subset of `hal_device` features and limits.
1217    pub unsafe fn create_device_and_queue_from_hal(
1218        self: &Arc<Self>,
1219        hal_device: hal::DynOpenDevice,
1220        desc: &DeviceDescriptor,
1221    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1222        profiling::scope!("Adapter::create_device_and_queue_from_hal");
1223        api_log!("Adapter::create_device_and_queue_from_hal");
1224
1225        if self
1226            .instance
1227            .flags
1228            .contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
1229            && self.consumed.load(Ordering::SeqCst)
1230        {
1231            return Err(RequestDeviceError::AdapterConsumed);
1232        }
1233
1234        let default_queue_desc = desc.default_queue.clone();
1235
1236        let device = Device::new(hal_device.device, self, desc, self.instance.flags)?;
1237        let device = Arc::new(device);
1238
1239        let queue = Queue::new(
1240            device.clone(),
1241            hal_device.queue,
1242            default_queue_desc,
1243            self.instance.flags,
1244        )?;
1245        let queue = Arc::new(queue);
1246
1247        device.set_queue(&queue);
1248        device.late_init_resources_with_queue()?;
1249
1250        resource_log!("Created Device {:?}", Arc::as_ptr(&device));
1251        resource_log!("Created Queue {:?}", Arc::as_ptr(&queue));
1252
1253        self.instance.devices.push(&device);
1254
1255        self.consumed.store(true, Ordering::SeqCst);
1256
1257        Ok((device, queue))
1258    }
1259
1260    /// Validate a device descriptor.
1261    ///
1262    /// This validates the provided device descriptor as if it were passed to
1263    /// [`Self::request_device`]. If [`InstanceFlags::STRICT_WEBGPU_COMPLIANCE`] is active,
1264    /// the requested extensions in the descriptor will be filtered to remove `wgpu`
1265    /// extensions, except for those that are included in [`limits::EXEMPT_FEATURES`].
1266    ///
1267    /// This may be useful when it is necessary to obtain the device itself from a raw hal
1268    /// API, but the rest of the `request_device` validation is still desired.
1269    pub fn validate_device_descriptor(
1270        &self,
1271        desc: &mut DeviceDescriptor,
1272    ) -> Result<(), RequestDeviceError> {
1273        filter_features_and_limits(
1274            self.instance.flags,
1275            &mut desc.required_features,
1276            &mut desc.required_limits,
1277        );
1278
1279        // Verify all features were exposed by the adapter
1280        if !self.raw.features.contains(desc.required_features) {
1281            return Err(RequestDeviceError::UnsupportedFeature(
1282                desc.required_features - self.raw.features,
1283            ));
1284        }
1285
1286        // Check if experimental features are permitted to be enabled.
1287        if desc
1288            .required_features
1289            .intersects(wgt::Features::all_experimental_mask())
1290            && !desc.experimental_features.is_enabled()
1291        {
1292            return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
1293                desc.required_features
1294                    .intersection(wgt::Features::all_experimental_mask()),
1295            ));
1296        }
1297
1298        let caps = &self.raw.capabilities;
1299        if Backends::PRIMARY.contains(Backends::from(self.backend()))
1300            && !caps.downlevel.is_webgpu_compliant()
1301        {
1302            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
1303            log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
1304            log::warn!("{:#?}", caps.downlevel);
1305        }
1306
1307        // Verify feature preconditions
1308        if desc
1309            .required_features
1310            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1311            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
1312        {
1313            log::warn!(
1314                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
1315                        This is a massive performance footgun and likely not what you wanted"
1316            );
1317        }
1318
1319        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
1320            return Err(RequestDeviceError::LimitsExceeded(failed));
1321        }
1322
1323        normalize_max_resource_per_shader_stage_limits(&mut desc.required_limits);
1324
1325        Ok(())
1326    }
1327
1328    pub fn request_device(
1329        self: &Arc<Self>,
1330        desc: &DeviceDescriptor,
1331    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1332        profiling::scope!("Adapter::request_device");
1333        api_log!("Adapter::request_device");
1334
1335        let mut desc = desc.clone();
1336        self.validate_device_descriptor(&mut desc)?;
1337
1338        let open = unsafe {
1339            self.raw.adapter.open(
1340                desc.required_features,
1341                &desc.required_limits,
1342                &desc.memory_hints,
1343            )
1344        }
1345        .map_err(DeviceError::from_hal)?;
1346
1347        unsafe { self.create_device_and_queue_from_hal(open, &desc) }
1348    }
1349}
1350
1351impl Drop for Adapter {
1352    #[allow(trivial_casts)]
1353    fn drop(&mut self) {
1354        profiling::scope!("Adapter::drop");
1355        api_log!("Adapter::drop {:?}", self as *const _);
1356    }
1357}
1358
1359crate::impl_resource_type!(Adapter);
1360crate::impl_storage_item!(Adapter);
1361
1362#[derive(Clone, Debug, Error)]
1363#[non_exhaustive]
1364pub enum GetSurfaceSupportError {
1365    #[error("Surface is not supported for the specified backend {0}")]
1366    NotSupportedByBackend(Backend),
1367    #[error("Failed to retrieve surface capabilities for the specified adapter.")]
1368    FailedToRetrieveSurfaceCapabilitiesForAdapter,
1369}
1370
1371#[derive(Clone, Debug, Error)]
1372/// Error when requesting a device from the adapter
1373#[non_exhaustive]
1374pub enum RequestDeviceError {
1375    #[error(transparent)]
1376    Device(#[from] DeviceError),
1377    #[error(transparent)]
1378    LimitsExceeded(#[from] FailedLimit),
1379    #[error("Failed to initialize Timestamp Normalizer")]
1380    TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
1381    #[error("Unsupported features were requested: {0}")]
1382    UnsupportedFeature(wgt::Features),
1383    #[error(
1384        "Some experimental features, {0}, were requested, but experimental features are not enabled"
1385    )]
1386    ExperimentalFeaturesNotEnabled(wgt::Features),
1387    #[error("The adapter has already been consumed.")]
1388    AdapterConsumed,
1389}
1390
1391#[derive(Clone, Debug, Error)]
1392#[non_exhaustive]
1393pub enum CreateSurfaceError {
1394    #[error("The backend {0} was not enabled on the instance.")]
1395    BackendNotEnabled(Backend),
1396    #[error("Failed to create surface for any enabled backend: {0:?}")]
1397    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
1398    #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
1399    MismatchingDisplayHandle,
1400    #[error(
1401        "No `DisplayHandle` is available to create this surface with.  When creating a surface with `create_surface()` \
1402        you must specify a display handle in `InstanceDescriptor::display`.  \
1403        Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
1404        you must use `create_surface_unsafe()`."
1405    )]
1406    MissingDisplayHandle,
1407}
1408
1409/// This function checks that the adapter obeys WebGPU's adapter capability
1410/// guarantees. Most of the limits are adjusted in wgpu-hal's
1411/// `adjust_raw_limits` fn. So we only check the remaining properties here.
1412/// See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>.
1413fn adapter_allowed(
1414    flags: InstanceFlags,
1415    info: &impl fmt::Debug,
1416    limits: &wgt::Limits,
1417    downlevel: &wgt::DownlevelCapabilities,
1418) -> bool {
1419    // Check "All alignment-class limits must be powers of 2."
1420    //
1421    // Even if the application has not requested strict WebGPU compliance,
1422    // non-power-of-two alignment limits are nonsensical, so don't attempt
1423    // to use such a device.
1424    let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1425    if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1426        log::error!(
1427            "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1428            info,
1429            min_uniform_buffer_offset_alignment
1430        );
1431        return false;
1432    }
1433    let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1434    if !min_storage_buffer_offset_alignment.is_power_of_two() {
1435        log::error!(
1436            "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1437            info,
1438            min_storage_buffer_offset_alignment
1439        );
1440        return false;
1441    }
1442
1443    // Following checks are only enabled if `STRICT_WEBGPU_COMPLIANCE` is set.
1444    if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1445        return true;
1446    }
1447
1448    // Check "All supported limits must be either the default value or better."
1449    let mut min_limits = wgt::Limits::defaults();
1450    min_limits.zero_native_only();
1451    let failed_limits = check_limits(&min_limits, limits);
1452    if !failed_limits.is_empty() {
1453        log::debug!(
1454            "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1455            info,
1456            failed_limits
1457        );
1458        return false;
1459    }
1460
1461    if !downlevel.is_webgpu_compliant() {
1462        let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1463        log::debug!(
1464            "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1465            info,
1466            missing_flags
1467        );
1468        return false;
1469    }
1470
1471    true
1472}
1473
1474fn filter_features_and_limits(
1475    flags: InstanceFlags,
1476    features: &mut wgt::Features,
1477    limits: &mut wgt::Limits,
1478) {
1479    if flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1480        *features &= wgt::Features::all_webgpu_mask() | limits::EXEMPT_FEATURES;
1481        limits.zero_native_only();
1482    }
1483}
1484
1485fn normalize_max_resource_per_shader_stage_limits(limits: &mut wgt::Limits) {
1486    // The next steps are from <https://www.w3.org/TR/webgpu/#a-new-device>.
1487
1488    // > 7. Set `limits.maxStorageBuffersPerShaderStage` to
1489    // >    `max(limits.maxStorageBuffersPerShaderStage, limits.maxStorageBuffersInVertexStage,
1490    // >    limits.maxStorageBuffersInFragmentStage)`.
1491
1492    limits.max_storage_buffers_per_shader_stage = [
1493        limits.max_storage_buffers_per_shader_stage,
1494        limits.max_storage_buffers_in_vertex_stage,
1495        limits.max_storage_buffers_in_fragment_stage,
1496    ]
1497    .into_iter()
1498    .max()
1499    .unwrap();
1500
1501    // > 8. Set `limits.maxStorageTexturesPerShaderStage` to
1502    // >    `max(limits.maxStorageTexturesPerShaderStage, limits.maxStorageTexturesInVertexStage,
1503    // >    limits.maxStorageTexturesInFragmentStage)`.
1504
1505    limits.max_storage_textures_per_shader_stage = [
1506        limits.max_storage_textures_per_shader_stage,
1507        limits.max_storage_textures_in_vertex_stage,
1508        limits.max_storage_textures_in_fragment_stage,
1509    ]
1510    .into_iter()
1511    .max()
1512    .unwrap();
1513
1514    // > 9. If features contains "core-features-and-limits":
1515    //
1516    // NOTE: We don't implement compat (yet?), so we do this unconditionally. See also:
1517    // <https://github.com/gfx-rs/wgpu/issues/8124>
1518
1519    // >   1. Set `limits.maxStorageBuffersInVertexStage` and
1520    // >      `limits.maxStorageBuffersInFragmentStage` to
1521    // >      `limits.maxStorageBuffersPerShaderStage`.
1522    limits.max_storage_buffers_in_vertex_stage = limits.max_storage_buffers_per_shader_stage;
1523    limits.max_storage_buffers_in_fragment_stage = limits.max_storage_buffers_per_shader_stage;
1524
1525    // >   2. Set `limits.maxStorageTexturesInVertexStage` and
1526    // >      `limits.maxStorageTexturesInFragmentStage` to
1527    // >      `limits.maxStorageTexturesPerShaderStage`.
1528    limits.max_storage_textures_in_vertex_stage = limits.max_storage_textures_per_shader_stage;
1529    limits.max_storage_textures_in_fragment_stage = limits.max_storage_textures_per_shader_stage;
1530}
1531
1532#[cfg(test)]
1533mod tests {
1534    use super::*;
1535
1536    fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1537        wgt::DownlevelCapabilities {
1538            flags: wgt::DownlevelFlags::compliant(),
1539            ..Default::default()
1540        }
1541    }
1542
1543    #[test]
1544    fn non_power_of_two_uniform_alignment_always_rejected() {
1545        let limits = wgt::Limits {
1546            min_uniform_buffer_offset_alignment: 3,
1547            ..wgt::Limits::defaults()
1548        };
1549        assert!(!adapter_allowed(
1550            InstanceFlags::empty(),
1551            &"",
1552            &limits,
1553            &compliant_downlevel()
1554        ));
1555        assert!(!adapter_allowed(
1556            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1557            &"",
1558            &limits,
1559            &compliant_downlevel()
1560        ));
1561    }
1562
1563    #[test]
1564    fn non_power_of_two_storage_alignment_always_rejected() {
1565        let limits = wgt::Limits {
1566            min_storage_buffer_offset_alignment: 96,
1567            ..wgt::Limits::defaults()
1568        };
1569        assert!(!adapter_allowed(
1570            InstanceFlags::empty(),
1571            &"",
1572            &limits,
1573            &compliant_downlevel()
1574        ));
1575        assert!(!adapter_allowed(
1576            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1577            &"",
1578            &limits,
1579            &compliant_downlevel()
1580        ));
1581    }
1582
1583    #[test]
1584    fn low_limits_allowed_without_strict_compliance() {
1585        let limits = wgt::Limits {
1586            max_texture_dimension_1d: 1,
1587            ..wgt::Limits::defaults()
1588        };
1589        assert!(adapter_allowed(
1590            InstanceFlags::empty(),
1591            &"",
1592            &limits,
1593            &wgt::DownlevelCapabilities::default()
1594        ));
1595    }
1596
1597    #[test]
1598    fn low_limits_rejected_with_strict_compliance() {
1599        let limits = wgt::Limits {
1600            max_texture_dimension_1d: 1,
1601            ..wgt::Limits::defaults()
1602        };
1603        assert!(!adapter_allowed(
1604            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1605            &"",
1606            &limits,
1607            &compliant_downlevel()
1608        ));
1609    }
1610
1611    #[test]
1612    fn missing_downlevel_flags_rejected_with_strict_compliance() {
1613        let downlevel = wgt::DownlevelCapabilities {
1614            flags: wgt::DownlevelFlags::empty(),
1615            ..Default::default()
1616        };
1617        assert!(!adapter_allowed(
1618            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1619            &"",
1620            &wgt::Limits::defaults(),
1621            &downlevel
1622        ));
1623    }
1624
1625    #[test]
1626    fn missing_surface_view_formats_allowed_with_strict_compliance() {
1627        let downlevel = wgt::DownlevelCapabilities {
1628            flags: wgt::DownlevelFlags::all() - wgt::DownlevelFlags::SURFACE_VIEW_FORMATS,
1629            ..Default::default()
1630        };
1631        assert!(adapter_allowed(
1632            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1633            &"",
1634            &wgt::Limits::defaults(),
1635            &downlevel
1636        ));
1637    }
1638
1639    #[test]
1640    fn fully_compliant_adapter_always_allowed() {
1641        assert!(adapter_allowed(
1642            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1643            &"",
1644            &wgt::Limits::defaults(),
1645            &compliant_downlevel()
1646        ));
1647    }
1648
1649    mod storage_resource_limits {
1650        use super::*;
1651
1652        #[track_caller]
1653        fn assert_normalized_eq(non_normalized: &wgt::Limits, expected: &wgt::Limits) {
1654            let mut normalized = non_normalized.clone();
1655            normalize_max_resource_per_shader_stage_limits(&mut normalized);
1656            assert_eq!(&normalized, expected);
1657        }
1658
1659        #[test]
1660        fn normalization_is_idempotent() {
1661            let original = wgt::Limits {
1662                max_storage_buffers_in_vertex_stage: 16,
1663                max_storage_textures_in_vertex_stage: 9,
1664                ..wgt::Limits::defaults()
1665            };
1666
1667            let mut first_normalization = original.clone();
1668            normalize_max_resource_per_shader_stage_limits(&mut first_normalization);
1669            assert_ne!(original, first_normalization);
1670
1671            let mut second_normalization = first_normalization.clone();
1672            normalize_max_resource_per_shader_stage_limits(&mut second_normalization);
1673            assert_eq!(first_normalization, second_normalization);
1674        }
1675
1676        #[test]
1677        fn limits_presets_already_normalized() {
1678            [
1679                wgt::Limits::defaults(),
1680                wgt::Limits::downlevel_defaults(),
1681                wgt::Limits::downlevel_webgl2_defaults(),
1682                wgt::Limits::unlimited(),
1683            ]
1684            .iter()
1685            .for_each(|l| assert_normalized_eq(l, l))
1686        }
1687
1688        #[test]
1689        fn in_stage_raises_per_shader_stage() {
1690            assert_normalized_eq(
1691                &wgt::Limits {
1692                    max_storage_buffers_per_shader_stage: 8,
1693                    max_storage_buffers_in_vertex_stage: 16,
1694                    max_storage_buffers_in_fragment_stage: 16,
1695                    ..wgt::Limits::defaults()
1696                },
1697                &wgt::Limits {
1698                    max_storage_buffers_per_shader_stage: 16,
1699                    max_storage_buffers_in_vertex_stage: 16,
1700                    max_storage_buffers_in_fragment_stage: 16,
1701                    ..wgt::Limits::defaults()
1702                },
1703            );
1704
1705            assert_normalized_eq(
1706                &wgt::Limits {
1707                    max_storage_textures_per_shader_stage: 8,
1708                    max_storage_textures_in_vertex_stage: 9,
1709                    max_storage_textures_in_fragment_stage: 9,
1710                    ..wgt::Limits::defaults()
1711                },
1712                &wgt::Limits {
1713                    max_storage_textures_per_shader_stage: 9,
1714                    max_storage_textures_in_vertex_stage: 9,
1715                    max_storage_textures_in_fragment_stage: 9,
1716                    ..wgt::Limits::defaults()
1717                },
1718            );
1719        }
1720
1721        #[test]
1722        fn per_shader_stage_raises_in_stage() {
1723            assert_normalized_eq(
1724                &wgt::Limits {
1725                    max_storage_buffers_per_shader_stage: 16,
1726                    max_storage_buffers_in_vertex_stage: 4,
1727                    max_storage_buffers_in_fragment_stage: 4,
1728                    max_storage_textures_per_shader_stage: 8,
1729                    max_storage_textures_in_vertex_stage: 4,
1730                    max_storage_textures_in_fragment_stage: 4,
1731                    ..wgt::Limits::defaults()
1732                },
1733                &wgt::Limits {
1734                    max_storage_buffers_per_shader_stage: 16,
1735                    max_storage_buffers_in_vertex_stage: 16,
1736                    max_storage_buffers_in_fragment_stage: 16,
1737                    max_storage_textures_per_shader_stage: 8,
1738                    max_storage_textures_in_vertex_stage: 8,
1739                    max_storage_textures_in_fragment_stage: 8,
1740                    ..wgt::Limits::defaults()
1741                },
1742            );
1743        }
1744
1745        #[test]
1746        fn lowering_per_shader_stage_noop() {
1747            assert_normalized_eq(
1748                &wgt::Limits {
1749                    max_storage_buffers_per_shader_stage: 1,
1750                    max_storage_textures_per_shader_stage: 1,
1751                    ..wgt::Limits::defaults()
1752                },
1753                &wgt::Limits::defaults(),
1754            );
1755        }
1756    }
1757
1758    #[test]
1759    fn downlevel_default_limits_less_than_default_limits() {
1760        let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
1761        assert!(
1762            res.is_empty(),
1763            "Downlevel limits are greater than default limits",
1764        )
1765    }
1766}