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#[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 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 pub fn unzip(self) -> (Index, Epoch) {
52 (self.0.get() as Index, (self.0.get() >> 32) as Epoch)
53 }
54}
55
56#[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#[allow(dead_code)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93enum SerialId {
94 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 pub unsafe fn from_raw(raw: RawId) -> Self {
121 Self(raw, PhantomData)
122 }
123
124 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
205pub trait Marker: 'static + WasmNotSendSync {}
210
211#[cfg(test)]
216impl Marker for () {}
217
218macro_rules! ids {
220 ($(
221 $(#[$($meta:meta)*])*
222 pub type $name:ident $marker:ident;
223 )*) => {
224 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
268impl 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}