Skip to main content

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, MutexGuard},
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    fn unconfigure_inner<'a>(
860        &self,
861        presentation: &mut MutexGuard<'a, Option<Presentation>>,
862    ) -> UserClosures {
863        let mut result = UserClosures::default();
864        if let Some(mut present) = presentation.take() {
865            if let Some(texture) = present.acquired_texture.take() {
866                texture.destroy();
867            }
868
869            let user_callbacks;
870            {
871                // Wait for all work that uses the surface texture to finish
872                let snatch_guard = present.device.snatchable_lock.read();
873
874                let result;
875                (user_callbacks, result) = present
876                    .device
877                    .maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
878                match result {
879                    Ok(_) => {}
880                    Err(WaitIdleError::Device(_)) => {
881                        // we can ignore device lost errors here, since we are just cleaning up
882                    }
883                    Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
884                        // On wasm, you cannot actually successfully wait for the surface.
885                        // However WebGL does not actually require you do this, so ignoring
886                        // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
887                    }
888                    Err(WaitIdleError::Timeout) => {
889                        unreachable!("wait_indefinitely() should never timeout")
890                    }
891                    Err(WaitIdleError::WrongSubmissionIndex(_, _)) => {
892                        unreachable!("no submission index was provided")
893                    }
894                }
895            }
896            result.extend(user_callbacks);
897
898            for (&backend, surface) in &self.surface_per_backend {
899                if backend == present.device.backend() {
900                    unsafe { surface.unconfigure(present.device.raw()) };
901                }
902            }
903        }
904        result
905    }
906
907    pub fn unconfigure(self: &Arc<Self>) {
908        profiling::scope!("Surface::unconfigure");
909        let user_callbacks;
910        {
911            let mut presentation = self.presentation.lock();
912            user_callbacks = self.unconfigure_inner(&mut presentation);
913        }
914        user_callbacks.fire();
915    }
916
917    pub fn configure(
918        self: &Arc<Self>,
919        device: &Arc<Device>,
920        config: &wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
921    ) -> Result<(), ConfigureSurfaceError> {
922        use ConfigureSurfaceError as E;
923        profiling::scope!("Surface::configure");
924
925        #[cfg(feature = "trace")]
926        if let Some(ref mut trace) = *device.trace.lock() {
927            use crate::device::trace::{Action, IntoTrace};
928
929            trace.add(Action::ConfigureSurface(self.to_trace(), config.clone()));
930        }
931
932        log::debug!("configuring surface with {config:?}");
933
934        let caps = self
935            .get_hal_capabilities(&device.adapter)
936            .map_err(|_| E::UnsupportedQueueFamily)?;
937
938        let mut hal_view_formats = Vec::new();
939        for format in config.view_formats.iter() {
940            if *format == config.format {
941                continue;
942            }
943            if !caps.formats.iter().any(|fc| fc.format == config.format) {
944                return Err(E::UnsupportedFormat {
945                    requested: config.format,
946                    available: caps.texture_formats().collect(),
947                });
948            }
949            if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
950                return Err(E::InvalidViewFormat(*format, config.format));
951            }
952            hal_view_formats.push(*format);
953        }
954
955        if !hal_view_formats.is_empty() {
956            device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)?;
957        }
958
959        let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
960            *caps.maximum_frame_latency.start(),
961            *caps.maximum_frame_latency.end(),
962        );
963        let mut hal_config = hal::SurfaceConfiguration {
964            maximum_frame_latency,
965            present_mode: config.present_mode,
966            composite_alpha_mode: config.alpha_mode,
967            format: config.format,
968            color_space: config.color_space,
969            extent: wgt::Extent3d {
970                width: config.width,
971                height: config.height,
972                depth_or_array_layers: 1,
973            },
974            usage: crate::conv::map_texture_usage(
975                config.usage,
976                hal::FormatAspects::COLOR,
977                wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
978                    | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
979                    | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
980            ),
981            view_formats: hal_view_formats,
982        };
983
984        crate::device::surface_config::validate_surface_configuration(
985            &mut hal_config,
986            &caps,
987            device.limits.max_texture_dimension_2d,
988        )?;
989
990        device.check_is_valid()?;
991
992        let user_callbacks;
993        {
994            // we keep presentation locked for the entire duration of the configure call,
995            // so that no change can happen in the middle of it.
996            let mut presentation = self.presentation.lock();
997
998            user_callbacks = self.unconfigure_inner(&mut presentation);
999
1000            let surface_raw = self.raw(device.backend()).unwrap();
1001            match unsafe { surface_raw.configure(device.raw(), &hal_config) } {
1002                Ok(()) => (),
1003                Err(error) => {
1004                    return Err(match error {
1005                        hal::SurfaceError::Outdated
1006                        | hal::SurfaceError::Lost
1007                        | hal::SurfaceError::Occluded
1008                        | hal::SurfaceError::Timeout => E::InvalidSurface,
1009                        hal::SurfaceError::Device(error) => {
1010                            E::Device(device.handle_hal_error(error))
1011                        }
1012                        hal::SurfaceError::Other(message) => {
1013                            log::error!("surface configuration failed: {message}");
1014                            E::InvalidSurface
1015                        }
1016                    });
1017                }
1018            }
1019
1020            *presentation = Some(Presentation {
1021                device: Arc::clone(device),
1022                config: config.clone(),
1023                acquired_texture: None,
1024            });
1025        }
1026        user_callbacks.fire();
1027
1028        Ok(())
1029    }
1030}
1031
1032impl Drop for Surface {
1033    #[allow(trivial_casts)]
1034    fn drop(&mut self) {
1035        profiling::scope!("Surface::drop");
1036
1037        api_log!("Surface::drop {:?}", self as *const _);
1038        let user_closures;
1039        {
1040            let mut presentation = self.presentation.lock();
1041            user_closures = self.unconfigure_inner(&mut presentation);
1042        }
1043        user_closures.fire();
1044    }
1045}
1046
1047pub struct Adapter {
1048    pub(crate) raw: hal::DynExposedAdapter,
1049    pub(crate) instance: Arc<Instance>,
1050}
1051
1052impl Adapter {
1053    pub(crate) fn new(raw: hal::DynExposedAdapter, instance: Arc<Instance>) -> Arc<Self> {
1054        Arc::new(Self { raw, instance })
1055    }
1056
1057    /// Returns the backend this adapter is using.
1058    pub fn backend(&self) -> Backend {
1059        self.raw.backend()
1060    }
1061
1062    pub fn is_surface_supported(&self, surface: &Surface) -> bool {
1063        // If get_capabilities returns Err, then the API does not advertise support for the surface.
1064        //
1065        // This could occur if the user is running their app on Wayland but Vulkan does not support
1066        // VK_KHR_wayland_surface.
1067        surface.get_hal_capabilities(self).is_ok()
1068    }
1069
1070    pub fn get_info(&self) -> wgt::AdapterInfo {
1071        self.raw.info.clone()
1072    }
1073
1074    pub fn features(&self) -> wgt::Features {
1075        self.raw.features
1076    }
1077
1078    pub fn limits(&self) -> wgt::Limits {
1079        self.raw.capabilities.limits.clone()
1080    }
1081
1082    pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
1083        self.raw.capabilities.downlevel.clone()
1084    }
1085
1086    pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1087        unsafe { self.raw.adapter.get_presentation_timestamp() }
1088    }
1089
1090    pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1091        self.raw.capabilities.cooperative_matrix_properties.clone()
1092    }
1093
1094    pub fn get_texture_format_features(
1095        &self,
1096        format: wgt::TextureFormat,
1097    ) -> wgt::TextureFormatFeatures {
1098        use hal::TextureFormatCapabilities as Tfc;
1099
1100        let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
1101        let mut allowed_usages = wgt::TextureUsages::empty();
1102
1103        allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
1104        allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
1105        allowed_usages.set(
1106            wgt::TextureUsages::TEXTURE_BINDING,
1107            caps.contains(Tfc::SAMPLED),
1108        );
1109        allowed_usages.set(
1110            wgt::TextureUsages::STORAGE_BINDING,
1111            caps.intersects(
1112                Tfc::STORAGE_WRITE_ONLY
1113                    | Tfc::STORAGE_READ_ONLY
1114                    | Tfc::STORAGE_READ_WRITE
1115                    | Tfc::STORAGE_ATOMIC,
1116            ),
1117        );
1118        allowed_usages.set(
1119            wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT_ATTACHMENT,
1120            caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
1121        );
1122        allowed_usages.set(
1123            wgt::TextureUsages::STORAGE_ATOMIC,
1124            caps.contains(Tfc::STORAGE_ATOMIC),
1125        );
1126
1127        let mut flags = wgt::TextureFormatFeatureFlags::empty();
1128        flags.set(
1129            wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
1130            caps.contains(Tfc::STORAGE_READ_ONLY),
1131        );
1132        flags.set(
1133            wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
1134            caps.contains(Tfc::STORAGE_WRITE_ONLY),
1135        );
1136        flags.set(
1137            wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1138            caps.contains(Tfc::STORAGE_READ_WRITE),
1139        );
1140
1141        flags.set(
1142            wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
1143            caps.contains(Tfc::STORAGE_ATOMIC),
1144        );
1145
1146        flags.set(
1147            wgt::TextureFormatFeatureFlags::FILTERABLE,
1148            caps.contains(Tfc::SAMPLED_LINEAR),
1149        );
1150
1151        flags.set(
1152            wgt::TextureFormatFeatureFlags::BLENDABLE,
1153            caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
1154        );
1155
1156        flags.set(
1157            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
1158            caps.contains(Tfc::MULTISAMPLE_X2),
1159        );
1160        flags.set(
1161            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
1162            caps.contains(Tfc::MULTISAMPLE_X4),
1163        );
1164        flags.set(
1165            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
1166            caps.contains(Tfc::MULTISAMPLE_X8),
1167        );
1168        flags.set(
1169            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1170            caps.contains(Tfc::MULTISAMPLE_X16),
1171        );
1172
1173        flags.set(
1174            wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
1175            caps.contains(Tfc::MULTISAMPLE_RESOLVE),
1176        );
1177
1178        wgt::TextureFormatFeatures {
1179            allowed_usages,
1180            flags,
1181        }
1182    }
1183
1184    /// # Safety
1185    ///
1186    /// - `hal_device` must be created from this adapter.
1187    /// - `desc` must be a subset of `hal_device` features and limits.
1188    pub unsafe fn create_device_and_queue_from_hal(
1189        self: &Arc<Self>,
1190        hal_device: hal::DynOpenDevice,
1191        desc: &DeviceDescriptor,
1192    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1193        profiling::scope!("Adapter::create_device_and_queue_from_hal");
1194        api_log!("Adapter::create_device_and_queue_from_hal");
1195
1196        let default_queue_desc = desc.default_queue.clone();
1197
1198        let device = Device::new(hal_device.device, self, desc, self.instance.flags)?;
1199        let device = Arc::new(device);
1200
1201        let queue = Queue::new(
1202            device.clone(),
1203            hal_device.queue,
1204            default_queue_desc,
1205            self.instance.flags,
1206        )?;
1207        let queue = Arc::new(queue);
1208
1209        device.set_queue(&queue);
1210        device.late_init_resources_with_queue()?;
1211
1212        resource_log!("Created Device {:?}", Arc::as_ptr(&device));
1213        resource_log!("Created Queue {:?}", Arc::as_ptr(&queue));
1214
1215        self.instance.devices.push(&device);
1216
1217        Ok((device, queue))
1218    }
1219
1220    /// Validate a device descriptor.
1221    ///
1222    /// This validates the provided device descriptor as if it were passed to
1223    /// [`Self::request_device`]. If [`InstanceFlags::STRICT_WEBGPU_COMPLIANCE`] is active,
1224    /// the requested extensions in the descriptor will be filtered to remove `wgpu`
1225    /// extensions, except for those that are included in [`limits::EXEMPT_FEATURES`].
1226    ///
1227    /// This may be useful when it is necessary to obtain the device itself from a raw hal
1228    /// API, but the rest of the `request_device` validation is still desired.
1229    pub fn validate_device_descriptor(
1230        &self,
1231        desc: &mut DeviceDescriptor,
1232    ) -> Result<(), RequestDeviceError> {
1233        filter_features_and_limits(
1234            self.instance.flags,
1235            &mut desc.required_features,
1236            &mut desc.required_limits,
1237        );
1238
1239        // Verify all features were exposed by the adapter
1240        if !self.raw.features.contains(desc.required_features) {
1241            return Err(RequestDeviceError::UnsupportedFeature(
1242                desc.required_features - self.raw.features,
1243            ));
1244        }
1245
1246        // Check if experimental features are permitted to be enabled.
1247        if desc
1248            .required_features
1249            .intersects(wgt::Features::all_experimental_mask())
1250            && !desc.experimental_features.is_enabled()
1251        {
1252            return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
1253                desc.required_features
1254                    .intersection(wgt::Features::all_experimental_mask()),
1255            ));
1256        }
1257
1258        let caps = &self.raw.capabilities;
1259        if Backends::PRIMARY.contains(Backends::from(self.backend()))
1260            && !caps.downlevel.is_webgpu_compliant()
1261        {
1262            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
1263            log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
1264            log::warn!("{:#?}", caps.downlevel);
1265        }
1266
1267        // Verify feature preconditions
1268        if desc
1269            .required_features
1270            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1271            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
1272        {
1273            log::warn!(
1274                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
1275                        This is a massive performance footgun and likely not what you wanted"
1276            );
1277        }
1278
1279        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
1280            return Err(RequestDeviceError::LimitsExceeded(failed));
1281        }
1282
1283        normalize_max_resource_per_shader_stage_limits(&mut desc.required_limits);
1284
1285        Ok(())
1286    }
1287
1288    pub fn request_device(
1289        self: &Arc<Self>,
1290        desc: &DeviceDescriptor,
1291    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1292        profiling::scope!("Adapter::request_device");
1293        api_log!("Adapter::request_device");
1294
1295        let mut desc = desc.clone();
1296        self.validate_device_descriptor(&mut desc)?;
1297
1298        let open = unsafe {
1299            self.raw.adapter.open(
1300                desc.required_features,
1301                &desc.required_limits,
1302                &desc.memory_hints,
1303            )
1304        }
1305        .map_err(DeviceError::from_hal)?;
1306
1307        unsafe { self.create_device_and_queue_from_hal(open, &desc) }
1308    }
1309}
1310
1311impl Drop for Adapter {
1312    #[allow(trivial_casts)]
1313    fn drop(&mut self) {
1314        profiling::scope!("Adapter::drop");
1315        api_log!("Adapter::drop {:?}", self as *const _);
1316    }
1317}
1318
1319crate::impl_resource_type!(Adapter);
1320crate::impl_storage_item!(Adapter);
1321
1322#[derive(Clone, Debug, Error)]
1323#[non_exhaustive]
1324pub enum GetSurfaceSupportError {
1325    #[error("Surface is not supported for the specified backend {0}")]
1326    NotSupportedByBackend(Backend),
1327    #[error("Failed to retrieve surface capabilities for the specified adapter.")]
1328    FailedToRetrieveSurfaceCapabilitiesForAdapter,
1329}
1330
1331#[derive(Clone, Debug, Error)]
1332/// Error when requesting a device from the adapter
1333#[non_exhaustive]
1334pub enum RequestDeviceError {
1335    #[error(transparent)]
1336    Device(#[from] DeviceError),
1337    #[error(transparent)]
1338    LimitsExceeded(#[from] FailedLimit),
1339    #[error("Failed to initialize Timestamp Normalizer")]
1340    TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
1341    #[error("Unsupported features were requested: {0}")]
1342    UnsupportedFeature(wgt::Features),
1343    #[error(
1344        "Some experimental features, {0}, were requested, but experimental features are not enabled"
1345    )]
1346    ExperimentalFeaturesNotEnabled(wgt::Features),
1347}
1348
1349#[derive(Clone, Debug, Error)]
1350#[non_exhaustive]
1351pub enum CreateSurfaceError {
1352    #[error("The backend {0} was not enabled on the instance.")]
1353    BackendNotEnabled(Backend),
1354    #[error("Failed to create surface for any enabled backend: {0:?}")]
1355    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
1356    #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
1357    MismatchingDisplayHandle,
1358    #[error(
1359        "No `DisplayHandle` is available to create this surface with.  When creating a surface with `create_surface()` \
1360        you must specify a display handle in `InstanceDescriptor::display`.  \
1361        Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
1362        you must use `create_surface_unsafe()`."
1363    )]
1364    MissingDisplayHandle,
1365}
1366
1367/// This function checks that the adapter obeys WebGPU's adapter capability
1368/// guarantees. Most of the limits are adjusted in wgpu-hal's
1369/// `adjust_raw_limits` fn. So we only check the remaining properties here.
1370/// See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>.
1371fn adapter_allowed(
1372    flags: InstanceFlags,
1373    info: &impl fmt::Debug,
1374    limits: &wgt::Limits,
1375    downlevel: &wgt::DownlevelCapabilities,
1376) -> bool {
1377    // Check "All alignment-class limits must be powers of 2."
1378    //
1379    // Even if the application has not requested strict WebGPU compliance,
1380    // non-power-of-two alignment limits are nonsensical, so don't attempt
1381    // to use such a device.
1382    let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1383    if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1384        log::error!(
1385            "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1386            info,
1387            min_uniform_buffer_offset_alignment
1388        );
1389        return false;
1390    }
1391    let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1392    if !min_storage_buffer_offset_alignment.is_power_of_two() {
1393        log::error!(
1394            "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1395            info,
1396            min_storage_buffer_offset_alignment
1397        );
1398        return false;
1399    }
1400
1401    // Following checks are only enabled if `STRICT_WEBGPU_COMPLIANCE` is set.
1402    if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1403        return true;
1404    }
1405
1406    // Check "All supported limits must be either the default value or better."
1407    let mut min_limits = wgt::Limits::defaults();
1408    min_limits.zero_native_only();
1409    let failed_limits = check_limits(&min_limits, limits);
1410    if !failed_limits.is_empty() {
1411        log::debug!(
1412            "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1413            info,
1414            failed_limits
1415        );
1416        return false;
1417    }
1418
1419    if !downlevel.is_webgpu_compliant() {
1420        let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1421        log::debug!(
1422            "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1423            info,
1424            missing_flags
1425        );
1426        return false;
1427    }
1428
1429    true
1430}
1431
1432fn filter_features_and_limits(
1433    flags: InstanceFlags,
1434    features: &mut wgt::Features,
1435    limits: &mut wgt::Limits,
1436) {
1437    if flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1438        *features &= wgt::Features::all_webgpu_mask() | limits::EXEMPT_FEATURES;
1439        limits.zero_native_only();
1440    }
1441}
1442
1443fn normalize_max_resource_per_shader_stage_limits(limits: &mut wgt::Limits) {
1444    // The next steps are from <https://www.w3.org/TR/webgpu/#a-new-device>.
1445
1446    // > 7. Set `limits.maxStorageBuffersPerShaderStage` to
1447    // >    `max(limits.maxStorageBuffersPerShaderStage, limits.maxStorageBuffersInVertexStage,
1448    // >    limits.maxStorageBuffersInFragmentStage)`.
1449
1450    limits.max_storage_buffers_per_shader_stage = [
1451        limits.max_storage_buffers_per_shader_stage,
1452        limits.max_storage_buffers_in_vertex_stage,
1453        limits.max_storage_buffers_in_fragment_stage,
1454    ]
1455    .into_iter()
1456    .max()
1457    .unwrap();
1458
1459    // > 8. Set `limits.maxStorageTexturesPerShaderStage` to
1460    // >    `max(limits.maxStorageTexturesPerShaderStage, limits.maxStorageTexturesInVertexStage,
1461    // >    limits.maxStorageTexturesInFragmentStage)`.
1462
1463    limits.max_storage_textures_per_shader_stage = [
1464        limits.max_storage_textures_per_shader_stage,
1465        limits.max_storage_textures_in_vertex_stage,
1466        limits.max_storage_textures_in_fragment_stage,
1467    ]
1468    .into_iter()
1469    .max()
1470    .unwrap();
1471
1472    // > 9. If features contains "core-features-and-limits":
1473    //
1474    // NOTE: We don't implement compat (yet?), so we do this unconditionally. See also:
1475    // <https://github.com/gfx-rs/wgpu/issues/8124>
1476
1477    // >   1. Set `limits.maxStorageBuffersInVertexStage` and
1478    // >      `limits.maxStorageBuffersInFragmentStage` to
1479    // >      `limits.maxStorageBuffersPerShaderStage`.
1480    limits.max_storage_buffers_in_vertex_stage = limits.max_storage_buffers_per_shader_stage;
1481    limits.max_storage_buffers_in_fragment_stage = limits.max_storage_buffers_per_shader_stage;
1482
1483    // >   2. Set `limits.maxStorageTexturesInVertexStage` and
1484    // >      `limits.maxStorageTexturesInFragmentStage` to
1485    // >      `limits.maxStorageTexturesPerShaderStage`.
1486    limits.max_storage_textures_in_vertex_stage = limits.max_storage_textures_per_shader_stage;
1487    limits.max_storage_textures_in_fragment_stage = limits.max_storage_textures_per_shader_stage;
1488}
1489
1490#[cfg(test)]
1491mod tests {
1492    use super::*;
1493
1494    fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1495        wgt::DownlevelCapabilities {
1496            flags: wgt::DownlevelFlags::compliant(),
1497            ..Default::default()
1498        }
1499    }
1500
1501    #[test]
1502    fn non_power_of_two_uniform_alignment_always_rejected() {
1503        let limits = wgt::Limits {
1504            min_uniform_buffer_offset_alignment: 3,
1505            ..wgt::Limits::defaults()
1506        };
1507        assert!(!adapter_allowed(
1508            InstanceFlags::empty(),
1509            &"",
1510            &limits,
1511            &compliant_downlevel()
1512        ));
1513        assert!(!adapter_allowed(
1514            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1515            &"",
1516            &limits,
1517            &compliant_downlevel()
1518        ));
1519    }
1520
1521    #[test]
1522    fn non_power_of_two_storage_alignment_always_rejected() {
1523        let limits = wgt::Limits {
1524            min_storage_buffer_offset_alignment: 96,
1525            ..wgt::Limits::defaults()
1526        };
1527        assert!(!adapter_allowed(
1528            InstanceFlags::empty(),
1529            &"",
1530            &limits,
1531            &compliant_downlevel()
1532        ));
1533        assert!(!adapter_allowed(
1534            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1535            &"",
1536            &limits,
1537            &compliant_downlevel()
1538        ));
1539    }
1540
1541    #[test]
1542    fn low_limits_allowed_without_strict_compliance() {
1543        let limits = wgt::Limits {
1544            max_texture_dimension_1d: 1,
1545            ..wgt::Limits::defaults()
1546        };
1547        assert!(adapter_allowed(
1548            InstanceFlags::empty(),
1549            &"",
1550            &limits,
1551            &wgt::DownlevelCapabilities::default()
1552        ));
1553    }
1554
1555    #[test]
1556    fn low_limits_rejected_with_strict_compliance() {
1557        let limits = wgt::Limits {
1558            max_texture_dimension_1d: 1,
1559            ..wgt::Limits::defaults()
1560        };
1561        assert!(!adapter_allowed(
1562            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1563            &"",
1564            &limits,
1565            &compliant_downlevel()
1566        ));
1567    }
1568
1569    #[test]
1570    fn missing_downlevel_flags_rejected_with_strict_compliance() {
1571        let downlevel = wgt::DownlevelCapabilities {
1572            flags: wgt::DownlevelFlags::empty(),
1573            ..Default::default()
1574        };
1575        assert!(!adapter_allowed(
1576            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1577            &"",
1578            &wgt::Limits::defaults(),
1579            &downlevel
1580        ));
1581    }
1582
1583    #[test]
1584    fn fully_compliant_adapter_always_allowed() {
1585        assert!(adapter_allowed(
1586            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1587            &"",
1588            &wgt::Limits::defaults(),
1589            &compliant_downlevel()
1590        ));
1591    }
1592
1593    mod storage_resource_limits {
1594        use super::*;
1595
1596        #[track_caller]
1597        fn assert_normalized_eq(non_normalized: &wgt::Limits, expected: &wgt::Limits) {
1598            let mut normalized = non_normalized.clone();
1599            normalize_max_resource_per_shader_stage_limits(&mut normalized);
1600            assert_eq!(&normalized, expected);
1601        }
1602
1603        #[test]
1604        fn normalization_is_idempotent() {
1605            let original = wgt::Limits {
1606                max_storage_buffers_in_vertex_stage: 16,
1607                max_storage_textures_in_vertex_stage: 9,
1608                ..wgt::Limits::defaults()
1609            };
1610
1611            let mut first_normalization = original.clone();
1612            normalize_max_resource_per_shader_stage_limits(&mut first_normalization);
1613            assert_ne!(original, first_normalization);
1614
1615            let mut second_normalization = first_normalization.clone();
1616            normalize_max_resource_per_shader_stage_limits(&mut second_normalization);
1617            assert_eq!(first_normalization, second_normalization);
1618        }
1619
1620        #[test]
1621        fn limits_presets_already_normalized() {
1622            [
1623                wgt::Limits::defaults(),
1624                wgt::Limits::downlevel_defaults(),
1625                wgt::Limits::downlevel_webgl2_defaults(),
1626                wgt::Limits::unlimited(),
1627            ]
1628            .iter()
1629            .for_each(|l| assert_normalized_eq(l, l))
1630        }
1631
1632        #[test]
1633        fn in_stage_raises_per_shader_stage() {
1634            assert_normalized_eq(
1635                &wgt::Limits {
1636                    max_storage_buffers_per_shader_stage: 8,
1637                    max_storage_buffers_in_vertex_stage: 16,
1638                    max_storage_buffers_in_fragment_stage: 16,
1639                    ..wgt::Limits::defaults()
1640                },
1641                &wgt::Limits {
1642                    max_storage_buffers_per_shader_stage: 16,
1643                    max_storage_buffers_in_vertex_stage: 16,
1644                    max_storage_buffers_in_fragment_stage: 16,
1645                    ..wgt::Limits::defaults()
1646                },
1647            );
1648
1649            assert_normalized_eq(
1650                &wgt::Limits {
1651                    max_storage_textures_per_shader_stage: 8,
1652                    max_storage_textures_in_vertex_stage: 9,
1653                    max_storage_textures_in_fragment_stage: 9,
1654                    ..wgt::Limits::defaults()
1655                },
1656                &wgt::Limits {
1657                    max_storage_textures_per_shader_stage: 9,
1658                    max_storage_textures_in_vertex_stage: 9,
1659                    max_storage_textures_in_fragment_stage: 9,
1660                    ..wgt::Limits::defaults()
1661                },
1662            );
1663        }
1664
1665        #[test]
1666        fn per_shader_stage_raises_in_stage() {
1667            assert_normalized_eq(
1668                &wgt::Limits {
1669                    max_storage_buffers_per_shader_stage: 16,
1670                    max_storage_buffers_in_vertex_stage: 4,
1671                    max_storage_buffers_in_fragment_stage: 4,
1672                    max_storage_textures_per_shader_stage: 8,
1673                    max_storage_textures_in_vertex_stage: 4,
1674                    max_storage_textures_in_fragment_stage: 4,
1675                    ..wgt::Limits::defaults()
1676                },
1677                &wgt::Limits {
1678                    max_storage_buffers_per_shader_stage: 16,
1679                    max_storage_buffers_in_vertex_stage: 16,
1680                    max_storage_buffers_in_fragment_stage: 16,
1681                    max_storage_textures_per_shader_stage: 8,
1682                    max_storage_textures_in_vertex_stage: 8,
1683                    max_storage_textures_in_fragment_stage: 8,
1684                    ..wgt::Limits::defaults()
1685                },
1686            );
1687        }
1688
1689        #[test]
1690        fn lowering_per_shader_stage_noop() {
1691            assert_normalized_eq(
1692                &wgt::Limits {
1693                    max_storage_buffers_per_shader_stage: 1,
1694                    max_storage_textures_per_shader_stage: 1,
1695                    ..wgt::Limits::defaults()
1696                },
1697                &wgt::Limits::defaults(),
1698            );
1699        }
1700    }
1701}