你好,我最近开始学习WGPU的计算着色器,我在弄清楚如何正确地将数据写入缓冲区方面没有什么困难.

我用下面的代码创建了一个映射到CPU的小缓冲区.

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

我假设这为我提供了一个可在CPU上访问的缓冲区,现在我使用此行代码将其写入其中.

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

之后,我从缓冲区中读出数据

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

我的输出现在是Some([0, 0, 0, 0]),在阅读文档后,我认为这是正常行为,因为文档说"WRITE不是立即提交的,而是在内部排队,等待下一个submit()调用开始时发生." 问题是,它并没有真正说明如何提交我试图参选的内容.

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

还有几个queue.submit();的不同变体,但输出仍然是Some([0, 0, 0, 0]),所以如果有人能指出我做得不正确的地方,我将非常感激.

复制的完整代码:

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

推荐答案

您必须映射缓冲区after writing it.不能只永久映射缓冲区;CPU和GPU必须轮流访问缓冲区.

现在,您正在使用由mapped_at_creation: true创建的映射,但该映射只允许您向其添加write个数据,或者读取您以这种方式写入的数据,因此在本例中它始终为零.为了读取在GPU上写入的数据(例如通过在队列中执行write_buffer()命令),您必须在写入之后使用.map_async().

必要的更改包括:

  1. mapped_at_creation: true更改为False,因为您实际上并没有使用它.(如果您愿意,您可以在write_buffer()调用中使用该映射instead of,但比这更简单的是DeviceExt::create_buffer_init().)
  2. submit()之后,添加一个到map_async()的呼叫,然后是poll(Maintain::Wait).然后,缓冲区将被映射为读取,您可以读取它.
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();
}

输出:

Some([1, 2, 3, 4])

Rust相关问答推荐

为什么函数不接受选项T参数的所有权?

如何容器化Linux上基于Rust的Windows应用程序的编译过程?

我怎样才能从一个Rust 的日期中go 掉3年?

这种获取-释放关系是如何运作的?

Box::new()会从一个堆栈复制到另一个堆吗?

在决定使用std::Sync::Mutex还是使用Tokio::Sync::Mutex时,操作系统线程调度是考虑因素吗?

写入引用会更新基础值,但引用会打印意外的值

在复制类型中使用std::ptr::WRITE_VILAR进行内部可变性的安全性(即没有UnSafeCell)

在 Rust 中,在需要引用 self 的 struct 体方法中使用闭包作为 while 循环条件

如何以与平台无关的方式将OsString转换为utf-8编码的字符串?

如何正确使用git2::Remote::push?

存储返回 impl Trait 作为特征对象的函数

中文优化标题:跳出特定循环并返回一个值

将多维数组转换为切片

为什么Rust编译器会忽略模板参数应具有静态生命周期?

Rust编译器通过哪些规则来确保锁被释放?

在 Rust 中,Weak 如何知道内部值何时被删除?

以 `static` 为前缀的闭包是什么意思?我什么时候使用它?

如何将参数传递给Rust 的线程?

这个 match 语句的默认值应该是什么,还有一种方法可以解开 Some case (chess in rust)