Skip to content

Commit

Permalink
Gpu readback belt for fast & easy data readback from gpu (#1687)
Browse files Browse the repository at this point in the history
* Initial GpuReadbackBelt implementation
* implement taking screenshots on view builder
* working screenshots in multiview example
* don't allocate row padding for depth cloud padding
  • Loading branch information
Wumpf authored Mar 27, 2023
1 parent 2ea2cd8 commit 3590184
Show file tree
Hide file tree
Showing 17 changed files with 715 additions and 106 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ __pycache__
perf.data*

**/dataset/

# Screenshots from samples etc.
screenshot*.png
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"memoffset",
"nyud",
"objectron",
"Readback",
"Skybox",
"smallvec",
"swapchain",
Expand Down
144 changes: 121 additions & 23 deletions crates/re_renderer/examples/multiview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,15 @@ use re_renderer::{
GenericSkyboxDrawData, LineDrawData, LineStripFlags, MeshDrawData, MeshInstance,
TestTriangleDrawData,
},
view_builder::{OrthographicCameraMode, Projection, TargetConfiguration, ViewBuilder},
view_builder::{
OrthographicCameraMode, Projection, ScheduledScreenshot, TargetConfiguration, ViewBuilder,
},
Color32, LineStripSeriesBuilder, PointCloudBuilder, RenderContext, Rgba, Size,
};
use winit::event::{ElementState, VirtualKeyCode};

mod framework;

fn draw_view<D: 'static + re_renderer::renderer::DrawData + Sync + Send + Clone>(
re_ctx: &mut RenderContext,
target_cfg: TargetConfiguration,
skybox: &GenericSkyboxDrawData,
draw_data: &D,
) -> (ViewBuilder, wgpu::CommandBuffer) {
let mut view_builder = ViewBuilder::default();
let command_buffer = view_builder
.setup_view(re_ctx, target_cfg)
.unwrap()
.queue_draw(skybox)
.queue_draw(draw_data)
.draw(re_ctx, Rgba::TRANSPARENT)
.unwrap();

(view_builder, command_buffer)
}

fn build_mesh_instances(
re_ctx: &mut RenderContext,
model_mesh_instances: &[MeshInstance],
Expand Down Expand Up @@ -165,6 +149,9 @@ struct Multiview {
random_points_positions: Vec<glam::Vec3>,
random_points_radii: Vec<Size>,
random_points_colors: Vec<Color32>,

take_screenshot_next_frame_for_view: Option<u32>,
scheduled_screenshots: Vec<ScheduledScreenshot>,
}

fn random_color(rnd: &mut impl rand::Rng) -> Color32 {
Expand All @@ -177,6 +164,94 @@ fn random_color(rnd: &mut impl rand::Rng) -> Color32 {
.into()
}

impl Multiview {
fn draw_view<D: 'static + re_renderer::renderer::DrawData + Sync + Send + Clone>(
&mut self,
re_ctx: &mut RenderContext,
target_cfg: TargetConfiguration,
skybox: &GenericSkyboxDrawData,
draw_data: &D,
index: u32,
) -> (ViewBuilder, wgpu::CommandBuffer) {
let mut view_builder = ViewBuilder::default();
view_builder.setup_view(re_ctx, target_cfg).unwrap();

if self
.take_screenshot_next_frame_for_view
.map_or(false, |i| i == index)
{
self.scheduled_screenshots
.push(view_builder.schedule_screenshot(re_ctx).unwrap());
re_log::info!("Scheduled screenshot for view {}", index);
}

let command_buffer = view_builder
.queue_draw(skybox)
.queue_draw(draw_data)
.draw(re_ctx, Rgba::TRANSPARENT)
.unwrap();

(view_builder, command_buffer)
}

fn handle_incoming_screenshots(&mut self, re_ctx: &mut RenderContext) {
re_ctx
.gpu_readback_belt
.lock()
.receive_data(|data, identifier| {
if let Some(index) = self
.scheduled_screenshots
.iter()
.position(|s| s.identifier == identifier)
{
re_log::info!(
"Received screenshot. Total bytes {:?}. Identifier {identifier}",
data.len()
);

#[cfg(target_arch = "wasm32")]
self.scheduled_screenshots.remove(index);

#[cfg(not(target_arch = "wasm32"))]
{
let screenshot = self.scheduled_screenshots.swap_remove(index);

// Need to do a memcpy to remove the padding.
let row_info = screenshot.row_info;
let mut buffer = Vec::with_capacity(
(row_info.bytes_per_row_unpadded * screenshot.height) as usize,
);
for row in 0..screenshot.height {
let offset = (row_info.bytes_per_row_padded * row) as usize;
buffer.extend_from_slice(
&data[offset..(offset + row_info.bytes_per_row_unpadded as usize)],
);
}

// Get next available file name.
let mut i = 1;
let filename = loop {
let filename = format!("screenshot_{i}.png");
if !std::path::Path::new(&filename).exists() {
break filename;
}
i += 1;
};

image::save_buffer(
filename,
&buffer,
screenshot.width,
screenshot.height,
image::ColorType::Rgba8,
)
.expect("Failed to save screenshot");
}
}
});
}
}

impl Example for Multiview {
fn title() -> &'static str {
"Multiple Views"
Expand Down Expand Up @@ -229,6 +304,9 @@ impl Example for Multiview {
random_points_positions,
random_points_radii,
random_points_colors,

take_screenshot_next_frame_for_view: None,
scheduled_screenshots: Vec::new(),
}
}

Expand All @@ -248,6 +326,8 @@ impl Example for Multiview {
) * 10.0;
}

