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.
- Staging
Buffer - A pool of staging buffers for one destination buffer, reused across frames.