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