spirv_std/
runtime_array.rs

1#[cfg(target_arch = "spirv")]
2use core::arch::asm;
3use core::marker::PhantomData;
4
5/// Dynamically-sized arrays in Rust carry around their length as the second half of a tuple.
6/// Unfortunately, sometimes SPIR-V provides an unsized array with no way of obtaining its length.
7/// Hence, this type represents something very similar to a slice, but with no way of knowing its
8/// length.
9#[spirv(runtime_array)]
10// HACK(eddyb) avoids "transparent newtype of `_anti_zst_padding`" misinterpretation.
11#[repr(C)]
12pub struct RuntimeArray<T> {
13    // HACK(eddyb) avoids the layout becoming ZST (and being elided in one way
14    // or another, before `#[spirv(runtime_array)]` can special-case it).
15    _anti_zst_padding: core::mem::MaybeUninit<u32>,
16    _phantom: PhantomData<T>,
17}
18
19// It would be nice to use the Index/IndexMut traits here, but because we do not have the length of
20// the array, it's impossible to make them be safe operations (indexing out of bounds), and
21// Index/IndexMut are marked as safe functions.
22impl<T> RuntimeArray<T> {
23    /// Index the array. Unfortunately, because the length of the runtime array cannot be known,
24    /// this function will happily index outside of the bounds of the array, and so is unsafe.
25    ///
26    /// # Safety
27    /// Bounds checking is not performed, and indexing outside the bounds of the array can happen,
28    /// and lead to UB.
29    #[spirv_std_macros::gpu_only]
30    pub unsafe fn index(&self, index: usize) -> &T {
31        // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this.
32        let mut result_slot = core::mem::MaybeUninit::uninit();
33        asm! {
34            "%result = OpAccessChain _ {arr} {index}",
35            "OpStore {result_slot} %result",
36            arr = in(reg) self,
37            index = in(reg) index,
38            result_slot = in(reg) result_slot.as_mut_ptr(),
39        }
40        result_slot.assume_init()
41    }
42
43    /// Index the array, returning a mutable reference to an element. Unfortunately, because the
44    /// length of the runtime array cannot be known, this function will happily index outside of
45    /// the bounds of the array, and so is unsafe.
46    ///
47    /// # Safety
48    /// Bounds checking is not performed, and indexing outside the bounds of the array can happen,
49    /// and lead to UB.
50    #[spirv_std_macros::gpu_only]
51    pub unsafe fn index_mut(&mut self, index: usize) -> &mut T {
52        // FIXME(eddyb) `let mut result = T::default()` uses (for `asm!`), with this.
53        let mut result_slot = core::mem::MaybeUninit::uninit();
54        asm! {
55            "%result = OpAccessChain _ {arr} {index}",
56            "OpStore {result_slot} %result",
57            arr = in(reg) self,
58            index = in(reg) index,
59            result_slot = in(reg) result_slot.as_mut_ptr(),
60        }
61        result_slot.assume_init()
62    }
63}