wgpu_core/
instance.rs

1use alloc::{
2    borrow::{Cow, ToOwned as _},
3    boxed::Box,
4    string::String,
5    sync::Arc,
6    vec,
7    vec::Vec,
8};
9
10use hashbrown::HashMap;
11use thiserror::Error;
12use wgt::error::{ErrorType, WebGpuError};
13
14use crate::{
15    api_log, api_log_debug,
16    device::{queue::Queue, resource::Device, DeviceDescriptor, DeviceError},
17    global::Global,
18    id::{markers, AdapterId, DeviceId, QueueId, SurfaceId},
19    lock::{rank, Mutex},
20    present::Presentation,
21    resource::ResourceType,
22    resource_log,
23    timestamp_normalization::TimestampNormalizerInitError,
24    DOWNLEVEL_WARNING_MESSAGE,
25};
26
27use wgt::{Backend, Backends, PowerPreference};
28
29pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
30
31#[derive(Clone, Debug, Error)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
34pub struct FailedLimit {
35    name: Cow<'static, str>,
36    requested: u64,
37    allowed: u64,
38}
39
40impl WebGpuError for FailedLimit {
41    fn webgpu_error_type(&self) -> ErrorType {
42        ErrorType::Validation
43    }
44}
45
46fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLimit> {
47    let mut failed = Vec::new();
48
49    requested.check_limits_with_fail_fn(allowed, false, |name, requested, allowed| {
50        failed.push(FailedLimit {
51            name: Cow::Borrowed(name),
52            requested,
53            allowed,
54        })
55    });
56
57    failed
58}
59
60#[test]
61fn downlevel_default_limits_less_than_default_limits() {
62    let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
63    assert!(
64        res.is_empty(),
65        "Downlevel limits are greater than default limits",
66    )
67}
68
69#[derive(Default)]
70pub struct Instance {
71    #[allow(dead_code)]
72    name: String,
73
74    /// List of instances per `wgpu-hal` backend.
75    ///
76    /// The ordering in this list implies prioritization and needs to be preserved.
77    instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,
78
79    /// The backends that were requested by the user.
80    requested_backends: Backends,
81
82    /// The backends that we could have attempted to obtain from `wgpu-hal` —
83    /// those for which support is compiled in, currently.
84    ///
85    /// The union of this and `requested_backends` is the set of backends that would be used,
86    /// independent of whether accessing the drivers/hardware for them succeeds.
87    /// To obtain the set of backends actually in use by this instance, check
88    /// `instance_per_backend` instead.
89    supported_backends: Backends,
90
91    pub flags: wgt::InstanceFlags,
92}
93
94impl Instance {
95    pub fn new(name: &str, instance_desc: &wgt::InstanceDescriptor) -> Self {
96        let mut this = Self {
97            name: name.to_owned(),
98            instance_per_backend: Vec::new(),
99            requested_backends: instance_desc.backends,
100            supported_backends: Backends::empty(),
101            flags: instance_desc.flags,
102        };
103
104        #[cfg(vulkan)]
105        this.try_add_hal(hal::api::Vulkan, instance_desc);
106        #[cfg(metal)]
107        this.try_add_hal(hal::api::Metal, instance_desc);
108        #[cfg(dx12)]
109        this.try_add_hal(hal::api::Dx12, instance_desc);
110        #[cfg(gles)]
111        this.try_add_hal(hal::api::Gles, instance_desc);
112        #[cfg(feature = "noop")]
113        this.try_add_hal(hal::api::Noop, instance_desc);
114
115        this
116    }
117
118    /// Helper for `Instance::new()`; attempts to add a single `wgpu-hal` backend to this instance.
119    fn try_add_hal<A: hal::Api>(&mut self, _: A, instance_desc: &wgt::InstanceDescriptor) {
120        // Whether or not the backend was requested, and whether or not it succeeds,
121        // note that we *could* try it.
122        self.supported_backends |= A::VARIANT.into();
123
124        if !instance_desc.backends.contains(A::VARIANT.into()) {
125            log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
126            return;
127        }
128
129        let hal_desc = hal::InstanceDescriptor {
130            name: "wgpu",
131            flags: self.flags,
132            memory_budget_thresholds: instance_desc.memory_budget_thresholds,
133            backend_options: instance_desc.backend_options.clone(),
134        };
135
136        use hal::Instance as _;
137        match unsafe { A::Instance::init(&hal_desc) } {
138            Ok(instance) => {
139                log::debug!("Instance::new: created {:?} backend", A::VARIANT);
140                self.instance_per_backend
141                    .push((A::VARIANT, Box::new(instance)));
142            }
143            Err(err) => {
144                log::debug!(
145                    "Instance::new: failed to create {:?} backend: {:?}",
146                    A::VARIANT,
147                    err
148                );
149            }
150        }
151    }
152
153    pub(crate) fn from_hal_instance<A: hal::Api>(
154        name: String,
155        hal_instance: <A as hal::Api>::Instance,
156    ) -> Self {
157        Self {
158            name,
159            instance_per_backend: vec![(A::VARIANT, Box::new(hal_instance))],
160            requested_backends: A::VARIANT.into(),
161            supported_backends: A::VARIANT.into(),
162            flags: wgt::InstanceFlags::default(),
163        }
164    }
165
166    pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynInstance> {
167        self.instance_per_backend
168            .iter()
169            .find_map(|(instance_backend, instance)| {
170                (*instance_backend == backend).then(|| instance.as_ref())
171            })
172    }
173
174    /// # Safety
175    ///
176    /// - The raw instance handle returned must not be manually destroyed.
177    pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {
178        self.raw(A::VARIANT).map(|instance| {
179            instance
180                .as_any()
181                .downcast_ref()
182                // This should be impossible. It would mean that backend instance and enum type are mismatching.
183                .expect("Stored instance is not of the correct type")
184        })
185    }
186
187    /// Creates a new surface targeting the given display/window handles.
188    ///
189    /// Internally attempts to create hal surfaces for all enabled backends.
190    ///
191    /// Fails only if creation for surfaces for all enabled backends fails in which case
192    /// the error for each enabled backend is listed.
193    /// Vice versa, if creation for any backend succeeds, success is returned.
194    /// Surface creation errors are logged to the debug log in any case.
195    ///
196    /// # Safety
197    ///
198    /// - `display_handle` must be a valid object to create a surface upon.
199    /// - `window_handle` must remain valid as long as the returned
200    ///   [`SurfaceId`] is being used.
201    #[cfg(feature = "raw-window-handle")]
202    pub unsafe fn create_surface(
203        &self,
204        display_handle: raw_window_handle::RawDisplayHandle,
205        window_handle: raw_window_handle::RawWindowHandle,
206    ) -> Result<Surface, CreateSurfaceError> {
207        profiling::scope!("Instance::create_surface");
208
209        let mut errors = HashMap::default();
210        let mut surface_per_backend = HashMap::default();
211
212        for (backend, instance) in &self.instance_per_backend {
213            match unsafe {
214                instance
215                    .as_ref()
216                    .create_surface(display_handle, window_handle)
217            } {
218                Ok(raw) => {
219                    surface_per_backend.insert(*backend, raw);
220                }
221                Err(err) => {
222                    log::debug!(
223                        "Instance::create_surface: failed to create surface for {backend:?}: {err:?}"
224                    );
225                    errors.insert(*backend, err);
226                }
227            }
228        }
229
230        if surface_per_backend.is_empty() {
231            Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
232                errors,
233            ))
234        } else {
235            let surface = Surface {
236                presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
237                surface_per_backend,
238            };
239
240            Ok(surface)
241        }
242    }
243
244    /// Creates a new surface from the given drm configuration.
245    ///
246    /// # Safety
247    ///
248    /// - All parameters must point to valid DRM values.
249    ///
250    /// # Platform Support
251    ///
252    /// This function is only available on non-apple Unix-like platforms (Linux, FreeBSD) and
253    /// currently only works with the Vulkan backend.
254    #[cfg(all(unix, not(target_vendor = "apple"), not(target_family = "wasm")))]
255    #[cfg_attr(not(vulkan), expect(unused_variables))]
256    pub unsafe fn create_surface_from_drm(
257        &self,
258        fd: i32,
259        plane: u32,
260        connector_id: u32,
261        width: u32,
262        height: u32,
263        refresh_rate: u32,
264    ) -> Result<Surface, CreateSurfaceError> {
265        profiling::scope!("Instance::create_surface_from_drm");
266
267        let mut errors = HashMap::default();
268        let mut surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>> =
269            HashMap::default();
270
271        #[cfg(vulkan)]
272        {
273            let instance = unsafe { self.as_hal::<hal::api::Vulkan>() }
274                .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Vulkan))?;
275
276            // Safety must be upheld by the caller
277            match unsafe {
278                instance.create_surface_from_drm(
279                    fd,
280                    plane,
281                    connector_id,
282                    width,
283                    height,
284                    refresh_rate,
285                )
286            } {
287                Ok(surface) => {
288                    surface_per_backend.insert(Backend::Vulkan, Box::new(surface));
289                }
290                Err(err) => {
291                    errors.insert(Backend::Vulkan, err);
292                }
293            }
294        }
295
296        if surface_per_backend.is_empty() {
297            Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
298                errors,
299            ))
300        } else {
301            let surface = Surface {
302                presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
303                surface_per_backend,
304            };
305
306            Ok(surface)
307        }
308    }
309
310    /// # Safety
311    ///
312    /// `layer` must be a valid pointer.
313    #[cfg(metal)]
314    pub unsafe fn create_surface_metal(
315        &self,
316        layer: *mut core::ffi::c_void,
317    ) -> Result<Surface, CreateSurfaceError> {
318        profiling::scope!("Instance::create_surface_metal");
319
320        let instance = unsafe { self.as_hal::<hal::api::Metal>() }
321            .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Metal))?;
322
323        let layer = layer.cast();
324        // SAFETY: We do this cast and deref. (rather than using `metal` to get the
325        // object we want) to avoid direct coupling on the `metal` crate.
326        //
327        // To wit, this pointer…
328        //
329        // - …is properly aligned.
330        // - …is dereferenceable to a `MetalLayerRef` as an invariant of the `metal`
331        //   field.
332        // - …points to an _initialized_ `MetalLayerRef`.
333        // - …is only ever aliased via an immutable reference that lives within this
334        //   lexical scope.
335        let layer = unsafe { &*layer };
336        let raw_surface: Box<dyn hal::DynSurface> =
337            Box::new(instance.create_surface_from_layer(layer));
338
339        let surface = Surface {
340            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
341            surface_per_backend: core::iter::once((Backend::Metal, raw_surface)).collect(),
342        };
343
344        Ok(surface)
345    }
346
347    #[cfg(dx12)]
348    fn create_surface_dx12(
349        &self,
350        create_surface_func: impl FnOnce(&hal::dx12::Instance) -> hal::dx12::Surface,
351    ) -> Result<Surface, CreateSurfaceError> {
352        let instance = unsafe { self.as_hal::<hal::api::Dx12>() }
353            .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?;
354        let surface: Box<dyn hal::DynSurface> = Box::new(create_surface_func(instance));
355
356        let surface = Surface {
357            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
358            surface_per_backend: core::iter::once((Backend::Dx12, surface)).collect(),
359        };
360
361        Ok(surface)
362    }
363
364    #[cfg(dx12)]
365    /// # Safety
366    ///
367    /// The visual must be valid and able to be used to make a swapchain with.
368    pub unsafe fn create_surface_from_visual(
369        &self,
370        visual: *mut core::ffi::c_void,
371    ) -> Result<Surface, CreateSurfaceError> {
372        profiling::scope!("Instance::instance_create_surface_from_visual");
373        self.create_surface_dx12(|inst| unsafe { inst.create_surface_from_visual(visual) })
374    }
375
376    #[cfg(dx12)]
377    /// # Safety
378    ///
379    /// The surface_handle must be valid and able to be used to make a swapchain with.
380    pub unsafe fn create_surface_from_surface_handle(
381        &self,
382        surface_handle: *mut core::ffi::c_void,
383    ) -> Result<Surface, CreateSurfaceError> {
384        profiling::scope!("Instance::instance_create_surface_from_surface_handle");
385        self.create_surface_dx12(|inst| unsafe {
386            inst.create_surface_from_surface_handle(surface_handle)
387        })
388    }
389
390    #[cfg(dx12)]
391    /// # Safety
392    ///
393    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.
394    pub unsafe fn create_surface_from_swap_chain_panel(
395        &self,
396        swap_chain_panel: *mut core::ffi::c_void,
397    ) -> Result<Surface, CreateSurfaceError> {
398        profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
399        self.create_surface_dx12(|inst| unsafe {
400            inst.create_surface_from_swap_chain_panel(swap_chain_panel)
401        })
402    }
403
404    pub fn enumerate_adapters(&self, backends: Backends) -> Vec<Adapter> {
405        profiling::scope!("Instance::enumerate_adapters");
406        api_log!("Instance::enumerate_adapters");
407
408        let mut adapters = Vec::new();
409        for (_backend, instance) in self
410            .instance_per_backend
411            .iter()
412            .filter(|(backend, _)| backends.contains(Backends::from(*backend)))
413        {
414            // NOTE: We might be using `profiling` without any features. The empty backend of this
415            // macro emits no code, so unused code linting changes depending on the backend.
416            profiling::scope!("enumerating", &*alloc::format!("{_backend:?}"));
417
418            let hal_adapters = unsafe { instance.enumerate_adapters(None) };
419            for raw in hal_adapters {
420                let adapter = Adapter::new(raw);
421                api_log_debug!("Adapter {:?}", adapter.raw.info);
422                adapters.push(adapter);
423            }
424        }
425        adapters
426    }
427
428    pub fn request_adapter(
429        &self,
430        desc: &wgt::RequestAdapterOptions<&Surface>,
431        backends: Backends,
432    ) -> Result<Adapter, wgt::RequestAdapterError> {
433        profiling::scope!("Instance::request_adapter");
434        api_log!("Instance::request_adapter");
435
436        let mut adapters = Vec::new();
437        let mut incompatible_surface_backends = Backends::empty();
438        let mut no_fallback_backends = Backends::empty();
439        let mut no_adapter_backends = Backends::empty();
440
441        for &(backend, ref instance) in self
442            .instance_per_backend
443            .iter()
444            .filter(|&&(backend, _)| backends.contains(Backends::from(backend)))
445        {
446            let compatible_hal_surface = desc
447                .compatible_surface
448                .and_then(|surface| surface.raw(backend));
449
450            let mut backend_adapters =
451                unsafe { instance.enumerate_adapters(compatible_hal_surface) };
452            if backend_adapters.is_empty() {
453                log::debug!("enabled backend `{backend:?}` has no adapters");
454                no_adapter_backends |= Backends::from(backend);
455                // by continuing, we avoid setting the further error bits below
456                continue;
457            }
458
459            if desc.force_fallback_adapter {
460                log::debug!("Filtering `{backend:?}` for `force_fallback_adapter`");
461                backend_adapters.retain(|exposed| {
462                    let keep = exposed.info.device_type == wgt::DeviceType::Cpu;
463                    if !keep {
464                        log::debug!("* Eliminating adapter `{}`", exposed.info.name);
465                    }
466                    keep
467                });
468                if backend_adapters.is_empty() {
469                    log::debug!("* Backend `{backend:?}` has no fallback adapters");
470                    no_fallback_backends |= Backends::from(backend);
471                    continue;
472                }
473            }
474
475            if let Some(surface) = desc.compatible_surface {
476                backend_adapters.retain(|exposed| {
477                    let capabilities = surface.get_capabilities_with_raw(exposed);
478                    if let Err(err) = capabilities {
479                        log::debug!(
480                            "Adapter {:?} not compatible with surface: {}",
481                            exposed.info,
482                            err
483                        );
484                        incompatible_surface_backends |= Backends::from(backend);
485                        false
486                    } else {
487                        true
488                    }
489                });
490                if backend_adapters.is_empty() {
491                    incompatible_surface_backends |= Backends::from(backend);
492                    continue;
493                }
494            }
495            adapters.extend(backend_adapters);
496        }
497
498        match desc.power_preference {
499            PowerPreference::LowPower => {
500                sort(&mut adapters, true);
501            }
502            PowerPreference::HighPerformance => {
503                sort(&mut adapters, false);
504            }
505            PowerPreference::None => {}
506        };
507
508        fn sort(adapters: &mut [hal::DynExposedAdapter], prefer_integrated_gpu: bool) {
509            adapters
510                .sort_by_key(|adapter| get_order(adapter.info.device_type, prefer_integrated_gpu));
511        }
512
513        fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 {
514            // Since devices of type "Other" might really be "Unknown" and come
515            // from APIs like OpenGL that don't specify device type, Prefer more
516            // Specific types over Other.
517            //
518            // This means that backends which do provide accurate device types
519            // will be preferred if their device type indicates an actual
520            // hardware GPU (integrated or discrete).
521            match device_type {
522                wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2,
523                wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1,
524                wgt::DeviceType::DiscreteGpu => 1,
525                wgt::DeviceType::IntegratedGpu => 2,
526                wgt::DeviceType::Other => 3,
527                wgt::DeviceType::VirtualGpu => 4,
528                wgt::DeviceType::Cpu => 5,
529            }
530        }
531
532        // `request_adapter` can be a bit of a black box.
533        // Shine some light on its decision in debug log.
534        if adapters.is_empty() {
535            log::debug!("Request adapter didn't find compatible adapters.");
536        } else {
537            log::debug!(
538                "Found {} compatible adapters. Sorted by preference:",
539                adapters.len()
540            );
541            for adapter in &adapters {
542                log::debug!("* {:?}", adapter.info);
543            }
544        }
545
546        if let Some(adapter) = adapters.into_iter().next() {
547            api_log_debug!("Request adapter result {:?}", adapter.info);
548            let adapter = Adapter::new(adapter);
549            Ok(adapter)
550        } else {
551            Err(wgt::RequestAdapterError::NotFound {
552                supported_backends: self.supported_backends,
553                requested_backends: self.requested_backends,
554                active_backends: self.active_backends(),
555                no_fallback_backends,
556                no_adapter_backends,
557                incompatible_surface_backends,
558            })
559        }
560    }
561
562    fn active_backends(&self) -> Backends {
563        self.instance_per_backend
564            .iter()
565            .map(|&(backend, _)| Backends::from(backend))
566            .collect()
567    }
568}
569
570pub struct Surface {
571    pub(crate) presentation: Mutex<Option<Presentation>>,
572    pub surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>>,
573}
574
575impl ResourceType for Surface {
576    const TYPE: &'static str = "Surface";
577}
578impl crate::storage::StorageItem for Surface {
579    type Marker = markers::Surface;
580}
581
582impl Surface {
583    pub fn get_capabilities(
584        &self,
585        adapter: &Adapter,
586    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
587        self.get_capabilities_with_raw(&adapter.raw)
588    }
589
590    pub fn get_capabilities_with_raw(
591        &self,
592        adapter: &hal::DynExposedAdapter,
593    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
594        let backend = adapter.backend();
595        let suf = self
596            .raw(backend)
597            .ok_or(GetSurfaceSupportError::NotSupportedByBackend(backend))?;
598        profiling::scope!("surface_capabilities");
599        let caps = unsafe { adapter.adapter.surface_capabilities(suf) }
600            .ok_or(GetSurfaceSupportError::FailedToRetrieveSurfaceCapabilitiesForAdapter)?;
601        Ok(caps)
602    }
603
604    pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynSurface> {
605        self.surface_per_backend
606            .get(&backend)
607            .map(|surface| surface.as_ref())
608    }
609}
610
611impl Drop for Surface {
612    fn drop(&mut self) {
613        if let Some(present) = self.presentation.lock().take() {
614            for (&backend, surface) in &self.surface_per_backend {
615                if backend == present.device.backend() {
616                    unsafe { surface.unconfigure(present.device.raw()) };
617                }
618            }
619        }
620    }
621}
622
623pub struct Adapter {
624    pub(crate) raw: hal::DynExposedAdapter,
625}
626
627impl Adapter {
628    pub fn new(mut raw: hal::DynExposedAdapter) -> Self {
629        // WebGPU requires this offset alignment as lower bound on all adapters.
630        const MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND: u32 = 32;
631
632        let limits = &mut raw.capabilities.limits;
633
634        limits.min_uniform_buffer_offset_alignment = limits
635            .min_uniform_buffer_offset_alignment
636            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
637        limits.min_storage_buffer_offset_alignment = limits
638            .min_storage_buffer_offset_alignment
639            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
640
641        Self { raw }
642    }
643
644    /// Returns the backend this adapter is using.
645    pub fn backend(&self) -> Backend {
646        self.raw.backend()
647    }
648
649    pub fn is_surface_supported(&self, surface: &Surface) -> bool {
650        // If get_capabilities returns Err, then the API does not advertise support for the surface.
651        //
652        // This could occur if the user is running their app on Wayland but Vulkan does not support
653        // VK_KHR_wayland_surface.
654        surface.get_capabilities(self).is_ok()
655    }
656
657    pub fn get_info(&self) -> wgt::AdapterInfo {
658        self.raw.info.clone()
659    }
660
661    pub fn features(&self) -> wgt::Features {
662        self.raw.features
663    }
664
665    pub fn limits(&self) -> wgt::Limits {
666        self.raw.capabilities.limits.clone()
667    }
668
669    pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
670        self.raw.capabilities.downlevel.clone()
671    }
672
673    pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
674        unsafe { self.raw.adapter.get_presentation_timestamp() }
675    }
676
677    pub fn get_texture_format_features(
678        &self,
679        format: wgt::TextureFormat,
680    ) -> wgt::TextureFormatFeatures {
681        use hal::TextureFormatCapabilities as Tfc;
682
683        let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
684        let mut allowed_usages = wgt::TextureUsages::empty();
685
686        allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
687        allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
688        allowed_usages.set(
689            wgt::TextureUsages::TEXTURE_BINDING,
690            caps.contains(Tfc::SAMPLED),
691        );
692        allowed_usages.set(
693            wgt::TextureUsages::STORAGE_BINDING,
694            caps.intersects(
695                Tfc::STORAGE_WRITE_ONLY
696                    | Tfc::STORAGE_READ_ONLY
697                    | Tfc::STORAGE_READ_WRITE
698                    | Tfc::STORAGE_ATOMIC,
699            ),
700        );
701        allowed_usages.set(
702            wgt::TextureUsages::RENDER_ATTACHMENT,
703            caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
704        );
705        allowed_usages.set(
706            wgt::TextureUsages::STORAGE_ATOMIC,
707            caps.contains(Tfc::STORAGE_ATOMIC),
708        );
709
710        let mut flags = wgt::TextureFormatFeatureFlags::empty();
711        flags.set(
712            wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
713            caps.contains(Tfc::STORAGE_READ_ONLY),
714        );
715        flags.set(
716            wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
717            caps.contains(Tfc::STORAGE_WRITE_ONLY),
718        );
719        flags.set(
720            wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
721            caps.contains(Tfc::STORAGE_READ_WRITE),
722        );
723
724        flags.set(
725            wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
726            caps.contains(Tfc::STORAGE_ATOMIC),
727        );
728
729        flags.set(
730            wgt::TextureFormatFeatureFlags::FILTERABLE,
731            caps.contains(Tfc::SAMPLED_LINEAR),
732        );
733
734        flags.set(
735            wgt::TextureFormatFeatureFlags::BLENDABLE,
736            caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
737        );
738
739        flags.set(
740            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
741            caps.contains(Tfc::MULTISAMPLE_X2),
742        );
743        flags.set(
744            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
745            caps.contains(Tfc::MULTISAMPLE_X4),
746        );
747        flags.set(
748            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
749            caps.contains(Tfc::MULTISAMPLE_X8),
750        );
751        flags.set(
752            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
753            caps.contains(Tfc::MULTISAMPLE_X16),
754        );
755
756        flags.set(
757            wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
758            caps.contains(Tfc::MULTISAMPLE_RESOLVE),
759        );
760
761        wgt::TextureFormatFeatures {
762            allowed_usages,
763            flags,
764        }
765    }
766
767    #[allow(clippy::type_complexity)]
768    fn create_device_and_queue_from_hal(
769        self: &Arc<Self>,
770        hal_device: hal::DynOpenDevice,
771        desc: &DeviceDescriptor,
772        instance_flags: wgt::InstanceFlags,
773    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
774        api_log!("Adapter::create_device");
775
776        let device = Device::new(hal_device.device, self, desc, instance_flags)?;
777        let device = Arc::new(device);
778
779        let queue = Queue::new(device.clone(), hal_device.queue, instance_flags)?;
780        let queue = Arc::new(queue);
781
782        device.set_queue(&queue);
783        device.late_init_resources_with_queue()?;
784
785        Ok((device, queue))
786    }
787
788    pub fn create_device_and_queue(
789        self: &Arc<Self>,
790        desc: &DeviceDescriptor,
791        instance_flags: wgt::InstanceFlags,
792    ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
793        // Verify all features were exposed by the adapter
794        if !self.raw.features.contains(desc.required_features) {
795            return Err(RequestDeviceError::UnsupportedFeature(
796                desc.required_features - self.raw.features,
797            ));
798        }
799
800        // Check if experimental features are permitted to be enabled.
801        if desc
802            .required_features
803            .intersects(wgt::Features::all_experimental_mask())
804            && !desc.experimental_features.is_enabled()
805        {
806            return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
807                desc.required_features
808                    .intersection(wgt::Features::all_experimental_mask()),
809            ));
810        }
811
812        let caps = &self.raw.capabilities;
813        if Backends::PRIMARY.contains(Backends::from(self.backend()))
814            && !caps.downlevel.is_webgpu_compliant()
815        {
816            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
817            log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
818            log::warn!("{:#?}", caps.downlevel);
819        }
820
821        // Verify feature preconditions
822        if desc
823            .required_features
824            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
825            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
826        {
827            log::warn!(
828                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
829                        This is a massive performance footgun and likely not what you wanted"
830            );
831        }
832
833        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
834            return Err(RequestDeviceError::LimitsExceeded(failed));
835        }
836
837        let open = unsafe {
838            self.raw.adapter.open(
839                desc.required_features,
840                &desc.required_limits,
841                &desc.memory_hints,
842            )
843        }
844        .map_err(DeviceError::from_hal)?;
845
846        self.create_device_and_queue_from_hal(open, desc, instance_flags)
847    }
848}
849
850crate::impl_resource_type!(Adapter);
851crate::impl_storage_item!(Adapter);
852
853#[derive(Clone, Debug, Error)]
854#[non_exhaustive]
855pub enum GetSurfaceSupportError {
856    #[error("Surface is not supported for the specified backend {0}")]
857    NotSupportedByBackend(Backend),
858    #[error("Failed to retrieve surface capabilities for the specified adapter.")]
859    FailedToRetrieveSurfaceCapabilitiesForAdapter,
860}
861
862#[derive(Clone, Debug, Error)]
863/// Error when requesting a device from the adapter
864#[non_exhaustive]
865pub enum RequestDeviceError {
866    #[error(transparent)]
867    Device(#[from] DeviceError),
868    #[error(transparent)]
869    LimitsExceeded(#[from] FailedLimit),
870    #[error("Failed to initialize Timestamp Normalizer")]
871    TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
872    #[error("Unsupported features were requested: {0}")]
873    UnsupportedFeature(wgt::Features),
874    #[error(
875        "Some experimental features, {0}, were requested, but experimental features are not enabled"
876    )]
877    ExperimentalFeaturesNotEnabled(wgt::Features),
878}
879
880#[derive(Clone, Debug, Error)]
881#[non_exhaustive]
882pub enum CreateSurfaceError {
883    #[error("The backend {0} was not enabled on the instance.")]
884    BackendNotEnabled(Backend),
885    #[error("Failed to create surface for any enabled backend: {0:?}")]
886    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
887}
888
889impl Global {
890    /// Creates a new surface targeting the given display/window handles.
891    ///
892    /// Internally attempts to create hal surfaces for all enabled backends.
893    ///
894    /// Fails only if creation for surfaces for all enabled backends fails in which case
895    /// the error for each enabled backend is listed.
896    /// Vice versa, if creation for any backend succeeds, success is returned.
897    /// Surface creation errors are logged to the debug log in any case.
898    ///
899    /// id_in:
900    /// - If `Some`, the id to assign to the surface. A new one will be generated otherwise.
901    ///
902    /// # Safety
903    ///
904    /// - `display_handle` must be a valid object to create a surface upon.
905    /// - `window_handle` must remain valid as long as the returned
906    ///   [`SurfaceId`] is being used.
907    #[cfg(feature = "raw-window-handle")]
908    pub unsafe fn instance_create_surface(
909        &self,
910        display_handle: raw_window_handle::RawDisplayHandle,
911        window_handle: raw_window_handle::RawWindowHandle,
912        id_in: Option<SurfaceId>,
913    ) -> Result<SurfaceId, CreateSurfaceError> {
914        let surface = unsafe { self.instance.create_surface(display_handle, window_handle) }?;
915        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
916        Ok(id)
917    }
918
919    /// Creates a new surface from the given drm configuration.
920    ///
921    /// # Safety
922    ///
923    /// - All parameters must point to valid DRM values.
924    ///
925    /// # Platform Support
926    ///
927    /// This function is only available on non-apple Unix-like platforms (Linux, FreeBSD) and
928    /// currently only works with the Vulkan backend.
929    #[cfg(all(unix, not(target_vendor = "apple"), not(target_family = "wasm")))]
930    pub unsafe fn instance_create_surface_from_drm(
931        &self,
932        fd: i32,
933        plane: u32,
934        connector_id: u32,
935        width: u32,
936        height: u32,
937        refresh_rate: u32,
938        id_in: Option<SurfaceId>,
939    ) -> Result<SurfaceId, CreateSurfaceError> {
940        let surface = unsafe {
941            self.instance.create_surface_from_drm(
942                fd,
943                plane,
944                connector_id,
945                width,
946                height,
947                refresh_rate,
948            )
949        }?;
950        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
951
952        Ok(id)
953    }
954
955    /// # Safety
956    ///
957    /// `layer` must be a valid pointer.
958    #[cfg(metal)]
959    pub unsafe fn instance_create_surface_metal(
960        &self,
961        layer: *mut core::ffi::c_void,
962        id_in: Option<SurfaceId>,
963    ) -> Result<SurfaceId, CreateSurfaceError> {
964        let surface = unsafe { self.instance.create_surface_metal(layer) }?;
965        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
966        Ok(id)
967    }
968
969    #[cfg(dx12)]
970    /// # Safety
971    ///
972    /// The visual must be valid and able to be used to make a swapchain with.
973    pub unsafe fn instance_create_surface_from_visual(
974        &self,
975        visual: *mut core::ffi::c_void,
976        id_in: Option<SurfaceId>,
977    ) -> Result<SurfaceId, CreateSurfaceError> {
978        let surface = unsafe { self.instance.create_surface_from_visual(visual) }?;
979        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
980        Ok(id)
981    }
982
983    #[cfg(dx12)]
984    /// # Safety
985    ///
986    /// The surface_handle must be valid and able to be used to make a swapchain with.
987    pub unsafe fn instance_create_surface_from_surface_handle(
988        &self,
989        surface_handle: *mut core::ffi::c_void,
990        id_in: Option<SurfaceId>,
991    ) -> Result<SurfaceId, CreateSurfaceError> {
992        let surface = unsafe {
993            self.instance
994                .create_surface_from_surface_handle(surface_handle)
995        }?;
996        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
997        Ok(id)
998    }
999
1000    #[cfg(dx12)]
1001    /// # Safety
1002    ///
1003    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.
1004    pub unsafe fn instance_create_surface_from_swap_chain_panel(
1005        &self,
1006        swap_chain_panel: *mut core::ffi::c_void,
1007        id_in: Option<SurfaceId>,
1008    ) -> Result<SurfaceId, CreateSurfaceError> {
1009        let surface = unsafe {
1010            self.instance
1011                .create_surface_from_swap_chain_panel(swap_chain_panel)
1012        }?;
1013        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
1014        Ok(id)
1015    }
1016
1017    pub fn surface_drop(&self, id: SurfaceId) {
1018        profiling::scope!("Surface::drop");
1019
1020        api_log!("Surface::drop {id:?}");
1021
1022        self.surfaces.remove(id);
1023    }
1024
1025    pub fn enumerate_adapters(&self, backends: Backends) -> Vec<AdapterId> {
1026        let adapters = self.instance.enumerate_adapters(backends);
1027        adapters
1028            .into_iter()
1029            .map(|adapter| self.hub.adapters.prepare(None).assign(Arc::new(adapter)))
1030            .collect()
1031    }
1032
1033    pub fn request_adapter(
1034        &self,
1035        desc: &RequestAdapterOptions,
1036        backends: Backends,
1037        id_in: Option<AdapterId>,
1038    ) -> Result<AdapterId, wgt::RequestAdapterError> {
1039        let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id));
1040        let desc = wgt::RequestAdapterOptions {
1041            power_preference: desc.power_preference,
1042            force_fallback_adapter: desc.force_fallback_adapter,
1043            compatible_surface: compatible_surface.as_deref(),
1044        };
1045        let adapter = self.instance.request_adapter(&desc, backends)?;
1046        let id = self.hub.adapters.prepare(id_in).assign(Arc::new(adapter));
1047        Ok(id)
1048    }
1049
1050    /// # Safety
1051    ///
1052    /// `hal_adapter` must be created from this global internal instance handle.
1053    pub unsafe fn create_adapter_from_hal(
1054        &self,
1055        hal_adapter: hal::DynExposedAdapter,
1056        input: Option<AdapterId>,
1057    ) -> AdapterId {
1058        profiling::scope!("Instance::create_adapter_from_hal");
1059
1060        let fid = self.hub.adapters.prepare(input);
1061        let id = fid.assign(Arc::new(Adapter::new(hal_adapter)));
1062
1063        resource_log!("Created Adapter {:?}", id);
1064        id
1065    }
1066
1067    pub fn adapter_get_info(&self, adapter_id: AdapterId) -> wgt::AdapterInfo {
1068        let adapter = self.hub.adapters.get(adapter_id);
1069        adapter.get_info()
1070    }
1071
1072    pub fn adapter_get_texture_format_features(
1073        &self,
1074        adapter_id: AdapterId,
1075        format: wgt::TextureFormat,
1076    ) -> wgt::TextureFormatFeatures {
1077        let adapter = self.hub.adapters.get(adapter_id);
1078        adapter.get_texture_format_features(format)
1079    }
1080
1081    pub fn adapter_features(&self, adapter_id: AdapterId) -> wgt::Features {
1082        let adapter = self.hub.adapters.get(adapter_id);
1083        adapter.features()
1084    }
1085
1086    pub fn adapter_limits(&self, adapter_id: AdapterId) -> wgt::Limits {
1087        let adapter = self.hub.adapters.get(adapter_id);
1088        adapter.limits()
1089    }
1090
1091    pub fn adapter_downlevel_capabilities(
1092        &self,
1093        adapter_id: AdapterId,
1094    ) -> wgt::DownlevelCapabilities {
1095        let adapter = self.hub.adapters.get(adapter_id);
1096        adapter.downlevel_capabilities()
1097    }
1098
1099    pub fn adapter_get_presentation_timestamp(
1100        &self,
1101        adapter_id: AdapterId,
1102    ) -> wgt::PresentationTimestamp {
1103        let adapter = self.hub.adapters.get(adapter_id);
1104        adapter.get_presentation_timestamp()
1105    }
1106
1107    pub fn adapter_drop(&self, adapter_id: AdapterId) {
1108        profiling::scope!("Adapter::drop");
1109        api_log!("Adapter::drop {adapter_id:?}");
1110
1111        self.hub.adapters.remove(adapter_id);
1112    }
1113}
1114
1115impl Global {
1116    pub fn adapter_request_device(
1117        &self,
1118        adapter_id: AdapterId,
1119        desc: &DeviceDescriptor,
1120        device_id_in: Option<DeviceId>,
1121        queue_id_in: Option<QueueId>,
1122    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1123        profiling::scope!("Adapter::request_device");
1124        api_log!("Adapter::request_device");
1125
1126        let device_fid = self.hub.devices.prepare(device_id_in);
1127        let queue_fid = self.hub.queues.prepare(queue_id_in);
1128
1129        let adapter = self.hub.adapters.get(adapter_id);
1130        let (device, queue) = adapter.create_device_and_queue(desc, self.instance.flags)?;
1131
1132        let device_id = device_fid.assign(device);
1133        resource_log!("Created Device {:?}", device_id);
1134
1135        let queue_id = queue_fid.assign(queue);
1136        resource_log!("Created Queue {:?}", queue_id);
1137
1138        Ok((device_id, queue_id))
1139    }
1140
1141    /// # Safety
1142    ///
1143    /// - `hal_device` must be created from `adapter_id` or its internal handle.
1144    /// - `desc` must be a subset of `hal_device` features and limits.
1145    pub unsafe fn create_device_from_hal(
1146        &self,
1147        adapter_id: AdapterId,
1148        hal_device: hal::DynOpenDevice,
1149        desc: &DeviceDescriptor,
1150        device_id_in: Option<DeviceId>,
1151        queue_id_in: Option<QueueId>,
1152    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1153        profiling::scope!("Global::create_device_from_hal");
1154
1155        let devices_fid = self.hub.devices.prepare(device_id_in);
1156        let queues_fid = self.hub.queues.prepare(queue_id_in);
1157
1158        let adapter = self.hub.adapters.get(adapter_id);
1159        let (device, queue) =
1160            adapter.create_device_and_queue_from_hal(hal_device, desc, self.instance.flags)?;
1161
1162        let device_id = devices_fid.assign(device);
1163        resource_log!("Created Device {:?}", device_id);
1164
1165        let queue_id = queues_fid.assign(queue);
1166        resource_log!("Created Queue {:?}", queue_id);
1167
1168        Ok((device_id, queue_id))
1169    }
1170}