@@ -11,37 +11,47 @@ struct Chunk {
11
11
offset : BufferAddress ,
12
12
}
13
13
14
- /// Staging belt is a machine that uploads data .
14
+ /// Efficiently performs many buffer writes by sharing and reusing temporary buffers .
15
15
///
16
16
/// 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,
18
18
/// which you can fill to avoid an extra data copy.
19
19
///
20
20
/// 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()`].
25
25
///
26
- /// [`Queue::write_buffer`]: crate::Queue::write_buffer
26
+ /// [`Queue::write_buffer() `]: crate::Queue::write_buffer
27
27
pub struct StagingBelt {
28
28
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 .
30
30
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.
32
34
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`.
34
37
free_chunks : Vec < Chunk > ,
38
+ /// When closed chunks are mapped again, the map callback sends them here.
35
39
sender : mpsc:: Sender < Chunk > ,
40
+ /// Free chunks are received here to be put on `self.free_chunks`.
36
41
receiver : mpsc:: Receiver < Chunk > ,
37
42
}
38
43
39
44
impl StagingBelt {
40
45
/// Create a new staging belt.
41
46
///
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.
45
55
pub fn new ( chunk_size : BufferAddress ) -> Self {
46
56
let ( sender, receiver) = mpsc:: channel ( ) ;
47
57
StagingBelt {
@@ -58,7 +68,12 @@ impl StagingBelt {
58
68
/// at the specified offset.
59
69
///
60
70
/// 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.
62
77
pub fn write_buffer (
63
78
& mut self ,
64
79
encoder : & mut CommandEncoder ,
@@ -108,8 +123,12 @@ impl StagingBelt {
108
123
109
124
/// Prepare currently mapped buffers for use in a submission.
110
125
///
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.
113
132
pub fn finish ( & mut self ) {
114
133
for chunk in self . active_chunks . drain ( ..) {
115
134
chunk. buffer . unmap ( ) ;
@@ -119,7 +138,9 @@ impl StagingBelt {
119
138
120
139
/// Recall all of the closed buffers back to be reused.
121
140
///
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.
123
144
pub fn recall ( & mut self ) {
124
145
while let Ok ( mut chunk) = self . receiver . try_recv ( ) {
125
146
chunk. offset = 0 ;
0 commit comments