wgpu_core/
instance.rs

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