li_wgpu_core/
instance.rs

1use crate::{
2    device::{Device, DeviceDescriptor},
3    global::Global,
4    hal_api::HalApi,
5    hub::Token,
6    id::{AdapterId, DeviceId, SurfaceId, Valid},
7    identity::{GlobalIdentityHandlerFactory, Input},
8    present::Presentation,
9    LabelHelpers, LifeGuard, Stored, DOWNLEVEL_WARNING_MESSAGE,
10};
11
12use wgt::{Backend, Backends, PowerPreference};
13
14use hal::{Adapter as _, Instance as _};
15use thiserror::Error;
16
17pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
18type HalInstance<A> = <A as hal::Api>::Instance;
19//TODO: remove this
20pub struct HalSurface<A: hal::Api> {
21    pub raw: A::Surface,
22    //pub acquired_texture: Option<A::SurfaceTexture>,
23}
24
25#[derive(Clone, Debug, Error)]
26#[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
27pub struct FailedLimit {
28    name: &'static str,
29    requested: u64,
30    allowed: u64,
31}
32
33fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLimit> {
34    let mut failed = Vec::new();
35
36    requested.check_limits_with_fail_fn(allowed, false, |name, requested, allowed| {
37        failed.push(FailedLimit {
38            name,
39            requested,
40            allowed,
41        })
42    });
43
44    failed
45}
46
47#[test]
48fn downlevel_default_limits_less_than_default_limits() {
49    let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
50    assert!(
51        res.is_empty(),
52        "Downlevel limits are greater than default limits",
53    )
54}
55
56#[derive(Default)]
57pub struct Instance {
58    #[allow(dead_code)]
59    pub name: String,
60    #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
61    pub vulkan: Option<HalInstance<hal::api::Vulkan>>,
62    #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
63    pub metal: Option<HalInstance<hal::api::Metal>>,
64    #[cfg(all(feature = "dx12", windows))]
65    pub dx12: Option<HalInstance<hal::api::Dx12>>,
66    #[cfg(all(feature = "dx11", windows))]
67    pub dx11: Option<HalInstance<hal::api::Dx11>>,
68    #[cfg(feature = "gles")]
69    pub gl: Option<HalInstance<hal::api::Gles>>,
70    pub flags: wgt::InstanceFlags,
71}
72
73impl Instance {
74    pub fn new(name: &str, instance_desc: wgt::InstanceDescriptor) -> Self {
75        fn init<A: HalApi>(_: A, instance_desc: &wgt::InstanceDescriptor) -> Option<A::Instance> {
76            if instance_desc.backends.contains(A::VARIANT.into()) {
77                let hal_desc = hal::InstanceDescriptor {
78                    name: "wgpu",
79                    flags: instance_desc.flags,
80                    dx12_shader_compiler: instance_desc.dx12_shader_compiler.clone(),
81                    gles_minor_version: instance_desc.gles_minor_version,
82                };
83                match unsafe { hal::Instance::init(&hal_desc) } {
84                    Ok(instance) => {
85                        log::debug!("Instance::new: created {:?} backend", A::VARIANT);
86                        Some(instance)
87                    }
88                    Err(err) => {
89                        log::debug!(
90                            "Instance::new: failed to create {:?} backend: {:?}",
91                            A::VARIANT,
92                            err
93                        );
94                        None
95                    }
96                }
97            } else {
98                log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
99                None
100            }
101        }
102
103        Self {
104            name: name.to_string(),
105            #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
106            vulkan: init(hal::api::Vulkan, &instance_desc),
107            #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
108            metal: init(hal::api::Metal, &instance_desc),
109            #[cfg(all(feature = "dx12", windows))]
110            dx12: init(hal::api::Dx12, &instance_desc),
111            #[cfg(all(feature = "dx11", windows))]
112            dx11: init(hal::api::Dx11, &instance_desc),
113            #[cfg(feature = "gles")]
114            gl: init(hal::api::Gles, &instance_desc),
115            flags: instance_desc.flags,
116        }
117    }
118
119    pub(crate) fn destroy_surface(&self, surface: Surface) {
120        fn destroy<A: HalApi>(
121            _: A,
122            instance: &Option<A::Instance>,
123            surface: Option<HalSurface<A>>,
124        ) {
125            unsafe {
126                if let Some(suf) = surface {
127                    instance.as_ref().unwrap().destroy_surface(suf.raw);
128                }
129            }
130        }
131        #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
132        destroy(hal::api::Vulkan, &self.vulkan, surface.vulkan);
133        #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
134        destroy(hal::api::Metal, &self.metal, surface.metal);
135        #[cfg(all(feature = "dx12", windows))]
136        destroy(hal::api::Dx12, &self.dx12, surface.dx12);
137        #[cfg(all(feature = "dx11", windows))]
138        destroy(hal::api::Dx11, &self.dx11, surface.dx11);
139        #[cfg(feature = "gles")]
140        destroy(hal::api::Gles, &self.gl, surface.gl);
141    }
142}
143
144pub struct Surface {
145    pub(crate) presentation: Option<Presentation>,
146    #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
147    pub vulkan: Option<HalSurface<hal::api::Vulkan>>,
148    #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
149    pub metal: Option<HalSurface<hal::api::Metal>>,
150    #[cfg(all(feature = "dx12", windows))]
151    pub dx12: Option<HalSurface<hal::api::Dx12>>,
152    #[cfg(all(feature = "dx11", windows))]
153    pub dx11: Option<HalSurface<hal::api::Dx11>>,
154    #[cfg(feature = "gles")]
155    pub gl: Option<HalSurface<hal::api::Gles>>,
156}
157
158impl crate::resource::Resource for Surface {
159    const TYPE: &'static str = "Surface";
160
161    fn life_guard(&self) -> &LifeGuard {
162        unreachable!()
163    }
164
165    fn label(&self) -> &str {
166        "<Surface>"
167    }
168}
169
170impl Surface {
171    pub fn get_capabilities<A: HalApi>(
172        &self,
173        adapter: &Adapter<A>,
174    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
175        let suf = A::get_surface(self).ok_or(GetSurfaceSupportError::Unsupported)?;
176        profiling::scope!("surface_capabilities");
177        let caps = unsafe {
178            adapter
179                .raw
180                .adapter
181                .surface_capabilities(&suf.raw)
182                .ok_or(GetSurfaceSupportError::Unsupported)?
183        };
184
185        Ok(caps)
186    }
187}
188
189pub struct Adapter<A: hal::Api> {
190    pub(crate) raw: hal::ExposedAdapter<A>,
191    life_guard: LifeGuard,
192}
193
194impl<A: HalApi> Adapter<A> {
195    fn new(mut raw: hal::ExposedAdapter<A>) -> Self {
196        // WebGPU requires this offset alignment as lower bound on all adapters.
197        const MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND: u32 = 32;
198
199        let limits = &mut raw.capabilities.limits;
200
201        limits.min_uniform_buffer_offset_alignment = limits
202            .min_uniform_buffer_offset_alignment
203            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
204        limits.min_storage_buffer_offset_alignment = limits
205            .min_storage_buffer_offset_alignment
206            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
207
208        Self {
209            raw,
210            life_guard: LifeGuard::new("<Adapter>"),
211        }
212    }
213
214    pub fn is_surface_supported(&self, surface: &Surface) -> bool {
215        let suf = A::get_surface(surface);
216
217        // If get_surface returns None, then the API does not advertise support for the surface.
218        //
219        // This could occur if the user is running their app on Wayland but Vulkan does not support
220        // VK_KHR_wayland_surface.
221        match suf {
222            Some(suf) => unsafe { self.raw.adapter.surface_capabilities(&suf.raw) }.is_some(),
223            None => false,
224        }
225    }
226
227    pub(crate) fn get_texture_format_features(
228        &self,
229        format: wgt::TextureFormat,
230    ) -> wgt::TextureFormatFeatures {
231        use hal::TextureFormatCapabilities as Tfc;
232
233        let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
234        let mut allowed_usages = wgt::TextureUsages::empty();
235
236        allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
237        allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
238        allowed_usages.set(
239            wgt::TextureUsages::TEXTURE_BINDING,
240            caps.contains(Tfc::SAMPLED),
241        );
242        allowed_usages.set(
243            wgt::TextureUsages::STORAGE_BINDING,
244            caps.contains(Tfc::STORAGE),
245        );
246        allowed_usages.set(
247            wgt::TextureUsages::RENDER_ATTACHMENT,
248            caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
249        );
250
251        let mut flags = wgt::TextureFormatFeatureFlags::empty();
252        flags.set(
253            wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
254            caps.contains(Tfc::STORAGE_READ_WRITE),
255        );
256
257        flags.set(
258            wgt::TextureFormatFeatureFlags::FILTERABLE,
259            caps.contains(Tfc::SAMPLED_LINEAR),
260        );
261
262        flags.set(
263            wgt::TextureFormatFeatureFlags::BLENDABLE,
264            caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
265        );
266
267        flags.set(
268            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
269            caps.contains(Tfc::MULTISAMPLE_X2),
270        );
271        flags.set(
272            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
273            caps.contains(Tfc::MULTISAMPLE_X4),
274        );
275        flags.set(
276            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
277            caps.contains(Tfc::MULTISAMPLE_X8),
278        );
279        flags.set(
280            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
281            caps.contains(Tfc::MULTISAMPLE_X16),
282        );
283
284        flags.set(
285            wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
286            caps.contains(Tfc::MULTISAMPLE_RESOLVE),
287        );
288
289        wgt::TextureFormatFeatures {
290            allowed_usages,
291            flags,
292        }
293    }
294
295    fn create_device_from_hal(
296        &self,
297        self_id: AdapterId,
298        open: hal::OpenDevice<A>,
299        desc: &DeviceDescriptor,
300        instance_flags: wgt::InstanceFlags,
301        trace_path: Option<&std::path::Path>,
302    ) -> Result<Device<A>, RequestDeviceError> {
303        log::trace!("Adapter::create_device");
304
305        let caps = &self.raw.capabilities;
306        Device::new(
307            open,
308            Stored {
309                value: Valid(self_id),
310                ref_count: self.life_guard.add_ref(),
311            },
312            caps.alignments.clone(),
313            caps.downlevel.clone(),
314            desc,
315            trace_path,
316            instance_flags,
317        )
318        .or(Err(RequestDeviceError::OutOfMemory))
319    }
320
321    fn create_device(
322        &self,
323        self_id: AdapterId,
324        desc: &DeviceDescriptor,
325        instance_flags: wgt::InstanceFlags,
326        trace_path: Option<&std::path::Path>,
327    ) -> Result<Device<A>, RequestDeviceError> {
328        // Verify all features were exposed by the adapter
329        if !self.raw.features.contains(desc.features) {
330            return Err(RequestDeviceError::UnsupportedFeature(
331                desc.features - self.raw.features,
332            ));
333        }
334
335        let caps = &self.raw.capabilities;
336        if wgt::Backends::PRIMARY.contains(wgt::Backends::from(A::VARIANT))
337            && !caps.downlevel.is_webgpu_compliant()
338        {
339            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
340            log::warn!(
341                "Missing downlevel flags: {:?}\n{}",
342                missing_flags,
343                DOWNLEVEL_WARNING_MESSAGE
344            );
345            log::info!("{:#?}", caps.downlevel);
346        }
347
348        // Verify feature preconditions
349        if desc
350            .features
351            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
352            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
353        {
354            log::warn!(
355                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
356                        This is a massive performance footgun and likely not what you wanted"
357            );
358        }
359
360        if let Some(_) = desc.label {
361            //TODO
362        }
363
364        if let Some(failed) = check_limits(&desc.limits, &caps.limits).pop() {
365            return Err(RequestDeviceError::LimitsExceeded(failed));
366        }
367
368        let open = unsafe { self.raw.adapter.open(desc.features, &desc.limits) }.map_err(
369            |err| match err {
370                hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
371                hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory,
372                hal::DeviceError::ResourceCreationFailed => RequestDeviceError::Internal,
373            },
374        )?;
375
376        self.create_device_from_hal(self_id, open, desc, instance_flags, trace_path)
377    }
378}
379
380impl<A: hal::Api> crate::resource::Resource for Adapter<A> {
381    const TYPE: &'static str = "Adapter";
382
383    fn life_guard(&self) -> &LifeGuard {
384        &self.life_guard
385    }
386}
387
388#[derive(Clone, Debug, Error)]
389#[non_exhaustive]
390pub enum IsSurfaceSupportedError {
391    #[error("Invalid adapter")]
392    InvalidAdapter,
393    #[error("Invalid surface")]
394    InvalidSurface,
395}
396
397#[derive(Clone, Debug, Error)]
398#[non_exhaustive]
399pub enum GetSurfaceSupportError {
400    #[error("Invalid adapter")]
401    InvalidAdapter,
402    #[error("Invalid surface")]
403    InvalidSurface,
404    #[error("Surface is not supported by the adapter")]
405    Unsupported,
406}
407
408#[derive(Clone, Debug, Error)]
409/// Error when requesting a device from the adaptor
410#[non_exhaustive]
411pub enum RequestDeviceError {
412    #[error("Parent adapter is invalid")]
413    InvalidAdapter,
414    #[error("Connection to device was lost during initialization")]
415    DeviceLost,
416    #[error("Device initialization failed due to implementation specific errors")]
417    Internal,
418    #[error(transparent)]
419    LimitsExceeded(#[from] FailedLimit),
420    #[error("Device has no queue supporting graphics")]
421    NoGraphicsQueue,
422    #[error("Not enough memory left")]
423    OutOfMemory,
424    #[error("Unsupported features were requested: {0:?}")]
425    UnsupportedFeature(wgt::Features),
426}
427
428pub enum AdapterInputs<'a, I> {
429    IdSet(&'a [I], fn(&I) -> Backend),
430    Mask(Backends, fn(Backend) -> I),
431}
432
433impl<I: Clone> AdapterInputs<'_, I> {
434    fn find(&self, b: Backend) -> Option<I> {
435        match *self {
436            Self::IdSet(ids, ref fun) => ids.iter().find(|id| fun(id) == b).cloned(),
437            Self::Mask(bits, ref fun) => {
438                if bits.contains(b.into()) {
439                    Some(fun(b))
440                } else {
441                    None
442                }
443            }
444        }
445    }
446}
447
448#[derive(Clone, Debug, Error)]
449#[error("Adapter is invalid")]
450pub struct InvalidAdapter;
451
452#[derive(Clone, Debug, Error)]
453#[non_exhaustive]
454pub enum RequestAdapterError {
455    #[error("No suitable adapter found")]
456    NotFound,
457    #[error("Surface {0:?} is invalid")]
458    InvalidSurface(SurfaceId),
459}
460
461impl<G: GlobalIdentityHandlerFactory> Global<G> {
462    #[cfg(feature = "raw-window-handle")]
463    pub fn instance_create_surface(
464        &self,
465        display_handle: raw_window_handle::RawDisplayHandle,
466        window_handle: raw_window_handle::RawWindowHandle,
467        id_in: Input<G, SurfaceId>,
468    ) -> SurfaceId {
469        profiling::scope!("Instance::create_surface");
470
471        fn init<A: hal::Api>(
472            inst: &Option<A::Instance>,
473            display_handle: raw_window_handle::RawDisplayHandle,
474            window_handle: raw_window_handle::RawWindowHandle,
475        ) -> Option<HalSurface<A>> {
476            inst.as_ref().and_then(|inst| unsafe {
477                match inst.create_surface(display_handle, window_handle) {
478                    Ok(raw) => Some(HalSurface {
479                        raw,
480                        //acquired_texture: None,
481                    }),
482                    Err(e) => {
483                        log::warn!("Error: {:?}", e);
484                        None
485                    }
486                }
487            })
488        }
489
490        let surface = Surface {
491            presentation: None,
492            #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
493            vulkan: init::<hal::api::Vulkan>(&self.instance.vulkan, display_handle, window_handle),
494            #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
495            metal: init::<hal::api::Metal>(&self.instance.metal, display_handle, window_handle),
496            #[cfg(all(feature = "dx12", windows))]
497            dx12: init::<hal::api::Dx12>(&self.instance.dx12, display_handle, window_handle),
498            #[cfg(all(feature = "dx11", windows))]
499            dx11: init::<hal::api::Dx11>(&self.instance.dx11, display_handle, window_handle),
500            #[cfg(feature = "gles")]
501            gl: init::<hal::api::Gles>(&self.instance.gl, display_handle, window_handle),
502        };
503
504        let mut token = Token::root();
505        let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
506        id.0
507    }
508
509    /// # Safety
510    ///
511    /// `layer` must be a valid pointer.
512    #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
513    pub unsafe fn instance_create_surface_metal(
514        &self,
515        layer: *mut std::ffi::c_void,
516        id_in: Input<G, SurfaceId>,
517    ) -> SurfaceId {
518        profiling::scope!("Instance::create_surface_metal");
519
520        let surface = Surface {
521            presentation: None,
522            metal: self.instance.metal.as_ref().map(|inst| HalSurface {
523                raw: {
524                    // we don't want to link to metal-rs for this
525                    #[allow(clippy::transmute_ptr_to_ref)]
526                    inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) })
527                },
528                //acquired_texture: None,
529            }),
530            #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
531            vulkan: None,
532            #[cfg(feature = "gles")]
533            gl: None,
534        };
535
536        let mut token = Token::root();
537        let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
538        id.0
539    }
540
541    #[cfg(all(
542        target_arch = "wasm32",
543        not(target_os = "emscripten"),
544        feature = "gles"
545    ))]
546    pub fn create_surface_webgl_canvas(
547        &self,
548        canvas: web_sys::HtmlCanvasElement,
549        id_in: Input<G, SurfaceId>,
550    ) -> Result<SurfaceId, hal::InstanceError> {
551        profiling::scope!("Instance::create_surface_webgl_canvas");
552
553        let surface = Surface {
554            presentation: None,
555            gl: self
556                .instance
557                .gl
558                .as_ref()
559                .map(|inst| {
560                    Ok(HalSurface {
561                        raw: inst.create_surface_from_canvas(canvas)?,
562                    })
563                })
564                .transpose()?,
565        };
566
567        let mut token = Token::root();
568        let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
569        Ok(id.0)
570    }
571
572    #[cfg(all(
573        target_arch = "wasm32",
574        not(target_os = "emscripten"),
575        feature = "gles"
576    ))]
577    pub fn create_surface_webgl_offscreen_canvas(
578        &self,
579        canvas: web_sys::OffscreenCanvas,
580        id_in: Input<G, SurfaceId>,
581    ) -> Result<SurfaceId, hal::InstanceError> {
582        profiling::scope!("Instance::create_surface_webgl_offscreen_canvas");
583
584        let surface = Surface {
585            presentation: None,
586            gl: self
587                .instance
588                .gl
589                .as_ref()
590                .map(|inst| {
591                    Ok(HalSurface {
592                        raw: inst.create_surface_from_offscreen_canvas(canvas)?,
593                    })
594                })
595                .transpose()?,
596        };
597
598        let mut token = Token::root();
599        let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
600        Ok(id.0)
601    }
602
603    #[cfg(all(feature = "dx12", windows))]
604    /// # Safety
605    ///
606    /// The visual must be valid and able to be used to make a swapchain with.
607    pub unsafe fn instance_create_surface_from_visual(
608        &self,
609        visual: *mut std::ffi::c_void,
610        id_in: Input<G, SurfaceId>,
611    ) -> SurfaceId {
612        profiling::scope!("Instance::instance_create_surface_from_visual");
613
614        let surface = Surface {
615            presentation: None,
616            #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
617            vulkan: None,
618            #[cfg(all(feature = "dx12", windows))]
619            dx12: self.instance.dx12.as_ref().map(|inst| HalSurface {
620                raw: unsafe { inst.create_surface_from_visual(visual as _) },
621            }),
622            #[cfg(all(feature = "dx11", windows))]
623            dx11: None,
624            #[cfg(feature = "gles")]
625            gl: None,
626        };
627
628        let mut token = Token::root();
629        let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
630        id.0
631    }
632
633    #[cfg(all(feature = "dx12", windows))]
634    /// # Safety
635    ///
636    /// The surface_handle must be valid and able to be used to make a swapchain with.
637    pub unsafe fn instance_create_surface_from_surface_handle(
638        &self,
639        surface_handle: *mut std::ffi::c_void,
640        id_in: Input<G, SurfaceId>,
641    ) -> SurfaceId {
642        profiling::scope!("Instance::instance_create_surface_from_surface_handle");
643
644        let surface = Surface {
645            presentation: None,
646            #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
647            vulkan: None,
648            #[cfg(all(feature = "dx12", windows))]
649            dx12: self.instance.dx12.as_ref().map(|inst| HalSurface {
650                raw: unsafe { inst.create_surface_from_surface_handle(surface_handle) },
651            }),
652            #[cfg(all(feature = "dx11", windows))]
653            dx11: None,
654            #[cfg(feature = "gles")]
655            gl: None,
656        };
657
658        let mut token = Token::root();
659        let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
660        id.0
661    }
662
663    #[cfg(all(feature = "dx12", windows))]
664    /// # Safety
665    ///
666    /// The swap_chain_panel must be valid and able to be used to make a swapchain with.
667    pub unsafe fn instance_create_surface_from_swap_chain_panel(
668        &self,
669        swap_chain_panel: *mut std::ffi::c_void,
670        id_in: Input<G, SurfaceId>,
671    ) -> SurfaceId {
672        profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
673
674        let surface = Surface {
675            presentation: None,
676            #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
677            vulkan: None,
678            #[cfg(all(feature = "dx12", windows))]
679            dx12: self.instance.dx12.as_ref().map(|inst| HalSurface {
680                raw: unsafe { inst.create_surface_from_swap_chain_panel(swap_chain_panel as _) },
681            }),
682            #[cfg(all(feature = "dx11", windows))]
683            dx11: None,
684            #[cfg(feature = "gles")]
685            gl: None,
686        };
687
688        let mut token = Token::root();
689        let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
690        id.0
691    }
692
693    pub fn surface_drop(&self, id: SurfaceId) {
694        profiling::scope!("Surface::drop");
695        log::trace!("Surface::drop {id:?}");
696
697        let mut token = Token::root();
698        let (surface, _) = self.surfaces.unregister(id, &mut token);
699        let mut surface = surface.unwrap();
700
701        fn unconfigure<G: GlobalIdentityHandlerFactory, A: HalApi>(
702            global: &Global<G>,
703            surface: &mut HalSurface<A>,
704            present: &Presentation,
705        ) {
706            let hub = HalApi::hub(global);
707            hub.surface_unconfigure(present.device_id.value, surface);
708        }
709
710        if let Some(present) = surface.presentation.take() {
711            match present.backend() {
712                #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
713                Backend::Vulkan => unconfigure(self, surface.vulkan.as_mut().unwrap(), &present),
714                #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
715                Backend::Metal => unconfigure(self, surface.metal.as_mut().unwrap(), &present),
716                #[cfg(all(feature = "dx12", windows))]
717                Backend::Dx12 => unconfigure(self, surface.dx12.as_mut().unwrap(), &present),
718                #[cfg(all(feature = "dx11", windows))]
719                Backend::Dx11 => unconfigure(self, surface.dx11.as_mut().unwrap(), &present),
720                #[cfg(feature = "gles")]
721                Backend::Gl => unconfigure(self, surface.gl.as_mut().unwrap(), &present),
722                _ => unreachable!(),
723            }
724        }
725
726        self.instance.destroy_surface(surface);
727    }
728
729    fn enumerate<A: HalApi>(
730        &self,
731        _: A,
732        instance: &Option<A::Instance>,
733        inputs: &AdapterInputs<Input<G, AdapterId>>,
734        list: &mut Vec<AdapterId>,
735    ) {
736        let inst = match *instance {
737            Some(ref inst) => inst,
738            None => return,
739        };
740        let id_backend = match inputs.find(A::VARIANT) {
741            Some(id) => id,
742            None => return,
743        };
744
745        profiling::scope!("enumerating", &*format!("{:?}", A::VARIANT));
746        let hub = HalApi::hub(self);
747        let mut token = Token::root();
748
749        let hal_adapters = unsafe { inst.enumerate_adapters() };
750        for raw in hal_adapters {
751            let adapter = Adapter::new(raw);
752            log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info);
753            let id = hub
754                .adapters
755                .prepare(id_backend.clone())
756                .assign(adapter, &mut token);
757            list.push(id.0);
758        }
759    }
760
761    pub fn enumerate_adapters(&self, inputs: AdapterInputs<Input<G, AdapterId>>) -> Vec<AdapterId> {
762        profiling::scope!("Instance::enumerate_adapters");
763        log::trace!("Instance::enumerate_adapters");
764
765        let mut adapters = Vec::new();
766
767        #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
768        self.enumerate(
769            hal::api::Vulkan,
770            &self.instance.vulkan,
771            &inputs,
772            &mut adapters,
773        );
774        #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
775        self.enumerate(
776            hal::api::Metal,
777            &self.instance.metal,
778            &inputs,
779            &mut adapters,
780        );
781        #[cfg(all(feature = "dx12", windows))]
782        self.enumerate(hal::api::Dx12, &self.instance.dx12, &inputs, &mut adapters);
783        #[cfg(all(feature = "dx11", windows))]
784        self.enumerate(hal::api::Dx11, &self.instance.dx11, &inputs, &mut adapters);
785        #[cfg(feature = "gles")]
786        self.enumerate(hal::api::Gles, &self.instance.gl, &inputs, &mut adapters);
787
788        adapters
789    }
790
791    fn select<A: HalApi>(
792        &self,
793        selected: &mut usize,
794        new_id: Option<Input<G, AdapterId>>,
795        mut list: Vec<hal::ExposedAdapter<A>>,
796    ) -> Option<AdapterId> {
797        match selected.checked_sub(list.len()) {
798            Some(left) => {
799                *selected = left;
800                None
801            }
802            None => {
803                let mut token = Token::root();
804                let adapter = Adapter::new(list.swap_remove(*selected));
805                log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info);
806                let id = HalApi::hub(self)
807                    .adapters
808                    .prepare(new_id.unwrap())
809                    .assign(adapter, &mut token);
810                Some(id.0)
811            }
812        }
813    }
814
815    pub fn request_adapter(
816        &self,
817        desc: &RequestAdapterOptions,
818        inputs: AdapterInputs<Input<G, AdapterId>>,
819    ) -> Result<AdapterId, RequestAdapterError> {
820        profiling::scope!("Instance::pick_adapter");
821        log::trace!("Instance::pick_adapter");
822
823        fn gather<A: HalApi, I: Clone>(
824            _: A,
825            instance: Option<&A::Instance>,
826            inputs: &AdapterInputs<I>,
827            compatible_surface: Option<&Surface>,
828            force_software: bool,
829            device_types: &mut Vec<wgt::DeviceType>,
830        ) -> (Option<I>, Vec<hal::ExposedAdapter<A>>) {
831            let id = inputs.find(A::VARIANT);
832            match instance {
833                Some(inst) if id.is_some() => {
834                    let mut adapters = unsafe { inst.enumerate_adapters() };
835                    if force_software {
836                        adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu);
837                    }
838                    if let Some(surface) = compatible_surface {
839                        let surface = &A::get_surface(surface);
840                        adapters.retain(|exposed| unsafe {
841                            // If the surface does not exist for this backend,
842                            // then the surface is not supported.
843                            surface.is_some()
844                                && exposed
845                                    .adapter
846                                    .surface_capabilities(&surface.unwrap().raw)
847                                    .is_some()
848                        });
849                    }
850                    device_types.extend(adapters.iter().map(|ad| ad.info.device_type));
851                    (id, adapters)
852                }
853                _ => (id, Vec::new()),
854            }
855        }
856
857        let mut token = Token::root();
858        let (surface_guard, _) = self.surfaces.read(&mut token);
859        let compatible_surface = desc
860            .compatible_surface
861            .map(|id| {
862                surface_guard
863                    .get(id)
864                    .map_err(|_| RequestAdapterError::InvalidSurface(id))
865            })
866            .transpose()?;
867        let mut device_types = Vec::new();
868
869        #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
870        let (id_vulkan, adapters_vk) = gather(
871            hal::api::Vulkan,
872            self.instance.vulkan.as_ref(),
873            &inputs,
874            compatible_surface,
875            desc.force_fallback_adapter,
876            &mut device_types,
877        );
878        #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
879        let (id_metal, adapters_metal) = gather(
880            hal::api::Metal,
881            self.instance.metal.as_ref(),
882            &inputs,
883            compatible_surface,
884            desc.force_fallback_adapter,
885            &mut device_types,
886        );
887        #[cfg(all(feature = "dx12", windows))]
888        let (id_dx12, adapters_dx12) = gather(
889            hal::api::Dx12,
890            self.instance.dx12.as_ref(),
891            &inputs,
892            compatible_surface,
893            desc.force_fallback_adapter,
894            &mut device_types,
895        );
896        #[cfg(all(feature = "dx11", windows))]
897        let (id_dx11, adapters_dx11) = gather(
898            hal::api::Dx11,
899            self.instance.dx11.as_ref(),
900            &inputs,
901            compatible_surface,
902            desc.force_fallback_adapter,
903            &mut device_types,
904        );
905        #[cfg(feature = "gles")]
906        let (id_gl, adapters_gl) = gather(
907            hal::api::Gles,
908            self.instance.gl.as_ref(),
909            &inputs,
910            compatible_surface,
911            desc.force_fallback_adapter,
912            &mut device_types,
913        );
914
915        // need to free the token to be used by `select`
916        drop(surface_guard);
917        drop(token);
918        if device_types.is_empty() {
919            return Err(RequestAdapterError::NotFound);
920        }
921
922        let (mut integrated, mut discrete, mut virt, mut cpu, mut other) =
923            (None, None, None, None, None);
924
925        for (i, ty) in device_types.into_iter().enumerate() {
926            match ty {
927                wgt::DeviceType::IntegratedGpu => {
928                    integrated = integrated.or(Some(i));
929                }
930                wgt::DeviceType::DiscreteGpu => {
931                    discrete = discrete.or(Some(i));
932                }
933                wgt::DeviceType::VirtualGpu => {
934                    virt = virt.or(Some(i));
935                }
936                wgt::DeviceType::Cpu => {
937                    cpu = cpu.or(Some(i));
938                }
939                wgt::DeviceType::Other => {
940                    other = other.or(Some(i));
941                }
942            }
943        }
944
945        let preferred_gpu = match desc.power_preference {
946            // Since devices of type "Other" might really be "Unknown" and come
947            // from APIs like OpenGL that don't specify device type, Prefer more
948            // Specific types over Other.
949            //
950            // This means that backends which do provide accurate device types
951            // will be preferred if their device type indicates an actual
952            // hardware GPU (integrated or discrete).
953            PowerPreference::LowPower => integrated.or(discrete).or(other).or(virt).or(cpu),
954            PowerPreference::HighPerformance => discrete.or(integrated).or(other).or(virt).or(cpu),
955            PowerPreference::None => {
956                let option_min = |a: Option<usize>, b: Option<usize>| {
957                    if let (Some(a), Some(b)) = (a, b) {
958                        Some(a.min(b))
959                    } else {
960                        a.or(b)
961                    }
962                };
963                // Pick the lowest id of these types
964                option_min(option_min(discrete, integrated), other)
965            }
966        };
967
968        let mut selected = preferred_gpu.unwrap_or(0);
969        #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
970        if let Some(id) = self.select(&mut selected, id_vulkan, adapters_vk) {
971            return Ok(id);
972        }
973        #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
974        if let Some(id) = self.select(&mut selected, id_metal, adapters_metal) {
975            return Ok(id);
976        }
977        #[cfg(all(feature = "dx12", windows))]
978        if let Some(id) = self.select(&mut selected, id_dx12, adapters_dx12) {
979            return Ok(id);
980        }
981        #[cfg(all(feature = "dx11", windows))]
982        if let Some(id) = self.select(&mut selected, id_dx11, adapters_dx11) {
983            return Ok(id);
984        }
985        #[cfg(feature = "gles")]
986        if let Some(id) = self.select(&mut selected, id_gl, adapters_gl) {
987            return Ok(id);
988        }
989        let _ = selected;
990
991        log::warn!("Some adapters are present, but enumerating them failed!");
992        Err(RequestAdapterError::NotFound)
993    }
994
995    /// # Safety
996    ///
997    /// `hal_adapter` must be created from this global internal instance handle.
998    pub unsafe fn create_adapter_from_hal<A: HalApi>(
999        &self,
1000        hal_adapter: hal::ExposedAdapter<A>,
1001        input: Input<G, AdapterId>,
1002    ) -> AdapterId {
1003        profiling::scope!("Instance::create_adapter_from_hal");
1004
1005        let mut token = Token::root();
1006        let fid = A::hub(self).adapters.prepare(input);
1007
1008        match A::VARIANT {
1009            #[cfg(all(feature = "vulkan", not(target_arch = "wasm32")))]
1010            Backend::Vulkan => fid.assign(Adapter::new(hal_adapter), &mut token).0,
1011            #[cfg(all(feature = "metal", any(target_os = "macos", target_os = "ios")))]
1012            Backend::Metal => fid.assign(Adapter::new(hal_adapter), &mut token).0,
1013            #[cfg(all(feature = "dx12", windows))]
1014            Backend::Dx12 => fid.assign(Adapter::new(hal_adapter), &mut token).0,
1015            #[cfg(all(feature = "dx11", windows))]
1016            Backend::Dx11 => fid.assign(Adapter::new(hal_adapter), &mut token).0,
1017            #[cfg(feature = "gles")]
1018            Backend::Gl => fid.assign(Adapter::new(hal_adapter), &mut token).0,
1019            _ => unreachable!(),
1020        }
1021    }
1022
1023    pub fn adapter_get_info<A: HalApi>(
1024        &self,
1025        adapter_id: AdapterId,
1026    ) -> Result<wgt::AdapterInfo, InvalidAdapter> {
1027        let hub = A::hub(self);
1028        let mut token = Token::root();
1029        let (adapter_guard, _) = hub.adapters.read(&mut token);
1030        adapter_guard
1031            .get(adapter_id)
1032            .map(|adapter| adapter.raw.info.clone())
1033            .map_err(|_| InvalidAdapter)
1034    }
1035
1036    pub fn adapter_get_texture_format_features<A: HalApi>(
1037        &self,
1038        adapter_id: AdapterId,
1039        format: wgt::TextureFormat,
1040    ) -> Result<wgt::TextureFormatFeatures, InvalidAdapter> {
1041        let hub = A::hub(self);
1042        let mut token = Token::root();
1043        let (adapter_guard, _) = hub.adapters.read(&mut token);
1044        adapter_guard
1045            .get(adapter_id)
1046            .map(|adapter| adapter.get_texture_format_features(format))
1047            .map_err(|_| InvalidAdapter)
1048    }
1049
1050    pub fn adapter_features<A: HalApi>(
1051        &self,
1052        adapter_id: AdapterId,
1053    ) -> Result<wgt::Features, InvalidAdapter> {
1054        let hub = A::hub(self);
1055        let mut token = Token::root();
1056        let (adapter_guard, _) = hub.adapters.read(&mut token);
1057        adapter_guard
1058            .get(adapter_id)
1059            .map(|adapter| adapter.raw.features)
1060            .map_err(|_| InvalidAdapter)
1061    }
1062
1063    pub fn adapter_limits<A: HalApi>(
1064        &self,
1065        adapter_id: AdapterId,
1066    ) -> Result<wgt::Limits, InvalidAdapter> {
1067        let hub = A::hub(self);
1068        let mut token = Token::root();
1069        let (adapter_guard, _) = hub.adapters.read(&mut token);
1070        adapter_guard
1071            .get(adapter_id)
1072            .map(|adapter| adapter.raw.capabilities.limits.clone())
1073            .map_err(|_| InvalidAdapter)
1074    }
1075
1076    pub fn adapter_downlevel_capabilities<A: HalApi>(
1077        &self,
1078        adapter_id: AdapterId,
1079    ) -> Result<wgt::DownlevelCapabilities, InvalidAdapter> {
1080        let hub = A::hub(self);
1081        let mut token = Token::root();
1082        let (adapter_guard, _) = hub.adapters.read(&mut token);
1083        adapter_guard
1084            .get(adapter_id)
1085            .map(|adapter| adapter.raw.capabilities.downlevel.clone())
1086            .map_err(|_| InvalidAdapter)
1087    }
1088
1089    pub fn adapter_get_presentation_timestamp<A: HalApi>(
1090        &self,
1091        adapter_id: AdapterId,
1092    ) -> Result<wgt::PresentationTimestamp, InvalidAdapter> {
1093        let hub = A::hub(self);
1094        let mut token = Token::root();
1095        let (adapter_guard, _) = hub.adapters.read(&mut token);
1096        let adapter = adapter_guard.get(adapter_id).map_err(|_| InvalidAdapter)?;
1097
1098        Ok(unsafe { adapter.raw.adapter.get_presentation_timestamp() })
1099    }
1100
1101    pub fn adapter_drop<A: HalApi>(&self, adapter_id: AdapterId) {
1102        profiling::scope!("Adapter::drop");
1103        log::trace!("Adapter::drop {adapter_id:?}");
1104
1105        let hub = A::hub(self);
1106        let mut token = Token::root();
1107        let (mut adapter_guard, _) = hub.adapters.write(&mut token);
1108
1109        let free = match adapter_guard.get_mut(adapter_id) {
1110            Ok(adapter) => adapter.life_guard.ref_count.take().unwrap().load() == 1,
1111            Err(_) => true,
1112        };
1113        if free {
1114            hub.adapters
1115                .unregister_locked(adapter_id, &mut *adapter_guard);
1116        }
1117    }
1118}
1119
1120impl<G: GlobalIdentityHandlerFactory> Global<G> {
1121    pub fn adapter_request_device<A: HalApi>(
1122        &self,
1123        adapter_id: AdapterId,
1124        desc: &DeviceDescriptor,
1125        trace_path: Option<&std::path::Path>,
1126        id_in: Input<G, DeviceId>,
1127    ) -> (DeviceId, Option<RequestDeviceError>) {
1128        profiling::scope!("Adapter::request_device");
1129        log::trace!("Adapter::request_device");
1130
1131        let hub = A::hub(self);
1132        let mut token = Token::root();
1133        let fid = hub.devices.prepare(id_in);
1134
1135        let error = loop {
1136            let (adapter_guard, mut token) = hub.adapters.read(&mut token);
1137            let adapter = match adapter_guard.get(adapter_id) {
1138                Ok(adapter) => adapter,
1139                Err(_) => break RequestDeviceError::InvalidAdapter,
1140            };
1141            let device =
1142                match adapter.create_device(adapter_id, desc, self.instance.flags, trace_path) {
1143                    Ok(device) => device,
1144                    Err(e) => break e,
1145                };
1146            let id = fid.assign(device, &mut token);
1147            return (id.0, None);
1148        };
1149
1150        let id = fid.assign_error(desc.label.borrow_or_default(), &mut token);
1151        (id, Some(error))
1152    }
1153
1154    /// # Safety
1155    ///
1156    /// - `hal_device` must be created from `adapter_id` or its internal handle.
1157    /// - `desc` must be a subset of `hal_device` features and limits.
1158    pub unsafe fn create_device_from_hal<A: HalApi>(
1159        &self,
1160        adapter_id: AdapterId,
1161        hal_device: hal::OpenDevice<A>,
1162        desc: &DeviceDescriptor,
1163        trace_path: Option<&std::path::Path>,
1164        id_in: Input<G, DeviceId>,
1165    ) -> (DeviceId, Option<RequestDeviceError>) {
1166        profiling::scope!("Adapter::create_device_from_hal");
1167
1168        let hub = A::hub(self);
1169        let mut token = Token::root();
1170        let fid = hub.devices.prepare(id_in);
1171
1172        let error = loop {
1173            let (adapter_guard, mut token) = hub.adapters.read(&mut token);
1174            let adapter = match adapter_guard.get(adapter_id) {
1175                Ok(adapter) => adapter,
1176                Err(_) => break RequestDeviceError::InvalidAdapter,
1177            };
1178            let device = match adapter.create_device_from_hal(
1179                adapter_id,
1180                hal_device,
1181                desc,
1182                self.instance.flags,
1183                trace_path,
1184            ) {
1185                Ok(device) => device,
1186                Err(e) => break e,
1187            };
1188            let id = fid.assign(device, &mut token);
1189            return (id.0, None);
1190        };
1191
1192        let id = fid.assign_error(desc.label.borrow_or_default(), &mut token);
1193        (id, Some(error))
1194    }
1195}
1196
1197/// Generates a set of backends from a comma separated list of case-insensitive backend names.
1198///
1199/// Whitespace is stripped, so both 'gl, dx12' and 'gl,dx12' are valid.
1200///
1201/// Always returns WEBGPU on wasm over webgpu.
1202///
1203/// Names:
1204/// - vulkan = "vulkan" or "vk"
1205/// - dx12   = "dx12" or "d3d12"
1206/// - dx11   = "dx11" or "d3d11"
1207/// - metal  = "metal" or "mtl"
1208/// - gles   = "opengl" or "gles" or "gl"
1209/// - webgpu = "webgpu"
1210pub fn parse_backends_from_comma_list(string: &str) -> Backends {
1211    let mut backends = Backends::empty();
1212    for backend in string.to_lowercase().split(',') {
1213        backends |= match backend.trim() {
1214            "vulkan" | "vk" => Backends::VULKAN,
1215            "dx12" | "d3d12" => Backends::DX12,
1216            "dx11" | "d3d11" => Backends::DX11,
1217            "metal" | "mtl" => Backends::METAL,
1218            "opengl" | "gles" | "gl" => Backends::GL,
1219            "webgpu" => Backends::BROWSER_WEBGPU,
1220            b => {
1221                log::warn!("unknown backend string '{}'", b);
1222                continue;
1223            }
1224        }
1225    }
1226
1227    if backends.is_empty() {
1228        log::warn!("no valid backend strings found!");
1229    }
1230
1231    backends
1232}