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#[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 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 pub fn unzip(self) -> (Index, Epoch) {
55 (self.0.get() as Index, (self.0.get() >> 32) as Epoch)
56 }
57}
58
59#[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#[cfg(feature = "serde")]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[derive(Clone, Debug)]
74pub enum SerialId {
75 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#[allow(dead_code)]
114#[cfg(feature = "serde")]
115#[derive(Debug, serde::Serialize, serde::Deserialize)]
116pub enum PointerId<T: Marker> {
117 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 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 pub unsafe fn from_raw(raw: RawId) -> Self {
176 Self(raw, PhantomData)
177 }
178
179 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
260pub trait Marker: 'static + WasmNotSendSync {
265 const TYPE: &'static str;
266}
267
268#[cfg(test)]
273impl Marker for () {
274 const TYPE: &'static str = "Untyped";
275}
276
277macro_rules! ids {
279 ($(
280 $(#[$($meta:meta)*])*
281 pub type $name:ident $marker:ident;
282 )*) => {
283 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}