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