wgpu_core/
instance.rs

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