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(any(feature = "serde", feature = "trace"), derive(serde::Serialize))]
86#[cfg_attr(any(feature = "serde", feature = "replay"), derive(serde::Deserialize))]
87#[cfg_attr(
88    any(feature = "serde", feature = "trace", feature = "replay"),
89    serde(transparent)
90)]
91pub struct Id<T: Marker>(RawId, PhantomData<T>);
92
93// This type represents Id in a more readable (and editable) way.
94#[allow(dead_code)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96enum SerialId {
97    // The only variant forces RON to not ignore "Id"
98    Id(Index, Epoch),
99}
100
101impl From<RawId> for SerialId {
102    fn from(id: RawId) -> Self {
103        let (index, epoch) = id.unzip();
104        Self::Id(index, epoch)
105    }
106}
107
108pub struct ZeroIdError;
109
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
116impl TryFrom<SerialId> for RawId {
117    type Error = ZeroIdError;
118    fn try_from(id: SerialId) -> Result<Self, ZeroIdError> {
119        let SerialId::Id(index, epoch) = id;
120        if index == 0 && epoch == 0 {
121            Err(ZeroIdError)
122        } else {
123            Ok(RawId::zip(index, epoch))
124        }
125    }
126}
127
128impl<T> Id<T>
129where
130    T: Marker,
131{
132    /// # Safety
133    ///
134    /// The raw id must be valid for the type.
135    pub unsafe fn from_raw(raw: RawId) -> Self {
136        Self(raw, PhantomData)
137    }
138
139    /// Coerce the identifiers into its raw underlying representation.
140    pub fn into_raw(self) -> RawId {
141        self.0
142    }
143
144    #[inline]
145    pub fn zip(index: Index, epoch: Epoch) -> Self {
146        Id(RawId::zip(index, epoch), PhantomData)
147    }
148
149    #[inline]
150    pub fn unzip(self) -> (Index, Epoch) {
151        self.0.unzip()
152    }
153}
154
155impl<T> Copy for Id<T> where T: Marker {}
156
157impl<T> Clone for Id<T>
158where
159    T: Marker,
160{
161    #[inline]
162    fn clone(&self) -> Self {
163        *self
164    }
165}
166
167impl<T> Debug for Id<T>
168where
169    T: Marker,
170{
171    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
172        let (index, epoch) = self.unzip();
173        write!(formatter, "Id({index},{epoch})")?;
174        Ok(())
175    }
176}
177
178impl<T> Hash for Id<T>
179where
180    T: Marker,
181{
182    #[inline]
183    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
184        self.0.hash(state);
185    }
186}
187
188impl<T> PartialEq for Id<T>
189where
190    T: Marker,
191{
192    #[inline]
193    fn eq(&self, other: &Self) -> bool {
194        self.0 == other.0
195    }
196}
197
198impl<T> Eq for Id<T> where T: Marker {}
199
200impl<T> PartialOrd for Id<T>
201where
202    T: Marker,
203{
204    #[inline]
205    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
206        Some(self.cmp(other))
207    }
208}
209
210impl<T> Ord for Id<T>
211where
212    T: Marker,
213{
214    #[inline]
215    fn cmp(&self, other: &Self) -> Ordering {
216        self.0.cmp(&other.0)
217    }
218}
219
220/// Marker trait used to determine which types uniquely identify a resource.
221///
222/// For example, `Device<A>` will have the same type of identifier as
223/// `Device<B>` because `Device<T>` for any `T` defines the same maker type.
224pub trait Marker: 'static + WasmNotSendSync {}
225
226// This allows `()` to be used as a marker type for tests.
227//
228// We don't want these in production code, since they essentially remove type
229// safety, like how identifiers across different types can be compared.
230#[cfg(test)]
231impl Marker for () {}
232
233/// Define identifiers for each resource.
234macro_rules! ids {
235    ($(
236        $(#[$($meta:meta)*])*
237        pub type $name:ident $marker:ident;
238    )*) => {
239        /// Marker types for each resource.
240        pub mod markers {
241            $(
242                #[derive(Debug)]
243                pub enum $marker {}
244                impl super::Marker for $marker {}
245            )*
246        }
247
248        $(
249            $(#[$($meta)*])*
250            pub type $name = Id<self::markers::$marker>;
251        )*
252    }
253}
254
255ids! {
256    pub type AdapterId Adapter;
257    pub type SurfaceId Surface;
258    pub type DeviceId Device;
259    pub type QueueId Queue;
260    pub type BufferId Buffer;
261    pub type StagingBufferId StagingBuffer;
262    pub type TextureViewId TextureView;
263    pub type TextureId Texture;
264    pub type ExternalTextureId ExternalTexture;
265    pub type SamplerId Sampler;
266    pub type BindGroupLayoutId BindGroupLayout;
267    pub type PipelineLayoutId PipelineLayout;
268    pub type BindGroupId BindGroup;
269    pub type ShaderModuleId ShaderModule;
270    pub type RenderPipelineId RenderPipeline;
271    pub type ComputePipelineId ComputePipeline;
272    pub type PipelineCacheId PipelineCache;
273    pub type CommandEncoderId CommandEncoder;
274    pub type CommandBufferId CommandBuffer;
275    pub type RenderPassEncoderId RenderPassEncoder;
276    pub type ComputePassEncoderId ComputePassEncoder;
277    pub type RenderBundleEncoderId RenderBundleEncoder;
278    pub type RenderBundleId RenderBundle;
279    pub type QuerySetId QuerySet;
280    pub type BlasId Blas;
281    pub type TlasId Tlas;
282}
283
284#[test]
285fn test_id() {
286    let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX];
287    let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX];
288    for &i in &indexes {
289        for &e in &epochs {
290            let id = Id::<()>::zip(i, e);
291            let (index, epoch) = id.unzip();
292            assert_eq!(index, i);
293            assert_eq!(epoch, e);
294        }
295    }
296}