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

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

Creating writing and reading from buffer wgpu

问题

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

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

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

  1. 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.

  1. let array_buffer = device.create_buffer(&amp;wgpu::BufferDescriptor{
  2. label: Some(&quot;gpu_test&quot;),
  3. size: (arr.len() * std::mem::size_of::&lt;[i32; 5]&gt;()) as u64,
  4. usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
  5. mapped_at_creation: false,
  6. });

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

  1. 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

  1. let (sender, receiver) = futures_channel::oneshot::channel();
  2. temp_buffer
  3. .slice(..)
  4. .map_async(wgpu::MapMode::Read, |result| {
  5. let _ = sender.send(result);
  6. });
  7. device.poll(wgpu::Maintain::Wait); // TODO: poll in the background instead of blocking
  8. receiver
  9. .await
  10. .expect("communication failed")
  11. .expect("buffer reading failed");
  12. 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:

  1. let (sender, receiver) = futures_channel::oneshot::channel();
  2. temp_buffer
  3. .slice(..)
  4. .map_async(wgpu::MapMode::Read, |result| {
  5. let _ = sender.send(result);
  6. });
  7. device.poll(wgpu::Maintain::Wait); // TODO: poll in the background instead of blocking
  8. receiver
  9. .await
  10. .expect(&quot;communication failed&quot;)
  11. .expect(&quot;buffer reading failed&quot;);
  12. 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:

确定