spirv_std/
runtime_array.rs

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