Skip to content

Commit 6413e4c

Browse files
committed
ByteAddressableBuffer: split up into read-only and mutable variants
1 parent e0198cf commit 6413e4c

File tree

2 files changed

+87
-32
lines changed

2 files changed

+87
-32
lines changed

crates/spirv-std/src/byte_addressable_buffer.rs

Lines changed: 83 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,74 +43,126 @@ unsafe fn buffer_store_intrinsic<T>(
4343
.write(value);
4444
}
4545

46-
/// `ByteAddressableBuffer` is an untyped blob of data, allowing loads and stores of arbitrary
47-
/// basic data types at arbitrary indices. However, all data must be aligned to size 4, each
48-
/// element within the data (e.g. struct fields) must have a size and alignment of a multiple of 4,
49-
/// and the `byte_index` passed to load and store must be a multiple of 4 (`byte_index` will be
50-
/// rounded down to the nearest multiple of 4). So, it's not technically a *byte* addressable
51-
/// buffer, but rather a *word* buffer, but this naming and behavior was inherited from HLSL (where
52-
/// it's UB to pass in an index not a multiple of 4).
46+
/// `ByteAddressableBuffer` is a read-only untyped blob of data, allowing loads
47+
/// of arbitrary basic data types at arbitrary indices. See
48+
/// [`MutByteAddressableBuffer`] for a writeable variant.
49+
///
50+
/// # Alignment
51+
/// All data must be aligned to size 4, each element within the data (e.g.
52+
/// struct fields) must have a size and alignment of a multiple of 4, and the
53+
/// `byte_index` passed to load and store must be a multiple of 4. Technically
54+
/// it's not a *byte* addressable buffer, but rather a *word* buffer, but this
55+
/// naming and behavior was inherited from HLSL (where it's UB to pass in an
56+
/// index not a multiple of 4).
57+
///
58+
/// # Safety
59+
/// Using these functions allows reading a different type from the buffer than
60+
/// was originally written (by [`MutByteAddressableBuffer`] or the host API),
61+
/// allowing all sorts of safety guarantees to be bypassed (effectively a
62+
/// transmute).
5363
#[repr(transparent)]
5464
pub struct ByteAddressableBuffer<'a> {
5565
/// The underlying array of bytes, able to be directly accessed.
56-
pub data: &'a mut [u32],
66+
pub data: &'a [u32],
5767
}
5868

