Skip to content

Commit 7b48768

Browse files
authored
Expand StagingBelt documentation. (#2905)
1 parent aae8c6a commit 7b48768

File tree

2 files changed

+42
-17
lines changed

2 files changed

+42
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ the same every time it is rendered, we now warn if it is missing.
7272
#### General
7373
- Added downlevel restriction error message for `InvalidFormatUsages` error by @Seamooo in [#2886](https://github.com/gfx-rs/wgpu/pull/2886)
7474

75+
### Documentation
76+
77+
- Expanded `StagingBelt` documentation by @kpreid in [#2905](https://github.com/gfx-rs/wgpu/pull/2905)
78+
7579
## wgpu-0.13.2 (2022-07-13)
7680

7781
### Bug Fixes

wgpu/src/util/belt.rs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,47 @@ struct Chunk {
1111
offset: BufferAddress,
1212
}
1313

14-
/// Staging belt is a machine that uploads data.
14+
/// Efficiently performs many buffer writes by sharing and reusing temporary buffers.
1515
///
1616
/// Internally it uses a ring-buffer of staging buffers that are sub-allocated.
17-
/// It has an advantage over [`Queue::write_buffer`] in a way that it returns a mutable slice,
17+
/// It has an advantage over [`Queue::write_buffer()`] in a way that it returns a mutable slice,
1818
/// which you can fill to avoid an extra data copy.
1919
///
2020
/// Using a staging belt is slightly complicated, and generally goes as follows:
21-
/// - Write to buffers that need writing to using [`StagingBelt::write_buffer`].
22-
/// - Call `finish`.
23-
/// - Submit all command encoders used with `StagingBelt::write_buffer`.
24-
/// - Call `recall`
21+
/// 1. Write to buffers that need writing to using [`StagingBelt::write_buffer()`].
22+
/// 2. Call [`StagingBelt::finish()`].
23+
/// 3. Submit all command encoders that were used in step 1.
24+
/// 4. Call [`StagingBelt::recall()`].
2525
///
26-
/// [`Queue::write_buffer`]: crate::Queue::write_buffer
26+
/// [`Queue::write_buffer()`]: crate::Queue::write_buffer
2727
pub struct StagingBelt {
2828
chunk_size: BufferAddress,
29-
/// Chunks that we are actively using for pending transfers at this moment.
29+
/// Chunks into which we are accumulating data to be transferred.
3030
active_chunks: Vec<Chunk>,
31-
/// Chunks that have scheduled transfers already.
31+
/// Chunks that have scheduled transfers already; they are unmapped and some
32+
/// command encoder has one or more `copy_buffer_to_buffer` commands with them
33+
/// as source.
3234
closed_chunks: Vec<Chunk>,
33-
/// Chunks that are back from the GPU and ready to be used.
35+
/// Chunks that are back from the GPU and ready to be mapped for write and put
36+
/// into `active_chunks`.
3437
free_chunks: Vec<Chunk>,
38+
/// When closed chunks are mapped again, the map callback sends them here.
3539
sender: mpsc::Sender<Chunk>,
40+
/// Free chunks are received here to be put on `self.free_chunks`.
3641
receiver: mpsc::Receiver<Chunk>,
3742
}
3843

3944
impl StagingBelt {
4045
/// Create a new staging belt.
4146
///
42-
/// The `chunk_size` is the unit of internal buffer allocation.
43-
/// It's better when it's big, but ideally still 1-4 times less than
44-
/// the total amount of data uploaded per submission.
47+
/// The `chunk_size` is the unit of internal buffer allocation; writes will be
48+
/// sub-allocated within each chunk. Therefore, for optimal use of memory, the
49+
/// chunk size should be:
50+
///
51+
/// * larger than the largest single [`StagingBelt::write_buffer()`] operation;
52+
/// * 1-4 times less than the total amount of data uploaded per submission
53+
/// (per [`StagingBelt::finish()`]); and
54+
/// * bigger is better, within these bounds.
4555
pub fn new(chunk_size: BufferAddress) -> Self {
4656
let (sender, receiver) = mpsc::channel();
4757
StagingBelt {
@@ -58,7 +68,12 @@ impl StagingBelt {
5868
/// at the specified offset.
5969
///
6070
/// The upload will be placed into the provided command encoder. This encoder
61-
/// must be submitted after `finish` is called and before `recall` is called.
71+
/// must be submitted after [`StagingBelt::finish()`] is called and before
72+
/// [`StagingBelt::recall()`] is called.
73+
///
74+
/// If the `size` is greater than the size of any free internal buffer, a new buffer
75+
/// will be allocated for it. Therefore, the `chunk_size` passed to [`StagingBelt::new()`]
76+
/// should ideally be larger than every such size.
6277
pub fn write_buffer(
6378
&mut self,
6479
encoder: &mut CommandEncoder,
@@ -108,8 +123,12 @@ impl StagingBelt {
108123

109124
/// Prepare currently mapped buffers for use in a submission.
110125
///
111-
/// At this point, all the partially used staging buffers are closed until
112-
/// the GPU is done copying the data from them.
126+
/// This must be called before the command encoder(s) provided to
127+
/// [`StagingBelt::write_buffer()`] are submitted.
128+
///
129+
/// At this point, all the partially used staging buffers are closed (cannot be used for
130+
/// further writes) until after [`StagingBelt::recall()`] is called *and* the GPU is done
131+
/// copying the data from them.
113132
pub fn finish(&mut self) {
114133
for chunk in self.active_chunks.drain(..) {
115134
chunk.buffer.unmap();
@@ -119,7 +138,9 @@ impl StagingBelt {
119138

120139
/// Recall all of the closed buffers back to be reused.
121140
///
122-
/// This has to be called after the command encoders written to `write_buffer` are submitted!
141+
/// This must only be called after the command encoder(s) provided to
142+
/// [`StagingBelt::write_buffer()`] are submitted. Additional calls are harmless.
143+
/// Not calling this as soon as possible may result in increased buffer memory usage.
123144
pub fn recall(&mut self) {
124145
while let Ok(mut chunk) = self.receiver.try_recv() {
125146
chunk.offset = 0;

0 commit comments

Comments
 (0)