wgpu_core/
id.rs

1use crate::{Epoch, Index};
2use std::{
3    cmp::Ordering,
4    fmt::{self, Debug},
5    hash::Hash,
6    marker::PhantomData,
7};
8use wgt::{Backend, WasmNotSendSync};
9
10type IdType = u64;
11type ZippedIndex = Index;
12type NonZeroId = std::num::NonZeroU64;
13
14const INDEX_BITS: usize = std::mem::size_of::<ZippedIndex>() * 8;
15const EPOCH_BITS: usize = INDEX_BITS - BACKEND_BITS;
16const BACKEND_BITS: usize = 3;
17const BACKEND_SHIFT: usize = INDEX_BITS * 2 - BACKEND_BITS;
18pub const EPOCH_MASK: u32 = (1 << (EPOCH_BITS)) - 1;
19
20/// The raw underlying representation of an identifier.
21#[repr(transparent)]
22#[cfg_attr(
23    any(feature = "serde", feature = "trace"),
24    derive(serde::Serialize),
25    serde(into = "SerialId")
26)]
27#[cfg_attr(
28    any(feature = "serde", feature = "replay"),
29    derive(serde::Deserialize),
30    serde(from = "SerialId")
31)]
32#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub struct RawId(NonZeroId);
34
35impl RawId {
36    #[doc(hidden)]
37    #[inline]
38    pub fn from_non_zero(non_zero: NonZeroId) -> Self {
39        Self(non_zero)
40    }
41
42    #[doc(hidden)]
43    #[inline]
44    pub fn into_non_zero(self) -> NonZeroId {
45        self.0
46    }
47
48    /// Zip together an identifier and return its raw underlying representation.
49    pub fn zip(index: Index, epoch: Epoch, backend: Backend) -> RawId {
50        assert_eq!(0, epoch >> EPOCH_BITS);
51        assert_eq!(0, (index as IdType) >> INDEX_BITS);
52        let v = index as IdType
53            | ((epoch as IdType) << INDEX_BITS)
54            | ((backend as IdType) << BACKEND_SHIFT);
55        Self(NonZeroId::new(v).unwrap())
56    }
57
58    /// Unzip a raw identifier into its components.
59    #[allow(trivial_numeric_casts)]
60    pub fn unzip(self) -> (Index, Epoch, Backend) {
61        (
62            (self.0.get() as ZippedIndex) as Index,
63            (((self.0.get() >> INDEX_BITS) as ZippedIndex) & (EPOCH_MASK as ZippedIndex)) as Index,
64            self.backend(),
65        )
66    }
67
68    pub fn backend(self) -> Backend {
69        match self.0.get() >> (BACKEND_SHIFT) as u8 {
70            0 => Backend::Empty,
71            1 => Backend::Vulkan,
72            2 => Backend::Metal,
73            3 => Backend::Dx12,
74            4 => Backend::Gl,
75            _ => unreachable!(),
76        }
77    }
78}
79
80/// Coerce a slice of identifiers into a slice of optional raw identifiers.
81///
82/// There's two reasons why we know this is correct:
83/// * `Option<T>` is guaranteed to be niche-filled to 0's.
84/// * The `T` in `Option<T>` can inhabit any representation except 0's, since
85///   its underlying representation is `NonZero*`.
86pub fn as_option_slice<T: Marker>(ids: &[Id<T>]) -> &[Option<Id<T>>] {
87    // SAFETY: Any Id<T> is repr(transparent) over `Option<RawId>`, since both
88    // are backed by non-zero types.
89    unsafe { std::slice::from_raw_parts(ids.as_ptr().cast(), ids.len()) }
90}
91
92/// An identifier for a wgpu object.
93///
94/// An `Id<T>` value identifies a value stored in a [`Global`]'s [`Hub`].
95///
96/// ## Note on `Id` typing
97///
98/// You might assume that an `Id<T>` can only be used to retrieve a resource of
99/// type `T`, but that is not quite the case. The id types in `wgpu-core`'s
100/// public API ([`TextureId`], for example) can refer to resources belonging to
101/// any backend, but the corresponding resource types ([`Texture<A>`], for
102/// example) are always parameterized by a specific backend `A`.
103///
104/// So the `T` in `Id<T>` is usually a resource type like `Texture<Empty>`,
105/// where [`Empty`] is the `wgpu_hal` dummy back end. These empty types are
106/// never actually used, beyond just making sure you access each `Storage` with
107/// the right kind of identifier. The members of [`Hub<A>`] pair up each
108/// `X<Empty>` type with the resource type `X<A>`, for some specific backend
109/// `A`.
110///
111/// [`Global`]: crate::global::Global
112/// [`Hub`]: crate::hub::Hub
113/// [`Hub<A>`]: crate::hub::Hub
114/// [`Texture<A>`]: crate::resource::Texture
115/// [`Registry`]: crate::hub::Registry
116/// [`Empty`]: hal::api::Empty
117#[repr(transparent)]
118#[cfg_attr(any(feature = "serde", feature = "trace"), derive(serde::Serialize))]
119#[cfg_attr(any(feature = "serde", feature = "replay"), derive(serde::Deserialize))]
120#[cfg_attr(
121    any(feature = "serde", feature = "trace", feature = "replay"),
122    serde(transparent)
123)]
124pub struct Id<T: Marker>(RawId, PhantomData<T>);
125
126// This type represents Id in a more readable (and editable) way.
127#[allow(dead_code)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
129enum SerialId {
130    // The only variant forces RON to not ignore "Id"
131    Id(Index, Epoch, Backend),
132}
133
134impl From<RawId> for SerialId {
135    fn from(id: RawId) -> Self {
136        let (index, epoch, backend) = id.unzip();
137        Self::Id(index, epoch, backend)
138    }
139}
140
141impl From<SerialId> for RawId {
142    fn from(id: SerialId) -> Self {
143        match id {
144            SerialId::Id(index, epoch, backend) => RawId::zip(index, epoch, backend),
145        }
146    }
147}
148
149impl<T> Id<T>
150where
151    T: Marker,
152{
153    /// # Safety
154    ///
155    /// The raw id must be valid for the type.
156    pub unsafe fn from_raw(raw: RawId) -> Self {
157        Self(raw, PhantomData)
158    }
159
160    /// Coerce the identifiers into its raw underlying representation.
161    pub fn into_raw(self) -> RawId {
162        self.0
163    }
164
165    #[allow(dead_code)]
166    pub(crate) fn dummy(index: u32) -> Self {
167        Id::zip(index, 1, Backend::Empty)
168    }
169
170    #[allow(dead_code)]
171    pub(crate) fn is_valid(&self) -> bool {
172        self.backend() != Backend::Empty
173    }
174
175    /// Get the backend this identifier corresponds to.
176    #[inline]
177    pub fn backend(self) -> Backend {
178        self.0.backend()
179    }
180
181    #[inline]
182    pub fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self {
183        Id(RawId::zip(index, epoch, backend), PhantomData)
184    }
185
186    #[inline]
187    pub fn unzip(self) -> (Index, Epoch, Backend) {
188        self.0.unzip()
189    }
190}
191
192impl<T> Copy for Id<T> where T: Marker {}
193
194impl<T> Clone for Id<T>
195where
196    T: Marker,
197{
198    #[inline]
199    fn clone(&self) -> Self {
200        *self
201    }
202}
203
204impl<T> Debug for Id<T>
205where
206    T: Marker,
207{
208    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
209        let (index, epoch, backend) = self.unzip();
210        let backend = match backend {
211            Backend::Empty => "_",
212            Backend::Vulkan => "vk",
213            Backend::Metal => "mtl",
214            Backend::Dx12 => "d3d12",
215            Backend::Gl => "gl",
216            Backend::BrowserWebGpu => "webgpu",
217        };
218        write!(formatter, "Id({index},{epoch},{backend})")?;
219        Ok(())
220    }
221}
222
223impl<T> Hash for Id<T>
224where
225    T: Marker,
226{
227    #[inline]
228    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
229        self.0.hash(state);
230    }
231}
232
233impl<T> PartialEq for Id<T>
234where
235    T: Marker,
236{
237    #[inline]
238    fn eq(&self, other: &Self) -> bool {
239        self.0 == other.0
240    }
241}
242
243impl<T> Eq for Id<T> where T: Marker {}
244
245impl<T> PartialOrd for Id<T>
246where
247    T: Marker,
248{
249    #[inline]
250    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
251        Some(self.cmp(other))
252    }
253}
254
255impl<T> Ord for Id<T>
256where
257    T: Marker,
258{
259    #[inline]
260    fn cmp(&self, other: &Self) -> Ordering {
261        self.0.cmp(&other.0)
262    }
263}
264
265/// Marker trait used to determine which types uniquely identify a resource.
266///
267/// For example, `Device<A>` will have the same type of identifier as
268/// `Device<B>` because `Device<T>` for any `T` defines the same maker type.
269pub trait Marker: 'static + WasmNotSendSync {}
270
271// This allows `()` to be used as a marker type for tests.
272//
273// We don't want these in production code, since they essentially remove type
274// safety, like how identifiers across different types can be compared.
275#[cfg(test)]
276impl Marker for () {}
277
278/// Define identifiers for each resource.
279macro_rules! ids {
280    ($(
281        $(#[$($meta:meta)*])*
282        pub type $name:ident $marker:ident;
283    )*) => {
284        /// Marker types for each resource.
285        pub mod markers {
286            $(
287                #[derive(Debug)]
288                pub enum $marker {}
289                impl super::Marker for $marker {}
290            )*
291        }
292
293        $(
294            $(#[$($meta)*])*
295            pub type $name = Id<self::markers::$marker>;
296        )*
297    }
298}
299
300ids! {
301    pub type AdapterId Adapter;
302    pub type SurfaceId Surface;
303    pub type DeviceId Device;
304    pub type QueueId Queue;
305    pub type BufferId Buffer;
306    pub type StagingBufferId StagingBuffer;
307    pub type TextureViewId TextureView;
308    pub type TextureId Texture;
309    pub type SamplerId Sampler;
310    pub type BindGroupLayoutId BindGroupLayout;
311    pub type PipelineLayoutId PipelineLayout;
312    pub type BindGroupId BindGroup;
313    pub type ShaderModuleId ShaderModule;
314    pub type RenderPipelineId RenderPipeline;
315    pub type ComputePipelineId ComputePipeline;
316    pub type CommandEncoderId CommandEncoder;
317    pub type CommandBufferId CommandBuffer;
318    pub type RenderPassEncoderId RenderPassEncoder;
319    pub type ComputePassEncoderId ComputePassEncoder;
320    pub type RenderBundleEncoderId RenderBundleEncoder;
321    pub type RenderBundleId RenderBundle;
322    pub type QuerySetId QuerySet;
323}
324
325impl CommandEncoderId {
326    pub fn into_command_buffer_id(self) -> CommandBufferId {
327        Id(self.0, PhantomData)
328    }
329}
330
331impl CommandBufferId {
332    pub fn into_command_encoder_id(self) -> CommandEncoderId {
333        Id(self.0, PhantomData)
334    }
335}
336
337impl DeviceId {
338    pub fn into_queue_id(self) -> QueueId {
339        Id(self.0, PhantomData)
340    }
341}
342
343#[test]
344fn test_id_backend() {
345    for &b in &[
346        Backend::Empty,
347        Backend::Vulkan,
348        Backend::Metal,
349        Backend::Dx12,
350        Backend::Gl,
351    ] {
352        let id = crate::id::Id::<()>::zip(1, 0, b);
353        let (_id, _epoch, backend) = id.unzip();
354        assert_eq!(id.backend(), b);
355        assert_eq!(backend, b);
356    }
357}
358
359#[test]
360fn test_id() {
361    let last_index = ((1u64 << INDEX_BITS) - 1) as Index;
362    let indexes = [1, last_index / 2 - 1, last_index / 2 + 1, last_index];
363    let epochs = [1, EPOCH_MASK / 2 - 1, EPOCH_MASK / 2 + 1, EPOCH_MASK];
364    let backends = [
365        Backend::Empty,
366        Backend::Vulkan,
367        Backend::Metal,
368        Backend::Dx12,
369        Backend::Gl,
370    ];
371    for &i in &indexes {
372        for &e in &epochs {
373            for &b in &backends {
374                let id = crate::id::Id::<()>::zip(i, e, b);
375                let (index, epoch, backend) = id.unzip();
376                assert_eq!(index, i);
377                assert_eq!(epoch, e);
378                assert_eq!(backend, b);
379            }
380        }
381    }
382}