-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Description
When mapping a buffer, wgpu can give a slice to memory that does not behave correctly with Rust atomics on x86-64, probably because it uses write-combining memory.
Repro steps
use std::sync::atomic::{AtomicPtr, AtomicU64, Ordering};
fn test(mem: &mut u8) {
const ITERS: u64 = 10_000;
let counter = AtomicU64::new(0);
let ptr = AtomicPtr::new(mem);
std::thread::scope(|s| {
s.spawn(|| {
let ptr = ptr.load(Ordering::Relaxed);
let mut value = 0;
while value < ITERS {
value += 1;
unsafe {
ptr.write(value as u8);
}
counter.store(value, Ordering::Release);
while !counter.load(Ordering::Acquire).is_multiple_of(2) {}
value += 1;
}
});
s.spawn(|| {
let ptr = ptr.load(Ordering::Relaxed);
let mut value = 0;
while value < ITERS {
while counter.load(Ordering::Acquire).is_multiple_of(2) {}
value += 1;
unsafe {
assert_eq!(ptr.read(), value as u8);
}
value += 1;
counter.store(value, Ordering::Release);
}
});
})
}
fn main() {
// This works
test(&mut 0);
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
let adapter_options = wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
..Default::default()
};
let adapter = pollster::block_on(instance.request_adapter(&adapter_options)).unwrap();
let (device, _queue) =
pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor::default())).unwrap();
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: wgpu::COPY_BUFFER_ALIGNMENT,
usage: wgpu::BufferUsages::MAP_WRITE,
mapped_at_creation: true,
});
let mut buffer = buffer.get_mapped_range_mut(..);
// This doesn't
test(&mut buffer[0]);
}The first thread writes sucessive odd numbers to the memory, and the second thread reads them. The raw pointer operations should be synchronised by the acquire/release operations, and the test sequence passes miri with regular memory.
Note that the sequence can be rewritten using relaxed atomic operations using AtomicU8::from_ptr, or without any unsafe code using AtomicU8::from_mut (unstable).
Expected vs observed behavior
The test function should exit successfully for both regular and wgpu mapped memory. However, with wgpu memory the test panics with:
assertion `left == right` failed
left: 0
right: 1
Platform
wgpu 28.0.0 on x86-64 Linux with Vulkan backend, Nvidia Pascal GPU with proprietary drivers. I suspect that this will reproduce on any discrete GPU with mappable device-local memory.