encase/core/
traits.rs

1use std::num::NonZeroU64;
2
3use super::{AlignmentValue, BufferMut, BufferRef, Reader, SizeValue, Writer};
4
5const UNIFORM_MIN_ALIGNMENT: AlignmentValue = AlignmentValue::new(16);
6
7pub struct Metadata<E> {
8    pub alignment: AlignmentValue,
9    pub has_uniform_min_alignment: bool,
10    pub min_size: SizeValue,
11    pub is_pod: bool,
12    pub extra: E,
13}
14
15impl Metadata<()> {
16    pub const fn from_alignment_and_size(alignment: u64, size: u64) -> Self {
17        Self {
18            alignment: AlignmentValue::new(alignment),
19            has_uniform_min_alignment: false,
20            min_size: SizeValue::new(size),
21            is_pod: false,
22            extra: (),
23        }
24    }
25}
26
27// using forget() avoids "destructors cannot be evaluated at compile-time" error
28// track #![feature(const_precise_live_drops)] (https://github.com/rust-lang/rust/issues/73255)
29
30impl<E> Metadata<E> {
31    #[inline]
32    pub const fn alignment(self) -> AlignmentValue {
33        let value = self.alignment;
34        core::mem::forget(self);
35        value
36    }
37
38    #[inline]
39    pub const fn uniform_min_alignment(self) -> Option<AlignmentValue> {
40        let value = self.has_uniform_min_alignment;
41        core::mem::forget(self);
42        match value {
43            true => Some(UNIFORM_MIN_ALIGNMENT),
44            false => None,
45        }
46    }
47
48    #[inline]
49    pub const fn min_size(self) -> SizeValue {
50        let value = self.min_size;
51        core::mem::forget(self);
52        value
53    }
54
55    #[inline]
56    pub const fn is_pod(self) -> bool {
57        let value = self.is_pod;
58        core::mem::forget(self);
59        value
60    }
61
62    #[inline]
63    pub const fn pod(mut self) -> Self {
64        self.is_pod = true;
65        self
66    }
67
68    #[inline]
69    pub const fn no_pod(mut self) -> Self {
70        self.is_pod = false;
71        self
72    }
73}
74
75/// Base trait for all [WGSL host-shareable types](https://gpuweb.github.io/gpuweb/wgsl/#host-shareable-types)
76pub trait ShaderType {
77    #[doc(hidden)]
78    type ExtraMetadata;
79    #[doc(hidden)]
80    const METADATA: Metadata<Self::ExtraMetadata>;
81
82    /// Represents the minimum size of `Self` (equivalent to [GPUBufferBindingLayout.minBindingSize](https://gpuweb.github.io/gpuweb/#dom-gpubufferbindinglayout-minbindingsize))
83    ///
84    /// For [WGSL fixed-footprint types](https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types)
85    /// it represents [WGSL Size](https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size)
86    /// (equivalent to [`ShaderSize::SHADER_SIZE`])
87    ///
88    /// For
89    /// [WGSL runtime-sized arrays](https://gpuweb.github.io/gpuweb/wgsl/#runtime-sized) and
90    /// [WGSL structs containing runtime-sized arrays](https://gpuweb.github.io/gpuweb/wgsl/#struct-types)
91    /// (non fixed-footprint types)
92    /// this will be calculated by assuming the array has one element
93    #[inline]
94    fn min_size() -> NonZeroU64 {
95        Self::METADATA.min_size().0
96    }
97
98    /// Returns the size of `Self` at runtime
99    ///
100    /// For [WGSL fixed-footprint types](https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types)
101    /// it's equivalent to [`Self::min_size`] and [`ShaderSize::SHADER_SIZE`]
102    #[inline]
103    fn size(&self) -> NonZeroU64 {
104        Self::METADATA.min_size().0
105    }
106
107    #[doc(hidden)]
108    const UNIFORM_COMPAT_ASSERT: fn() = || {};
109
110    /// Asserts that `Self` meets the requirements of the
111    /// [uniform address space restrictions on stored values](https://gpuweb.github.io/gpuweb/wgsl/#address-spaces-uniform) and the
112    /// [uniform address space layout constraints](https://gpuweb.github.io/gpuweb/wgsl/#address-space-layout-constraints)
113    ///
114    /// # Examples
115    ///
116    /// ## Array
117    ///
118    /// Will panic since runtime-sized arrays are not compatible with the
119    /// uniform address space restrictions on stored values
120    ///
121    /// ```should_panic
122    /// # use crate::encase::ShaderType;
123    /// <Vec<mint::Vector4<f32>>>::assert_uniform_compat();
124    /// ```
125    ///
126    /// Will panic since the stride is 4 bytes
127    ///
128    /// ```should_panic
129    /// # use crate::encase::ShaderType;
130    /// <[f32; 2]>::assert_uniform_compat();
131    /// ```
132    ///
133    /// Will not panic since the stride is 16 bytes
134    ///
135    /// ```
136    /// # use crate::encase::ShaderType;
137    /// # use mint;
138    /// <[mint::Vector4<f32>; 2]>::assert_uniform_compat();
139    /// ```
140    ///
141    /// ## Struct
142    ///
143    /// Will panic since runtime-sized arrays are not compatible with the
144    /// uniform address space restrictions on stored values
145    ///
146    /// ```should_panic
147    /// # use crate::encase::ShaderType;
148    /// # use mint;
149    /// #[derive(ShaderType)]
150    /// struct Invalid {
151    ///     #[size(runtime)]
152    ///     vec: Vec<mint::Vector4<f32>>
153    /// }
154    /// Invalid::assert_uniform_compat();
155    /// ```
156    ///
157    /// Will panic since the inner struct's size must be a multiple of 16
158    ///
159    /// ```should_panic
160    /// # use crate::encase::ShaderType;
161    /// #[derive(ShaderType)]
162    /// struct S {
163    ///     x: f32,
164    /// }
165    ///
166    /// #[derive(ShaderType)]
167    /// struct Invalid {
168    ///     a: f32,
169    ///     b: S, // offset between fields 'a' and 'b' must be at least 16 (currently: 4)
170    /// }
171    /// Invalid::assert_uniform_compat();
172    /// ```
173    ///
174    /// Will not panic (fixed via align attribute)
175    ///
176    /// ```
177    /// # use crate::encase::ShaderType;
178    /// # #[derive(ShaderType)]
179    /// # struct S {
180    /// #     x: f32,
181    /// # }
182    /// #[derive(ShaderType)]
183    /// struct Valid {
184    ///     a: f32,
185    ///     #[align(16)]
186    ///     b: S,
187    /// }
188    /// Valid::assert_uniform_compat();
189    /// ```
190    ///
191    /// Will not panic (fixed via size attribute)
192    ///
193    /// ```
194    /// # use crate::encase::ShaderType;
195    /// # #[derive(ShaderType)]
196    /// # struct S {
197    /// #     x: f32,
198    /// # }
199    /// #[derive(ShaderType)]
200    /// struct Valid {
201    ///     #[size(16)]
202    ///     a: f32,
203    ///     b: S,
204    /// }
205    /// Valid::assert_uniform_compat();
206    /// ```
207    #[inline]
208    fn assert_uniform_compat() {
209        Self::UNIFORM_COMPAT_ASSERT();
210    }
211
212    // fn assert_can_write_into()
213    // where
214    //     Self: WriteInto,
215    // {
216    // }
217
218    // fn assert_can_read_from()
219    // where
220    //     Self: ReadFrom,
221    // {
222    // }
223
224    // fn assert_can_create_from()
225    // where
226    //     Self: CreateFrom,
227    // {
228    // }
229}
230
231/// Trait implemented for all [WGSL fixed-footprint types](https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types)
232pub trait ShaderSize: ShaderType {
233    /// Represents [WGSL Size](https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size) (equivalent to [`ShaderType::min_size`])
234    const SHADER_SIZE: NonZeroU64 = Self::METADATA.min_size().0;
235}
236
237/// Trait implemented for
238/// [WGSL runtime-sized arrays](https://gpuweb.github.io/gpuweb/wgsl/#runtime-sized) and
239/// [WGSL structs containing runtime-sized arrays](https://gpuweb.github.io/gpuweb/wgsl/#struct-types)
240/// (non fixed-footprint types)
241pub trait CalculateSizeFor {
242    /// Returns the size of `Self` assuming the (contained) runtime-sized array has `nr_of_el` elements
243    fn calculate_size_for(nr_of_el: u64) -> NonZeroU64;
244}
245
246#[allow(clippy::len_without_is_empty)]
247pub trait RuntimeSizedArray {
248    fn len(&self) -> usize;
249}
250
251pub trait WriteInto {
252    fn write_into<B>(&self, writer: &mut Writer<B>)
253    where
254        B: BufferMut;
255}
256
257pub trait ReadFrom {
258    fn read_from<B>(&mut self, reader: &mut Reader<B>)
259    where
260        B: BufferRef;
261}
262
263pub trait CreateFrom: Sized {
264    fn create_from<B>(reader: &mut Reader<B>) -> Self
265    where
266        B: BufferRef;
267}