Skip to content

Commit 3fa7196

Browse files
committed
Improve commentary on the read/write belts and why one can not simply use map_buffer_on_submit
1 parent 0e014b7 commit 3fa7196

File tree

3 files changed

+28
-10
lines changed

3 files changed

+28
-10
lines changed

crates/viewer/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ pub enum CpuWriteGpuReadError {
4949
/// Note that the "vec like behavior" further encourages
5050
/// * not leaving holes
5151
/// * keeping writes sequential
52+
///
53+
/// Must be dropped before calling [`CpuWriteGpuReadBelt::before_queue_submit`] (typically the end of a frame).
5254
pub struct CpuWriteGpuReadBuffer<T: bytemuck::Pod + Send + Sync> {
5355
/// Write view into the relevant buffer portion.
5456
write_view: wgpu::BufferViewMut,
@@ -551,18 +553,21 @@ impl CpuWriteGpuReadBelt {
551553

552554
/// Prepare currently mapped buffers for use in a submission.
553555
///
554-
/// This must be called before the command encoder(s) used in [`CpuWriteGpuReadBuffer`] copy operations are submitted.
556+
/// All existing [`CpuWriteGpuReadBuffer`] MUST be dropped before calling this function.
557+
/// Any not dropped [`CpuWriteGpuReadBuffer`] will cause a validation error.
555558
///
556-
/// At this point, all the partially used staging buffers are closed (cannot be used for
557-
/// further writes) until after [`CpuWriteGpuReadBelt::after_queue_submit`] is called *and* the GPU is done
558-
/// copying the data from them.
559+
/// This must be called BEFORE the command encoder(s) used in any [`CpuWriteGpuReadBuffer`] copy operations are submitted.
559560
pub fn before_queue_submit(&mut self) {
560561
re_tracing::profile_function!();
561562

562563
// This would be a great usecase for persistent memory mapping, i.e. mapping without the need to unmap
563564
// https://github.com/gfx-rs/wgpu/issues/1468
564565
// However, WebGPU does not support this!
565566

567+
// We're done with writing to this chunk and are ready to have the GPU read it!
568+
//
569+
// This part has to happen before submit, otherwise we get a validation error that
570+
// the buffers are still mapped and can't be read by the gpu.
566571
for chunk in self.active_chunks.drain(..) {
567572
chunk.buffer.unmap();
568573
self.closed_chunks.push(chunk);
@@ -574,12 +579,16 @@ impl CpuWriteGpuReadBelt {
574579
/// This must only be called after the command encoder(s) used in [`CpuWriteGpuReadBuffer`]
575580
/// copy operations are submitted. Additional calls are harmless.
576581
/// Not calling this as soon as possible may result in increased buffer memory usage.
582+
///
583+
/// Implementation note:
584+
/// We can't use [`wgpu::CommandEncoder::map_buffer_on_submit`] here because for that we'd need to know which
585+
/// command encoder is the last one scheduling any cpu->gpu copy operations.
586+
/// Note that if chunks were fully tied to a single encoder, we could call [`wgpu::CommandEncoder::map_buffer_on_submit`]
587+
/// once we know a chunk has all its cpu->gpu copy operations scheduled on that very encoder.
577588
pub fn after_queue_submit(&mut self) {
578589
re_tracing::profile_function!();
579590
self.receive_chunks();
580591

581-
// TODO(andreas): Use `map_buffer_on_submit` https://github.com/gfx-rs/wgpu/pull/8125 once available.
582-
583592
let sender = &self.sender;
584593
for chunk in self.closed_chunks.drain(..) {
585594
let sender = sender.clone();

crates/viewer/re_renderer/src/allocator/gpu_readback_belt.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,12 @@ impl GpuReadbackBelt {
311311
/// Prepare used buffers for CPU read.
312312
///
313313
/// This should be called after the command encoder(s) used in [`GpuReadbackBuffer`] copy operations are submitted.
314+
///
315+
/// Implementation note:
316+
/// We can't use [`wgpu::CommandEncoder::map_buffer_on_submit`] here because for that we'd need to know which
317+
/// command encoder is the last one scheduling any gpu->cpu copy operations.
318+
/// Note that if chunks were fully tied to a single encoder, we could call [`wgpu::CommandEncoder::map_buffer_on_submit`]
319+
/// once we know a chunk has all its gpu->cpu copy operations scheduled on that very encoder.
314320
pub fn after_queue_submit(&mut self) {
315321
re_tracing::profile_function!();
316322

crates/viewer/re_renderer/src/context.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,12 @@ This means, either a call to RenderContext::before_submit was omitted, or the pr
413413
self.before_submit();
414414
}
415415

416-
// Request write used staging buffer back.
417-
// TODO(andreas): If we'd control all submissions, we could move this directly after the submission which would be a bit better.
416+
// Request write-staging buffers back.
417+
// Ideally we'd do this as closely as possible to the last submission containing any cpu->gpu operations as possible.
418418
self.cpu_write_gpu_read_belt.get_mut().after_queue_submit();
419-
// Map all read staging buffers.
419+
420+
// Schedule mapping for all read staging buffers.
421+
// Ideally we'd do this as closely as possible to the last submission containing any gpu->cpu operations as possible.
420422
self.gpu_readback_belt.get_mut().after_queue_submit();
421423

422424
// Close previous' frame error scope.
@@ -502,7 +504,8 @@ This means, either a call to RenderContext::before_submit was omitted, or the pr
502504
pub fn before_submit(&mut self) {
503505
re_tracing::profile_function!();
504506

505-
// Unmap all write staging buffers.
507+
// Unmap all write staging buffers, so we don't get validation errors about buffers still being mapped
508+
// that the gpu wants to read from.
506509
self.cpu_write_gpu_read_belt.lock().before_queue_submit();
507510

508511
if let Some(command_encoder) = self

0 commit comments

Comments
 (0)