wgpu_core/
id.rs

1use crate::{Epoch, Index};
2use core::{
3    cmp::Ordering,
4    fmt::{self, Debug},
5    hash::Hash,
6    marker::PhantomData,
7    num::NonZeroU64,
8};
9use wgt::WasmNotSendSync;
10
11const _: () = {
12    if size_of::<Index>() != 4 {
13        panic!()
14    }
15};
16const _: () = {
17    if size_of::<Epoch>() != 4 {
18        panic!()
19    }
20};
21const _: () = {
22    if size_of::<RawId>() != 8 {
23        panic!()
24    }
25};
26
27/// The raw underlying representation of an identifier.
28#[repr(transparent)]
29#[cfg_attr(
30    any(feature = "serde", feature = "trace"),
31    derive(serde::Serialize),
32    serde(into = "SerialId")
33)]
34#[cfg_attr(
35    any(feature = "serde", feature = "replay"),
36    derive(serde::Deserialize),
37    serde(try_from = "SerialId")
38)]
39#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub struct RawId(NonZeroU64);
41
42impl RawId {
43    /// Zip together an identifier and return its raw underlying representation.
44    ///
45    /// # Panics
46    ///
47    /// If both ID components are zero.
48    pub fn zip(index: Index, epoch: Epoch) -> RawId {
49        let v = (index as u64) | ((epoch as u64) << 32);
50        Self(NonZeroU64::new(v).expect("IDs may not be zero"))
51    }
52
53    /// Unzip a raw identifier into its components.
54    pub fn unzip(self) -> (Index, Epoch) {
55        (self.0.get() as Index, (self.0.get() >> 32) as Epoch)
56    }
57}
58
59/// An identifier for a wgpu object.
60///
61/// An `Id<T>` value identifies a value stored in a [`Global`]'s [`Hub`].
62///
63/// [`Global`]: crate::global::Global
64/// [`Hub`]: crate::hub::Hub
65#[repr(transparent)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
67#[cfg_attr(feature = "serde", serde(transparent))]
68pub struct Id<T: Marker>(RawId, PhantomData<T>);
69
70// This type represents Id in a more readable (and editable) way.
71#[cfg(feature = "serde")]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[derive(Clone, Debug)]
74pub enum SerialId {
75    // The only variant forces RON to not ignore "Id"
76    Id(Index, Epoch),
77}
78
79#[cfg(feature = "serde")]
80impl From<RawId> for SerialId {
81    fn from(id: RawId) -> Self {
82        let (index, epoch) = id.unzip();
83        Self::Id(index, epoch)
84    }
85}
86
87#[cfg(feature = "serde")]
88pub struct ZeroIdError;
89
90#[cfg(feature = "serde")]
91impl fmt::Display for ZeroIdError {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        write!(f, "IDs may not be zero")
94    }
95}
96
97#[cfg(feature = "serde")]
98impl TryFrom<SerialId> for RawId {
99    type Error = ZeroIdError;
100    fn try_from(id: SerialId) -> Result<Self, ZeroIdError> {
101        let SerialId::Id(index, epoch) = id;
102        if index == 0 && epoch == 0 {
103            Err(ZeroIdError)
104        } else {
105            Ok(RawId::zip(index, epoch))
106        }
107    }
108}
109
110/// Identify an object by the pointer returned by `Arc::as_ptr`.
111///
112/// This is used for tracing. See [IDs and tracing](crate::hub#ids-and-tracing).
113#[allow(dead_code)]
114#[cfg(feature = "serde")]
115#[derive(Debug, serde::Serialize, serde::Deserialize)]
116pub enum PointerId<T: Marker> {
117    // The only variant forces RON to not ignore "Id"
118    PointerId(core::num::NonZeroUsize, #[serde(skip)] PhantomData<T>),
119}
120
121#[cfg(feature = "serde")]
122impl<T: Marker> Copy for PointerId<T> {}
123
124#[cfg(feature = "serde")]
125impl<T: Marker> Clone for PointerId<T> {
126    fn clone(&self) -> Self {
127        *self
128    }
129}
130
131#[cfg(feature = "serde")]
132impl<T: Marker> PartialEq for PointerId<T> {
133    fn eq(&self, other: &Self) -> bool {
134        let PointerId::PointerId(this, _) = self;
135        let PointerId::PointerId(other, _) = other;
136        this == other
137    }
138}
139
140#[cfg(feature = "serde")]
141impl<T: Marker> Eq for PointerId<T> {}
142
143#[cfg(feature = "serde")]
144impl<T: Marker> Hash for PointerId<T> {
145    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
146        let PointerId::PointerId(this, _) = self;
147        this.hash(state);
148    }
149}
150
151#[cfg(feature = "serde")]
152impl<T: crate::storage::StorageItem> From<&alloc::sync::Arc<T>> for PointerId<T::Marker> {
153    fn from(arc: &alloc::sync::Arc<T>) -> Self {
154        // Since the memory representation of `Arc<T>` is just a pointer to
155        // `ArcInner<T>`, it would be nice to use that pointer as the trace ID,
156        // since many `into_trace` implementations would then be no-ops at
157        // runtime. However, `Arc::as_ptr` returns a pointer to the contained
158        // data, not to the `ArcInner`. The `ArcInner` stores the reference
159        // counts before the data, so the machine code for this conversion has
160        // to add an offset to the pointer.
161        PointerId::PointerId(
162            core::num::NonZeroUsize::new(alloc::sync::Arc::as_ptr(arc) as usize).unwrap(),
163            PhantomData,
164        )
165    }
166}
167
168impl<T> Id<T>
169where
170    T: Marker,
171{
172    /// # Safety
173    ///
174    /// The raw id must be valid for the type.
175    pub unsafe fn from_raw(raw: RawId) -> Self {
176        Self(raw, PhantomData)
177    }
178
179    /// Coerce the identifiers into its raw underlying representation.
180    pub fn into_raw(self) -> RawId {
181        self.0
182    }
183
184    #[inline]
185    pub fn zip(index: Index, epoch: Epoch) -> Self {
186        Id(RawId::zip(index, epoch), PhantomData)
187    }
188
189    #[inline]
190    pub fn unzip(self) -> (Index, Epoch) {
191        self.0.unzip()
192    }
193}
194
195impl<T> Copy for Id<T> where T: Marker {}
196
197impl<T> Clone for Id<T>
198where
199    T: Marker,
200{
201    #[inline]
202    fn clone(&self) -> Self {
203        *self
204    }
205}
206
207impl<T> Debug for Id<T>
208where
209    T: Marker,
210{
211    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
212        let (index, epoch) = self.unzip();
213        write!(formatter, "{}Id({index},{epoch})", T::TYPE)?;
214        Ok(())
215    }
216}
217
218impl<T> Hash for Id<T>
219where
220    T: Marker,
221{
222    #[inline]
223    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
224        self.0.hash(state);
225    }
226}
227
228impl<T> PartialEq for Id<T>
229where
230    T: Marker,
231{
232    #[inline]
233    fn eq(&self, other: &Self) -> bool {
234        self.0 == other.0
235    }
236}
237
238impl<T> Eq for Id<T> where T: Marker {}
239
240impl<T> PartialOrd for Id<T>
241where
242    T: Marker,
243{
244    #[inline]
245    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
246        Some(self.cmp(other))
247    }
248}
249
250impl<T> Ord for Id<T>
251where
252    T: Marker,
253{
254    #[inline]
255    fn cmp(&self, other: &Self) -> Ordering {
256        self.0.cmp(&other.0)
257    }
258}
259
260/// Marker trait used to determine which types uniquely identify a resource.
261///
262/// For example, `Device<A>` will have the same type of identifier as
263/// `Device<B>` because `Device<T>` for any `T` defines the same maker type.
264pub trait Marker: 'static + WasmNotSendSync {
265    const TYPE: &'static str;
266}
267
268// This allows `()` to be used as a marker type for tests.
269//
270// We don't want these in production code, since they essentially remove type
271// safety, like how identifiers across different types can be compared.
272#[cfg(test)]
273impl Marker for () {
274    const TYPE: &'static str = "Untyped";
275}
276
277/// Define identifiers for each resource.
278macro_rules! ids {
279    ($(
280        $(#[$($meta:meta)*])*
281        pub type $name:ident $marker:ident;
282    )*) => {
283        /// Marker types for each resource.
284        pub mod markers {
285            $(
286                #[derive(Debug)]
287                pub enum $marker {}
288                impl super::Marker for $marker {
289                    const TYPE: &'static str = stringify!($marker);
290                }
291            )*
292        }
293
294        $(
295            $(#[$($meta)*])*
296            pub type $name = Id<self::markers::$marker>;
297        )*
298    }
299}
300
301ids! {
302    pub type AdapterId Adapter;
303    pub type SurfaceId Surface;
304    pub type DeviceId Device;
305    pub type QueueId Queue;
306    pub type BufferId Buffer;
307    pub type StagingBufferId StagingBuffer;
308    pub type TextureViewId TextureView;
309    pub type TextureId Texture;
310    pub type ExternalTextureId ExternalTexture;
311    pub type SamplerId Sampler;
312    pub type BindGroupLayoutId BindGroupLayout;
313    pub type PipelineLayoutId PipelineLayout;
314    pub type BindGroupId BindGroup;
315    pub type ShaderModuleId ShaderModule;
316    pub type RenderPipelineId RenderPipeline;
317    pub type ComputePipelineId ComputePipeline;
318    pub type PipelineCacheId PipelineCache;
319    pub type CommandEncoderId CommandEncoder;
320    pub type CommandBufferId CommandBuffer;
321    pub type RenderPassEncoderId RenderPassEncoder;
322    pub type ComputePassEncoderId ComputePassEncoder;
323    pub type RenderBundleEncoderId RenderBundleEncoder;
324    pub type RenderBundleId RenderBundle;
325    pub type QuerySetId QuerySet;
326    pub type BlasId Blas;
327    pub type TlasId Tlas;
328}
329
330#[test]
331fn test_id() {
332    let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX];
333    let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX];
334    for &i in &indexes {
335        for &e in &epochs {
336            let id = Id::<()>::zip(i, e);
337            let (index, epoch) = id.unzip();
338            assert_eq!(index, i);
339            assert_eq!(epoch, e);
340        }
341    }
342}