5969
impl<'a> ByteAddressableBuffer<'a> {
70+
/// Creates a `ByteAddressableBuffer` from the untyped blob of data.
71+
#[inline]
72+
pub fn new(data: &'a [u32]) -> Self {
73+
Self { data }
74+
}
75+
76+
/// Loads an arbitrary type from the buffer. `byte_index` must be a
77+
/// multiple of 4.
78+
///
79+
/// # Safety
80+
/// See [`Self`].
81+
pub unsafe fn load<T>(&self, byte_index: u32) -> T {
82+
if byte_index % 4 != 0 {
83+
panic!("`byte_index` should be a multiple of 4");
84+
}
85+
if byte_index + mem::size_of::<T>() as u32 > self.data.len() as u32 {
86+
panic!(
87+
"index out of bounds: the len is {} but the `byte_index` is {}",
88+
self.data.len(),
89+
byte_index
90+
);
91+
}
92+
buffer_load_intrinsic(self.data, byte_index)
93+
}
94+
95+
/// Loads an arbitrary type from the buffer. `byte_index` must be a
96+
/// multiple of 4.
97+
///
98+
/// # Safety
99+
/// See [`Self`]. Additionally, bounds or alignment checking is not performed.
100+
pub unsafe fn load_unchecked<T>(&self, byte_index: u32) -> T {
101+
buffer_load_intrinsic(self.data, byte_index)
102+
}
103+
}
104+
105+
/// `MutByteAddressableBuffer` is a mutable untyped blob of data, allowing
106+
/// loads and stores of arbitrary basic data types at arbitrary indices. See
107+
/// [`ByteAddressableBuffer`] for details.
108+
#[repr(transparent)]
109+
pub struct MutByteAddressableBuffer<'a> {
110+
/// The underlying array of bytes, able to be directly accessed.
111+
pub data: &'a mut [u32],
112+
}
113+
114+
impl<'a> MutByteAddressableBuffer<'a> {
60115
/// Creates a `ByteAddressableBuffer` from the untyped blob of data.
61116
#[inline]
62117
pub fn new(data: &'a mut [u32]) -> Self {
63118
Self { data }
64119
}
65120

66-
/// Loads an arbitrary type from the buffer. `byte_index` must be a multiple of 4, otherwise,
67-
/// it will get silently rounded down to the nearest multiple of 4.
121+
/// Loads an arbitrary type from the buffer. `byte_index` must be a
122+
/// multiple of 4.
68123
///
69124
/// # Safety
70-
/// This function allows writing a type to an untyped buffer, then reading a different type
71-
/// from the same buffer, allowing all sorts of safety guarantees to be bypassed (effectively a
72-
/// transmute)
125+
/// See [`Self`].
73126
pub unsafe fn load<T>(&self, byte_index: u32) -> T {
127+
if byte_index % 4 != 0 {
128+
panic!("`byte_index` should be a multiple of 4");
129+
}
74130
if byte_index + mem::size_of::<T>() as u32 > self.data.len() as u32 {
75-
panic!("Index out of range");
131+
panic!(
132+
"index out of bounds: the len is {} but the `byte_index` is {}",
133+
self.data.len(),
134+
byte_index
135+
);
76136
}
77137
buffer_load_intrinsic(self.data, byte_index)
78138
}
79139

80-
/// Loads an arbitrary type from the buffer. `byte_index` must be a multiple of 4, otherwise,
81-
/// it will get silently rounded down to the nearest multiple of 4. Bounds checking is not
82-
/// performed.
140+
/// Loads an arbitrary type from the buffer. `byte_index` must be a
141+
/// multiple of 4.
83142
///
84143
/// # Safety
85-
/// This function allows writing a type to an untyped buffer, then reading a different type
86-
/// from the same buffer, allowing all sorts of safety guarantees to be bypassed (effectively a
87-
/// transmute). Additionally, bounds checking is not performed.
144+
/// See [`Self`]. Additionally, bounds or alignment checking is not performed.
88145
pub unsafe fn load_unchecked<T>(&self, byte_index: u32) -> T {
89146
buffer_load_intrinsic(self.data, byte_index)
90147
}
91148

92-
/// Stores an arbitrary type int the buffer. `byte_index` must be a multiple of 4, otherwise,
93-
/// it will get silently rounded down to the nearest multiple of 4.
149+
/// Stores an arbitrary type into the buffer. `byte_index` must be a
150+
/// multiple of 4.
94151
///
95152
/// # Safety
96-
/// This function allows writing a type to an untyped buffer, then reading a different type
97-
/// from the same buffer, allowing all sorts of safety guarantees to be bypassed (effectively a
98-
/// transmute)
153+
/// See [`Self`].
99154
pub unsafe fn store<T>(&mut self, byte_index: u32, value: T) {
100155
if byte_index + mem::size_of::<T>() as u32 > self.data.len() as u32 {
101156
panic!("Index out of range");
102157
}
103158
buffer_store_intrinsic(self.data, byte_index, value);
104159
}
105160

106-
/// Stores an arbitrary type int the buffer. `byte_index` must be a multiple of 4, otherwise,
107-
/// it will get silently rounded down to the nearest multiple of 4. Bounds checking is not
108-
/// performed.
161+
/// Stores an arbitrary type into the buffer. `byte_index` must be a
162+
/// multiple of 4.
109163
///
110164
/// # Safety
111-
/// This function allows writing a type to an untyped buffer, then reading a different type
112-
/// from the same buffer, allowing all sorts of safety guarantees to be bypassed (effectively a
113-
/// transmute). Additionally, bounds checking is not performed.
165+
/// See [`Self`]. Additionally, bounds or alignment checking is not performed.
114166
pub unsafe fn store_unchecked<T>(&mut self, byte_index: u32, value: T) {
115167
buffer_store_intrinsic(self.data, byte_index, value);
116168
}

crates/spirv-std/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,13 @@ pub mod vector;
112112

113113
pub use self::sampler::Sampler;
114114
pub use crate::macros::Image;
115-
pub use byte_addressable_buffer::ByteAddressableBuffer;
116115
pub use num_traits;
117116
pub use runtime_array::*;
118117
pub use typed_buffer::*;
118+
pub use {
119+
byte_addressable_buffer::ByteAddressableBuffer,
120+
byte_addressable_buffer::MutByteAddressableBuffer,
121+
};
119122

120123
pub use glam;
121124

0 commit comments

Comments
 (0)