use std::sync::Arc; use wgt::Backend; use crate::{ hal_api::HalApi, hub::{HubReport, Hubs}, instance::{Instance, Surface}, registry::{Registry, RegistryReport}, resource_log, storage::Element, }; #[derive(Debug, PartialEq, Eq)] pub struct GlobalReport { pub surfaces: RegistryReport, #[cfg(vulkan)] pub vulkan: Option, #[cfg(metal)] pub metal: Option, #[cfg(dx12)] pub dx12: Option, #[cfg(gles)] pub gl: Option, } impl GlobalReport { pub fn surfaces(&self) -> &RegistryReport { &self.surfaces } pub fn hub_report(&self, backend: Backend) -> &HubReport { match backend { #[cfg(vulkan)] Backend::Vulkan => self.vulkan.as_ref().unwrap(), #[cfg(metal)] Backend::Metal => self.metal.as_ref().unwrap(), #[cfg(dx12)] Backend::Dx12 => self.dx12.as_ref().unwrap(), #[cfg(gles)] Backend::Gl => self.gl.as_ref().unwrap(), _ => panic!("HubReport is not supported on this backend"), } } } pub struct Global { pub instance: Instance, pub surfaces: Registry, pub(crate) hubs: Hubs, } impl Global { pub fn new(name: &str, instance_desc: wgt::InstanceDescriptor) -> Self { profiling::scope!("Global::new"); Self { instance: Instance::new(name, instance_desc), surfaces: Registry::without_backend(), hubs: Hubs::new(), } } /// # Safety /// /// Refer to the creation of wgpu-hal Instance for every backend. pub unsafe fn from_hal_instance(name: &str, hal_instance: A::Instance) -> Self { profiling::scope!("Global::new"); Self { instance: A::create_instance_from_hal(name, hal_instance), surfaces: Registry::without_backend(), hubs: Hubs::new(), } } /// # Safety /// /// - The raw instance handle returned must not be manually destroyed. pub unsafe fn instance_as_hal(&self) -> Option<&A::Instance> { A::instance_as_hal(&self.instance) } /// # Safety /// /// - The raw handles obtained from the Instance must not be manually destroyed pub unsafe fn from_instance(instance: Instance) -> Self { profiling::scope!("Global::new"); Self { instance, surfaces: Registry::without_backend(), hubs: Hubs::new(), } } pub fn clear_backend(&self, _dummy: ()) { let hub = A::hub(self); let surfaces_locked = self.surfaces.read(); // this is used for tests, which keep the adapter hub.clear(&surfaces_locked, false); } pub fn generate_report(&self) -> GlobalReport { GlobalReport { surfaces: self.surfaces.generate_report(), #[cfg(vulkan)] vulkan: if self.instance.vulkan.is_some() { Some(self.hubs.vulkan.generate_report()) } else { None }, #[cfg(metal)] metal: if self.instance.metal.is_some() { Some(self.hubs.metal.generate_report()) } else { None }, #[cfg(dx12)] dx12: if self.instance.dx12.is_some() { Some(self.hubs.dx12.generate_report()) } else { None }, #[cfg(gles)] gl: if self.instance.gl.is_some() { Some(self.hubs.gl.generate_report()) } else { None }, } } } impl Drop for Global { fn drop(&mut self) { profiling::scope!("Global::drop"); resource_log!("Global::drop"); let mut surfaces_locked = self.surfaces.write(); // destroy hubs before the instance gets dropped #[cfg(vulkan)] { self.hubs.vulkan.clear(&surfaces_locked, true); } #[cfg(metal)] { self.hubs.metal.clear(&surfaces_locked, true); } #[cfg(dx12)] { self.hubs.dx12.clear(&surfaces_locked, true); } #[cfg(gles)] { self.hubs.gl.clear(&surfaces_locked, true); } // destroy surfaces for element in surfaces_locked.map.drain(..) { if let Element::Occupied(arc_surface, _) = element { if let Some(surface) = Arc::into_inner(arc_surface) { self.instance.destroy_surface(surface); } else { panic!("Surface cannot be destroyed because is still in use"); } } } } } #[cfg(send_sync)] fn _test_send_sync(global: &Global) { fn test_internal(_: T) {} test_internal(global) }