self.handle_incoming_screenshots(re_ctx);

let seconds_since_startup = time.seconds_since_startup();
let view_from_world =
IsoTransform::look_at_rh(self.camera_position, Vec3::ZERO, Vec3::Y).unwrap();
Expand Down Expand Up @@ -295,7 +375,7 @@ impl Example for Multiview {
#[rustfmt::skip]
macro_rules! draw {
($name:ident @ split #$n:expr) => {{
let (view_builder, command_buffer) = draw_view(re_ctx,
let (view_builder, command_buffer) = self.draw_view(re_ctx,
TargetConfiguration {
name: stringify!($name).into(),
resolution_in_pixel: splits[$n].resolution_in_pixel,
Expand All @@ -305,7 +385,8 @@ impl Example for Multiview {
..Default::default()
},
&skybox,
&$name
&$name,
$n,
);
framework::ViewDrawResult {
view_builder,
Expand All @@ -315,12 +396,16 @@ impl Example for Multiview {
}};
}

vec![
let draw_results = vec![
draw!(triangle @ split #0),
draw!(lines @ split #1),
draw!(meshes @ split #2),
draw!(point_cloud @ split #3),
]
];

self.take_screenshot_next_frame_for_view = None;

draw_results
}

fn on_keyboard_input(&mut self, input: winit::event::KeyboardInput) {
Expand All @@ -336,6 +421,19 @@ impl Example for Multiview {
};
}

(ElementState::Pressed, Some(VirtualKeyCode::Key1)) => {
self.take_screenshot_next_frame_for_view = Some(0);
}
(ElementState::Pressed, Some(VirtualKeyCode::Key2)) => {
self.take_screenshot_next_frame_for_view = Some(1);
}
(ElementState::Pressed, Some(VirtualKeyCode::Key3)) => {
self.take_screenshot_next_frame_for_view = Some(2);
}
(ElementState::Pressed, Some(VirtualKeyCode::Key4)) => {
self.take_screenshot_next_frame_for_view = Some(3);
}

_ => {}
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ impl CpuWriteGpuReadBelt {
) -> CpuWriteGpuReadBuffer<T> {
crate::profile_function!();

debug_assert!(num_elements > 0, "Cannot allocate zero-sized buffer");

// Potentially overestimate alignment with Self::MIN_ALIGNMENT, see Self::MIN_ALIGNMENT doc string.
let alignment = (std::mem::align_of::<T>() as wgpu::BufferAddress).max(Self::MIN_ALIGNMENT);
// Pad out the size of the used buffer to a multiple of Self::MIN_ALIGNMENT.
Expand Down Expand Up @@ -425,7 +427,7 @@ impl CpuWriteGpuReadBelt {

impl std::fmt::Debug for CpuWriteGpuReadBelt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StagingBelt")
f.debug_struct("CpuWriteGpuReadBelt")
.field("chunk_size", &self.chunk_size)
.field("active_chunks", &self.active_chunks.len())
.field("closed_chunks", &self.closed_chunks.len())
Expand Down
Loading

1 comment on commit 3590184

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust Benchmark

Benchmark suite Current: 3590184 Previous: 17a21bf Ratio
datastore/insert/batch/rects/insert 596304 ns/iter (± 1701) 590043 ns/iter (± 5508) 1.01
datastore/latest_at/batch/rects/query 1839 ns/iter (± 1) 1860 ns/iter (± 8) 0.99
datastore/latest_at/missing_components/primary 290 ns/iter (± 0) 286 ns/iter (± 3) 1.01
datastore/latest_at/missing_components/secondaries 442 ns/iter (± 1) 449 ns/iter (± 5) 0.98
datastore/range/batch/rects/query 151218 ns/iter (± 750) 151042 ns/iter (± 1473) 1.00
mono_points_arrow/generate_message_bundles 26921137 ns/iter (± 666418) 29095634 ns/iter (± 1333541) 0.93
mono_points_arrow/generate_messages 118083579 ns/iter (± 1027172) 119014936 ns/iter (± 1374794) 0.99
mono_points_arrow/encode_log_msg 154723587 ns/iter (± 1193846) 154653153 ns/iter (± 1118379) 1.00
mono_points_arrow/encode_total 304142327 ns/iter (± 1532975) 304640531 ns/iter (± 2118477) 1.00
mono_points_arrow/decode_log_msg 175723066 ns/iter (± 1008143) 173545782 ns/iter (± 1309046) 1.01
mono_points_arrow/decode_message_bundles 52533199 ns/iter (± 976735) 52990283 ns/iter (± 1041855) 0.99
mono_points_arrow/decode_total 227693607 ns/iter (± 1636019) 223851542 ns/iter (± 1851584) 1.02
batch_points_arrow/generate_message_bundles 318864 ns/iter (± 1280) 314821 ns/iter (± 2630) 1.01
batch_points_arrow/generate_messages 5973 ns/iter (± 24) 5892 ns/iter (± 59) 1.01
batch_points_arrow/encode_log_msg 347562 ns/iter (± 985) 348381 ns/iter (± 3122) 1.00
batch_points_arrow/encode_total 700172 ns/iter (± 2134) 693811 ns/iter (± 7159) 1.01
batch_points_arrow/decode_log_msg 348645 ns/iter (± 1931) 344026 ns/iter (± 2478) 1.01
batch_points_arrow/decode_message_bundles 1589 ns/iter (± 7) 1553 ns/iter (± 18) 1.02
batch_points_arrow/decode_total 349681 ns/iter (± 807) 351462 ns/iter (± 2125) 0.99
arrow_mono_points/insert 6211198075 ns/iter (± 9796586) 6240768409 ns/iter (± 19864832) 1.00
arrow_mono_points/query 1788365 ns/iter (± 8355) 1759378 ns/iter (± 16492) 1.02
arrow_batch_points/insert 3085065 ns/iter (± 9184) 3060805 ns/iter (± 22787) 1.01
arrow_batch_points/query 16463 ns/iter (± 54) 16418 ns/iter (± 151) 1.00
arrow_batch_vecs/insert 44240 ns/iter (± 113) 44053 ns/iter (± 323) 1.00
arrow_batch_vecs/query 388409 ns/iter (± 599) 385099 ns/iter (± 4018) 1.01
tuid/Tuid::random 34 ns/iter (± 0) 34 ns/iter (± 0) 1

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.