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#[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 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 #[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
80pub fn as_option_slice<T: Marker>(ids: &[Id<T>]) -> &[Option<Id<T>>] {
87 unsafe { std::slice::from_raw_parts(ids.as_ptr().cast(), ids.len()) }
90}
91
92#[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#[allow(dead_code)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
129enum SerialId {
130 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 pub unsafe fn from_raw(raw: RawId) -> Self {
157 Self(raw, PhantomData)
158 }
159
160 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 #[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
265pub trait Marker: 'static + WasmNotSendSync {}
270
271#[cfg(test)]
276impl Marker for () {}
277
278macro_rules! ids {
280 ($(
281 $(#[$($meta:meta)*])*
282 pub type $name:ident $marker:ident;
283 )*) => {
284 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}