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)]
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#[allow(dead_code)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96enum SerialId {
97 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 pub unsafe fn from_raw(raw: RawId) -> Self {
136 Self(raw, PhantomData)
137 }
138
139 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
220pub trait Marker: 'static + WasmNotSendSync {}
225
226#[cfg(test)]
231impl Marker for () {}
232
233macro_rules! ids {
235 ($(
236 $(#[$($meta:meta)*])*
237 pub type $name:ident $marker:ident;
238 )*) => {
239 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}