1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
use wgt::Backend;
/// The `AnySurface` type: a `Arc` of a `HalSurface<A>` for any backend `A`.
use crate::hal_api::HalApi;
use crate::instance::HalSurface;
use std::any::Any;
use std::fmt;
use std::sync::Arc;
/// A `Arc` of a `HalSurface<A>`, for any backend `A`.
///
/// Any `AnySurface` is just like an `Arc<HalSurface<A>>`, except that the
/// `A` type parameter is erased. To access the `Surface`, you must
/// downcast to a particular backend with the \[`downcast_ref`\] or
/// \[`take`\] methods.
pub struct AnySurface(Arc<dyn Any + 'static>);
impl AnySurface {
/// Return an `AnySurface` that holds an owning `Arc` to `HalSurface`.
pub fn new<A: HalApi>(surface: HalSurface<A>) -> AnySurface {
AnySurface(Arc::new(surface))
}
pub fn backend(&self) -> Backend {
#[cfg(vulkan)]
if self.downcast_ref::<hal::api::Vulkan>().is_some() {
return Backend::Vulkan;
}
#[cfg(metal)]
if self.downcast_ref::<hal::api::Metal>().is_some() {
return Backend::Metal;
}
#[cfg(dx12)]
if self.downcast_ref::<hal::api::Dx12>().is_some() {
return Backend::Dx12;
}
#[cfg(gles)]
if self.downcast_ref::<hal::api::Gles>().is_some() {
return Backend::Gl;
}
Backend::Empty
}
/// If `self` is an `Arc<HalSurface<A>>`, return a reference to the
/// HalSurface.
pub fn downcast_ref<A: HalApi>(&self) -> Option<&HalSurface<A>> {
self.0.downcast_ref::<HalSurface<A>>()
}
/// If `self` is an `Arc<HalSurface<A>>`, returns that.
pub fn take<A: HalApi>(self) -> Option<Arc<HalSurface<A>>> {
// `Arc::downcast` returns `Arc<T>`, but requires that `T` be `Sync` and
// `Send`, and this is not the case for `HalSurface` in wasm builds.
//
// But as far as I can see, `Arc::downcast` has no particular reason to
// require that `T` be `Sync` and `Send`; the steps used here are sound.
if (self.0).is::<HalSurface<A>>() {
// Turn the `Arc`, which is a pointer to an `ArcInner` struct, into
// a pointer to the `ArcInner`'s `data` field. Carry along the
// vtable from the original `Arc`.
let raw_erased: *const (dyn Any + 'static) = Arc::into_raw(self.0);
// Remove the vtable, and supply the concrete type of the `data`.
let raw_typed: *const HalSurface<A> = raw_erased.cast::<HalSurface<A>>();
// Convert the pointer to the `data` field back into a pointer to
// the `ArcInner`, and restore reference-counting behavior.
let arc_typed: Arc<HalSurface<A>> = unsafe {
// Safety:
// - We checked that the `dyn Any` was indeed a `HalSurface<A>` above.
// - We're calling `Arc::from_raw` on the same pointer returned
// by `Arc::into_raw`, except that we stripped off the vtable
// pointer.
// - The pointer must still be live, because we've borrowed `self`,
// which holds another reference to it.
// - The format of a `ArcInner<dyn Any>` must be the same as
// that of an `ArcInner<HalSurface<A>>`, or else `AnyHalSurface::new`
// wouldn't be possible.
Arc::from_raw(raw_typed)
};
Some(arc_typed)
} else {
None
}
}
}
impl fmt::Debug for AnySurface {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("AnySurface")
}
}
#[cfg(send_sync)]
unsafe impl Send for AnySurface {}
#[cfg(send_sync)]
unsafe impl Sync for AnySurface {}