wgpu_core/
instance.rs

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