spirv_std/
typed_buffer.rs

1#[cfg(target_arch = "spirv")]
2use core::arch::asm;
3use core::marker::PhantomData;
4use core::ops::{Deref, DerefMut};
5
6/// Explicit (uniform/storage) buffer handle for descriptor indexing.
7///
8/// Examples (for an `#[spirv(storage_buffer)]`-annotated entry-point parameter):
9/// - `buffer: &[u32]` (implicit, 1 buffer)
10/// - `buffer: &TypedBuffer<[u32]>` (explicit, one buffer)
11/// - `buffers: &RuntimeArray<TypedBuffer<[u32]>>` (explicit, many buffers)
12//
13// TODO(eddyb) fully document!
14#[spirv(typed_buffer)]
15// HACK(eddyb) avoids "transparent newtype of `_anti_zst_padding`" misinterpretation.
16#[repr(C)]
17// HACK(eddyb) false positive due to `rustc` not understanding e.g. entry-points.
18#[allow(dead_code)]
19pub struct TypedBuffer<T: ?Sized> {
20    // HACK(eddyb) avoids the layout becoming ZST (and being elided in one way
21    // or another, before `#[spirv(runtime_array)]` can special-case it).
22    _anti_zst_padding: core::mem::MaybeUninit<u32>,
23    _phantom: PhantomData<T>,
24}
25
26impl<T> Deref for TypedBuffer<T> {
27    type Target = T;
28    #[spirv_std_macros::gpu_only]
29    fn deref(&self) -> &T {
30        unsafe {
31            let mut result_slot = core::mem::MaybeUninit::uninit();
32            asm! {
33                "%uint = OpTypeInt 32 0",
34                "%uint_0 = OpConstant %uint 0",
35                "%result = OpAccessChain _ {buffer} %uint_0",
36                "OpStore {result_slot} %result",
37                buffer = in(reg) self,
38                result_slot = in(reg) result_slot.as_mut_ptr(),
39            }
40            result_slot.assume_init()
41        }
42    }
43}
44
45impl<T> DerefMut for TypedBuffer<T> {
46    #[spirv_std_macros::gpu_only]
47    fn deref_mut(&mut self) -> &mut T {
48        unsafe {
49            let mut result_slot = core::mem::MaybeUninit::uninit();
50            asm! {
51                "%uint = OpTypeInt 32 0",
52                "%uint_0 = OpConstant %uint 0",
53                "%result = OpAccessChain _ {buffer} %uint_0",
54                "OpStore {result_slot} %result",
55                buffer = in(reg) self,
56                result_slot = in(reg) result_slot.as_mut_ptr(),
57            }
58            result_slot.assume_init()
59        }
60    }
61}
62
63impl<T> Deref for TypedBuffer<[T]> {
64    type Target = [T];
65    #[spirv_std_macros::gpu_only]
66    fn deref(&self) -> &[T] {
67        unsafe {
68            let mut result_slot = core::mem::MaybeUninit::uninit();
69            asm! {
70                "%uint = OpTypeInt 32 0",
71                "%uint_0 = OpConstant %uint 0",
72                "%inner_ptr = OpAccessChain _ {buffer} %uint_0",
73                "%inner_len = OpArrayLength %uint {buffer} 0",
74                "%result = OpCompositeConstruct typeof*{result_slot} %inner_ptr %inner_len",
75                "OpStore {result_slot} %result",
76                buffer = in(reg) self,
77                result_slot = in(reg) result_slot.as_mut_ptr(),
78            }
79            result_slot.assume_init()
80        }
81    }
82}
83
84impl<T> DerefMut for TypedBuffer<[T]> {
85    #[spirv_std_macros::gpu_only]
86    fn deref_mut(&mut self) -> &mut Self::Target {
87        unsafe {
88            let mut result_slot = core::mem::MaybeUninit::uninit();
89            asm! {
90                "%uint = OpTypeInt 32 0",
91                "%uint_0 = OpConstant %uint 0",
92                "%inner_ptr = OpAccessChain _ {buffer} %uint_0",
93                "%inner_len = OpArrayLength %uint {buffer} 0",
94                "%result = OpCompositeConstruct typeof*{result_slot} %inner_ptr %inner_len",
95                "OpStore {result_slot} %result",
96                buffer = in(reg) self,
97                result_slot = in(reg) result_slot.as_mut_ptr(),
98            }
99            result_slot.assume_init()
100        }
101    }
102}