bevy_crevice/std430/
traits.rs

1use core::mem::{size_of, MaybeUninit};
2#[cfg(feature = "std")]
3use std::io::{self, Write};
4
5use bytemuck::{bytes_of, Pod, Zeroable};
6
7#[cfg(feature = "std")]
8use crate::std430::Writer;
9
10/// Trait implemented for all `std430` primitives. Generally should not be
11/// implemented outside this crate.
12pub unsafe trait Std430: Copy + Zeroable + Pod {
13    /// The required alignment of the type. Must be a power of two.
14    ///
15    /// This is distinct from the value returned by `std::mem::align_of` because
16    /// `AsStd430` structs do not use Rust's alignment. This enables them to
17    /// control and zero their padding bytes, making converting them to and from
18    /// slices safe.
19    const ALIGNMENT: usize;
20
21    /// Whether this type requires a padding at the end (ie, is a struct or an array
22    /// of primitives).
23    /// See <https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159>
24    /// (rule 4 and 9)
25    const PAD_AT_END: bool = false;
26    /// Padded type (Std430Padded specialization)
27    /// The usual implementation is
28    /// type Padded = Std430Padded<Self, {align_offset(size_of::<Self>(), ALIGNMENT)}>;
29    type Padded: Std430Convertible<Self>;
30
31    /// Casts the type to a byte array. Implementors should not override this
32    /// method.
33    ///
34    /// # Safety
35    /// This is always safe due to the requirements of [`bytemuck::Pod`] being a
36    /// prerequisite for this trait.
37    fn as_bytes(&self) -> &[u8] {
38        bytes_of(self)
39    }
40}
41
42unsafe impl Std430 for () {
43    const ALIGNMENT: usize = 0;
44
45    const PAD_AT_END: bool = false;
46
47    type Padded = ();
48}
49
50/// Trait specifically for Std430::Padded, implements conversions between padded type and base type.
51pub trait Std430Convertible<T: Std430>: Copy {
52    /// Convert from self to Std430
53    fn into_std430(self) -> T;
54    /// Convert from Std430 to self
55    fn from_std430(_: T) -> Self;
56}
57
58impl<T: Std430> Std430Convertible<T> for T {
59    fn into_std430(self) -> T {
60        self
61    }
62    fn from_std430(also_self: T) -> Self {
63        also_self
64    }
65}
66
67/// Unfortunately, we cannot easily derive padded representation for generic Std140 types.
68/// For now, we'll just use this empty enum with no values.
69#[derive(Copy, Clone)]
70pub enum InvalidPadded {}
71impl<T: Std430> Std430Convertible<T> for InvalidPadded {
72    fn into_std430(self) -> T {
73        unimplemented!()
74    }
75    fn from_std430(_: T) -> Self {
76        unimplemented!()
77    }
78}
79/**
80Trait implemented for all types that can be turned into `std430` values.
81
82This trait can often be `#[derive]`'d instead of manually implementing it. Any
83struct which contains only fields that also implement `AsStd430` can derive
84`AsStd430`.
85
86Types from the mint crate implement `AsStd430`, making them convenient for use
87in uniform types. Most Rust geometry crates, like cgmath, nalgebra, and
88ultraviolet support mint.
89
90## Example
91
92```glsl
93uniform CAMERA {
94    mat4 view;
95    mat4 projection;
96} camera;
97```
98
99```no_run
100use bevy_crevice::std430::{AsStd430, Std430};
101
102#[derive(AsStd430)]
103struct CameraUniform {
104    view: mint::ColumnMatrix4<f32>,
105    projection: mint::ColumnMatrix4<f32>,
106}
107
108let view: mint::ColumnMatrix4<f32> = todo!("your math code here");
109let projection: mint::ColumnMatrix4<f32> = todo!("your math code here");
110
111let camera = CameraUniform {
112    view,
113    projection,
114};
115
116# fn write_to_gpu_buffer(bytes: &[u8]) {}
117let camera_std430 = camera.as_std430();
118write_to_gpu_buffer(camera_std430.as_bytes());
119```
120*/
121pub trait AsStd430 {
122    /// The `std430` version of this value.
123    type Output: Std430;
124
125    /// Convert this value into the `std430` version of itself.
126    fn as_std430(&self) -> Self::Output;
127
128    /// Returns the size of the `std430` version of this type. Useful for
129    /// pre-sizing buffers.
130    fn std430_size_static() -> usize {
131        size_of::<Self::Output>()
132    }
133
134    /// Converts from `std430` version of self to self.
135    fn from_std430(value: Self::Output) -> Self;
136}
137
138impl<T> AsStd430 for T
139where
140    T: Std430,
141{
142    type Output = Self;
143
144    fn as_std430(&self) -> Self {
145        *self
146    }
147
148    fn from_std430(value: Self) -> Self {
149        value
150    }
151}
152
153#[doc(hidden)]
154#[derive(Copy, Clone, Debug)]
155pub struct Std430Padded<T: Std430, const PAD: usize> {
156    inner: T,
157    _padding: [u8; PAD],
158}
159
160unsafe impl<T: Std430, const PAD: usize> Zeroable for Std430Padded<T, PAD> {}
161unsafe impl<T: Std430, const PAD: usize> Pod for Std430Padded<T, PAD> {}
162
163impl<T: Std430, const PAD: usize> Std430Convertible<T> for Std430Padded<T, PAD> {
164    fn into_std430(self) -> T {
165        self.inner
166    }
167
168    fn from_std430(inner: T) -> Self {
169        Self {
170            inner,
171            _padding: [0u8; PAD],
172        }
173    }
174}
175
176#[doc(hidden)]
177#[derive(Copy, Clone, Debug)]
178#[repr(transparent)]
179pub struct Std430Array<T: Std430, const N: usize>([T::Padded; N]);
180
181unsafe impl<T: Std430, const N: usize> Zeroable for Std430Array<T, N> where T::Padded: Zeroable {}
182unsafe impl<T: Std430, const N: usize> Pod for Std430Array<T, N> where T::Padded: Pod {}
183unsafe impl<T: Std430, const N: usize> Std430 for Std430Array<T, N>
184where
185    T::Padded: Pod,
186{
187    const ALIGNMENT: usize = T::ALIGNMENT;
188    type Padded = Self;
189}
190
191impl<T: Std430, const N: usize> Std430Array<T, N> {
192    fn uninit_array() -> [MaybeUninit<T::Padded>; N] {
193        unsafe { MaybeUninit::uninit().assume_init() }
194    }
195
196    fn from_uninit_array(a: [MaybeUninit<T::Padded>; N]) -> Self {
197        unsafe { core::mem::transmute_copy(&a) }
198    }
199}
200
201impl<T: AsStd430, const N: usize> AsStd430 for [T; N]
202where
203    <T::Output as Std430>::Padded: Pod,
204{
205    type Output = Std430Array<T::Output, N>;
206    fn as_std430(&self) -> Self::Output {
207        let mut res = Self::Output::uninit_array();
208
209        for i in 0..N {
210            res[i] = MaybeUninit::new(Std430Convertible::from_std430(self[i].as_std430()));
211        }
212
213        Self::Output::from_uninit_array(res)
214    }
215
216    fn from_std430(val: Self::Output) -> Self {
217        let mut res: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
218        for i in 0..N {
219            res[i] = MaybeUninit::new(T::from_std430(val.0[i].into_std430()));
220        }
221        unsafe { core::mem::transmute_copy(&res) }
222    }
223}
224
225/// Trait implemented for all types that can be written into a buffer as
226/// `std430` bytes. This type is more general than [`AsStd430`]: all `AsStd430`
227/// types implement `WriteStd430`, but not the other way around.
228///
229/// While `AsStd430` requires implementers to return a type that implements the
230/// `Std430` trait, `WriteStd430` directly writes bytes using a [`Writer`]. This
231/// makes `WriteStd430` usable for writing slices or other DSTs that could not
232/// implement `AsStd430` without allocating new memory on the heap.
233#[cfg(feature = "std")]
234pub trait WriteStd430 {
235    /// Writes this value into the given [`Writer`] using `std430` layout rules.
236    ///
237    /// Should return the offset of the first byte of this type, as returned by
238    /// the first call to [`Writer::write`].
239    fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize>;
240
241    /// The space required to write this value using `std430` layout rules. This
242    /// does not include alignment padding that may be needed before or after
243    /// this type when written as part of a larger buffer.
244    fn std430_size(&self) -> usize {
245        let mut writer = Writer::new(io::sink());
246        self.write_std430(&mut writer).unwrap();
247        writer.len()
248    }
249}
250
251#[cfg(feature = "std")]
252impl<T> WriteStd430 for T
253where
254    T: AsStd430,
255{
256    fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
257        writer.write_std430(&self.as_std430())
258    }
259
260    fn std430_size(&self) -> usize {
261        size_of::<<Self as AsStd430>::Output>()
262    }
263}
264
265#[cfg(feature = "std")]
266impl<T> WriteStd430 for [T]
267where
268    T: WriteStd430,
269{
270    fn write_std430<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
271        let mut offset = writer.len();
272
273        let mut iter = self.iter();
274
275        if let Some(item) = iter.next() {
276            offset = item.write_std430(writer)?;
277        }
278
279        for item in iter {
280            item.write_std430(writer)?;
281        }
282
283        Ok(offset)
284    }
285
286    fn std430_size(&self) -> usize {
287        let mut writer = Writer::new(io::sink());
288        self.write_std430(&mut writer).unwrap();
289        writer.len()
290    }
291}