Buffers readback
note
The code described in this section can be run from the
buffer_readback.rs example with
cargo run --features derive --example buffer_readback
. Complete source code is also provided at the
end of this page.
After our buffer have been initialized, and our compute kernels have run, you might need to read the results back to RAM for further processing on the CPU side. Reading the content of a GPU buffers require a few steps:
- Be sure that the buffer you want to read from was initialized with
BufferUsages::COPY_SRC
:
let buffer_to_read_from = GpuVector::init(
gpu.device(),
&buffer_data,
BufferUsages::STORAGE | BufferUsages::COPY_SRC,
);
- Be sure that a staging buffer was created with
BufferUsages::COPY_DST | BufferUsages::MAP_READ
. This is an intermediate buffer that can be mapped to the CPU-side virtual memory:
let staging_buffer = GpuVector::uninit(
gpu.device(),
buffer_data.len(),
BufferUsages::COPY_DST | BufferUsages::MAP_READ,
);
- Instruct the GPU to copy the content of your buffer into the staging buffer:
staging_buffer.copy_from(&mut encoder, &buffer_to_read_from);
- Read the staging buffer into a vector of typed values.
let buffer_read_from_gpu = staging.read(gpu.device()).await;
Note that the staging buffer can be uninitialized considering its will never be read before being written to
by staging_buffer.copy_from
. The length provided to GpuVector::uninit
is the number of (typed) elements
the buffer can contain, not the size in bytes.
Complete example
- main.rs
use nalgebra::DVector;
use wgcore::gpu::GpuInstance;
use wgcore::tensor::GpuVector;
use wgcore::Shader;
use wgpu::BufferUsages;
#[async_std::main]
async fn main() -> anyhow::Result<()> {
// Initialize the gpu device and its queue.
//
// Note that `GpuInstance` is just a simple helper struct for initializing the gpu resources.
// You are free to initialize them independently if more control is needed, or reuse the ones
// that were already created/owned by e.g., a game engine.
let gpu = GpuInstance::new().await?;
// Create the buffers.
const LEN: u32 = 10;
let buffer_data = DVector::from_fn(LEN as usize, |i, _| i as u32);
let buffer = GpuVector::init(
gpu.device(),
&buffer_data,
BufferUsages::STORAGE | BufferUsages::COPY_SRC,
);
let staging = GpuVector::uninit(
gpu.device(),
LEN,
BufferUsages::COPY_DST | BufferUsages::MAP_READ,
);
// Queue the operation.
// Encode & submit the operation to the gpu.
let mut encoder = gpu.device().create_command_encoder(&Default::default());
// Copy the result to the staging buffer.
staging.copy_from(&mut encoder, &buffer);
gpu.queue().submit(Some(encoder.finish()));
let read = DVector::from(staging.read(gpu.device()).await?);
assert_eq!(buffer_data, read);
println!("Buffer copy & read succeeded!");
println!("Original: {:?}", buffer_data);
println!("Readback: {:?}", read);
Ok(())
}