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(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 {
45 let v = (index as u64) | ((epoch as u64) << 32);
46 Self(NonZeroU64::new(v).unwrap())
47 }
48
49 pub fn unzip(self) -> (Index, Epoch) {
51 (self.0.get() as Index, (self.0.get() >> 32) as Epoch)
52 }
53}
54
55#[repr(transparent)]
81#[cfg_attr(any(feature = "serde", feature = "trace"), derive(serde::Serialize))]
82#[cfg_attr(any(feature = "serde", feature = "replay"), derive(serde::Deserialize))]
83#[cfg_attr(
84 any(feature = "serde", feature = "trace", feature = "replay"),
85 serde(transparent)
86)]
87pub struct Id<T: Marker>(RawId, PhantomData<T>);
88
89#[allow(dead_code)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
92enum SerialId {
93 Id(Index, Epoch),
95}
96
97impl From<RawId> for SerialId {
98 fn from(id: RawId) -> Self {
99 let (index, epoch) = id.unzip();
100 Self::Id(index, epoch)
101 }
102}
103
104impl From<SerialId> for RawId {
105 fn from(id: SerialId) -> Self {
106 match id {
107 SerialId::Id(index, epoch) => RawId::zip(index, epoch),
108 }
109 }
110}
111
112impl<T> Id<T>
113where
114 T: Marker,
115{
116 pub unsafe fn from_raw(raw: RawId) -> Self {
120 Self(raw, PhantomData)
121 }
122
123 pub fn into_raw(self) -> RawId {
125 self.0
126 }
127
128 #[inline]
129 pub fn zip(index: Index, epoch: Epoch) -> Self {
130 Id(RawId::zip(index, epoch), PhantomData)
131 }
132
133 #[inline]
134 pub fn unzip(self) -> (Index, Epoch) {
135 self.0.unzip()
136 }
137}
138
139impl<T> Copy for Id<T> where T: Marker {}
140
141impl<T> Clone for Id<T>
142where
143 T: Marker,
144{
145 #[inline]
146 fn clone(&self) -> Self {
147 *self
148 }
149}
150
151impl<T> Debug for Id<T>
152where
153 T: Marker,
154{
155 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
156 let (index, epoch) = self.unzip();
157 write!(formatter, "Id({index},{epoch})")?;
158 Ok(())
159 }
160}
161
162impl<T> Hash for Id<T>
163where
164 T: Marker,
165{
166 #[inline]
167 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
168 self.0.hash(state);
169 }
170}
171
172impl<T> PartialEq for Id<T>
173where
174 T: Marker,
175{
176 #[inline]
177 fn eq(&self, other: &Self) -> bool {
178 self.0 == other.0
179 }
180}
181
182impl<T> Eq for Id<T> where T: Marker {}
183
184impl<T> PartialOrd for Id<T>
185where
186 T: Marker,
187{
188 #[inline]
189 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
190 Some(self.cmp(other))
191 }
192}
193
194impl<T> Ord for Id<T>
195where
196 T: Marker,
197{
198 #[inline]
199 fn cmp(&self, other: &Self) -> Ordering {
200 self.0.cmp(&other.0)
201 }
202}
203
204pub trait Marker: 'static + WasmNotSendSync {}
209
210#[cfg(test)]
215impl Marker for () {}
216
217macro_rules! ids {
219 ($(
220 $(#[$($meta:meta)*])*
221 pub type $name:ident $marker:ident;
222 )*) => {
223 pub mod markers {
225 $(
226 #[derive(Debug)]
227 pub enum $marker {}
228 impl super::Marker for $marker {}
229 )*
230 }
231
232 $(
233 $(#[$($meta)*])*
234 pub type $name = Id<self::markers::$marker>;
235 )*
236 }
237}
238
239ids! {
240 pub type AdapterId Adapter;
241 pub type SurfaceId Surface;
242 pub type DeviceId Device;
243 pub type QueueId Queue;
244 pub type BufferId Buffer;
245 pub type StagingBufferId StagingBuffer;
246 pub type TextureViewId TextureView;
247 pub type TextureId Texture;
248 pub type SamplerId Sampler;
249 pub type BindGroupLayoutId BindGroupLayout;
250 pub type PipelineLayoutId PipelineLayout;
251 pub type BindGroupId BindGroup;
252 pub type ShaderModuleId ShaderModule;
253 pub type RenderPipelineId RenderPipeline;
254 pub type ComputePipelineId ComputePipeline;
255 pub type PipelineCacheId PipelineCache;
256 pub type CommandEncoderId CommandEncoder;
257 pub type CommandBufferId CommandBuffer;
258 pub type RenderPassEncoderId RenderPassEncoder;
259 pub type ComputePassEncoderId ComputePassEncoder;
260 pub type RenderBundleEncoderId RenderBundleEncoder;
261 pub type RenderBundleId RenderBundle;
262 pub type QuerySetId QuerySet;
263 pub type BlasId Blas;
264 pub type TlasId Tlas;
265}
266
267impl CommandEncoderId {
271 pub fn into_command_buffer_id(self) -> CommandBufferId {
272 Id(self.0, PhantomData)
273 }
274}
275
276impl CommandBufferId {
277 pub fn into_command_encoder_id(self) -> CommandEncoderId {
278 Id(self.0, PhantomData)
279 }
280}
281
282#[test]
283fn test_id() {
284 let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX];
285 let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX];
286 for &i in &indexes {
287 for &e in &epochs {
288 let id = Id::<()>::zip(i, e);
289 let (index, epoch) = id.unzip();
290 assert_eq!(index, i);
291 assert_eq!(epoch, e);
292 }
293 }
294}