Skip to main content

wgpu_core/
instance.rs

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