Skip to main content

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