Skip to content

Commit 07657e9

Browse files
authored
fix: unaligned read for Metal buffers (#882)
After Rust 1.77 `u128` starts requiring 16 bytes aligned values. This breaks Metal, where `retrieve_contents` assumed it could just cast the pointer and treat it as a slice. Instead, we need to assume no alignment by doing the following: 1. Read the `i-th` element from the buffer with `ptr.add(i).read_unaligned()`; 2. `clone` the read value, as the bitwise copy may cause aliasing otherwise; 3. `push` the `clone`d value into the vector; 4. `mem::forget` the element we read to avoid calling its `drop` implementation twice, one for the copy and one for the `Buffer`.
1 parent 2a68c19 commit 07657e9

File tree

1 file changed

+11
-4
lines changed

1 file changed

+11
-4
lines changed

gpu/src/metal/abstractions/state.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,21 @@ impl MetalState {
9595
/// Returns a vector of a copy of the data that `buffer` holds, interpreting it into a specific
9696
/// type `T`.
9797
///
98-
/// BEWARE: this function uses an unsafe function for retrieveing the data, if the buffer's
98+
/// SAFETY: this function uses an unsafe function for retrieveing the data, if the buffer's
9999
/// contents don't match the specified `T`, expect undefined behaviour. Always make sure the
100100
/// buffer you are retreiving from holds data of type `T`.
101101
pub fn retrieve_contents<T: Clone>(buffer: &Buffer) -> Vec<T> {
102102
let ptr = buffer.contents() as *const T;
103103
let len = buffer.length() as usize / mem::size_of::<T>();
104-
let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
105-
106-
slice.to_vec()
104+
let mut contents = Vec::with_capacity(len);
105+
for i in 0..len {
106+
// 1. Read possibly unaligned data producing a bitwise copy
107+
let val = unsafe { ptr.add(i).read_unaligned() };
108+
// 2. Clone into the vector to avoid both `contents` and `buffer` dropping it
109+
contents.push(val.clone());
110+
// 3. Forget the bitwise copy to avoid both `val` and `buffer` dropping it
111+
core::mem::forget(val);
112+
}
113+
contents
107114
}
108115
}

0 commit comments

Comments
 (0)