wgpu_core/
instance.rs

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