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