2

Hello I have recently began learning compute shaders with wgpu and I'm having little difficulties figuring out how to correctly write data into a buffer.

I have created a small buffer mapped to the cpu with this code.

    let array_buffer = device.create_buffer(&wgpu::BufferDescriptor{
        label: Some("gpu_test"),
        size: 4,
        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
        mapped_at_creation: true,
    });

I assume this gives me a buffer accessible on a CPU which I now write into using this line of code.

queue.write_buffer(&array_buffer, 0, &[1,2,3,4]);

After which I read off the data off the buffer

    let slice = array_buffer.slice(..).get_mapped_range();
    println!("{:?}", slice.get(0..4));

My output is now Some([0, 0, 0, 0]) which after reading the docs I assume is normal behavier as docs say "Write is not immediately submitted, and instead enqueued internally to happen at the start of the next submit() call." The problem is that it doesn't really say how to submit I have attempted to run.

    queue.write_buffer(&array_buffer, 0, &[1,2,3,4]);
    queue.submit([]);

And a couple different variations of queue.submit(); but the output is still as it was Some([0, 0, 0, 0]) so if anyone could point at what am I not doing correctly I would be very thankful.

Full code for replication:

use wgpu;
use pollster;

fn main() {
    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor{
        backends: wgpu::Backends::all(),
        dx12_shader_compiler: Default::default(),
    });

    let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();
    // connect to gpu
    let (device, queue) = pollster::block_on(adapter.request_device(&Default::default(), None)).unwrap();

    let array_buffer = device.create_buffer(&wgpu::BufferDescriptor{
        label: Some("gpu_test"),
        size: 4,
        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
        mapped_at_creation: true,
    });

    queue.write_buffer(&array_buffer, 0, &[1,2,3,4]);
    queue.submit([]);

    let slice = array_buffer.slice(..).get_mapped_range();
    println!("{:?}", slice.get(0..4));

    drop(slice);
    array_buffer.unmap();
}
LAYTOR
  • 381
  • 4
  • 12
  • 1
    As I wrote [previously](https://stackoverflow.com/questions/76839881/creating-writing-and-reading-from-buffer-wgpu/76842198?noredirect=1#comment135471088_76842198), please create a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). The bug you are experiencing isn't in the code you've posted. We need code that is complete enough to be run and debugged. – Kevin Reid Aug 06 '23 at 14:53
  • I'm sorry just one question for me to better understand. Complete section is saying "Provide all parts someone else needs to reproduce your problem in the question itself" So does it want a code block that you can just copy, and past in your editor and it will give you the error? Because I assumed it just wanted the part of the code that is causing an error. I just want to get a yes or no if I got what I was doing incorrectly sorry for wasting your time. – LAYTOR Aug 06 '23 at 22:50
  • 1
    The full code you posted in your latest edit is exactly what is needed here. The only thing you could have done to improve it even more is specifying the version of your dependencies (e.g. `wgpu = "0.17.0"`, `pollster = "0.3.0"`). – frankenapps Aug 07 '23 at 15:24
  • @LAYTOR Thank you for updating your question. The reasons reproducible examples are so important are (1) often the problem is in a different part of the code than you think, (2) sometimes (like in this case) the problem is code that is *missing*, and (3) it's valuable for people to be able to run the code themselves — to bypass human error in explaining the problem or in understanding the code, and to be able to test a proposed fix. – Kevin Reid Aug 07 '23 at 15:56

1 Answers1

2

You must map the buffer after writing it. You can't just have a buffer perpetually mapped; the CPU and GPU must take turns accessing the buffer.

Right now, you're making use of the mapping created by mapped_at_creation: true, but that mapping only allows you to write data to it, or read the data you wrote that way, so in this case it is always zeroes. In order to read data that was written on the GPU (such as by executing a write_buffer() command in the queue), you must use .map_async() after the write.

The necessary changes are:

  1. Change mapped_at_creation: true to false, since you're not actually using it. (You could use that mapping instead of the write_buffer() call, if you wanted, but even simpler than that is DeviceExt::create_buffer_init().)
  2. After the submit(), add a call to map_async() followed by poll(Maintain::Wait). Then the buffer will be mapped for reading and you can read it.
fn main() {
    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
        backends: wgpu::Backends::all(),
        dx12_shader_compiler: Default::default(),
    });

    let adapter = pollster::block_on(instance.request_adapter(&Default::default())).unwrap();
    // connect to gpu
    let (device, queue) =
        pollster::block_on(adapter.request_device(&Default::default(), None)).unwrap();

    let array_buffer = device.create_buffer(&wgpu::BufferDescriptor {
        label: Some("gpu_test"),
        size: 4,
        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
        mapped_at_creation: false,
    });

    queue.write_buffer(&array_buffer, 0, &[1, 2, 3, 4]);
    queue.submit([]);

    array_buffer
        .slice(..)
        .map_async(wgpu::MapMode::Read, |result| {
            // In a real program this should be a message channel of some sort.
            // Unwrapping here will, if it fails, panic in whichever thread next
            // calls poll(), which isn't good error handling.
            result.unwrap();
        });
    device.poll(wgpu::Maintain::Wait);

    let slice = array_buffer.slice(..).get_mapped_range();
    println!("{:?}", slice.get(0..4));

    drop(slice);
    array_buffer.unmap();
}

Output:

Some([1, 2, 3, 4])
Kevin Reid
  • 37,492
  • 13
  • 80
  • 108