bevy_crevice/std140/
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::std140::Writer;
9
10/// Trait implemented for all `std140` primitives. Generally should not be
11/// implemented outside this crate.
12pub unsafe trait Std140: 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    /// `AsStd140` 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 (Std140Padded specialization)
27    /// The usual implementation is
28    /// type Padded = Std140Padded<Self, {align_offset(size_of::<Self>(), max(16, ALIGNMENT))}>;
29    type Padded: Std140Convertible<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
42/// Trait specifically for Std140::Padded, implements conversions between padded type and base type.
43pub trait Std140Convertible<T: Std140>: Copy {
44    /// Convert from self to Std140
45    fn into_std140(self) -> T;
46    /// Convert from Std140 to self
47    fn from_std140(_: T) -> Self;
48}
49
50impl<T: Std140> Std140Convertible<T> for T {
51    fn into_std140(self) -> T {
52        self
53    }
54    fn from_std140(also_self: T) -> Self {
55        also_self
56    }
57}
58
59/// Unfortunately, we cannot easily derive padded representation for generic Std140 types.
60/// For now, we'll just use this empty enum with no values.
61#[derive(Copy, Clone)]
62pub enum InvalidPadded {}
63impl<T: Std140> Std140Convertible<T> for InvalidPadded {
64    fn into_std140(self) -> T {
65        unimplemented!()
66    }
67    fn from_std140(_: T) -> Self {
68        unimplemented!()
69    }
70}
71/**
72Trait implemented for all types that can be turned into `std140` values.
73*
74This trait can often be `#[derive]`'d instead of manually implementing it. Any
75struct which contains only fields that also implement `AsStd140` can derive
76`AsStd140`.
77
78Types from the mint crate implement `AsStd140`, making them convenient for use
79in uniform types. Most Rust math crates, like cgmath, nalgebra, and
80ultraviolet support mint.
81
82## Example
83
84```glsl
85uniform CAMERA {
86    mat4 view;
87    mat4 projection;
88} camera;
89```
90
91```no_run
92use bevy_crevice::std140::{AsStd140, Std140};
93
94#[derive(AsStd140)]
95struct CameraUniform {
96    view: mint::ColumnMatrix4<f32>,
97    projection: mint::ColumnMatrix4<f32>,
98}
99
100let view: mint::ColumnMatrix4<f32> = todo!("your math code here");
101let projection: mint::ColumnMatrix4<f32> = todo!("your math code here");
102
103let camera = CameraUniform {
104    view,
105    projection,
106};
107
108# fn write_to_gpu_buffer(bytes: &[u8]) {}
109let camera_std140 = camera.as_std140();
110write_to_gpu_buffer(camera_std140.as_bytes());
111```
112*/
113pub trait AsStd140 {
114    /// The `std140` version of this value.
115    type Output: Std140;
116
117    /// Convert this value into the `std140` version of itself.
118    fn as_std140(&self) -> Self::Output;
119
120    /// Returns the size of the `std140` version of this type. Useful for
121    /// pre-sizing buffers.
122    fn std140_size_static() -> usize {
123        size_of::<Self::Output>()
124    }
125
126    /// Converts from `std140` version of self to self.
127    fn from_std140(val: Self::Output) -> Self;
128}
129
130impl<T> AsStd140 for T
131where
132    T: Std140,
133{
134    type Output = Self;
135
136    fn as_std140(&self) -> Self {
137        *self
138    }
139
140    fn from_std140(x: Self) -> Self {
141        x
142    }
143}
144
145#[doc(hidden)]
146#[derive(Copy, Clone, Debug)]
147pub struct Std140Padded<T: Std140, const PAD: usize> {
148    inner: T,
149    _padding: [u8; PAD],
150}
151
152unsafe impl<T: Std140, const PAD: usize> Zeroable for Std140Padded<T, PAD> {}
153unsafe impl<T: Std140, const PAD: usize> Pod for Std140Padded<T, PAD> {}
154
155impl<T: Std140, const PAD: usize> Std140Convertible<T> for Std140Padded<T, PAD> {
156    fn into_std140(self) -> T {
157        self.inner
158    }
159
160    fn from_std140(inner: T) -> Self {
161        Self {
162            inner,
163            _padding: [0u8; PAD],
164        }
165    }
166}
167
168#[doc(hidden)]
169#[derive(Copy, Clone, Debug)]
170#[repr(transparent)]
171pub struct Std140Array<T: Std140, const N: usize>([T::Padded; N]);
172
173unsafe impl<T: Std140, const N: usize> Zeroable for Std140Array<T, N> where T::Padded: Zeroable {}
174unsafe impl<T: Std140, const N: usize> Pod for Std140Array<T, N> where T::Padded: Pod {}
175unsafe impl<T: Std140, const N: usize> Std140 for Std140Array<T, N>
176where
177    T::Padded: Pod,
178{
179    const ALIGNMENT: usize = crate::internal::max(T::ALIGNMENT, 16);
180    type Padded = Self;
181}
182
183impl<T: Std140, const N: usize> Std140Array<T, N> {
184    fn uninit_array() -> [MaybeUninit<T::Padded>; N] {
185        unsafe { MaybeUninit::uninit().assume_init() }
186    }
187
188    fn from_uninit_array(a: [MaybeUninit<T::Padded>; N]) -> Self {
189        unsafe { core::mem::transmute_copy(&a) }
190    }
191}
192
193impl<T: AsStd140, const N: usize> AsStd140 for [T; N]
194where
195    <T::Output as Std140>::Padded: Pod,
196{
197    type Output = Std140Array<T::Output, N>;
198    fn as_std140(&self) -> Self::Output {
199        let mut res = Self::Output::uninit_array();
200
201        for i in 0..N {
202            res[i] = MaybeUninit::new(Std140Convertible::from_std140(self[i].as_std140()));
203        }
204
205        Self::Output::from_uninit_array(res)
206    }
207
208    fn from_std140(val: Self::Output) -> Self {
209        let mut res: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
210        for i in 0..N {
211            res[i] = MaybeUninit::new(T::from_std140(Std140Convertible::into_std140(val.0[i])));
212        }
213        unsafe { core::mem::transmute_copy(&res) }
214    }
215}
216
217/// Trait implemented for all types that can be written into a buffer as
218/// `std140` bytes. This type is more general than [`AsStd140`]: all `AsStd140`
219/// types implement `WriteStd140`, but not the other way around.
220///
221/// While `AsStd140` requires implementers to return a type that implements the
222/// `Std140` trait, `WriteStd140` directly writes bytes using a [`Writer`]. This
223/// makes `WriteStd140` usable for writing slices or other DSTs that could not
224/// implement `AsStd140` without allocating new memory on the heap.
225#[cfg(feature = "std")]
226pub trait WriteStd140 {
227    /// Writes this value into the given [`Writer`] using `std140` layout rules.
228    ///
229    /// Should return the offset of the first byte of this type, as returned by
230    /// the first call to [`Writer::write`].
231    fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize>;
232
233    /// The space required to write this value using `std140` layout rules. This
234    /// does not include alignment padding that may be needed before or after
235    /// this type when written as part of a larger buffer.
236    fn std140_size(&self) -> usize {
237        let mut writer = Writer::new(io::sink());
238        self.write_std140(&mut writer).unwrap();
239        writer.len()
240    }
241}
242
243#[cfg(feature = "std")]
244impl<T> WriteStd140 for T
245where
246    T: AsStd140,
247{
248    fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
249        writer.write_std140(&self.as_std140())
250    }
251
252    fn std140_size(&self) -> usize {
253        size_of::<<Self as AsStd140>::Output>()
254    }
255}
256
257#[cfg(feature = "std")]
258impl<T> WriteStd140 for [T]
259where
260    T: WriteStd140,
261{
262    fn write_std140<W: Write>(&self, writer: &mut Writer<W>) -> io::Result<usize> {
263        // if no items are written, offset is current position of the writer
264        let mut offset = writer.len();
265
266        let mut iter = self.iter();
267
268        if let Some(item) = iter.next() {
269            offset = item.write_std140(writer)?;
270        }
271
272        for item in iter {
273            item.write_std140(writer)?;
274        }
275
276        Ok(offset)
277    }
278
279    fn std140_size(&self) -> usize {
280        let mut writer = Writer::new(io::sink());
281        self.write_std140(&mut writer).unwrap();
282        writer.len()
283    }
284}