创建从缓冲区写入和读取的wgpu。

huangapple go评论74阅读模式
英文:

Creating writing and reading from buffer wgpu

问题

我刚刚开始学习如何在GPU上进行计算,我决定从WGPU开始,因为我熟悉Rust并且它可以在几乎所有的GPU上运行。根据我目前的理解,首先我需要创建一个可以被CPU访问的缓冲区,我已经使用以下代码完成了这一步。

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,
});

之后,我使用以下代码将一些随机数据写入此缓冲区。

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

截止目前为止,我没有出现任何错误,但问题出现在我现在想要读取此缓冲区中的数据时,wgpu文档中没有关于如何从缓冲区读取数据的示例,而且我在WebGPU文档中也没有看到相关内容。

此外,我如何知道缓冲区是否可由CPU或GPU访问,WebGPU文档提到了这一点,但没有明确的示例说明如何为每个缓冲区定义它。

英文:

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(&amp;wgpu::BufferDescriptor{
        label: Some(&quot;gpu_test&quot;),
        size: (arr.len() * std::mem::size_of::&lt;[i32; 5]&gt;()) 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(&amp;array_buffer, 0, &amp;[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.

答案1

得分: 2

首先,为了能够读取缓冲区,它必须具有 BufferUsages::MAP_READ。你已经完成了这一步。如果你想要读取一个不能具有这个用途的缓冲区,你需要首先将数据复制到临时缓冲区。

在具备这个前提条件的情况下,以下是步骤:

  1. 调用 BufferView::map_async()
  2. 调用 Device::poll()
  3. 确认 map_async() 回调已经成功调用,并返回了成功的 Result
  4. 调用 BufferView::get_mapped_range()

以下是我用于执行这些操作的代码,它将缓冲区映射到变量 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();

请注意,根据 TODO 注释,这段特定的代码正处于两种合理状态之间的工作进展中:

  • 如果你只打算在缓冲区准备好读取时阻塞,那么你不需要一个 channel,只需要一个 OnceCell,因为在 poll(Maintain::Wait) 返回时消息总是已经到达了。
  • 如果你想要真正的异步操作,那么需要有一些东西在后台反复调用 poll(Maintain::Poll)Queue::submit(),以触发检查完成状态和回调的操作。
英文:

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(&quot;communication failed&quot;)
    .expect(&quot;buffer reading failed&quot;);
let slice: &amp;[u8] = &amp;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.

huangapple
  • 本文由 发表于 2023年8月5日 10:05:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76839881.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定