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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#[cfg(target_arch = "spirv")]
use core::arch::asm;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};

/// Explicit (uniform/storage) buffer handle for descriptor indexing.
///
/// Examples (for an `#[spirv(storage_buffer)]`-annotated entry-point parameter):
/// - `buffer: &[u32]` (implicit, 1 buffer)
/// - `buffer: &TypedBuffer<[u32]>` (explicit, one buffer)
/// - `buffers: &RuntimeArray<TypedBuffer<[u32]>>` (explicit, many buffers)
//
// TODO(eddyb) fully document!
#[spirv(typed_buffer)]
// HACK(eddyb) avoids "transparent newtype of `_anti_zst_padding`" misinterpretation.
#[repr(C)]
pub struct TypedBuffer<T: ?Sized> {
    // 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>,
}

impl<T> Deref for TypedBuffer<T> {
    type Target = T;
    #[spirv_std_macros::gpu_only]
    fn deref(&self) -> &T {
        unsafe {
            let mut result_slot = core::mem::MaybeUninit::uninit();
            asm! {
                "%uint = OpTypeInt 32 0",
                "%uint_0 = OpConstant %uint 0",
                "%result = OpAccessChain _ {buffer} %uint_0",
                "OpStore {result_slot} %result",
                buffer = in(reg) self,
                result_slot = in(reg) result_slot.as_mut_ptr(),
            }
            result_slot.assume_init()
        }
    }
}

impl<T> DerefMut for TypedBuffer<T> {
    #[spirv_std_macros::gpu_only]
    fn deref_mut(&mut self) -> &mut T {
        unsafe {
            let mut result_slot = core::mem::MaybeUninit::uninit();
            asm! {
                "%uint = OpTypeInt 32 0",
                "%uint_0 = OpConstant %uint 0",
                "%result = OpAccessChain _ {buffer} %uint_0",
                "OpStore {result_slot} %result",
                buffer = in(reg) self,
                result_slot = in(reg) result_slot.as_mut_ptr(),
            }
            result_slot.assume_init()
        }
    }
}

impl<T> Deref for TypedBuffer<[T]> {
    type Target = [T];
    #[spirv_std_macros::gpu_only]
    fn deref(&self) -> &[T] {
        unsafe {
            let mut result_slot = core::mem::MaybeUninit::uninit();
            asm! {
                "%uint = OpTypeInt 32 0",
                "%uint_0 = OpConstant %uint 0",
                "%inner_ptr = OpAccessChain _ {buffer} %uint_0",
                "%inner_len = OpArrayLength %uint {buffer} 0",
                "%result = OpCompositeConstruct typeof*{result_slot} %inner_ptr %inner_len",
                "OpStore {result_slot} %result",
                buffer = in(reg) self,
                result_slot = in(reg) &mut result_slot,
            }
            result_slot.assume_init()
        }
    }
}

impl<T> DerefMut for TypedBuffer<[T]> {
    #[spirv_std_macros::gpu_only]
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe {
            let mut result_slot = core::mem::MaybeUninit::uninit();
            asm! {
                "%uint = OpTypeInt 32 0",
                "%uint_0 = OpConstant %uint 0",
                "%inner_ptr = OpAccessChain _ {buffer} %uint_0",
                "%inner_len = OpArrayLength %uint {buffer} 0",
                "%result = OpCompositeConstruct typeof*{result_slot} %inner_ptr %inner_len",
                "OpStore {result_slot} %result",
                buffer = in(reg) self,
                result_slot = in(reg) &mut result_slot,
            }
            result_slot.assume_init()
        }
    }
}