-
Notifications
You must be signed in to change notification settings - Fork 923
/
any_surface.rs
96 lines (87 loc) · 3.65 KB
/
any_surface.rs
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 {}