0

I have just recently started learning about how to compute on a GPU and I have decided to start with WGPU as I'm familiar with rust and it can be run on pretty much every GPU. As far as my current understanding goes first I have to create a buffer that is accessible to my CPU, which I have done with the following code.

    let array_buffer = device.create_buffer(&wgpu::BufferDescriptor{
        label: Some("gpu_test"),
        size: (arr.len() * std::mem::size_of::<[i32; 5]>()) as u64,
        usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
        mapped_at_creation: false,
    });

After that I have wrote some random data to this buffer with the following line.

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

As of right now I have no errors but the problem appears when I now want to read the data in this buffer, there is no example of how to read the data off a buffer in wgpu docs and I didn't see one in webGPU docs too.

In addition how do I know if the buffer is accessible on CPU or GPU webGPU docs talk about it but they don't have an explicate example of how to define a buffer for each one.

LAYTOR
  • 381
  • 4
  • 12

1 Answers1

2

First, in order to be able to read a buffer, it must have BufferUsages::MAP_READ. You've already done that. If you wanted to read a buffer that can't have that usage, you'd need to copy data to a temporary buffer first.

Given that prerequisite, the steps are:

  1. Call BufferView::map_async()
  2. Call Device::poll()
  3. Confirm that the map_async() callback has been called with a successful Result
  4. Call BufferView::get_mapped_range()

Here's the code I've used for that, which maps the buffer in the variable temp_buffer:

let (sender, receiver) = futures_channel::oneshot::channel();
temp_buffer
    .slice(..)
    .map_async(wgpu::MapMode::Read, |result| {
        let _ = sender.send(result);
    });
device.poll(wgpu::Maintain::Wait); // TODO: poll in the background instead of blocking
receiver
    .await
    .expect("communication failed")
    .expect("buffer reading failed");
let slice: &[u8] = &temp_buffer.slice(..).get_mapped_range();

Note that as per the TODO, this particular code is a work in progress halfway between two sensible states:

  • If you intend only to block on the buffer being ready to read, then you don't need a channel, just a OnceCell because the message will always have arrived when poll(Maintain::Wait) returns.
  • If you want to be truly async, then something needs to be calling either poll(Maintain::Poll) or Queue::submit() repeatedly in the background to trigger checking for completion and thus the callback.
Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
  • So I have done what you said and I do get an output now, but it is all 0 I'm going to update the post with new code if you don't mind tell me what I have done wrong. – LAYTOR Aug 06 '23 at 00:11
  • 1
    @LAYTOR Sorry, I don't see any problems in that small snippet. It's not good to change what your question is after it's been answered — I get that you want a working solution, but the question was “how is this done”, not “spot the bug in…”. Please consider constructing a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) and posting it as a new question. – Kevin Reid Aug 06 '23 at 02:28
  • Thank you, I will create a new question it is just that I had wanted to get an answer which could be used as reference by people who are new to shaders and have decides to start learning wgpu first. As docs don't really talk about buffers I made this post on creating writhing and reading off the buffer. It is just that I hadn't realized that there was another bug in my code relating to a write operation, but just how you advised I will ask about it in a new post thank you for resolving my problem with reading from the buffer you saved me a lot of time! – LAYTOR Aug 06 '23 at 08:27