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                    .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);
464                        api_log_debug!("Adapter {:?}", adapter.raw.info);
465                        adapter
466                    }),
467            );
468        }
469        adapters
470    }
471
472    pub fn request_adapter(
473        &self,
474        desc: &wgt::RequestAdapterOptions<&Surface>,
475        backends: Backends,
476    ) -> Result<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                .filter(|raw| self.adapter_allowed(raw));
547
548            if desc.apply_limit_buckets {
549                adapters.extend(backend_adapters.filter_map(limits::apply_limit_buckets));
550            } else {
551                adapters.extend(backend_adapters);
552            }
553        }
554
555        match desc.power_preference {
556            PowerPreference::LowPower => {
557                sort(&mut adapters, true);
558            }
559            PowerPreference::HighPerformance => {
560                sort(&mut adapters, false);
561            }
562            PowerPreference::None => {}
563        };
564
565        fn sort(adapters: &mut [hal::DynExposedAdapter], prefer_integrated_gpu: bool) {
566            adapters
567                .sort_by_key(|adapter| get_order(adapter.info.device_type, prefer_integrated_gpu));
568        }
569
570        fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 {
571            // Since devices of type "Other" might really be "Unknown" and come
572            // from APIs like OpenGL that don't specify device type, Prefer more
573            // Specific types over Other.
574            //
575            // This means that backends which do provide accurate device types
576            // will be preferred if their device type indicates an actual
577            // hardware GPU (integrated or discrete).
578            match device_type {
579                wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2,
580                wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1,
581                wgt::DeviceType::DiscreteGpu => 1,
582                wgt::DeviceType::IntegratedGpu => 2,
583                wgt::DeviceType::Other => 3,
584                wgt::DeviceType::VirtualGpu => 4,
585                wgt::DeviceType::Cpu => 5,
586            }
587        }
588
589        // `request_adapter` can be a bit of a black box.
590        // Shine some light on its decision in debug log.
591        if adapters.is_empty() {
592            log::debug!("Request adapter didn't find compatible adapters.");
593        } else {
594            log::debug!(
595                "Found {} compatible adapters. Sorted by preference:",
596                adapters.len()
597            );
598            for adapter in &adapters {
599                log::debug!("* {:?}", adapter.info);
600            }
601        }
602
603        if let Some(adapter) = adapters.into_iter().next() {
604            api_log_debug!("Request adapter result {:?}", adapter.info);
605            let adapter = Adapter::new(adapter);
606            Ok(adapter)
607        } else {
608            Err(wgt::RequestAdapterError::NotFound {
609                supported_backends: self.supported_backends,
610                requested_backends: self.requested_backends,
611                active_backends: self.active_backends(),
612                no_fallback_backends,
613                no_adapter_backends,
614                incompatible_surface_backends,
615            })
616        }
617    }
618
619    /// This is similar to wgpu-hal's `adjust_raw_limits` but tailored to
620    /// wgpu-core's constraints.
621    fn adjust_limits_for_indirect_validation(&self, limits: &mut wgt::Limits) {
622        // Indirect draw validation can't support u64 offsets,
623        // lower max buffer and binding size to fit in an u32.
624        if self.flags.contains(InstanceFlags::VALIDATION_INDIRECT_CALL) {
625            limits.max_buffer_size = limits.max_buffer_size.min(u32::MAX as u64);
626            limits.max_uniform_buffer_binding_size =
627                limits.max_uniform_buffer_binding_size.min(u32::MAX as u64);
628            limits.max_storage_buffer_binding_size =
629                limits.max_storage_buffer_binding_size.min(u32::MAX as u64);
630        }
631    }
632
633    fn active_backends(&self) -> Backends {
634        self.instance_per_backend
635            .iter()
636            .map(|&(backend, _)| Backends::from(backend))
637            .collect()
638    }
639}
640
641pub struct Surface {
642    pub(crate) presentation: Mutex<Option<Presentation>>,
643    pub surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>>,
644}
645
646impl ResourceType for Surface {
647    const TYPE: &'static str = "Surface";
648}
649impl crate::storage::StorageItem for Surface {
650    type Marker = markers::Surface;
651}
652
653impl Surface {
654    pub fn get_capabilities(
655        &self,
656        adapter: &Adapter,
657    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
658        self.get_capabilities_with_raw(&adapter.raw)
659    }
660
661    pub fn get_capabilities_with_raw(
662        &self,
663        adapter: &hal::DynExposedAdapter,
664    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
665        let backend = adapter.backend();
666        let suf = self
667            .raw(backend)
668            .ok_or(GetSurfaceSupportError::NotSupportedByBackend(backend))?;
669        profiling::scope!("surface_capabilities");
670        let caps = unsafe { adapter.adapter.surface_capabilities(suf) }
671            .ok_or(GetSurfaceSupportError::FailedToRetrieveSurfaceCapabilitiesForAdapter)?;
672        Ok(caps)
673    }
674
675    pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynSurface> {
676        self.surface_per_backend
677            .get(&backend)
678            .map(|surface| surface.as_ref())
679    }
680}
681
682impl Drop for Surface {
683    fn drop(&mut self) {
684        if let Some(present) = self.presentation.lock().take() {
685            for (&backend, surface) in &self.surface_per_backend {
686                if backend == present.device.backend() {
687                    unsafe { surface.unconfigure(present.device.raw()) };
688                }
689            }
690        }
691    }
692}
693
694pub struct Adapter {
695    pub(crate) raw: hal::DynExposedAdapter,
696}
697
698impl Adapter {
699    pub fn new(raw: hal::DynExposedAdapter) -> Self {
700        Self { raw }
701    }
702
703    /// Returns the backend this adapter is using.
704    pub fn backend(&self) -> Backend {
705        self.raw.backend()
706    }
707
708    pub fn is_surface_supported(&self, surface: &Surface) -> bool {
709        // If get_capabilities returns Err, then the API does not advertise support for the surface.
710        //
711        // This could occur if the user is running their app on Wayland but Vulkan does not support
712        // VK_KHR_wayland_surface.
713        surface.get_capabilities(self).is_ok()
714    }
715
716    pub fn get_info(&self) -> wgt::AdapterInfo {
717        self.raw.info.clone()
718    }
719
720    pub fn features(&self) -> wgt::Features {
721        self.raw.features
722    }
723
724    pub fn limits(&self) -> wgt::Limits {
725        self.raw.capabilities.limits.clone()
726    }
727
728    pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
729        self.raw.capabilities.downlevel.clone()
730    }
731
732    pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
733        unsafe { self.raw.adapter.get_presentation_timestamp() }
734    }
735
736    pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
737        self.raw.capabilities.cooperative_matrix_properties.clone()
738    }
739
740    pub fn get_texture_format_features(
741        &self,
742        format: wgt::TextureFormat,
743    ) -> wgt::TextureFormatFeatures {
744        use hal::TextureFormatCapabilities as Tfc;
745
746        let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
747        let mut allowed_usages = wgt::TextureUsages::empty();
748
749        allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
750        allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
751        allowed_usages.set(
752            wgt::TextureUsages::TEXTURE_BINDING,
753            caps.contains(Tfc::SAMPLED),
754        );
755        allowed_usages.set(
756            wgt::TextureUsages::STORAGE_BINDING,
757            caps.intersects(
758                Tfc::STORAGE_WRITE_ONLY
759                    | Tfc::STORAGE_READ_ONLY
760                    | Tfc::STORAGE_READ_WRITE
761                    | Tfc::STORAGE_ATOMIC,
762            ),
763        );
764        allowed_usages.set(
765            wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT,
766            caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
767        );
768        allowed_usages.set(
769            wgt::TextureUsages::STORAGE_ATOMIC,
770            caps.contains(Tfc::STORAGE_ATOMIC),
771        );
772
773        let mut flags = wgt::TextureFormatFeatureFlags::empty();
774        flags.set(
775            wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
776            caps.contains(Tfc::STORAGE_READ_ONLY),
777        );
778        flags.set(
779            wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
780            caps.contains(Tfc::STORAGE_WRITE_ONLY),
781        );
782        flags.set(
783            wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
784            caps.contains(Tfc::STORAGE_READ_WRITE),
785        );
786
787        flags.set(
788            wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
789            caps.contains(Tfc::STORAGE_ATOMIC),
790        );
791
792        flags.set(
793            wgt::TextureFormatFeatureFlags::FILTERABLE,
794            caps.contains(Tfc::SAMPLED_LINEAR),
795        );
796
797        flags.set(
798            wgt::TextureFormatFeatureFlags::BLENDABLE,
799            caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
800        );
801
802        flags.set(
803            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
804            caps.contains(Tfc::MULTISAMPLE_X2),
805        );
806        flags.set(
807            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
808            caps.contains(Tfc::MULTISAMPLE_X4),
809        );
810        flags.set(
811            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
812            caps.contains(Tfc::MULTISAMPLE_X8),
813        );
814        flags.set(
815            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
816            caps.contains(Tfc::MULTISAMPLE_X16),
817        );
818
819        flags.set(
820            wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
821            caps.contains(Tfc::MULTISAMPLE_RESOLVE),
822        );
823
824        wgt::TextureFormatFeatures {
825            allowed_usages,
826            flags,
827        }
828    }
829
830    fn create_device_and_queue_from_hal(
831        self: &Arc<Self>,
832        hal_device: hal::DynOpenDevice,
833        desc: &DeviceDescriptor,
834        instance_flags: InstanceFlags,
835    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
836        api_log!("Adapter::create_device");
837
838        let device = Device::new(hal_device.device, self, desc, instance_flags)?;
839        let device = Arc::new(device);
840
841        let queue = Queue::new(device.clone(), hal_device.queue, instance_flags)?;
842        let queue = Arc::new(queue);
843
844        device.set_queue(&queue);
845        device.late_init_resources_with_queue()?;
846
847        Ok((device, queue))
848    }
849
850    pub fn create_device_and_queue(
851        self: &Arc<Self>,
852        desc: &DeviceDescriptor,
853        instance_flags: InstanceFlags,
854    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
855        // Verify all features were exposed by the adapter
856        if !self.raw.features.contains(desc.required_features) {
857            return Err(RequestDeviceError::UnsupportedFeature(
858                desc.required_features - self.raw.features,
859            ));
860        }
861
862        // Check if experimental features are permitted to be enabled.
863        if desc
864            .required_features
865            .intersects(wgt::Features::all_experimental_mask())
866            && !desc.experimental_features.is_enabled()
867        {
868            return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
869                desc.required_features
870                    .intersection(wgt::Features::all_experimental_mask()),
871            ));
872        }
873
874        let caps = &self.raw.capabilities;
875        if Backends::PRIMARY.contains(Backends::from(self.backend()))
876            && !caps.downlevel.is_webgpu_compliant()
877        {
878            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
879            log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
880            log::warn!("{:#?}", caps.downlevel);
881        }
882
883        // Verify feature preconditions
884        if desc
885            .required_features
886            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
887            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
888        {
889            log::warn!(
890                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
891                        This is a massive performance footgun and likely not what you wanted"
892            );
893        }
894
895        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
896            return Err(RequestDeviceError::LimitsExceeded(failed));
897        }
898
899        let open = unsafe {
900            self.raw.adapter.open(
901                desc.required_features,
902                &desc.required_limits,
903                &desc.memory_hints,
904            )
905        }
906        .map_err(DeviceError::from_hal)?;
907
908        self.create_device_and_queue_from_hal(open, desc, instance_flags)
909    }
910}
911
912crate::impl_resource_type!(Adapter);
913crate::impl_storage_item!(Adapter);
914
915#[derive(Clone, Debug, Error)]
916#[non_exhaustive]
917pub enum GetSurfaceSupportError {
918    #[error("Surface is not supported for the specified backend {0}")]
919    NotSupportedByBackend(Backend),
920    #[error("Failed to retrieve surface capabilities for the specified adapter.")]
921    FailedToRetrieveSurfaceCapabilitiesForAdapter,
922}
923
924#[derive(Clone, Debug, Error)]
925/// Error when requesting a device from the adapter
926#[non_exhaustive]
927pub enum RequestDeviceError {
928    #[error(transparent)]
929    Device(#[from] DeviceError),
930    #[error(transparent)]
931    LimitsExceeded(#[from] FailedLimit),
932    #[error("Failed to initialize Timestamp Normalizer")]
933    TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
934    #[error("Unsupported features were requested: {0}")]
935    UnsupportedFeature(wgt::Features),
936    #[error(
937        "Some experimental features, {0}, were requested, but experimental features are not enabled"
938    )]
939    ExperimentalFeaturesNotEnabled(wgt::Features),
940}
941
942#[derive(Clone, Debug, Error)]
943#[non_exhaustive]
944pub enum CreateSurfaceError {
945    #[error("The backend {0} was not enabled on the instance.")]
946    BackendNotEnabled(Backend),
947    #[error("Failed to create surface for any enabled backend: {0:?}")]
948    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
949    #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
950    MismatchingDisplayHandle,
951    #[error(
952        "No `DisplayHandle` is available to create this surface with.  When creating a surface with `create_surface()` \
953        you must specify a display handle in `InstanceDescriptor::display`.  \
954        Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
955        you must use `create_surface_unsafe()`."
956    )]
957    MissingDisplayHandle,
958}
959
960impl Global {
961    /// Creates a new surface targeting the given display/window handles.
962    ///
963    /// Internally attempts to create hal surfaces for all enabled backends.
964    ///
965    /// Fails only if creation for surfaces for all enabled backends fails in which case
966    /// the error for each enabled backend is listed.
967    /// Vice versa, if creation for any backend succeeds, success is returned.
968    /// Surface creation errors are logged to the debug log in any case.
969    ///
970    /// id_in:
971    /// - If `Some`, the id to assign to the surface. A new one will be generated otherwise.
972    ///
973    /// # Safety
974    ///
975    /// - `display_handle` must be a valid object to create a surface upon,
976    ///   falls back to the instance display handle otherwise.
977    /// - `window_handle` must remain valid as long as the returned
978    ///   [`SurfaceId`] is being used.
979    pub unsafe fn instance_create_surface(
980        &self,
981        display_handle: Option<raw_window_handle::RawDisplayHandle>,
982        window_handle: raw_window_handle::RawWindowHandle,
983        id_in: Option<SurfaceId>,
984    ) -> Result<SurfaceId, CreateSurfaceError> {
985        let surface = unsafe { self.instance.create_surface(display_handle, window_handle) }?;
986        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
987        Ok(id)
988    }
989
990    /// Creates a new surface from the given drm configuration.
991    ///
992    /// # Safety
993    ///
994    /// - All parameters must point to valid DRM values.
995    ///
996    /// # Platform Support
997    ///
998    /// This function requires the `"drm"` feature, and is only available on
999    /// non-apple Unix-like platforms (Linux, FreeBSD) and currently only works
1000    /// with the Vulkan backend.
1001    #[cfg(drm)]
1002    pub unsafe fn instance_create_surface_from_drm(
1003        &self,
1004        fd: i32,
1005        plane: u32,
1006        connector_id: u32,
1007        width: u32,
1008        height: u32,
1009        refresh_rate: u32,
1010        id_in: Option<SurfaceId>,
1011    ) -> Result<SurfaceId, CreateSurfaceError> {
1012        let surface = unsafe {
1013            self.instance.create_surface_from_drm(
1014                fd,
1015                plane,
1016                connector_id,
1017                width,
1018                height,
1019                refresh_rate,
1020            )
1021        }?;
1022        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
1023
1024        Ok(id)
1025    }
1026
1027    /// # Safety
1028    ///
1029    /// `layer` must be a valid pointer.
1030    #[cfg(metal)]
1031    pub unsafe fn instance_create_surface_metal(
1032        &self,
1033        layer: *mut core::ffi::c_void,
1034        id_in: Option<SurfaceId>,
1035    ) -> Result<SurfaceId, CreateSurfaceError> {
1036        let surface = unsafe { self.instance.create_surface_metal(layer) }?;
1037        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
1038        Ok(id)
1039    }
1040
1041    #[cfg(dx12)]
1042    /// # Safety
1043    ///
1044    /// The visual must be valid and able to be used to make a swapchain with.
1045    pub unsafe fn instance_create_surface_from_visual(
1046        &self,
1047        visual: *mut core::ffi::c_void,
1048        id_in: Option<SurfaceId>,
1049    ) -> Result<SurfaceId, CreateSurfaceError> {
1050        let surface = unsafe { self.instance.create_surface_from_visual(visual) }?;
1051        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
1052        Ok(id)
1053    }
1054
1055    #[cfg(dx12)]
1056    /// # Safety
1057    ///
1058    /// The surface_handle must be valid and able to be used to make a swapchain with.
1059    pub unsafe fn instance_create_surface_from_surface_handle(
1060        &self,
1061        surface_handle: *mut core::ffi::c_void,
1062        id_in: Option<SurfaceId>,
1063    ) -> Result<SurfaceId, CreateSurfaceError> {
1064        let surface = unsafe {
1065            self.instance
1066                .create_surface_from_surface_handle(surface_handle)
1067        }?;
1068        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
1069        Ok(id)
1070    }
1071
1072    #[cfg(dx12)]
1073    /// # Safety
1074    ///
1075    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.
1076    pub unsafe fn instance_create_surface_from_swap_chain_panel(
1077        &self,
1078        swap_chain_panel: *mut core::ffi::c_void,
1079        id_in: Option<SurfaceId>,
1080    ) -> Result<SurfaceId, CreateSurfaceError> {
1081        let surface = unsafe {
1082            self.instance
1083                .create_surface_from_swap_chain_panel(swap_chain_panel)
1084        }?;
1085        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
1086        Ok(id)
1087    }
1088
1089    pub fn surface_drop(&self, id: SurfaceId) {
1090        profiling::scope!("Surface::drop");
1091
1092        api_log!("Surface::drop {id:?}");
1093
1094        self.surfaces.remove(id);
1095    }
1096
1097    pub fn enumerate_adapters(
1098        &self,
1099        backends: Backends,
1100        apply_limit_buckets: bool,
1101    ) -> Vec<AdapterId> {
1102        let adapters = self
1103            .instance
1104            .enumerate_adapters(backends, apply_limit_buckets);
1105        adapters
1106            .into_iter()
1107            .map(|adapter| self.hub.adapters.prepare(None).assign(Arc::new(adapter)))
1108            .collect()
1109    }
1110
1111    pub fn request_adapter(
1112        &self,
1113        desc: &RequestAdapterOptions,
1114        backends: Backends,
1115        id_in: Option<AdapterId>,
1116    ) -> Result<AdapterId, wgt::RequestAdapterError> {
1117        let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id));
1118        let desc = wgt::RequestAdapterOptions {
1119            power_preference: desc.power_preference,
1120            force_fallback_adapter: desc.force_fallback_adapter,
1121            compatible_surface: compatible_surface.as_deref(),
1122            apply_limit_buckets: desc.apply_limit_buckets,
1123        };
1124        let adapter = self.instance.request_adapter(&desc, backends)?;
1125        let id = self.hub.adapters.prepare(id_in).assign(Arc::new(adapter));
1126        Ok(id)
1127    }
1128
1129    /// Create an adapter from a HAL adapter.
1130    ///
1131    /// The HAL adapter may be obtained e.g. by calling `enumerate_adapters` on
1132    /// the HAL directly.
1133    ///
1134    /// If [limit bucketing][lt] is desired, [`crate::limits::apply_limit_buckets`]
1135    /// should be called with the HAL adapter before calling this function.
1136    ///
1137    /// # Safety
1138    ///
1139    /// `hal_adapter` must be created from this global internal instance handle.
1140    ///
1141    /// [lt]: crate::limits#Limit-bucketing
1142    pub unsafe fn create_adapter_from_hal(
1143        &self,
1144        hal_adapter: hal::DynExposedAdapter,
1145        input: Option<AdapterId>,
1146    ) -> AdapterId {
1147        profiling::scope!("Instance::create_adapter_from_hal");
1148
1149        let fid = self.hub.adapters.prepare(input);
1150        let id = fid.assign(Arc::new(Adapter::new(hal_adapter)));
1151
1152        resource_log!("Created Adapter {:?}", id);
1153        id
1154    }
1155
1156    pub fn adapter_get_info(&self, adapter_id: AdapterId) -> wgt::AdapterInfo {
1157        let adapter = self.hub.adapters.get(adapter_id);
1158        adapter.get_info()
1159    }
1160
1161    pub fn adapter_get_texture_format_features(
1162        &self,
1163        adapter_id: AdapterId,
1164        format: wgt::TextureFormat,
1165    ) -> wgt::TextureFormatFeatures {
1166        let adapter = self.hub.adapters.get(adapter_id);
1167        adapter.get_texture_format_features(format)
1168    }
1169
1170    pub fn adapter_features(&self, adapter_id: AdapterId) -> wgt::Features {
1171        let adapter = self.hub.adapters.get(adapter_id);
1172        adapter.features()
1173    }
1174
1175    pub fn adapter_limits(&self, adapter_id: AdapterId) -> wgt::Limits {
1176        let adapter = self.hub.adapters.get(adapter_id);
1177        adapter.limits()
1178    }
1179
1180    pub fn adapter_downlevel_capabilities(
1181        &self,
1182        adapter_id: AdapterId,
1183    ) -> wgt::DownlevelCapabilities {
1184        let adapter = self.hub.adapters.get(adapter_id);
1185        adapter.downlevel_capabilities()
1186    }
1187
1188    pub fn adapter_get_presentation_timestamp(
1189        &self,
1190        adapter_id: AdapterId,
1191    ) -> wgt::PresentationTimestamp {
1192        let adapter = self.hub.adapters.get(adapter_id);
1193        adapter.get_presentation_timestamp()
1194    }
1195
1196    pub fn adapter_cooperative_matrix_properties(
1197        &self,
1198        adapter_id: AdapterId,
1199    ) -> Vec<wgt::CooperativeMatrixProperties> {
1200        let adapter = self.hub.adapters.get(adapter_id);
1201        adapter.cooperative_matrix_properties()
1202    }
1203
1204    pub fn adapter_drop(&self, adapter_id: AdapterId) {
1205        profiling::scope!("Adapter::drop");
1206        api_log!("Adapter::drop {adapter_id:?}");
1207
1208        self.hub.adapters.remove(adapter_id);
1209    }
1210}
1211
1212impl Global {
1213    pub fn adapter_request_device(
1214        &self,
1215        adapter_id: AdapterId,
1216        desc: &DeviceDescriptor,
1217        device_id_in: Option<DeviceId>,
1218        queue_id_in: Option<QueueId>,
1219    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1220        profiling::scope!("Adapter::request_device");
1221        api_log!("Adapter::request_device");
1222
1223        let device_fid = self.hub.devices.prepare(device_id_in);
1224        let queue_fid = self.hub.queues.prepare(queue_id_in);
1225
1226        let adapter = self.hub.adapters.get(adapter_id);
1227        let (device, queue) = adapter.create_device_and_queue(desc, self.instance.flags)?;
1228
1229        let device_id = device_fid.assign(device);
1230        resource_log!("Created Device {:?}", device_id);
1231
1232        let queue_id = queue_fid.assign(queue);
1233        resource_log!("Created Queue {:?}", queue_id);
1234
1235        Ok((device_id, queue_id))
1236    }
1237
1238    /// # Safety
1239    ///
1240    /// - `hal_device` must be created from `adapter_id` or its internal handle.
1241    /// - `desc` must be a subset of `hal_device` features and limits.
1242    pub unsafe fn create_device_from_hal(
1243        &self,
1244        adapter_id: AdapterId,
1245        hal_device: hal::DynOpenDevice,
1246        desc: &DeviceDescriptor,
1247        device_id_in: Option<DeviceId>,
1248        queue_id_in: Option<QueueId>,
1249    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1250        profiling::scope!("Global::create_device_from_hal");
1251
1252        let devices_fid = self.hub.devices.prepare(device_id_in);
1253        let queues_fid = self.hub.queues.prepare(queue_id_in);
1254
1255        let adapter = self.hub.adapters.get(adapter_id);
1256        let (device, queue) =
1257            adapter.create_device_and_queue_from_hal(hal_device, desc, self.instance.flags)?;
1258
1259        let device_id = devices_fid.assign(device);
1260        resource_log!("Created Device {:?}", device_id);
1261
1262        let queue_id = queues_fid.assign(queue);
1263        resource_log!("Created Queue {:?}", queue_id);
1264
1265        Ok((device_id, queue_id))
1266    }
1267}
1268
1269/// This function checks that the adapter obeys WebGPU's adapter capability
1270/// guarantees. Most of the limits are adjusted in wgpu-hal's
1271/// `adjust_raw_limits` fn. So we only check the remaining properties here.
1272/// See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>.
1273fn adapter_allowed(
1274    flags: InstanceFlags,
1275    info: &impl fmt::Debug,
1276    limits: &wgt::Limits,
1277    downlevel: &wgt::DownlevelCapabilities,
1278) -> bool {
1279    // Check "All alignment-class limits must be powers of 2."
1280    //
1281    // Even if the application has not requested strict WebGPU compliance,
1282    // non-power-of-two alignment limits are nonsensical, so don't attempt
1283    // to use such a device.
1284    let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1285    if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1286        log::error!(
1287            "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1288            info,
1289            min_uniform_buffer_offset_alignment
1290        );
1291        return false;
1292    }
1293    let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1294    if !min_storage_buffer_offset_alignment.is_power_of_two() {
1295        log::error!(
1296            "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1297            info,
1298            min_storage_buffer_offset_alignment
1299        );
1300        return false;
1301    }
1302
1303    // Following checks are only enabled if `STRICT_WEBGPU_COMPLIANCE` is set.
1304    if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1305        return true;
1306    }
1307
1308    // Check "All supported limits must be either the default value or better."
1309    let failed_limits = check_limits(&wgt::Limits::defaults(), limits);
1310    if !failed_limits.is_empty() {
1311        log::debug!(
1312            "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1313            info,
1314            failed_limits
1315        );
1316        return false;
1317    }
1318
1319    if !downlevel.is_webgpu_compliant() {
1320        let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1321        log::debug!(
1322            "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1323            info,
1324            missing_flags
1325        );
1326        return false;
1327    }
1328
1329    true
1330}
1331
1332#[cfg(test)]
1333mod tests {
1334    use super::*;
1335
1336    fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1337        wgt::DownlevelCapabilities {
1338            flags: wgt::DownlevelFlags::compliant(),
1339            ..Default::default()
1340        }
1341    }
1342
1343    #[test]
1344    fn non_power_of_two_uniform_alignment_always_rejected() {
1345        let limits = wgt::Limits {
1346            min_uniform_buffer_offset_alignment: 3,
1347            ..wgt::Limits::defaults()
1348        };
1349        assert!(!adapter_allowed(
1350            InstanceFlags::empty(),
1351            &"",
1352            &limits,
1353            &compliant_downlevel()
1354        ));
1355        assert!(!adapter_allowed(
1356            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1357            &"",
1358            &limits,
1359            &compliant_downlevel()
1360        ));
1361    }
1362
1363    #[test]
1364    fn non_power_of_two_storage_alignment_always_rejected() {
1365        let limits = wgt::Limits {
1366            min_storage_buffer_offset_alignment: 96,
1367            ..wgt::Limits::defaults()
1368        };
1369        assert!(!adapter_allowed(
1370            InstanceFlags::empty(),
1371            &"",
1372            &limits,
1373            &compliant_downlevel()
1374        ));
1375        assert!(!adapter_allowed(
1376            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1377            &"",
1378            &limits,
1379            &compliant_downlevel()
1380        ));
1381    }
1382
1383    #[test]
1384    fn low_limits_allowed_without_strict_compliance() {
1385        let limits = wgt::Limits {
1386            max_texture_dimension_1d: 1,
1387            ..wgt::Limits::defaults()
1388        };
1389        assert!(adapter_allowed(
1390            InstanceFlags::empty(),
1391            &"",
1392            &limits,
1393            &wgt::DownlevelCapabilities::default()
1394        ));
1395    }
1396
1397    #[test]
1398    fn low_limits_rejected_with_strict_compliance() {
1399        let limits = wgt::Limits {
1400            max_texture_dimension_1d: 1,
1401            ..wgt::Limits::defaults()
1402        };
1403        assert!(!adapter_allowed(
1404            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1405            &"",
1406            &limits,
1407            &compliant_downlevel()
1408        ));
1409    }
1410
1411    #[test]
1412    fn missing_downlevel_flags_rejected_with_strict_compliance() {
1413        let downlevel = wgt::DownlevelCapabilities {
1414            flags: wgt::DownlevelFlags::empty(),
1415            ..Default::default()
1416        };
1417        assert!(!adapter_allowed(
1418            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1419            &"",
1420            &wgt::Limits::defaults(),
1421            &downlevel
1422        ));
1423    }
1424
1425    #[test]
1426    fn fully_compliant_adapter_always_allowed() {
1427        assert!(adapter_allowed(
1428            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1429            &"",
1430            &wgt::Limits::defaults(),
1431            &compliant_downlevel()
1432        ));
1433    }
1434}