Skip to main content

Module staging

Module staging 

Source
Expand description

A reused host-visible staging buffer for per-frame uploads.

Data that changes every frame cannot be written straight into the buffer the GPU reads from: a buffer is either mapped by the host or read by a submission, never both. The usual shortcut, [wgpu::Queue::write_buffer], hides that by allocating a fresh staging buffer per call and freeing it only after a later submission has finished, so a scene that uploads every frame allocates every frame.

StagingBuffer keeps those staging buffers instead: one per destination buffer, written through a mapping carried across frames, with the copy recorded into an encoder the caller already owns. A steady scene then uploads without allocating.

Both routes reach the GPU the same way — the copy joins the next [wgpu::Queue::submit] either way — so what a staging buffer buys is the allocation, which is worth most where the allocation is largest. On WebGL2 it is host memory: buffer mapping is emulated there, so a staging buffer is a plain byte buffer the copy is uploaded from. See the section below.

use unlit_wgpu::staging::StagingBuffer;

let mut staging = StagingBuffer::new();
let mut encoder = device.create_command_encoder(&Default::default());
staging.write(&device, &mut encoder, &target, 0, &[0u8; 16]);
queue.submit([encoder.finish()]);

§How a staging buffer is recycled

A staging buffer is created mapped, so the first frame writes into it without waiting for anything. To copy out of it, it is unmapped and the copy is recorded; the mapping is then re-requested with [wgpu::CommandEncoder::map_buffer_on_submit], whose callback runs once the submission holding the copy has finished and marks the buffer writable again.

Nothing polls: [wgpu::Queue::submit] maintains the device itself, which is what fires the callbacks. Until one has fired the buffer is treated as busy, and a frame that needs a staging buffer while the previous ones are still in flight — a frame or two ahead of the GPU — gets one of its own. The pool therefore settles at as many buffers as there are frames in flight, rather than growing per frame.

§What a staging buffer is on WebGL2

WebGL2 has no glMapBufferRange, so wgpu emulates mapping in software: a buffer created with [wgpu::BufferUsages::MAP_WRITE] is given no GL buffer at all and holds its bytes in a host-side Vec<u8>, which a copy out of it uploads with bufferSubData when the submission runs.

A staging buffer is worth even more there than on a native backend, because the allocation it saves is a host one. [wgpu::Queue::write_buffer] on WebGL2 allocates such a Vec per call and keeps it alive until a later submission has finished with it, so a scene uploading every frame churns host memory every frame. The pool replaces that with a fixed set of buffers that the host keeps writing into.

The recycling scheme is unchanged: the platform supplies [wgpu::CommandEncoder::map_buffer_on_submit] like any other, so nothing here branches on the backend.

Re-exports§

use core::sync::atomic::AtomicBool; 🔒
use core::sync::atomic::Ordering; 🔒
use std::sync::Arc; 🔒

Structs§

Slot 🔒
One staging buffer and whether the host may write into its mapping.
StagingBuffer
A pool of staging buffers for one destination buffer, reused across frames.