wgpu_core/
id.rs

1use crate::{Epoch, Index};
2use std::{
3    cmp::Ordering,
4    fmt::{self, Debug},
5    hash::Hash,
6    marker::PhantomData,
7    mem::size_of,
8    num::NonZeroU64,
9};
10use wgt::WasmNotSendSync;
11
12const _: () = {
13    if size_of::<Index>() != 4 {
14        panic!()
15    }
16};
17const _: () = {
18    if size_of::<Epoch>() != 4 {
19        panic!()
20    }
21};
22const _: () = {
23    if size_of::<RawId>() != 8 {
24        panic!()
25    }
26};
27
28/// The raw underlying representation of an identifier.
29#[repr(transparent)]
30#[cfg_attr(
31    any(feature = "serde", feature = "trace"),
32    derive(serde::Serialize),
33    serde(into = "SerialId")
34)]
35#[cfg_attr(
36    any(feature = "serde", feature = "replay"),
37    derive(serde::Deserialize),
38    serde(from = "SerialId")
39)]
40#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct RawId(NonZeroU64);
42
43impl RawId {
44    /// Zip together an identifier and return its raw underlying representation.
45    pub fn zip(index: Index, epoch: Epoch) -> RawId {
46        let v = (index as u64) | ((epoch as u64) << 32);
47        Self(NonZeroU64::new(v).unwrap())
48    }
49
50    /// Unzip a raw identifier into its components.
51    pub fn unzip(self) -> (Index, Epoch) {
52        (self.0.get() as Index, (self.0.get() >> 32) as Epoch)
53    }
54}
55
56/// An identifier for a wgpu object.
57///
58/// An `Id<T>` value identifies a value stored in a [`Global`]'s [`Hub`].
59///
60/// ## Note on `Id` typing
61///
62/// You might assume that an `Id<T>` can only be used to retrieve a resource of
63/// type `T`, but that is not quite the case. The id types in `wgpu-core`'s
64/// public API ([`TextureId`], for example) can refer to resources belonging to
65/// any backend, but the corresponding resource types ([`Texture<A>`], for
66/// example) are always parameterized by a specific backend `A`.
67///
68/// So the `T` in `Id<T>` is usually a resource type like `Texture<Empty>`,
69/// where [`Empty`] is the `wgpu_hal` dummy back end. These empty types are
70/// never actually used, beyond just making sure you access each `Storage` with
71/// the right kind of identifier. The members of [`Hub<A>`] pair up each
72/// `X<Empty>` type with the resource type `X<A>`, for some specific backend
73/// `A`.
74///
75/// [`Global`]: crate::global::Global
76/// [`Hub`]: crate::hub::Hub
77/// [`Hub<A>`]: crate::hub::Hub
78/// [`Texture<A>`]: crate::resource::Texture
79/// [`Registry`]: crate::hub::Registry
80/// [`Empty`]: hal::api::Empty
81#[repr(transparent)]
82#[cfg_attr(any(feature = "serde", feature = "trace"), derive(serde::Serialize))]
83#[cfg_attr(any(feature = "serde", feature = "replay"), derive(serde::Deserialize))]
84#[cfg_attr(
85    any(feature = "serde", feature = "trace", feature = "replay"),
86    serde(transparent)
87)]
88pub struct Id<T: Marker>(RawId, PhantomData<T>);
89
90// This type represents Id in a more readable (and editable) way.
91#[allow(dead_code)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93enum SerialId {
94    // The only variant forces RON to not ignore "Id"
95    Id(Index, Epoch),
96}
97
98impl From<RawId> for SerialId {
99    fn from(id: RawId) -> Self {
100        let (index, epoch) = id.unzip();
101        Self::Id(index, epoch)
102    }
103}
104
105impl From<SerialId> for RawId {
106    fn from(id: SerialId) -> Self {
107        match id {
108            SerialId::Id(index, epoch) => RawId::zip(index, epoch),
109        }
110    }
111}
112
113impl<T> Id<T>
114where
115    T: Marker,
116{
117    /// # Safety
118    ///
119    /// The raw id must be valid for the type.
120    pub unsafe fn from_raw(raw: RawId) -> Self {
121        Self(raw, PhantomData)
122    }
123
124    /// Coerce the identifiers into its raw underlying representation.
125    pub fn into_raw(self) -> RawId {
126        self.0
127    }
128
129    #[inline]
130    pub fn zip(index: Index, epoch: Epoch) -> Self {
131        Id(RawId::zip(index, epoch), PhantomData)
132    }
133
134    #[inline]
135    pub fn unzip(self) -> (Index, Epoch) {
136        self.0.unzip()
137    }
138}
139
140impl<T> Copy for Id<T> where T: Marker {}
141
142impl<T> Clone for Id<T>
143where
144    T: Marker,
145{
146    #[inline]
147    fn clone(&self) -> Self {
148        *self
149    }
150}
151
152impl<T> Debug for Id<T>
153where
154    T: Marker,
155{
156    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
157        let (index, epoch) = self.unzip();
158        write!(formatter, "Id({index},{epoch})")?;
159        Ok(())
160    }
161}
162
163impl<T> Hash for Id<T>
164where
165    T: Marker,
166{
167    #[inline]
168    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
169        self.0.hash(state);
170    }
171}
172
173impl<T> PartialEq for Id<T>
174where
175    T: Marker,
176{
177    #[inline]
178    fn eq(&self, other: &Self) -> bool {
179        self.0 == other.0
180    }
181}
182
183impl<T> Eq for Id<T> where T: Marker {}
184
185impl<T> PartialOrd for Id<T>
186where
187    T: Marker,
188{
189    #[inline]
190    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
191        Some(self.cmp(other))
192    }
193}
194
195impl<T> Ord for Id<T>
196where
197    T: Marker,
198{
199    #[inline]
200    fn cmp(&self, other: &Self) -> Ordering {
201        self.0.cmp(&other.0)
202    }
203}
204
205/// Marker trait used to determine which types uniquely identify a resource.
206///
207/// For example, `Device<A>` will have the same type of identifier as
208/// `Device<B>` because `Device<T>` for any `T` defines the same maker type.
209pub trait Marker: 'static + WasmNotSendSync {}
210
211// This allows `()` to be used as a marker type for tests.
212//
213// We don't want these in production code, since they essentially remove type
214// safety, like how identifiers across different types can be compared.
215#[cfg(test)]
216impl Marker for () {}
217
218/// Define identifiers for each resource.
219macro_rules! ids {
220    ($(
221        $(#[$($meta:meta)*])*
222        pub type $name:ident $marker:ident;
223    )*) => {
224        /// Marker types for each resource.
225        pub mod markers {
226            $(
227                #[derive(Debug)]
228                pub enum $marker {}
229                impl super::Marker for $marker {}
230            )*
231        }
232
233        $(
234            $(#[$($meta)*])*
235            pub type $name = Id<self::markers::$marker>;
236        )*
237    }
238}
239
240ids! {
241    pub type AdapterId Adapter;
242    pub type SurfaceId Surface;
243    pub type DeviceId Device;
244    pub type QueueId Queue;
245    pub type BufferId Buffer;
246    pub type StagingBufferId StagingBuffer;
247    pub type TextureViewId TextureView;
248    pub type TextureId Texture;
249    pub type SamplerId Sampler;
250    pub type BindGroupLayoutId BindGroupLayout;
251    pub type PipelineLayoutId PipelineLayout;
252    pub type BindGroupId BindGroup;
253    pub type ShaderModuleId ShaderModule;
254    pub type RenderPipelineId RenderPipeline;
255    pub type ComputePipelineId ComputePipeline;
256    pub type PipelineCacheId PipelineCache;
257    pub type CommandEncoderId CommandEncoder;
258    pub type CommandBufferId CommandBuffer;
259    pub type RenderPassEncoderId RenderPassEncoder;
260    pub type ComputePassEncoderId ComputePassEncoder;
261    pub type RenderBundleEncoderId RenderBundleEncoder;
262    pub type RenderBundleId RenderBundle;
263    pub type QuerySetId QuerySet;
264    pub type BlasId Blas;
265    pub type TlasId Tlas;
266}
267
268// The CommandBuffer type serves both as encoder and
269// buffer, which is why the 2 functions below exist.
270
271impl CommandEncoderId {
272    pub fn into_command_buffer_id(self) -> CommandBufferId {
273        Id(self.0, PhantomData)
274    }
275}
276
277impl CommandBufferId {
278    pub fn into_command_encoder_id(self) -> CommandEncoderId {
279        Id(self.0, PhantomData)
280    }
281}
282
283#[test]
284fn test_id() {
285    let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX];
286    let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX];
287    for &i in &indexes {
288        for &e in &epochs {
289            let id = Id::<()>::zip(i, e);
290            let (index, epoch) = id.unzip();
291            assert_eq!(index, i);
292            assert_eq!(epoch, e);
293        }
294    }
295}