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