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)]
38pub(crate) struct InstanceDevices(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(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    ) -> Arc<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        Arc::new(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 fn from_hal_instance<A: hal::Api>(
211        name: String,
212        hal_instance: <A as hal::Api>::Instance,
213    ) -> Arc<Self> {
214        Arc::new(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<Arc<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 = Arc::new(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<Arc<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 = Arc::new(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<Arc<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 = Arc::new(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<Arc<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 = Arc::new(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<Arc<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<Arc<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<Arc<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: &Arc<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.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: &Arc<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.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: &Arc<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.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) instance: Arc<Instance>,
1047}
1048
1049impl Adapter {
1050    pub(crate) fn new(raw: hal::DynExposedAdapter, instance: Arc<Instance>) -> Arc<Self> {
1051        Arc::new(Self { raw, instance })
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    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1190        profiling::scope!("Adapter::create_device_and_queue_from_hal");
1191        api_log!("Adapter::create_device_and_queue_from_hal");
1192
1193        let device = Device::new(hal_device.device, self, desc, self.instance.flags)?;
1194        let device = Arc::new(device);
1195
1196        let queue = Queue::new(device.clone(), hal_device.queue, self.instance.flags)?;
1197        let queue = Arc::new(queue);
1198
1199        device.set_queue(&queue);
1200        device.late_init_resources_with_queue()?;
1201
1202        resource_log!("Created Device {:?}", Arc::as_ptr(&device));
1203        resource_log!("Created Queue {:?}", Arc::as_ptr(&queue));
1204
1205        self.instance.devices.push(&device);
1206
1207        Ok((device, queue))
1208    }
1209
1210    /// Validate a device descriptor.
1211    ///
1212    /// This validates the provided device descriptor as if it were passed to
1213    /// [`Self::request_device`]. If [`InstanceFlags::STRICT_WEBGPU_COMPLIANCE`] is active,
1214    /// the requested extensions in the descriptor will be filtered to remove `wgpu`
1215    /// extensions, except for those that are included in [`limits::EXEMPT_FEATURES`].
1216    ///
1217    /// This may be useful when it is necessary to obtain the device itself from a raw hal
1218    /// API, but the rest of the `request_device` validation is still desired.
1219    pub fn validate_device_descriptor(
1220        &self,
1221        desc: &mut DeviceDescriptor,
1222    ) -> Result<(), RequestDeviceError> {
1223        filter_features_and_limits(
1224            self.instance.flags,
1225            &mut desc.required_features,
1226            &mut desc.required_limits,
1227        );
1228
1229        // Verify all features were exposed by the adapter
1230        if !self.raw.features.contains(desc.required_features) {
1231            return Err(RequestDeviceError::UnsupportedFeature(
1232                desc.required_features - self.raw.features,
1233            ));
1234        }
1235
1236        // Check if experimental features are permitted to be enabled.
1237        if desc
1238            .required_features
1239            .intersects(wgt::Features::all_experimental_mask())
1240            && !desc.experimental_features.is_enabled()
1241        {
1242            return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
1243                desc.required_features
1244                    .intersection(wgt::Features::all_experimental_mask()),
1245            ));
1246        }
1247
1248        let caps = &self.raw.capabilities;
1249        if Backends::PRIMARY.contains(Backends::from(self.backend()))
1250            && !caps.downlevel.is_webgpu_compliant()
1251        {
1252            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
1253            log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
1254            log::warn!("{:#?}", caps.downlevel);
1255        }
1256
1257        // Verify feature preconditions
1258        if desc
1259            .required_features
1260            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1261            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
1262        {
1263            log::warn!(
1264                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
1265                        This is a massive performance footgun and likely not what you wanted"
1266            );
1267        }
1268
1269        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
1270            return Err(RequestDeviceError::LimitsExceeded(failed));
1271        }
1272
1273        Ok(())
1274    }
1275
1276    pub fn request_device(
1277        self: &Arc<Self>,
1278        desc: &DeviceDescriptor,
1279    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1280        profiling::scope!("Adapter::request_device");
1281        api_log!("Adapter::request_device");
1282
1283        let mut desc = desc.clone();
1284        self.validate_device_descriptor(&mut desc)?;
1285
1286        let open = unsafe {
1287            self.raw.adapter.open(
1288                desc.required_features,
1289                &desc.required_limits,
1290                &desc.memory_hints,
1291            )
1292        }
1293        .map_err(DeviceError::from_hal)?;
1294
1295        unsafe { self.create_device_and_queue_from_hal(open, &desc) }
1296    }
1297}
1298
1299impl Drop for Adapter {
1300    #[allow(trivial_casts)]
1301    fn drop(&mut self) {
1302        profiling::scope!("Adapter::drop");
1303        api_log!("Adapter::drop {:?}", self as *const _);
1304    }
1305}
1306
1307crate::impl_resource_type!(Adapter);
1308crate::impl_storage_item!(Adapter);
1309
1310#[derive(Clone, Debug, Error)]
1311#[non_exhaustive]
1312pub enum GetSurfaceSupportError {
1313    #[error("Surface is not supported for the specified backend {0}")]
1314    NotSupportedByBackend(Backend),
1315    #[error("Failed to retrieve surface capabilities for the specified adapter.")]
1316    FailedToRetrieveSurfaceCapabilitiesForAdapter,
1317}
1318
1319#[derive(Clone, Debug, Error)]
1320/// Error when requesting a device from the adapter
1321#[non_exhaustive]
1322pub enum RequestDeviceError {
1323    #[error(transparent)]
1324    Device(#[from] DeviceError),
1325    #[error(transparent)]
1326    LimitsExceeded(#[from] FailedLimit),
1327    #[error("Failed to initialize Timestamp Normalizer")]
1328    TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
1329    #[error("Unsupported features were requested: {0}")]
1330    UnsupportedFeature(wgt::Features),
1331    #[error(
1332        "Some experimental features, {0}, were requested, but experimental features are not enabled"
1333    )]
1334    ExperimentalFeaturesNotEnabled(wgt::Features),
1335}
1336
1337#[derive(Clone, Debug, Error)]
1338#[non_exhaustive]
1339pub enum CreateSurfaceError {
1340    #[error("The backend {0} was not enabled on the instance.")]
1341    BackendNotEnabled(Backend),
1342    #[error("Failed to create surface for any enabled backend: {0:?}")]
1343    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
1344    #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
1345    MismatchingDisplayHandle,
1346    #[error(
1347        "No `DisplayHandle` is available to create this surface with.  When creating a surface with `create_surface()` \
1348        you must specify a display handle in `InstanceDescriptor::display`.  \
1349        Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
1350        you must use `create_surface_unsafe()`."
1351    )]
1352    MissingDisplayHandle,
1353}
1354
1355impl Global {
1356    /// Creates a new surface targeting the given display/window handles.
1357    ///
1358    /// Internally attempts to create hal surfaces for all enabled backends.
1359    ///
1360    /// Fails only if creation for surfaces for all enabled backends fails in which case
1361    /// the error for each enabled backend is listed.
1362    /// Vice versa, if creation for any backend succeeds, success is returned.
1363    /// Surface creation errors are logged to the debug log in any case.
1364    ///
1365    /// id_in:
1366    /// - If `Some`, the id to assign to the surface. A new one will be generated otherwise.
1367    ///
1368    /// # Safety
1369    ///
1370    /// - `display_handle` must be a valid object to create a surface upon,
1371    ///   falls back to the instance display handle otherwise.
1372    /// - `window_handle` must remain valid as long as the returned
1373    ///   [`SurfaceId`] is being used.
1374    pub unsafe fn instance_create_surface(
1375        &self,
1376        display_handle: Option<raw_window_handle::RawDisplayHandle>,
1377        window_handle: raw_window_handle::RawWindowHandle,
1378        id_in: Option<SurfaceId>,
1379    ) -> Result<SurfaceId, CreateSurfaceError> {
1380        let surface = unsafe { self.instance.create_surface(display_handle, window_handle) }?;
1381        let id = self.surfaces.prepare(id_in).assign(surface);
1382        Ok(id)
1383    }
1384
1385    /// Creates a new surface from the given drm configuration.
1386    ///
1387    /// # Safety
1388    ///
1389    /// - All parameters must point to valid DRM values.
1390    ///
1391    /// # Platform Support
1392    ///
1393    /// This function requires the `"drm"` feature, and is only available on
1394    /// non-apple Unix-like platforms (Linux, FreeBSD) and currently only works
1395    /// with the Vulkan backend.
1396    #[cfg(drm)]
1397    pub unsafe fn instance_create_surface_from_drm(
1398        &self,
1399        fd: i32,
1400        plane: u32,
1401        connector_id: u32,
1402        width: u32,
1403        height: u32,
1404        refresh_rate: u32,
1405        id_in: Option<SurfaceId>,
1406    ) -> Result<SurfaceId, CreateSurfaceError> {
1407        let surface = unsafe {
1408            self.instance.create_surface_from_drm(
1409                fd,
1410                plane,
1411                connector_id,
1412                width,
1413                height,
1414                refresh_rate,
1415            )
1416        }?;
1417        let id = self.surfaces.prepare(id_in).assign(surface);
1418
1419        Ok(id)
1420    }
1421
1422    /// # Safety
1423    ///
1424    /// `layer` must be a valid pointer.
1425    #[cfg(metal)]
1426    pub unsafe fn instance_create_surface_metal(
1427        &self,
1428        layer: *mut core::ffi::c_void,
1429        id_in: Option<SurfaceId>,
1430    ) -> Result<SurfaceId, CreateSurfaceError> {
1431        let surface = unsafe { self.instance.create_surface_metal(layer) }?;
1432        let id = self.surfaces.prepare(id_in).assign(surface);
1433        Ok(id)
1434    }
1435
1436    #[cfg(dx12)]
1437    /// # Safety
1438    ///
1439    /// The visual must be valid and able to be used to make a swapchain with.
1440    pub unsafe fn instance_create_surface_from_visual(
1441        &self,
1442        visual: *mut core::ffi::c_void,
1443        id_in: Option<SurfaceId>,
1444    ) -> Result<SurfaceId, CreateSurfaceError> {
1445        let surface = unsafe { self.instance.create_surface_from_visual(visual) }?;
1446        let id = self.surfaces.prepare(id_in).assign(surface);
1447        Ok(id)
1448    }
1449
1450    #[cfg(dx12)]
1451    /// # Safety
1452    ///
1453    /// The surface_handle must be valid and able to be used to make a swapchain with.
1454    pub unsafe fn instance_create_surface_from_surface_handle(
1455        &self,
1456        surface_handle: *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_surface_handle(surface_handle)
1462        }?;
1463        let id = self.surfaces.prepare(id_in).assign(surface);
1464        Ok(id)
1465    }
1466
1467    #[cfg(dx12)]
1468    /// # Safety
1469    ///
1470    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.
1471    pub unsafe fn instance_create_surface_from_swap_chain_panel(
1472        &self,
1473        swap_chain_panel: *mut core::ffi::c_void,
1474        id_in: Option<SurfaceId>,
1475    ) -> Result<SurfaceId, CreateSurfaceError> {
1476        let surface = unsafe {
1477            self.instance
1478                .create_surface_from_swap_chain_panel(swap_chain_panel)
1479        }?;
1480        let id = self.surfaces.prepare(id_in).assign(surface);
1481        Ok(id)
1482    }
1483
1484    pub fn surface_drop(&self, id: SurfaceId) {
1485        self.surfaces.remove(id);
1486    }
1487
1488    pub fn enumerate_adapters(
1489        &self,
1490        backends: Backends,
1491        apply_limit_buckets: bool,
1492    ) -> Vec<AdapterId> {
1493        let adapters = self
1494            .instance
1495            .enumerate_adapters(backends, apply_limit_buckets);
1496        adapters
1497            .into_iter()
1498            .map(|adapter| self.hub.adapters.prepare(None).assign(adapter))
1499            .collect()
1500    }
1501
1502    pub fn request_adapter(
1503        &self,
1504        desc: &RequestAdapterOptions,
1505        backends: Backends,
1506        id_in: Option<AdapterId>,
1507    ) -> Result<AdapterId, wgt::RequestAdapterError> {
1508        let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id));
1509        let desc = wgt::RequestAdapterOptions {
1510            power_preference: desc.power_preference,
1511            force_fallback_adapter: desc.force_fallback_adapter,
1512            compatible_surface: compatible_surface.as_deref(),
1513            apply_limit_buckets: desc.apply_limit_buckets,
1514        };
1515        let adapter = self.instance.request_adapter(&desc, backends)?;
1516        let id = self.hub.adapters.prepare(id_in).assign(adapter);
1517        Ok(id)
1518    }
1519
1520    /// Create an adapter from a HAL adapter.
1521    ///
1522    /// The HAL adapter may be obtained e.g. by calling `enumerate_adapters` on
1523    /// the HAL directly.
1524    ///
1525    /// If [limit bucketing][lt] is desired, [`crate::limits::apply_limit_buckets`]
1526    /// should be called with the HAL adapter before calling this function.
1527    ///
1528    /// # Safety
1529    ///
1530    /// `hal_adapter` must be created from this global internal instance handle.
1531    ///
1532    /// [lt]: crate::limits#Limit-bucketing
1533    pub unsafe fn create_adapter_from_hal(
1534        &self,
1535        hal_adapter: hal::DynExposedAdapter,
1536        input: Option<AdapterId>,
1537    ) -> AdapterId {
1538        let fid = self.hub.adapters.prepare(input);
1539        fid.assign(unsafe { self.instance.create_adapter_from_hal(hal_adapter) })
1540    }
1541
1542    pub fn adapter_get_info(&self, adapter_id: AdapterId) -> wgt::AdapterInfo {
1543        let adapter = self.hub.adapters.get(adapter_id);
1544        adapter.get_info()
1545    }
1546
1547    pub fn adapter_get_texture_format_features(
1548        &self,
1549        adapter_id: AdapterId,
1550        format: wgt::TextureFormat,
1551    ) -> wgt::TextureFormatFeatures {
1552        let adapter = self.hub.adapters.get(adapter_id);
1553        adapter.get_texture_format_features(format)
1554    }
1555
1556    pub fn adapter_features(&self, adapter_id: AdapterId) -> wgt::Features {
1557        let adapter = self.hub.adapters.get(adapter_id);
1558        adapter.features()
1559    }
1560
1561    pub fn adapter_limits(&self, adapter_id: AdapterId) -> wgt::Limits {
1562        let adapter = self.hub.adapters.get(adapter_id);
1563        adapter.limits()
1564    }
1565
1566    pub fn adapter_downlevel_capabilities(
1567        &self,
1568        adapter_id: AdapterId,
1569    ) -> wgt::DownlevelCapabilities {
1570        let adapter = self.hub.adapters.get(adapter_id);
1571        adapter.downlevel_capabilities()
1572    }
1573
1574    pub fn adapter_get_presentation_timestamp(
1575        &self,
1576        adapter_id: AdapterId,
1577    ) -> wgt::PresentationTimestamp {
1578        let adapter = self.hub.adapters.get(adapter_id);
1579        adapter.get_presentation_timestamp()
1580    }
1581
1582    pub fn adapter_cooperative_matrix_properties(
1583        &self,
1584        adapter_id: AdapterId,
1585    ) -> Vec<wgt::CooperativeMatrixProperties> {
1586        let adapter = self.hub.adapters.get(adapter_id);
1587        adapter.cooperative_matrix_properties()
1588    }
1589
1590    pub fn adapter_drop(&self, adapter_id: AdapterId) {
1591        self.hub.adapters.remove(adapter_id);
1592    }
1593}
1594
1595impl Global {
1596    pub fn adapter_request_device(
1597        &self,
1598        adapter_id: AdapterId,
1599        desc: &DeviceDescriptor,
1600        device_id_in: Option<DeviceId>,
1601        queue_id_in: Option<QueueId>,
1602    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1603        let device_fid = self.hub.devices.prepare(device_id_in);
1604        let queue_fid = self.hub.queues.prepare(queue_id_in);
1605
1606        let adapter = self.hub.adapters.get(adapter_id);
1607        let (device, queue) = adapter.request_device(desc)?;
1608
1609        let device_id = device_fid.assign(device);
1610        resource_log!("Created Device {:?}", device_id);
1611
1612        let queue_id = queue_fid.assign(queue);
1613        resource_log!("Created Queue {:?}", queue_id);
1614
1615        Ok((device_id, queue_id))
1616    }
1617
1618    pub fn adapter_validate_device_descriptor(
1619        &self,
1620        adapter_id: AdapterId,
1621        desc: &mut DeviceDescriptor,
1622    ) -> Result<(), RequestDeviceError> {
1623        let adapter = self.hub.adapters.get(adapter_id);
1624        adapter.validate_device_descriptor(desc)
1625    }
1626
1627    /// # Safety
1628    ///
1629    /// - `hal_device` must be created from `adapter_id` or its internal handle.
1630    /// - `desc` must be a subset of `hal_device` features and limits.
1631    pub unsafe fn create_device_from_hal(
1632        &self,
1633        adapter_id: AdapterId,
1634        hal_device: hal::DynOpenDevice,
1635        desc: &DeviceDescriptor,
1636        device_id_in: Option<DeviceId>,
1637        queue_id_in: Option<QueueId>,
1638    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1639        let devices_fid = self.hub.devices.prepare(device_id_in);
1640        let queues_fid = self.hub.queues.prepare(queue_id_in);
1641
1642        let adapter = self.hub.adapters.get(adapter_id);
1643        let (device, queue) =
1644            unsafe { adapter.create_device_and_queue_from_hal(hal_device, desc) }?;
1645
1646        let device_id = devices_fid.assign(device);
1647
1648        let queue_id = queues_fid.assign(queue);
1649
1650        Ok((device_id, queue_id))
1651    }
1652}
1653
1654/// This function checks that the adapter obeys WebGPU's adapter capability
1655/// guarantees. Most of the limits are adjusted in wgpu-hal's
1656/// `adjust_raw_limits` fn. So we only check the remaining properties here.
1657/// See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>.
1658fn adapter_allowed(
1659    flags: InstanceFlags,
1660    info: &impl fmt::Debug,
1661    limits: &wgt::Limits,
1662    downlevel: &wgt::DownlevelCapabilities,
1663) -> bool {
1664    // Check "All alignment-class limits must be powers of 2."
1665    //
1666    // Even if the application has not requested strict WebGPU compliance,
1667    // non-power-of-two alignment limits are nonsensical, so don't attempt
1668    // to use such a device.
1669    let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1670    if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1671        log::error!(
1672            "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1673            info,
1674            min_uniform_buffer_offset_alignment
1675        );
1676        return false;
1677    }
1678    let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1679    if !min_storage_buffer_offset_alignment.is_power_of_two() {
1680        log::error!(
1681            "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1682            info,
1683            min_storage_buffer_offset_alignment
1684        );
1685        return false;
1686    }
1687
1688    // Following checks are only enabled if `STRICT_WEBGPU_COMPLIANCE` is set.
1689    if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1690        return true;
1691    }
1692
1693    // Check "All supported limits must be either the default value or better."
1694    let mut min_limits = wgt::Limits::defaults();
1695    min_limits.zero_native_only();
1696    let failed_limits = check_limits(&min_limits, limits);
1697    if !failed_limits.is_empty() {
1698        log::debug!(
1699            "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1700            info,
1701            failed_limits
1702        );
1703        return false;
1704    }
1705
1706    if !downlevel.is_webgpu_compliant() {
1707        let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1708        log::debug!(
1709            "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1710            info,
1711            missing_flags
1712        );
1713        return false;
1714    }
1715
1716    true
1717}
1718
1719fn filter_features_and_limits(
1720    flags: InstanceFlags,
1721    features: &mut wgt::Features,
1722    limits: &mut wgt::Limits,
1723) {
1724    if flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1725        *features &= wgt::Features::all_webgpu_mask() | limits::EXEMPT_FEATURES;
1726        limits.zero_native_only();
1727    }
1728}
1729
1730#[cfg(test)]
1731mod tests {
1732    use super::*;
1733
1734    fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1735        wgt::DownlevelCapabilities {
1736            flags: wgt::DownlevelFlags::compliant(),
1737            ..Default::default()
1738        }
1739    }
1740
1741    #[test]
1742    fn non_power_of_two_uniform_alignment_always_rejected() {
1743        let limits = wgt::Limits {
1744            min_uniform_buffer_offset_alignment: 3,
1745            ..wgt::Limits::defaults()
1746        };
1747        assert!(!adapter_allowed(
1748            InstanceFlags::empty(),
1749            &"",
1750            &limits,
1751            &compliant_downlevel()
1752        ));
1753        assert!(!adapter_allowed(
1754            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1755            &"",
1756            &limits,
1757            &compliant_downlevel()
1758        ));
1759    }
1760
1761    #[test]
1762    fn non_power_of_two_storage_alignment_always_rejected() {
1763        let limits = wgt::Limits {
1764            min_storage_buffer_offset_alignment: 96,
1765            ..wgt::Limits::defaults()
1766        };
1767        assert!(!adapter_allowed(
1768            InstanceFlags::empty(),
1769            &"",
1770            &limits,
1771            &compliant_downlevel()
1772        ));
1773        assert!(!adapter_allowed(
1774            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1775            &"",
1776            &limits,
1777            &compliant_downlevel()
1778        ));
1779    }
1780
1781    #[test]
1782    fn low_limits_allowed_without_strict_compliance() {
1783        let limits = wgt::Limits {
1784            max_texture_dimension_1d: 1,
1785            ..wgt::Limits::defaults()
1786        };
1787        assert!(adapter_allowed(
1788            InstanceFlags::empty(),
1789            &"",
1790            &limits,
1791            &wgt::DownlevelCapabilities::default()
1792        ));
1793    }
1794
1795    #[test]
1796    fn low_limits_rejected_with_strict_compliance() {
1797        let limits = wgt::Limits {
1798            max_texture_dimension_1d: 1,
1799            ..wgt::Limits::defaults()
1800        };
1801        assert!(!adapter_allowed(
1802            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1803            &"",
1804            &limits,
1805            &compliant_downlevel()
1806        ));
1807    }
1808
1809    #[test]
1810    fn missing_downlevel_flags_rejected_with_strict_compliance() {
1811        let downlevel = wgt::DownlevelCapabilities {
1812            flags: wgt::DownlevelFlags::empty(),
1813            ..Default::default()
1814        };
1815        assert!(!adapter_allowed(
1816            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1817            &"",
1818            &wgt::Limits::defaults(),
1819            &downlevel
1820        ));
1821    }
1822
1823    #[test]
1824    fn fully_compliant_adapter_always_allowed() {
1825        assert!(adapter_allowed(
1826            InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1827            &"",
1828            &wgt::Limits::defaults(),
1829            &compliant_downlevel()
1830        ));
1831    }
1832}