wgpu_core/device/
any_device.rs

1use wgt::Backend;
2
3use super::Device;
4/// The `AnyDevice` type: a pointer to a `Device<A>` for any backend `A`.
5use crate::hal_api::HalApi;
6
7use std::fmt;
8use std::mem::ManuallyDrop;
9use std::ptr::NonNull;
10use std::sync::Arc;
11
12struct AnyDeviceVtable {
13    // We oppurtunistically store the backend here, since we now it will be used
14    // with backend selection and it can be stored in static memory.
15    backend: Backend,
16    // Drop glue which knows how to drop the stored data.
17    drop: unsafe fn(*mut ()),
18}
19
20/// A pointer to a `Device<A>`, for any backend `A`.
21///
22/// Any `AnyDevice` is just like an `Arc<Device<A>>`, except that the `A` type
23/// parameter is erased. To access the `Device`, you must downcast to a
24/// particular backend with the \[`downcast_ref`\] or \[`downcast_clone`\]
25/// methods.
26pub struct AnyDevice {
27    data: NonNull<()>,
28    vtable: &'static AnyDeviceVtable,
29}
30
31impl AnyDevice {
32    /// Return an `AnyDevice` that holds an owning `Arc` pointer to `device`.
33    pub fn new<A: HalApi>(device: Arc<Device<A>>) -> AnyDevice {
34        unsafe fn drop_glue<A: HalApi>(ptr: *mut ()) {
35            // Drop the arc this instance is holding.
36            unsafe {
37                _ = Arc::from_raw(ptr.cast::<A::Device>());
38            }
39        }
40
41        // SAFETY: The pointer returned by Arc::into_raw is guaranteed to be
42        // non-null.
43        let data = unsafe { NonNull::new_unchecked(Arc::into_raw(device).cast_mut()) };
44
45        AnyDevice {
46            data: data.cast(),
47            vtable: &AnyDeviceVtable {
48                backend: A::VARIANT,
49                drop: drop_glue::<A>,
50            },
51        }
52    }
53
54    /// If `self` is an `Arc<Device<A>>`, return a reference to the
55    /// device.
56    pub fn downcast_ref<A: HalApi>(&self) -> Option<&Device<A>> {
57        if self.vtable.backend != A::VARIANT {
58            return None;
59        }
60
61        // SAFETY: We just checked the instance above implicitly by the backend
62        // that it was statically constructed through.
63        Some(unsafe { &*(self.data.as_ptr().cast::<Device<A>>()) })
64    }
65
66    /// If `self` is an `Arc<Device<A>>`, return a clone of that.
67    pub fn downcast_clone<A: HalApi>(&self) -> Option<Arc<Device<A>>> {
68        if self.vtable.backend != A::VARIANT {
69            return None;
70        }
71
72        // We need to prevent the destructor of the arc from running, since it
73        // refers to the instance held by this object. Dropping it would
74        // invalidate this object.
75        //
76        // SAFETY: We just checked the instance above implicitly by the backend
77        // that it was statically constructed through.
78        let this =
79            ManuallyDrop::new(unsafe { Arc::from_raw(self.data.as_ptr().cast::<Device<A>>()) });
80
81        // Cloning it increases the reference count, and we return a new arc
82        // instance.
83        Some((*this).clone())
84    }
85}
86
87impl Drop for AnyDevice {
88    fn drop(&mut self) {
89        unsafe { (self.vtable.drop)(self.data.as_ptr()) }
90    }
91}
92
93impl fmt::Debug for AnyDevice {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        write!(f, "AnyDevice<{}>", self.vtable.backend)
96    }
97}
98
99#[cfg(send_sync)]
100unsafe impl Send for AnyDevice {}
101#[cfg(send_sync)]
102unsafe impl Sync for AnyDevice {}