use std::collections::HashMap;
use std::sync::Arc;
use crate::{
    api_log,
    device::{queue::Queue, resource::Device, DeviceDescriptor},
    global::Global,
    hal_api::HalApi,
    id::markers,
    id::{AdapterId, DeviceId, Id, Marker, QueueId, SurfaceId},
    lock::{rank, Mutex},
    present::Presentation,
    resource::{Resource, ResourceInfo, ResourceType},
    resource_log, LabelHelpers, DOWNLEVEL_WARNING_MESSAGE,
};
use wgt::{Backend, Backends, PowerPreference};
use hal::{Adapter as _, Instance as _, OpenDevice};
use thiserror::Error;
pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
type HalInstance<A> = <A as hal::Api>::Instance;
type HalSurface<A> = <A as hal::Api>::Surface;
#[derive(Clone, Debug, Error)]
#[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
pub struct FailedLimit {
    name: &'static str,
    requested: u64,
    allowed: u64,
}
fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLimit> {
    let mut failed = Vec::new();
    requested.check_limits_with_fail_fn(allowed, false, |name, requested, allowed| {
        failed.push(FailedLimit {
            name,
            requested,
            allowed,
        })
    });
    failed
}
#[test]
fn downlevel_default_limits_less_than_default_limits() {
    let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
    assert!(
        res.is_empty(),
        "Downlevel limits are greater than default limits",
    )
}
#[derive(Default)]
pub struct Instance {
    #[allow(dead_code)]
    pub name: String,
    #[cfg(vulkan)]
    pub vulkan: Option<HalInstance<hal::api::Vulkan>>,
    #[cfg(metal)]
    pub metal: Option<HalInstance<hal::api::Metal>>,
    #[cfg(dx12)]
    pub dx12: Option<HalInstance<hal::api::Dx12>>,
    #[cfg(gles)]
    pub gl: Option<HalInstance<hal::api::Gles>>,
    pub flags: wgt::InstanceFlags,
}
impl Instance {
    pub fn new(name: &str, instance_desc: wgt::InstanceDescriptor) -> Self {
        fn init<A: HalApi>(_: A, instance_desc: &wgt::InstanceDescriptor) -> Option<A::Instance> {
            if instance_desc.backends.contains(A::VARIANT.into()) {
                let hal_desc = hal::InstanceDescriptor {
                    name: "wgpu",
                    flags: instance_desc.flags,
                    dx12_shader_compiler: instance_desc.dx12_shader_compiler.clone(),
                    gles_minor_version: instance_desc.gles_minor_version,
                };
                match unsafe { hal::Instance::init(&hal_desc) } {
                    Ok(instance) => {
                        log::debug!("Instance::new: created {:?} backend", A::VARIANT);
                        Some(instance)
                    }
                    Err(err) => {
                        log::debug!(
                            "Instance::new: failed to create {:?} backend: {:?}",
                            A::VARIANT,
                            err
                        );
                        None
                    }
                }
            } else {
                log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
                None
            }
        }
        Self {
            name: name.to_string(),
            #[cfg(vulkan)]
            vulkan: init(hal::api::Vulkan, &instance_desc),
            #[cfg(metal)]
            metal: init(hal::api::Metal, &instance_desc),
            #[cfg(dx12)]
            dx12: init(hal::api::Dx12, &instance_desc),
            #[cfg(gles)]
            gl: init(hal::api::Gles, &instance_desc),
            flags: instance_desc.flags,
        }
    }
    pub(crate) fn destroy_surface(&self, surface: Surface) {
        fn destroy<A: HalApi>(instance: &Option<A::Instance>, mut surface: Option<HalSurface<A>>) {
            if let Some(surface) = surface.take() {
                unsafe {
                    instance.as_ref().unwrap().destroy_surface(surface);
                }
            }
        }
        #[cfg(vulkan)]
        destroy::<hal::api::Vulkan>(&self.vulkan, surface.vulkan);
        #[cfg(metal)]
        destroy::<hal::api::Metal>(&self.metal, surface.metal);
        #[cfg(dx12)]
        destroy::<hal::api::Dx12>(&self.dx12, surface.dx12);
        #[cfg(gles)]
        destroy::<hal::api::Gles>(&self.gl, surface.gl);
    }
}
pub struct Surface {
    pub(crate) presentation: Mutex<Option<Presentation>>,
    pub(crate) info: ResourceInfo<Surface>,
    #[cfg(vulkan)]
    pub vulkan: Option<HalSurface<hal::api::Vulkan>>,
    #[cfg(metal)]
    pub metal: Option<HalSurface<hal::api::Metal>>,
    #[cfg(dx12)]
    pub dx12: Option<HalSurface<hal::api::Dx12>>,
    #[cfg(gles)]
    pub gl: Option<HalSurface<hal::api::Gles>>,
}
impl Resource for Surface {
    const TYPE: ResourceType = "Surface";
    type Marker = markers::Surface;
    fn as_info(&self) -> &ResourceInfo<Self> {
        &self.info
    }
    fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
        &mut self.info
    }
    fn label(&self) -> String {
        String::from("<Surface>")
    }
}
impl Surface {
    pub fn get_capabilities<A: HalApi>(
        &self,
        adapter: &Adapter<A>,
    ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
        let suf = A::surface_as_hal(self).ok_or(GetSurfaceSupportError::Unsupported)?;
        profiling::scope!("surface_capabilities");
        let caps = unsafe {
            adapter
                .raw
                .adapter
                .surface_capabilities(suf)
                .ok_or(GetSurfaceSupportError::Unsupported)?
        };
        Ok(caps)
    }
}
pub struct Adapter<A: HalApi> {
    pub(crate) raw: hal::ExposedAdapter<A>,
    pub(crate) info: ResourceInfo<Adapter<A>>,
}
impl<A: HalApi> Adapter<A> {
    fn new(mut raw: hal::ExposedAdapter<A>) -> Self {
        const MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND: u32 = 32;
        let limits = &mut raw.capabilities.limits;
        limits.min_uniform_buffer_offset_alignment = limits
            .min_uniform_buffer_offset_alignment
            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
        limits.min_storage_buffer_offset_alignment = limits
            .min_storage_buffer_offset_alignment
            .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
        Self {
            raw,
            info: ResourceInfo::new("<Adapter>", None),
        }
    }
    pub fn is_surface_supported(&self, surface: &Surface) -> bool {
        let suf = A::surface_as_hal(surface);
        match suf {
            Some(suf) => unsafe { self.raw.adapter.surface_capabilities(suf) }.is_some(),
            None => false,
        }
    }
    pub(crate) fn get_texture_format_features(
        &self,
        format: wgt::TextureFormat,
    ) -> wgt::TextureFormatFeatures {
        use hal::TextureFormatCapabilities as Tfc;
        let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
        let mut allowed_usages = wgt::TextureUsages::empty();
        allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
        allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
        allowed_usages.set(
            wgt::TextureUsages::TEXTURE_BINDING,
            caps.contains(Tfc::SAMPLED),
        );
        allowed_usages.set(
            wgt::TextureUsages::STORAGE_BINDING,
            caps.contains(Tfc::STORAGE),
        );
        allowed_usages.set(
            wgt::TextureUsages::RENDER_ATTACHMENT,
            caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
        );
        let mut flags = wgt::TextureFormatFeatureFlags::empty();
        flags.set(
            wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
            caps.contains(Tfc::STORAGE_READ_WRITE),
        );
        flags.set(
            wgt::TextureFormatFeatureFlags::FILTERABLE,
            caps.contains(Tfc::SAMPLED_LINEAR),
        );
        flags.set(
            wgt::TextureFormatFeatureFlags::BLENDABLE,
            caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
        );
        flags.set(
            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
            caps.contains(Tfc::MULTISAMPLE_X2),
        );
        flags.set(
            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
            caps.contains(Tfc::MULTISAMPLE_X4),
        );
        flags.set(
            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
            caps.contains(Tfc::MULTISAMPLE_X8),
        );
        flags.set(
            wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
            caps.contains(Tfc::MULTISAMPLE_X16),
        );
        flags.set(
            wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
            caps.contains(Tfc::MULTISAMPLE_RESOLVE),
        );
        wgt::TextureFormatFeatures {
            allowed_usages,
            flags,
        }
    }
    fn create_device_and_queue_from_hal(
        self: &Arc<Self>,
        hal_device: OpenDevice<A>,
        desc: &DeviceDescriptor,
        instance_flags: wgt::InstanceFlags,
        trace_path: Option<&std::path::Path>,
    ) -> Result<(Device<A>, Queue<A>), RequestDeviceError> {
        api_log!("Adapter::create_device");
        if let Ok(device) = Device::new(
            hal_device.device,
            &hal_device.queue,
            self,
            desc,
            trace_path,
            instance_flags,
        ) {
            let queue = Queue {
                device: None,
                raw: Some(hal_device.queue),
                info: ResourceInfo::new("<Queue>", None),
            };
            return Ok((device, queue));
        }
        Err(RequestDeviceError::OutOfMemory)
    }
    fn create_device_and_queue(
        self: &Arc<Self>,
        desc: &DeviceDescriptor,
        instance_flags: wgt::InstanceFlags,
        trace_path: Option<&std::path::Path>,
    ) -> Result<(Device<A>, Queue<A>), RequestDeviceError> {
        if !self.raw.features.contains(desc.required_features) {
            return Err(RequestDeviceError::UnsupportedFeature(
                desc.required_features - self.raw.features,
            ));
        }
        let caps = &self.raw.capabilities;
        if wgt::Backends::PRIMARY.contains(wgt::Backends::from(A::VARIANT))
            && !caps.downlevel.is_webgpu_compliant()
        {
            let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
            log::warn!(
                "Missing downlevel flags: {:?}\n{}",
                missing_flags,
                DOWNLEVEL_WARNING_MESSAGE
            );
            log::warn!("{:#?}", caps.downlevel);
        }
        if desc
            .required_features
            .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
            && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
        {
            log::warn!(
                "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
                        This is a massive performance footgun and likely not what you wanted"
            );
        }
        if let Some(_) = desc.label {
            }
        if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
            return Err(RequestDeviceError::LimitsExceeded(failed));
        }
        let open = unsafe {
            self.raw
                .adapter
                .open(desc.required_features, &desc.required_limits)
        }
        .map_err(|err| match err {
            hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
            hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory,
            hal::DeviceError::ResourceCreationFailed => RequestDeviceError::Internal,
        })?;
        self.create_device_and_queue_from_hal(open, desc, instance_flags, trace_path)
    }
}
impl<A: HalApi> Resource for Adapter<A> {
    const TYPE: ResourceType = "Adapter";
    type Marker = markers::Adapter;
    fn as_info(&self) -> &ResourceInfo<Self> {
        &self.info
    }
    fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
        &mut self.info
    }
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum IsSurfaceSupportedError {
    #[error("Invalid adapter")]
    InvalidAdapter,
    #[error("Invalid surface")]
    InvalidSurface,
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum GetSurfaceSupportError {
    #[error("Invalid adapter")]
    InvalidAdapter,
    #[error("Invalid surface")]
    InvalidSurface,
    #[error("Surface is not supported by the adapter")]
    Unsupported,
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum RequestDeviceError {
    #[error("Parent adapter is invalid")]
    InvalidAdapter,
    #[error("Connection to device was lost during initialization")]
    DeviceLost,
    #[error("Device initialization failed due to implementation specific errors")]
    Internal,
    #[error(transparent)]
    LimitsExceeded(#[from] FailedLimit),
    #[error("Device has no queue supporting graphics")]
    NoGraphicsQueue,
    #[error("Not enough memory left to request device")]
    OutOfMemory,
    #[error("Unsupported features were requested: {0:?}")]
    UnsupportedFeature(wgt::Features),
}
pub enum AdapterInputs<'a, M: Marker> {
    IdSet(&'a [Id<M>]),
    Mask(Backends, fn(Backend) -> Option<Id<M>>),
}
impl<M: Marker> AdapterInputs<'_, M> {
    fn find(&self, b: Backend) -> Option<Option<Id<M>>> {
        match *self {
            Self::IdSet(ids) => Some(Some(ids.iter().find(|id| id.backend() == b).copied()?)),
            Self::Mask(bits, ref fun) => {
                if bits.contains(b.into()) {
                    Some(fun(b))
                } else {
                    None
                }
            }
        }
    }
}
#[derive(Clone, Debug, Error)]
#[error("Adapter is invalid")]
pub struct InvalidAdapter;
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum RequestAdapterError {
    #[error("No suitable adapter found")]
    NotFound,
    #[error("Surface {0:?} is invalid")]
    InvalidSurface(SurfaceId),
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum CreateSurfaceError {
    #[error("The backend {0} was not enabled on the instance.")]
    BackendNotEnabled(Backend),
    #[error("Failed to create surface for any enabled backend: {0:?}")]
    FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
}
impl Global {
    #[cfg(feature = "raw-window-handle")]
    pub unsafe fn instance_create_surface(
        &self,
        display_handle: raw_window_handle::RawDisplayHandle,
        window_handle: raw_window_handle::RawWindowHandle,
        id_in: Option<SurfaceId>,
    ) -> Result<SurfaceId, CreateSurfaceError> {
        profiling::scope!("Instance::create_surface");
        fn init<A: HalApi>(
            errors: &mut HashMap<Backend, hal::InstanceError>,
            any_created: &mut bool,
            backend: Backend,
            inst: &Option<A::Instance>,
            display_handle: raw_window_handle::RawDisplayHandle,
            window_handle: raw_window_handle::RawWindowHandle,
        ) -> Option<HalSurface<A>> {
            inst.as_ref().and_then(|inst| {
                match unsafe { inst.create_surface(display_handle, window_handle) } {
                    Ok(raw) => {
                        *any_created = true;
                        Some(raw)
                    }
                    Err(err) => {
                        log::debug!(
                            "Instance::create_surface: failed to create surface for {:?}: {:?}",
                            backend,
                            err
                        );
                        errors.insert(backend, err);
                        None
                    }
                }
            })
        }
        let mut errors = HashMap::default();
        let mut any_created = false;
        let surface = Surface {
            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
            info: ResourceInfo::new("<Surface>", None),
            #[cfg(vulkan)]
            vulkan: init::<hal::api::Vulkan>(
                &mut errors,
                &mut any_created,
                Backend::Vulkan,
                &self.instance.vulkan,
                display_handle,
                window_handle,
            ),
            #[cfg(metal)]
            metal: init::<hal::api::Metal>(
                &mut errors,
                &mut any_created,
                Backend::Metal,
                &self.instance.metal,
                display_handle,
                window_handle,
            ),
            #[cfg(dx12)]
            dx12: init::<hal::api::Dx12>(
                &mut errors,
                &mut any_created,
                Backend::Dx12,
                &self.instance.dx12,
                display_handle,
                window_handle,
            ),
            #[cfg(gles)]
            gl: init::<hal::api::Gles>(
                &mut errors,
                &mut any_created,
                Backend::Gl,
                &self.instance.gl,
                display_handle,
                window_handle,
            ),
        };
        if any_created {
            #[allow(clippy::arc_with_non_send_sync)]
            let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
            Ok(id)
        } else {
            Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
                errors,
            ))
        }
    }
    #[cfg(metal)]
    pub unsafe fn instance_create_surface_metal(
        &self,
        layer: *mut std::ffi::c_void,
        id_in: Option<SurfaceId>,
    ) -> Result<SurfaceId, CreateSurfaceError> {
        profiling::scope!("Instance::create_surface_metal");
        let surface = Surface {
            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
            info: ResourceInfo::new("<Surface>", None),
            metal: Some(self.instance.metal.as_ref().map_or(
                Err(CreateSurfaceError::BackendNotEnabled(Backend::Metal)),
                |inst| {
                    #[allow(clippy::transmute_ptr_to_ref)]
                    Ok(inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) }))
                },
            )?),
            #[cfg(dx12)]
            dx12: None,
            #[cfg(vulkan)]
            vulkan: None,
            #[cfg(gles)]
            gl: None,
        };
        let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
        Ok(id)
    }
    #[cfg(dx12)]
    fn instance_create_surface_dx12(
        &self,
        id_in: Option<SurfaceId>,
        create_surface_func: impl FnOnce(&HalInstance<hal::api::Dx12>) -> HalSurface<hal::api::Dx12>,
    ) -> Result<SurfaceId, CreateSurfaceError> {
        let surface = Surface {
            presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
            info: ResourceInfo::new("<Surface>", None),
            dx12: Some(create_surface_func(
                self.instance
                    .dx12
                    .as_ref()
                    .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?,
            )),
            #[cfg(metal)]
            metal: None,
            #[cfg(vulkan)]
            vulkan: None,
            #[cfg(gles)]
            gl: None,
        };
        let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
        Ok(id)
    }
    #[cfg(dx12)]
    pub unsafe fn instance_create_surface_from_visual(
        &self,
        visual: *mut std::ffi::c_void,
        id_in: Option<SurfaceId>,
    ) -> Result<SurfaceId, CreateSurfaceError> {
        profiling::scope!("Instance::instance_create_surface_from_visual");
        self.instance_create_surface_dx12(id_in, |inst| unsafe {
            inst.create_surface_from_visual(visual as _)
        })
    }
    #[cfg(dx12)]
    pub unsafe fn instance_create_surface_from_surface_handle(
        &self,
        surface_handle: *mut std::ffi::c_void,
        id_in: Option<SurfaceId>,
    ) -> Result<SurfaceId, CreateSurfaceError> {
        profiling::scope!("Instance::instance_create_surface_from_surface_handle");
        self.instance_create_surface_dx12(id_in, |inst| unsafe {
            inst.create_surface_from_surface_handle(surface_handle)
        })
    }
    #[cfg(dx12)]
    pub unsafe fn instance_create_surface_from_swap_chain_panel(
        &self,
        swap_chain_panel: *mut std::ffi::c_void,
        id_in: Option<SurfaceId>,
    ) -> Result<SurfaceId, CreateSurfaceError> {
        profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
        self.instance_create_surface_dx12(id_in, |inst| unsafe {
            inst.create_surface_from_swap_chain_panel(swap_chain_panel as _)
        })
    }
    pub fn surface_drop(&self, id: SurfaceId) {
        profiling::scope!("Surface::drop");
        api_log!("Surface::drop {id:?}");
        fn unconfigure<A: HalApi>(
            global: &Global,
            surface: &Option<HalSurface<A>>,
            present: &Presentation,
        ) {
            if let Some(surface) = surface {
                let hub = HalApi::hub(global);
                if let Some(device) = present.device.downcast_ref::<A>() {
                    hub.surface_unconfigure(device, surface);
                }
            }
        }
        let surface = self.surfaces.unregister(id);
        let surface = Arc::into_inner(surface.unwrap())
            .expect("Surface cannot be destroyed because is still in use");
        if let Some(present) = surface.presentation.lock().take() {
            #[cfg(vulkan)]
            unconfigure::<hal::api::Vulkan>(self, &surface.vulkan, &present);
            #[cfg(metal)]
            unconfigure::<hal::api::Metal>(self, &surface.metal, &present);
            #[cfg(dx12)]
            unconfigure::<hal::api::Dx12>(self, &surface.dx12, &present);
            #[cfg(gles)]
            unconfigure::<hal::api::Gles>(self, &surface.gl, &present);
        }
        self.instance.destroy_surface(surface);
    }
    fn enumerate<A: HalApi>(
        &self,
        _: A,
        instance: &Option<A::Instance>,
        inputs: &AdapterInputs<markers::Adapter>,
        list: &mut Vec<AdapterId>,
    ) {
        let inst = match *instance {
            Some(ref inst) => inst,
            None => return,
        };
        let id_backend = match inputs.find(A::VARIANT) {
            Some(id) => id,
            None => return,
        };
        profiling::scope!("enumerating", &*format!("{:?}", A::VARIANT));
        let hub = HalApi::hub(self);
        let hal_adapters = unsafe { inst.enumerate_adapters() };
        for raw in hal_adapters {
            let adapter = Adapter::new(raw);
            log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info);
            let (id, _) = hub.adapters.prepare(id_backend).assign(Arc::new(adapter));
            list.push(id);
        }
    }
    pub fn enumerate_adapters(&self, inputs: AdapterInputs<markers::Adapter>) -> Vec<AdapterId> {
        profiling::scope!("Instance::enumerate_adapters");
        api_log!("Instance::enumerate_adapters");
        let mut adapters = Vec::new();
        #[cfg(vulkan)]
        self.enumerate(
            hal::api::Vulkan,
            &self.instance.vulkan,
            &inputs,
            &mut adapters,
        );
        #[cfg(metal)]
        self.enumerate(
            hal::api::Metal,
            &self.instance.metal,
            &inputs,
            &mut adapters,
        );
        #[cfg(dx12)]
        self.enumerate(hal::api::Dx12, &self.instance.dx12, &inputs, &mut adapters);
        #[cfg(gles)]
        self.enumerate(hal::api::Gles, &self.instance.gl, &inputs, &mut adapters);
        adapters
    }
    fn select<A: HalApi>(
        &self,
        selected: &mut usize,
        new_id: Option<AdapterId>,
        mut list: Vec<hal::ExposedAdapter<A>>,
    ) -> Option<AdapterId> {
        match selected.checked_sub(list.len()) {
            Some(left) => {
                *selected = left;
                None
            }
            None => {
                let adapter = Adapter::new(list.swap_remove(*selected));
                log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info);
                let (id, _) = HalApi::hub(self)
                    .adapters
                    .prepare(new_id)
                    .assign(Arc::new(adapter));
                Some(id)
            }
        }
    }
    pub fn request_adapter(
        &self,
        desc: &RequestAdapterOptions,
        inputs: AdapterInputs<markers::Adapter>,
    ) -> Result<AdapterId, RequestAdapterError> {
        profiling::scope!("Instance::request_adapter");
        api_log!("Instance::request_adapter");
        fn gather<A: HalApi>(
            _: A,
            instance: Option<&A::Instance>,
            inputs: &AdapterInputs<markers::Adapter>,
            compatible_surface: Option<&Surface>,
            force_software: bool,
            device_types: &mut Vec<wgt::DeviceType>,
        ) -> (Option<Id<markers::Adapter>>, Vec<hal::ExposedAdapter<A>>) {
            let id = inputs.find(A::VARIANT);
            match (id, instance) {
                (Some(id), Some(inst)) => {
                    let mut adapters = unsafe { inst.enumerate_adapters() };
                    if force_software {
                        adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu);
                    }
                    if let Some(surface) = compatible_surface {
                        let surface = &A::surface_as_hal(surface);
                        adapters.retain(|exposed| unsafe {
                            surface.is_some()
                                && exposed
                                    .adapter
                                    .surface_capabilities(surface.unwrap())
                                    .is_some()
                        });
                    }
                    device_types.extend(adapters.iter().map(|ad| ad.info.device_type));
                    (id, adapters)
                }
                _ => (None, Vec::new()),
            }
        }
        let compatible_surface = desc
            .compatible_surface
            .map(|id| {
                self.surfaces
                    .get(id)
                    .map_err(|_| RequestAdapterError::InvalidSurface(id))
            })
            .transpose()?;
        let compatible_surface = compatible_surface.as_ref().map(|surface| surface.as_ref());
        let mut device_types = Vec::new();
        #[cfg(vulkan)]
        let (id_vulkan, adapters_vk) = gather(
            hal::api::Vulkan,
            self.instance.vulkan.as_ref(),
            &inputs,
            compatible_surface,
            desc.force_fallback_adapter,
            &mut device_types,
        );
        #[cfg(metal)]
        let (id_metal, adapters_metal) = gather(
            hal::api::Metal,
            self.instance.metal.as_ref(),
            &inputs,
            compatible_surface,
            desc.force_fallback_adapter,
            &mut device_types,
        );
        #[cfg(dx12)]
        let (id_dx12, adapters_dx12) = gather(
            hal::api::Dx12,
            self.instance.dx12.as_ref(),
            &inputs,
            compatible_surface,
            desc.force_fallback_adapter,
            &mut device_types,
        );
        #[cfg(gles)]
        let (id_gl, adapters_gl) = gather(
            hal::api::Gles,
            self.instance.gl.as_ref(),
            &inputs,
            compatible_surface,
            desc.force_fallback_adapter,
            &mut device_types,
        );
        if device_types.is_empty() {
            return Err(RequestAdapterError::NotFound);
        }
        let (mut integrated, mut discrete, mut virt, mut cpu, mut other) =
            (None, None, None, None, None);
        for (i, ty) in device_types.into_iter().enumerate() {
            match ty {
                wgt::DeviceType::IntegratedGpu => {
                    integrated = integrated.or(Some(i));
                }
                wgt::DeviceType::DiscreteGpu => {
                    discrete = discrete.or(Some(i));
                }
                wgt::DeviceType::VirtualGpu => {
                    virt = virt.or(Some(i));
                }
                wgt::DeviceType::Cpu => {
                    cpu = cpu.or(Some(i));
                }
                wgt::DeviceType::Other => {
                    other = other.or(Some(i));
                }
            }
        }
        let preferred_gpu = match desc.power_preference {
            PowerPreference::LowPower => integrated.or(discrete).or(other).or(virt).or(cpu),
            PowerPreference::HighPerformance => discrete.or(integrated).or(other).or(virt).or(cpu),
            PowerPreference::None => {
                let option_min = |a: Option<usize>, b: Option<usize>| {
                    if let (Some(a), Some(b)) = (a, b) {
                        Some(a.min(b))
                    } else {
                        a.or(b)
                    }
                };
                option_min(option_min(discrete, integrated), other)
            }
        };
        let mut selected = preferred_gpu.unwrap_or(0);
        #[cfg(vulkan)]
        if let Some(id) = self.select(&mut selected, id_vulkan, adapters_vk) {
            return Ok(id);
        }
        #[cfg(metal)]
        if let Some(id) = self.select(&mut selected, id_metal, adapters_metal) {
            return Ok(id);
        }
        #[cfg(dx12)]
        if let Some(id) = self.select(&mut selected, id_dx12, adapters_dx12) {
            return Ok(id);
        }
        #[cfg(gles)]
        if let Some(id) = self.select(&mut selected, id_gl, adapters_gl) {
            return Ok(id);
        }
        let _ = selected;
        log::warn!("Some adapters are present, but enumerating them failed!");
        Err(RequestAdapterError::NotFound)
    }
    pub unsafe fn create_adapter_from_hal<A: HalApi>(
        &self,
        hal_adapter: hal::ExposedAdapter<A>,
        input: Option<AdapterId>,
    ) -> AdapterId {
        profiling::scope!("Instance::create_adapter_from_hal");
        let fid = A::hub(self).adapters.prepare(input);
        let (id, _adapter): (_, Arc<Adapter<A>>) = match A::VARIANT {
            #[cfg(vulkan)]
            Backend::Vulkan => fid.assign(Arc::new(Adapter::new(hal_adapter))),
            #[cfg(metal)]
            Backend::Metal => fid.assign(Arc::new(Adapter::new(hal_adapter))),
            #[cfg(dx12)]
            Backend::Dx12 => fid.assign(Arc::new(Adapter::new(hal_adapter))),
            #[cfg(gles)]
            Backend::Gl => fid.assign(Arc::new(Adapter::new(hal_adapter))),
            _ => unreachable!(),
        };
        resource_log!("Created Adapter {:?}", id);
        id
    }
    pub fn adapter_get_info<A: HalApi>(
        &self,
        adapter_id: AdapterId,
    ) -> Result<wgt::AdapterInfo, InvalidAdapter> {
        let hub = A::hub(self);
        hub.adapters
            .get(adapter_id)
            .map(|adapter| adapter.raw.info.clone())
            .map_err(|_| InvalidAdapter)
    }
    pub fn adapter_get_texture_format_features<A: HalApi>(
        &self,
        adapter_id: AdapterId,
        format: wgt::TextureFormat,
    ) -> Result<wgt::TextureFormatFeatures, InvalidAdapter> {
        let hub = A::hub(self);
        hub.adapters
            .get(adapter_id)
            .map(|adapter| adapter.get_texture_format_features(format))
            .map_err(|_| InvalidAdapter)
    }
    pub fn adapter_features<A: HalApi>(
        &self,
        adapter_id: AdapterId,
    ) -> Result<wgt::Features, InvalidAdapter> {
        let hub = A::hub(self);
        hub.adapters
            .get(adapter_id)
            .map(|adapter| adapter.raw.features)
            .map_err(|_| InvalidAdapter)
    }
    pub fn adapter_limits<A: HalApi>(
        &self,
        adapter_id: AdapterId,
    ) -> Result<wgt::Limits, InvalidAdapter> {
        let hub = A::hub(self);
        hub.adapters
            .get(adapter_id)
            .map(|adapter| adapter.raw.capabilities.limits.clone())
            .map_err(|_| InvalidAdapter)
    }
    pub fn adapter_downlevel_capabilities<A: HalApi>(
        &self,
        adapter_id: AdapterId,
    ) -> Result<wgt::DownlevelCapabilities, InvalidAdapter> {
        let hub = A::hub(self);
        hub.adapters
            .get(adapter_id)
            .map(|adapter| adapter.raw.capabilities.downlevel.clone())
            .map_err(|_| InvalidAdapter)
    }
    pub fn adapter_get_presentation_timestamp<A: HalApi>(
        &self,
        adapter_id: AdapterId,
    ) -> Result<wgt::PresentationTimestamp, InvalidAdapter> {
        let hub = A::hub(self);
        let adapter = hub.adapters.get(adapter_id).map_err(|_| InvalidAdapter)?;
        Ok(unsafe { adapter.raw.adapter.get_presentation_timestamp() })
    }
    pub fn adapter_drop<A: HalApi>(&self, adapter_id: AdapterId) {
        profiling::scope!("Adapter::drop");
        api_log!("Adapter::drop {adapter_id:?}");
        let hub = A::hub(self);
        let mut adapters_locked = hub.adapters.write();
        let free = match adapters_locked.get(adapter_id) {
            Ok(adapter) => Arc::strong_count(adapter) == 1,
            Err(_) => true,
        };
        if free {
            hub.adapters
                .unregister_locked(adapter_id, &mut *adapters_locked);
        }
    }
}
impl Global {
    pub fn adapter_request_device<A: HalApi>(
        &self,
        adapter_id: AdapterId,
        desc: &DeviceDescriptor,
        trace_path: Option<&std::path::Path>,
        device_id_in: Option<DeviceId>,
        queue_id_in: Option<QueueId>,
    ) -> (DeviceId, QueueId, Option<RequestDeviceError>) {
        profiling::scope!("Adapter::request_device");
        api_log!("Adapter::request_device");
        let hub = A::hub(self);
        let device_fid = hub.devices.prepare(device_id_in);
        let queue_fid = hub.queues.prepare(queue_id_in);
        let error = loop {
            let adapter = match hub.adapters.get(adapter_id) {
                Ok(adapter) => adapter,
                Err(_) => break RequestDeviceError::InvalidAdapter,
            };
            let (device, mut queue) =
                match adapter.create_device_and_queue(desc, self.instance.flags, trace_path) {
                    Ok((device, queue)) => (device, queue),
                    Err(e) => break e,
                };
            let (device_id, _) = device_fid.assign(Arc::new(device));
            resource_log!("Created Device {:?}", device_id);
            let device = hub.devices.get(device_id).unwrap();
            queue.device = Some(device.clone());
            let (queue_id, queue) = queue_fid.assign(Arc::new(queue));
            resource_log!("Created Queue {:?}", queue_id);
            device.set_queue(queue);
            return (device_id, queue_id, None);
        };
        let device_id = device_fid.assign_error(desc.label.borrow_or_default());
        let queue_id = queue_fid.assign_error(desc.label.borrow_or_default());
        (device_id, queue_id, Some(error))
    }
    pub unsafe fn create_device_from_hal<A: HalApi>(
        &self,
        adapter_id: AdapterId,
        hal_device: OpenDevice<A>,
        desc: &DeviceDescriptor,
        trace_path: Option<&std::path::Path>,
        device_id_in: Option<DeviceId>,
        queue_id_in: Option<QueueId>,
    ) -> (DeviceId, QueueId, Option<RequestDeviceError>) {
        profiling::scope!("Global::create_device_from_hal");
        let hub = A::hub(self);
        let devices_fid = hub.devices.prepare(device_id_in);
        let queues_fid = hub.queues.prepare(queue_id_in);
        let error = loop {
            let adapter = match hub.adapters.get(adapter_id) {
                Ok(adapter) => adapter,
                Err(_) => break RequestDeviceError::InvalidAdapter,
            };
            let (device, mut queue) = match adapter.create_device_and_queue_from_hal(
                hal_device,
                desc,
                self.instance.flags,
                trace_path,
            ) {
                Ok(device) => device,
                Err(e) => break e,
            };
            let (device_id, _) = devices_fid.assign(Arc::new(device));
            resource_log!("Created Device {:?}", device_id);
            let device = hub.devices.get(device_id).unwrap();
            queue.device = Some(device.clone());
            let (queue_id, queue) = queues_fid.assign(Arc::new(queue));
            resource_log!("Created Queue {:?}", queue_id);
            device.set_queue(queue);
            return (device_id, queue_id, None);
        };
        let device_id = devices_fid.assign_error(desc.label.borrow_or_default());
        let queue_id = queues_fid.assign_error(desc.label.borrow_or_default());
        (device_id, queue_id, Some(error))
    }
}
pub fn parse_backends_from_comma_list(string: &str) -> Backends {
    let mut backends = Backends::empty();
    for backend in string.to_lowercase().split(',') {
        backends |= match backend.trim() {
            "vulkan" | "vk" => Backends::VULKAN,
            "dx12" | "d3d12" => Backends::DX12,
            "metal" | "mtl" => Backends::METAL,
            "opengl" | "gles" | "gl" => Backends::GL,
            "webgpu" => Backends::BROWSER_WEBGPU,
            b => {
                log::warn!("unknown backend string '{}'", b);
                continue;
            }
        }
    }
    if backends.is_empty() {
        log::warn!("no valid backend strings found!");
    }
    backends
}