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