Skip to main content

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/// ## Note on `Id` typing
64///
65/// You might assume that an `Id<T>` can only be used to retrieve a resource of
66/// type `T`, but that is not quite the case. The id types in `wgpu-core`'s
67/// public API ([`TextureId`], for example) can refer to resources belonging to
68/// any backend, but the corresponding resource types ([`Texture<A>`], for
69/// example) are always parameterized by a specific backend `A`.
70///
71/// So the `T` in `Id<T>` is usually a resource type like `Texture<Noop>`,
72/// where [`Noop`] is the `wgpu_hal` dummy back end. These empty types are
73/// never actually used, beyond just making sure you access each `Storage` with
74/// the right kind of identifier. The members of [`Hub<A>`] pair up each
75/// `X<Noop>` type with the resource type `X<A>`, for some specific backend
76/// `A`.
77///
78/// [`Global`]: crate::global::Global
79/// [`Hub`]: crate::hub::Hub
80/// [`Hub<A>`]: crate::hub::Hub
81/// [`Texture<A>`]: crate::resource::Texture
82/// [`Registry`]: crate::registry::Registry
83/// [`Noop`]: hal::api::Noop
84#[repr(transparent)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86#[cfg_attr(feature = "serde", serde(transparent))]
87pub struct Id<T: Marker>(RawId, PhantomData<T>);
88
89// This type represents Id in a more readable (and editable) way.
90#[cfg(feature = "serde")]
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
92#[derive(Clone, Debug)]
93pub enum SerialId {
94    // The only variant forces RON to not ignore "Id"
95    Id(Index, Epoch),
96}
97
98#[cfg(feature = "serde")]
99impl From<RawId> for SerialId {
100    fn from(id: RawId) -> Self {
101        let (index, epoch) = id.unzip();
102        Self::Id(index, epoch)
103    }
104}
105
106#[cfg(feature = "serde")]
107pub struct ZeroIdError;
108
109#[cfg(feature = "serde")]
110impl fmt::Display for ZeroIdError {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        write!(f, "IDs may not be zero")
113    }
114}
115
116#[cfg(feature = "serde")]
117impl TryFrom<SerialId> for RawId {
118    type Error = ZeroIdError;
119    fn try_from(id: SerialId) -> Result<Self, ZeroIdError> {
120        let SerialId::Id(index, epoch) = id;
121        if index == 0 && epoch == 0 {
122            Err(ZeroIdError)
123        } else {
124            Ok(RawId::zip(index, epoch))
125        }
126    }
127}
128
129/// Identify an object by the pointer returned by `Arc::as_ptr`.
130///
131/// This is used for tracing. See [IDs and tracing](crate::hub#ids-and-tracing).
132#[allow(dead_code)]
133#[cfg(feature = "serde")]
134#[derive(Debug, serde::Serialize, serde::Deserialize)]
135pub enum PointerId<T: Marker> {
136    // The only variant forces RON to not ignore "Id"
137    PointerId(core::num::NonZeroUsize, #[serde(skip)] PhantomData<T>),
138}
139
140#[cfg(feature = "serde")]
141impl<T: Marker> Copy for PointerId<T> {}
142
143#[cfg(feature = "serde")]
144impl<T: Marker> Clone for PointerId<T> {
145    fn clone(&self) -> Self {
146        *self
147    }
148}
149
150#[cfg(feature = "serde")]
151impl<T: Marker> PartialEq for PointerId<T> {
152    fn eq(&self, other: &Self) -> bool {
153        let PointerId::PointerId(this, _) = self;
154        let PointerId::PointerId(other, _) = other;
155        this == other
156    }
157}
158
159#[cfg(feature = "serde")]
160impl<T: Marker> Eq for PointerId<T> {}
161
162#[cfg(feature = "serde")]
163impl<T: Marker> Hash for PointerId<T> {
164    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
165        let PointerId::PointerId(this, _) = self;
166        this.hash(state);
167    }
168}
169
170#[cfg(feature = "serde")]
171impl<T: crate::storage::StorageItem> From<&alloc::sync::Arc<T>> for PointerId<T::Marker> {
172    fn from(arc: &alloc::sync::Arc<T>) -> Self {
173        // Since the memory representation of `Arc<T>` is just a pointer to
174        // `ArcInner<T>`, it would be nice to use that pointer as the trace ID,
175        // since many `into_trace` implementations would then be no-ops at
176        // runtime. However, `Arc::as_ptr` returns a pointer to the contained
177        // data, not to the `ArcInner`. The `ArcInner` stores the reference
178        // counts before the data, so the machine code for this conversion has
179        // to add an offset to the pointer.
180        PointerId::PointerId(
181            core::num::NonZeroUsize::new(alloc::sync::Arc::as_ptr(arc) as usize).unwrap(),
182            PhantomData,
183        )
184    }
185}
186
187impl<T> Id<T>
188where
189    T: Marker,
190{
191    /// # Safety
192    ///
193    /// The raw id must be valid for the type.
194    pub unsafe fn from_raw(raw: RawId) -> Self {
195        Self(raw, PhantomData)
196    }
197
198    /// Coerce the identifiers into its raw underlying representation.
199    pub fn into_raw(self) -> RawId {
200        self.0
201    }
202
203    #[inline]
204    pub fn zip(index: Index, epoch: Epoch) -> Self {
205        Id(RawId::zip(index, epoch), PhantomData)
206    }
207
208    #[inline]
209    pub fn unzip(self) -> (Index, Epoch) {
210        self.0.unzip()
211    }
212}
213
214impl<T> Copy for Id<T> where T: Marker {}
215
216impl<T> Clone for Id<T>
217where
218    T: Marker,
219{
220    #[inline]
221    fn clone(&self) -> Self {
222        *self
223    }
224}
225
226impl<T> Debug for Id<T>
227where
228    T: Marker,
229{
230    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
231        let (index, epoch) = self.unzip();
232        write!(formatter, "Id({index},{epoch})")?;
233        Ok(())
234    }
235}
236
237impl<T> Hash for Id<T>
238where
239    T: Marker,
240{
241    #[inline]
242    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
243        self.0.hash(state);
244    }
245}
246
247impl<T> PartialEq for Id<T>
248where
249    T: Marker,
250{
251    #[inline]
252    fn eq(&self, other: &Self) -> bool {
253        self.0 == other.0
254    }
255}
256
257impl<T> Eq for Id<T> where T: Marker {}
258
259impl<T> PartialOrd for Id<T>
260where
261    T: Marker,
262{
263    #[inline]
264    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
265        Some(self.cmp(other))
266    }
267}
268
269impl<T> Ord for Id<T>
270where
271    T: Marker,
272{
273    #[inline]
274    fn cmp(&self, other: &Self) -> Ordering {
275        self.0.cmp(&other.0)
276    }
277}
278
279/// Marker trait used to determine which types uniquely identify a resource.
280///
281/// For example, `Device<A>` will have the same type of identifier as
282/// `Device<B>` because `Device<T>` for any `T` defines the same maker type.
283pub trait Marker: 'static + WasmNotSendSync {}
284
285// This allows `()` to be used as a marker type for tests.
286//
287// We don't want these in production code, since they essentially remove type
288// safety, like how identifiers across different types can be compared.
289#[cfg(test)]
290impl Marker for () {}
291
292/// Define identifiers for each resource.
293macro_rules! ids {
294    ($(
295        $(#[$($meta:meta)*])*
296        pub type $name:ident $marker:ident;
297    )*) => {
298        /// Marker types for each resource.
299        pub mod markers {
300            $(
301                #[derive(Debug)]
302                pub enum $marker {}
303                impl super::Marker for $marker {}
304            )*
305        }
306
307        $(
308            $(#[$($meta)*])*
309            pub type $name = Id<self::markers::$marker>;
310        )*
311    }
312}
313
314ids! {
315    pub type AdapterId Adapter;
316    pub type SurfaceId Surface;
317    pub type DeviceId Device;
318    pub type QueueId Queue;
319    pub type BufferId Buffer;
320    pub type StagingBufferId StagingBuffer;
321    pub type TextureViewId TextureView;
322    pub type TextureId Texture;
323    pub type ExternalTextureId ExternalTexture;
324    pub type SamplerId Sampler;
325    pub type BindGroupLayoutId BindGroupLayout;
326    pub type PipelineLayoutId PipelineLayout;
327    pub type BindGroupId BindGroup;
328    pub type ShaderModuleId ShaderModule;
329    pub type RenderPipelineId RenderPipeline;
330    pub type ComputePipelineId ComputePipeline;
331    pub type PipelineCacheId PipelineCache;
332    pub type CommandEncoderId CommandEncoder;
333    pub type CommandBufferId CommandBuffer;
334    pub type RenderPassEncoderId RenderPassEncoder;
335    pub type ComputePassEncoderId ComputePassEncoder;
336    pub type RenderBundleEncoderId RenderBundleEncoder;
337    pub type RenderBundleId RenderBundle;
338    pub type QuerySetId QuerySet;
339    pub type BlasId Blas;
340    pub type TlasId Tlas;
341}
342
343#[test]
344fn test_id() {
345    let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX];
346    let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX];
347    for &i in &indexes {
348        for &e in &epochs {
349            let id = Id::<()>::zip(i, e);
350            let (index, epoch) = id.unzip();
351            assert_eq!(index, i);
352            assert_eq!(epoch, e);
353        }
354    }
355}