spirv_cross2/
handle.rs

1use crate::error::SpirvCrossError;
2use crate::{error, Compiler, PhantomCompiler};
3use spirv_cross_sys::spvc_compiler_s;
4use std::fmt::{Debug, Formatter};
5use std::ptr::NonNull;
6
7use crate::sealed::Sealed;
8
9/// A SPIR-V ID to a specialization constant.
10pub use spirv_cross_sys::ConstantId;
11
12/// A SPIR-V ID to a type.
13pub use spirv_cross_sys::TypeId;
14
15/// A SPIR-V ID to a variable.
16pub use spirv_cross_sys::VariableId;
17
18#[derive(Copy, Clone)]
19#[repr(transparent)]
20struct PointerOnlyForComparison<T>(NonNull<T>);
21
22// SAFETY: pointer is only for comparison.
23unsafe impl<T> Send for PointerOnlyForComparison<T> {}
24unsafe impl<T> Sync for PointerOnlyForComparison<T> {}
25
26impl<T> PartialEq for PointerOnlyForComparison<T> {
27    fn eq(&self, other: &Self) -> bool {
28        other.0.as_ptr() == self.0.as_ptr()
29    }
30}
31
32impl<T> Eq for PointerOnlyForComparison<T> {}
33
34impl<T> Debug for PointerOnlyForComparison<T> {
35    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
36        // Truncate the tag, we don't really care about the upper 32 bytes.
37        // - Chop off ignored 16 bits
38        // - Low 2 bits are always 0, so we can ignore that too.
39        // - Either the low or high 32 bits remaining are good enough to show uniqueness.
40        write!(
41            f,
42            "Tag({:x})",
43            (((self.0.as_ptr() as usize) << 16) >> 18) as u32
44        )
45    }
46}
47
48/// A reference to an ID referring to an item in the compiler instance.
49///
50/// The usage of `Handle<T>` ensures that item IDs can not be forged from
51/// a different compiler instance or from a `u32`.
52#[derive(Debug, Copy, Clone, Eq, PartialEq)]
53pub struct Handle<T> {
54    id: T,
55    tag: PointerOnlyForComparison<spvc_compiler_s>,
56}
57
58impl<T: Id> Handle<T> {
59    /// Return the `u32` part of the Id.
60    ///
61    /// Note that [`Handle<T>`] **can not** implement [`Id`]
62    /// for safety reasons. Getting an `impl Id` out of a
63    /// [`Handle<T>`] requires using [`Compiler::yield_id`].
64    pub fn id(&self) -> u32 {
65        self.id.id()
66    }
67}
68
69/// Trait for SPIRV-Cross ID types.
70pub trait Id: Sealed + Debug + Send + Sync + 'static {
71    /// Return the `u32` part of the Id.
72    fn id(&self) -> u32;
73}
74
75impl Sealed for TypeId {}
76impl Id for TypeId {
77    #[inline(always)]
78    fn id(&self) -> u32 {
79        self.0 .0
80    }
81}
82
83impl Sealed for VariableId {}
84impl Id for VariableId {
85    #[inline(always)]
86    fn id(&self) -> u32 {
87        self.0 .0
88    }
89}
90
91impl Sealed for ConstantId {}
92impl Id for ConstantId {
93    #[inline(always)]
94    fn id(&self) -> u32 {
95        self.0 .0
96    }
97}
98
99impl<T: Id> Handle<T> {
100    /// Erase the type of the handle, this is useful for errors
101    /// but is otherwise useless.
102    #[cold]
103    fn erase_type(self) -> Handle<Box<dyn Id>> {
104        Handle {
105            id: Box::new(self.id) as Box<dyn Id>,
106            tag: self.tag,
107        }
108    }
109}
110
111/// APIs for comparing handles
112impl<T> Compiler<T> {
113    #[inline(always)]
114    /// Create a handle for the given ID tagged with this compiler instance.
115    ///
116    /// # Safety
117    /// When creating a handle, the ID must be valid for the compilation.
118    pub unsafe fn create_handle<I>(&self, id: I) -> Handle<I> {
119        Handle {
120            id,
121            tag: PointerOnlyForComparison(self.ptr),
122        }
123    }
124
125    #[inline(always)]
126    /// Create a handle for the given ID tagged with this compiler instance,
127    /// if the provided ID is not zero.
128    ///
129    /// # Safety
130    /// When creating a handle, the ID must be valid for the compilation.
131    pub unsafe fn create_handle_if_not_zero<I: Id>(&self, id: I) -> Option<Handle<I>> {
132        let raw = id.id();
133        if raw == 0 {
134            return None;
135        }
136        Some(Handle {
137            id,
138            tag: PointerOnlyForComparison(self.ptr),
139        })
140    }
141
142    /// Returns whether the given handle is valid for this compiler instance.
143    pub fn handle_is_valid<I>(&self, handle: &Handle<I>) -> bool {
144        handle.tag == PointerOnlyForComparison(self.ptr)
145    }
146
147    /// Yield the value of the handle, if it originated from the same compiler instance,
148    /// otherwise return [`SpirvCrossError::InvalidHandle`].
149    pub fn yield_id<I: Id>(&self, handle: Handle<I>) -> error::Result<I> {
150        if self.handle_is_valid(&handle) {
151            Ok(handle.id)
152        } else {
153            Err(SpirvCrossError::InvalidHandle(handle.erase_type()))
154        }
155    }
156}
157
158impl PhantomCompiler {
159    /// Internal method for creating a handle
160    ///
161    /// This is not marked unsafe, because it is only ever used internally
162    /// for handles valid for a compiler instance, i.e. we never smuggle
163    /// an invalid handle. Marking it unsafe would make it too noisy to
164    /// audit actually unsafe code.
165    ///
166    /// This is not necessarily the case for the public API.
167    #[inline(always)]
168    pub(crate) fn create_handle<I>(&self, id: I) -> Handle<I> {
169        Handle {
170            id,
171            tag: PointerOnlyForComparison(self.ptr),
172        }
173    }
174}