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