use wgt::Backend;
use super::Device;
/// The `AnyDevice` type: a pointer to a `Device` for any backend `A`.
use crate::hal_api::HalApi;
use std::fmt;
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
use std::sync::Arc;
struct AnyDeviceVtable {
// We oppurtunistically store the backend here, since we now it will be used
// with backend selection and it can be stored in static memory.
backend: Backend,
// Drop glue which knows how to drop the stored data.
drop: unsafe fn(*mut ()),
}
/// A pointer to a `Device`, for any backend `A`.
///
/// Any `AnyDevice` is just like an `Arc>`, except that the `A` type
/// parameter is erased. To access the `Device`, you must downcast to a
/// particular backend with the \[`downcast_ref`\] or \[`downcast_clone`\]
/// methods.
pub struct AnyDevice {
data: NonNull<()>,
vtable: &'static AnyDeviceVtable,
}
impl AnyDevice {
/// Return an `AnyDevice` that holds an owning `Arc` pointer to `device`.
pub fn new(device: Arc>) -> AnyDevice {
unsafe fn drop_glue(ptr: *mut ()) {
// Drop the arc this instance is holding.
unsafe {
_ = Arc::from_raw(ptr.cast::());
}
}
// SAFETY: The pointer returned by Arc::into_raw is guaranteed to be
// non-null.
let data = unsafe { NonNull::new_unchecked(Arc::into_raw(device).cast_mut()) };
AnyDevice {
data: data.cast(),
vtable: &AnyDeviceVtable {
backend: A::VARIANT,
drop: drop_glue::,
},
}
}
/// If `self` is an `Arc>`, return a reference to the
/// device.
pub fn downcast_ref(&self) -> Option<&Device> {
if self.vtable.backend != A::VARIANT {
return None;
}
// SAFETY: We just checked the instance above implicitly by the backend
// that it was statically constructed through.
Some(unsafe { &*(self.data.as_ptr().cast::>()) })
}
/// If `self` is an `Arc>`, return a clone of that.
pub fn downcast_clone(&self) -> Option>> {
if self.vtable.backend != A::VARIANT {
return None;
}
// We need to prevent the destructor of the arc from running, since it
// refers to the instance held by this object. Dropping it would
// invalidate this object.
//
// SAFETY: We just checked the instance above implicitly by the backend
// that it was statically constructed through.
let this =
ManuallyDrop::new(unsafe { Arc::from_raw(self.data.as_ptr().cast::>()) });
// Cloning it increases the reference count, and we return a new arc
// instance.
Some((*this).clone())
}
}
impl Drop for AnyDevice {
fn drop(&mut self) {
unsafe { (self.vtable.drop)(self.data.as_ptr()) }
}
}
impl fmt::Debug for AnyDevice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AnyDevice<{}>", self.vtable.backend)
}
}
#[cfg(send_sync)]
unsafe impl Send for AnyDevice {}
#[cfg(send_sync)]
unsafe impl Sync for AnyDevice {}