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        let caps = &self.raw.capabilities;
801        if Backends::PRIMARY.contains(Backends::from(self.backend()))
802            && !caps.downlevel.is_webgpu_compliant()
803        {
804            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
805            log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
806            log::warn!("{:#?}", caps.downlevel);
807        }
808
809        // Verify feature preconditions
810        if desc
811            .required_features
812            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
813            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
814        {
815            log::warn!(
816                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
817                        This is a massive performance footgun and likely not what you wanted"
818            );
819        }
820
821        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
822            return Err(RequestDeviceError::LimitsExceeded(failed));
823        }
824
825        let open = unsafe {
826            self.raw.adapter.open(
827                desc.required_features,
828                &desc.required_limits,
829                &desc.memory_hints,
830            )
831        }
832        .map_err(DeviceError::from_hal)?;
833
834        self.create_device_and_queue_from_hal(open, desc, instance_flags)
835    }
836}
837
838crate::impl_resource_type!(Adapter);
839crate::impl_storage_item!(Adapter);
840
841#[derive(Clone, Debug, Error)]
842#[non_exhaustive]
843pub enum GetSurfaceSupportError {
844    #[error("Surface is not supported for the specified backend {0}")]
845    NotSupportedByBackend(Backend),
846    #[error("Failed to retrieve surface capabilities for the specified adapter.")]
847    FailedToRetrieveSurfaceCapabilitiesForAdapter,
848}
849
850#[derive(Clone, Debug, Error)]
851/// Error when requesting a device from the adapter
852#[non_exhaustive]
853pub enum RequestDeviceError {
854    #[error(transparent)]
855    Device(#[from] DeviceError),
856    #[error(transparent)]
857    LimitsExceeded(#[from] FailedLimit),
858    #[error("Failed to initialize Timestamp Normalizer")]
859    TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
860    #[error("Unsupported features were requested: {0:?}")]
861    UnsupportedFeature(wgt::Features),
862}
863
864#[derive(Clone, Debug, Error)]
865#[non_exhaustive]
866pub enum CreateSurfaceError {
867    #[error("The backend {0} was not enabled on the instance.")]
868    BackendNotEnabled(Backend),
869    #[error("Failed to create surface for any enabled backend: {0:?}")]
870    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
871}
872
873impl Global {
874    /// Creates a new surface targeting the given display/window handles.
875    ///
876    /// Internally attempts to create hal surfaces for all enabled backends.
877    ///
878    /// Fails only if creation for surfaces for all enabled backends fails in which case
879    /// the error for each enabled backend is listed.
880    /// Vice versa, if creation for any backend succeeds, success is returned.
881    /// Surface creation errors are logged to the debug log in any case.
882    ///
883    /// id_in:
884    /// - If `Some`, the id to assign to the surface. A new one will be generated otherwise.
885    ///
886    /// # Safety
887    ///
888    /// - `display_handle` must be a valid object to create a surface upon.
889    /// - `window_handle` must remain valid as long as the returned
890    ///   [`SurfaceId`] is being used.
891    #[cfg(feature = "raw-window-handle")]
892    pub unsafe fn instance_create_surface(
893        &self,
894        display_handle: raw_window_handle::RawDisplayHandle,
895        window_handle: raw_window_handle::RawWindowHandle,
896        id_in: Option<SurfaceId>,
897    ) -> Result<SurfaceId, CreateSurfaceError> {
898        let surface = unsafe { self.instance.create_surface(display_handle, window_handle) }?;
899        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
900        Ok(id)
901    }
902
903    /// Creates a new surface from the given drm configuration.
904    ///
905    /// # Safety
906    ///
907    /// - All parameters must point to valid DRM values.
908    ///
909    /// # Platform Support
910    ///
911    /// This function is only available on non-apple Unix-like platforms (Linux, FreeBSD) and
912    /// currently only works with the Vulkan backend.
913    #[cfg(all(unix, not(target_vendor = "apple"), not(target_family = "wasm")))]
914    pub unsafe fn instance_create_surface_from_drm(
915        &self,
916        fd: i32,
917        plane: u32,
918        connector_id: u32,
919        width: u32,
920        height: u32,
921        refresh_rate: u32,
922        id_in: Option<SurfaceId>,
923    ) -> Result<SurfaceId, CreateSurfaceError> {
924        let surface = unsafe {
925            self.instance.create_surface_from_drm(
926                fd,
927                plane,
928                connector_id,
929                width,
930                height,
931                refresh_rate,
932            )
933        }?;
934        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
935
936        Ok(id)
937    }
938
939    /// # Safety
940    ///
941    /// `layer` must be a valid pointer.
942    #[cfg(metal)]
943    pub unsafe fn instance_create_surface_metal(
944        &self,
945        layer: *mut core::ffi::c_void,
946        id_in: Option<SurfaceId>,
947    ) -> Result<SurfaceId, CreateSurfaceError> {
948        let surface = unsafe { self.instance.create_surface_metal(layer) }?;
949        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
950        Ok(id)
951    }
952
953    #[cfg(dx12)]
954    /// # Safety
955    ///
956    /// The visual must be valid and able to be used to make a swapchain with.
957    pub unsafe fn instance_create_surface_from_visual(
958        &self,
959        visual: *mut core::ffi::c_void,
960        id_in: Option<SurfaceId>,
961    ) -> Result<SurfaceId, CreateSurfaceError> {
962        let surface = unsafe { self.instance.create_surface_from_visual(visual) }?;
963        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
964        Ok(id)
965    }
966
967    #[cfg(dx12)]
968    /// # Safety
969    ///
970    /// The surface_handle must be valid and able to be used to make a swapchain with.
971    pub unsafe fn instance_create_surface_from_surface_handle(
972        &self,
973        surface_handle: *mut core::ffi::c_void,
974        id_in: Option<SurfaceId>,
975    ) -> Result<SurfaceId, CreateSurfaceError> {
976        let surface = unsafe {
977            self.instance
978                .create_surface_from_surface_handle(surface_handle)
979        }?;
980        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
981        Ok(id)
982    }
983
984    #[cfg(dx12)]
985    /// # Safety
986    ///
987    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.
988    pub unsafe fn instance_create_surface_from_swap_chain_panel(
989        &self,
990        swap_chain_panel: *mut core::ffi::c_void,
991        id_in: Option<SurfaceId>,
992    ) -> Result<SurfaceId, CreateSurfaceError> {
993        let surface = unsafe {
994            self.instance
995                .create_surface_from_swap_chain_panel(swap_chain_panel)
996        }?;
997        let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
998        Ok(id)
999    }
1000
1001    pub fn surface_drop(&self, id: SurfaceId) {
1002        profiling::scope!("Surface::drop");
1003
1004        api_log!("Surface::drop {id:?}");
1005
1006        self.surfaces.remove(id);
1007    }
1008
1009    pub fn enumerate_adapters(&self, backends: Backends) -> Vec<AdapterId> {
1010        let adapters = self.instance.enumerate_adapters(backends);
1011        adapters
1012            .into_iter()
1013            .map(|adapter| self.hub.adapters.prepare(None).assign(Arc::new(adapter)))
1014            .collect()
1015    }
1016
1017    pub fn request_adapter(
1018        &self,
1019        desc: &RequestAdapterOptions,
1020        backends: Backends,
1021        id_in: Option<AdapterId>,
1022    ) -> Result<AdapterId, wgt::RequestAdapterError> {
1023        let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id));
1024        let desc = wgt::RequestAdapterOptions {
1025            power_preference: desc.power_preference,
1026            force_fallback_adapter: desc.force_fallback_adapter,
1027            compatible_surface: compatible_surface.as_deref(),
1028        };
1029        let adapter = self.instance.request_adapter(&desc, backends)?;
1030        let id = self.hub.adapters.prepare(id_in).assign(Arc::new(adapter));
1031        Ok(id)
1032    }
1033
1034    /// # Safety
1035    ///
1036    /// `hal_adapter` must be created from this global internal instance handle.
1037    pub unsafe fn create_adapter_from_hal(
1038        &self,
1039        hal_adapter: hal::DynExposedAdapter,
1040        input: Option<AdapterId>,
1041    ) -> AdapterId {
1042        profiling::scope!("Instance::create_adapter_from_hal");
1043
1044        let fid = self.hub.adapters.prepare(input);
1045        let id = fid.assign(Arc::new(Adapter::new(hal_adapter)));
1046
1047        resource_log!("Created Adapter {:?}", id);
1048        id
1049    }
1050
1051    pub fn adapter_get_info(&self, adapter_id: AdapterId) -> wgt::AdapterInfo {
1052        let adapter = self.hub.adapters.get(adapter_id);
1053        adapter.get_info()
1054    }
1055
1056    pub fn adapter_get_texture_format_features(
1057        &self,
1058        adapter_id: AdapterId,
1059        format: wgt::TextureFormat,
1060    ) -> wgt::TextureFormatFeatures {
1061        let adapter = self.hub.adapters.get(adapter_id);
1062        adapter.get_texture_format_features(format)
1063    }
1064
1065    pub fn adapter_features(&self, adapter_id: AdapterId) -> wgt::Features {
1066        let adapter = self.hub.adapters.get(adapter_id);
1067        adapter.features()
1068    }
1069
1070    pub fn adapter_limits(&self, adapter_id: AdapterId) -> wgt::Limits {
1071        let adapter = self.hub.adapters.get(adapter_id);
1072        adapter.limits()
1073    }
1074
1075    pub fn adapter_downlevel_capabilities(
1076        &self,
1077        adapter_id: AdapterId,
1078    ) -> wgt::DownlevelCapabilities {
1079        let adapter = self.hub.adapters.get(adapter_id);
1080        adapter.downlevel_capabilities()
1081    }
1082
1083    pub fn adapter_get_presentation_timestamp(
1084        &self,
1085        adapter_id: AdapterId,
1086    ) -> wgt::PresentationTimestamp {
1087        let adapter = self.hub.adapters.get(adapter_id);
1088        adapter.get_presentation_timestamp()
1089    }
1090
1091    pub fn adapter_drop(&self, adapter_id: AdapterId) {
1092        profiling::scope!("Adapter::drop");
1093        api_log!("Adapter::drop {adapter_id:?}");
1094
1095        self.hub.adapters.remove(adapter_id);
1096    }
1097}
1098
1099impl Global {
1100    pub fn adapter_request_device(
1101        &self,
1102        adapter_id: AdapterId,
1103        desc: &DeviceDescriptor,
1104        device_id_in: Option<DeviceId>,
1105        queue_id_in: Option<QueueId>,
1106    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1107        profiling::scope!("Adapter::request_device");
1108        api_log!("Adapter::request_device");
1109
1110        let device_fid = self.hub.devices.prepare(device_id_in);
1111        let queue_fid = self.hub.queues.prepare(queue_id_in);
1112
1113        let adapter = self.hub.adapters.get(adapter_id);
1114        let (device, queue) = adapter.create_device_and_queue(desc, self.instance.flags)?;
1115
1116        let device_id = device_fid.assign(device);
1117        resource_log!("Created Device {:?}", device_id);
1118
1119        let queue_id = queue_fid.assign(queue);
1120        resource_log!("Created Queue {:?}", queue_id);
1121
1122        Ok((device_id, queue_id))
1123    }
1124
1125    /// # Safety
1126    ///
1127    /// - `hal_device` must be created from `adapter_id` or its internal handle.
1128    /// - `desc` must be a subset of `hal_device` features and limits.
1129    pub unsafe fn create_device_from_hal(
1130        &self,
1131        adapter_id: AdapterId,
1132        hal_device: hal::DynOpenDevice,
1133        desc: &DeviceDescriptor,
1134        device_id_in: Option<DeviceId>,
1135        queue_id_in: Option<QueueId>,
1136    ) -> Result<(DeviceId, QueueId), RequestDeviceError> {
1137        profiling::scope!("Global::create_device_from_hal");
1138
1139        let devices_fid = self.hub.devices.prepare(device_id_in);
1140        let queues_fid = self.hub.queues.prepare(queue_id_in);
1141
1142        let adapter = self.hub.adapters.get(adapter_id);
1143        let (device, queue) =
1144            adapter.create_device_and_queue_from_hal(hal_device, desc, self.instance.flags)?;
1145
1146        let device_id = devices_fid.assign(device);
1147        resource_log!("Created Device {:?}", device_id);
1148
1149        let queue_id = queues_fid.assign(queue);
1150        resource_log!("Created Queue {:?}", queue_id);
1151
1152        Ok((device_id, queue_id))
1153    }
1154}