Skip to content

Commit 2f5cbe4

Browse files
committed
fix(examples): use pod_collect_to_vec for mapped buffer reads
Replace bytemuck::cast_slice with bytemuck::allocation::pod_collect_to_vec when reading from mapped buffer ranges. cast_slice requires the source slice to be properly aligned for the target type, but BufferView from get_mapped_range() is not guaranteed to meet alignment requirements. pod_collect_to_vec copies bytes into a new properly-aligned Vec, avoiding potential undefined behavior from misaligned reads. Closes #6191
1 parent 3a8b7ea commit 2f5cbe4

8 files changed

Lines changed: 16 additions & 10 deletions

File tree

examples/features/src/big_compute_buffers/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ pub async fn execute_gpu_inner(
8686
for staging_buffer in &staging_buffers {
8787
let slice = staging_buffer.slice(..);
8888
let mapped = slice.get_mapped_range();
89-
data.extend_from_slice(bytemuck::cast_slice(&mapped));
89+
let chunk: Vec<f32> = bytemuck::allocation::pod_collect_to_vec(&mapped);
90+
data.extend_from_slice(&chunk);
9091
drop(mapped);
9192
staging_buffer.unmap();
9293
}

examples/features/src/cooperative_matrix/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,10 +415,10 @@ async fn execute(
415415

416416
// Convert result back to f32 for comparison
417417
let result: Vec<f32> = if use_f16 {
418-
let result_f16: &[f16] = bytemuck::cast_slice(&data);
418+
let result_f16: Vec<f16> = bytemuck::allocation::pod_collect_to_vec(&data);
419419
result_f16.iter().map(|x| x.to_f32()).collect()
420420
} else {
421-
bytemuck::cast_slice::<_, f32>(&data).to_vec()
421+
bytemuck::allocation::pod_collect_to_vec(&data)
422422
};
423423

424424
// Compute reference result on CPU for verification

examples/features/src/hello_synchronization/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ async fn get_data<T: bytemuck::Pod>(
184184
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
185185
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
186186
receiver.recv_async().await.unwrap().unwrap();
187-
output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));
187+
let data: Vec<T> =
188+
bytemuck::allocation::pod_collect_to_vec(&buffer_slice.get_mapped_range()[..]);
189+
output.copy_from_slice(&data);
188190
staging_buffer.unmap();
189191
}
190192

examples/features/src/hello_workgroups/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ async fn get_data<T: bytemuck::Pod>(
173173
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
174174
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
175175
receiver.recv_async().await.unwrap().unwrap();
176-
output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));
176+
let data: Vec<T> =
177+
bytemuck::allocation::pod_collect_to_vec(&buffer_slice.get_mapped_range()[..]);
178+
output.copy_from_slice(&data);
177179
staging_buffer.unmap();
178180
}
179181

examples/features/src/repeated_compute/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ async fn compute(local_buffer: &mut [u32], context: &WgpuContext) {
116116
// NOW we can call get_mapped_range.
117117
{
118118
let view = buffer_slice.get_mapped_range();
119-
local_buffer.copy_from_slice(bytemuck::cast_slice(&view));
119+
let data: Vec<u32> = bytemuck::allocation::pod_collect_to_vec(&view);
120+
local_buffer.copy_from_slice(&data);
120121
}
121122
log::info!("Results written to local buffer.");
122123
// We need to make sure all `BufferView`'s are dropped before we do what we're about

examples/features/src/timestamp_queries/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl Queries {
182182
.destination_buffer
183183
.slice(..(size_of::<u64>() as wgpu::BufferAddress * self.num_queries))
184184
.get_mapped_range();
185-
bytemuck::cast_slice(&timestamp_view).to_vec()
185+
bytemuck::allocation::pod_collect_to_vec(&timestamp_view)
186186
};
187187

188188
self.destination_buffer.unmap();

examples/standalone/01_hello_compute/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ rust-version = "1.87"
55
publish = false
66

77
[dependencies]
8-
bytemuck = "1.22.0"
8+
bytemuck = { version = "1.22.0", features = ["extern_crate_alloc"] }
99
env_logger = "0.11"
1010
pollster = "0.4"
1111
wgpu = "29.0.0"

examples/standalone/01_hello_compute/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ fn main() {
246246

247247
// We can now read the data from the buffer.
248248
let data = buffer_slice.get_mapped_range();
249-
// Convert the data back to a slice of f32.
250-
let result: &[f32] = bytemuck::cast_slice(&data);
249+
// Convert the data back to f32 via an aligned copy.
250+
let result: Vec<f32> = bytemuck::allocation::pod_collect_to_vec(&data);
251251

252252
// Print out the result.
253253
println!("Result: {result:?}");

0 commit comments

Comments
 (0)