Skip to content

Commit

Permalink
Integrate depth clouds into Rerun (#1421)
Browse files Browse the repository at this point in the history
* implement depth cloud renderer

* proper rp config

* self-review

* handle breaking changes from main

* upload depth textures through staging belt

* addressing PR comments part 1 of N

* work around wgpu d16 web issue

* addressing PR comments part 2 of N

* big refactoring of the whole quad spanning story

* accomodating breaking changes

* bring back coverage

* todos

* procedural example

* clean

* fix wasm cranky

* set up entity props & selection panel for backprojection

* integrate depth clouds into rerun

* last merge conflicts

* huh, nasty

* everything working as i want

* cleanin up

* self review

* compute auto heuristics properly

* gentle radius scale
  • Loading branch information
teh-cmc authored Mar 3, 2023
1 parent e6b0093 commit cb48497
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 149 deletions.
30 changes: 30 additions & 0 deletions crates/re_data_store/src/entity_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ impl EntityPropertyMap {

// ----------------------------------------------------------------------------

// TODO(#1423): We need to properly split entity properties that only apply to specific
// views/primitives.
#[cfg(feature = "serde")]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand All @@ -50,6 +52,20 @@ pub struct EntityProperties {
/// Only applies to pinhole cameras when in a spatial view, using 3D navigation.
///
pub pinhole_image_plane_distance: EditableAutoValue<f32>,

/// Should the depth texture be backprojected into a point cloud?
///
/// Only applies to tensors with meaning=depth that are affected by a pinhole transform when
/// in a spatial view, using 3D navigation.
pub backproject_depth: bool,
/// Entity path of the pinhole transform used for the backprojection.
///
/// `None` means backprojection is disabled.
pub backproject_pinhole_ent_path: Option<EntityPath>,
/// Used to scale the resulting point cloud.
pub backproject_scale: EditableAutoValue<f32>,
/// Used to scale the radii of the points in the resulting point cloud.
pub backproject_radius_scale: EditableAutoValue<f32>,
}

#[cfg(feature = "serde")]
Expand All @@ -64,6 +80,16 @@ impl EntityProperties {
.pinhole_image_plane_distance
.or(&child.pinhole_image_plane_distance)
.clone(),
backproject_depth: self.backproject_depth || child.backproject_depth,
backproject_pinhole_ent_path: self
.backproject_pinhole_ent_path
.clone()
.or(child.backproject_pinhole_ent_path.clone()),
backproject_scale: self.backproject_scale.or(&child.backproject_scale).clone(),
backproject_radius_scale: self
.backproject_radius_scale
.or(&child.backproject_radius_scale)
.clone(),
}
}
}
Expand All @@ -76,6 +102,10 @@ impl Default for EntityProperties {
visible_history: ExtraQueryHistory::default(),
interactive: true,
pinhole_image_plane_distance: EditableAutoValue::default(),
backproject_depth: false,
backproject_pinhole_ent_path: None,
backproject_scale: EditableAutoValue::default(),
backproject_radius_scale: EditableAutoValue::default(),
}
}
}
Expand Down
19 changes: 6 additions & 13 deletions crates/re_renderer/examples/depth_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,14 @@ impl RenderDepthClouds {
} = self;

let focal_length = glam::Vec2::new(intrinsics.x_axis.x, intrinsics.y_axis.y);
let offset = glam::Vec2::new(intrinsics.x_axis.z, intrinsics.y_axis.z);
let offset = glam::Vec2::new(intrinsics.z_axis.x, intrinsics.z_axis.y);

let point_cloud_draw_data = {
let num_points = depth.dimensions.x * depth.dimensions.y;
let (points, colors, radii): (Vec<_>, Vec<_>, Vec<_>) = (0..depth.dimensions.y)
.flat_map(|y| (0..depth.dimensions.x).map(move |x| glam::UVec2::new(x, y)))
.map(|texcoords| {
let linear_depth = depth.get_linear(
depth.dimensions.x - texcoords.x - 1,
depth.dimensions.y - texcoords.y - 1,
);
let linear_depth = depth.get_linear(texcoords.x, texcoords.y);
let pos_in_world = ((texcoords.as_vec2() - offset) * linear_depth
/ focal_length)
.extend(linear_depth);
Expand Down Expand Up @@ -171,17 +168,12 @@ impl RenderDepthClouds {
..
} = self;

let world_from_obj = glam::Mat4::from_cols(
glam::Vec4::NEG_X * *scale,
glam::Vec4::NEG_Y * *scale,
glam::Vec4::Z * *scale,
glam::Vec4::W,
);
let world_from_obj = glam::Mat4::from_scale(glam::Vec3::splat(*scale));

let depth_cloud_draw_data = DepthCloudDrawData::new(
re_ctx,
&[DepthCloud {
world_from_obj,
depth_camera_extrinsics: world_from_obj,
depth_camera_intrinsics: *intrinsics,
radius_scale: *radius_scale,
depth_dimensions: depth.dimensions,
Expand Down Expand Up @@ -261,7 +253,8 @@ impl framework::Example for RenderDepthClouds {
Vec3::new(focal_length, 0.0, offset.x),
Vec3::new(0.0, focal_length, offset.y),
Vec3::new(0.0, 0.0, 1.0),
);
)
.transpose();

RenderDepthClouds {
depth,
Expand Down
7 changes: 4 additions & 3 deletions crates/re_renderer/shader/depth_cloud.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn compute_point_data(quad_idx: i32) -> PointData {
let color = Vec4(linear_from_srgb(Vec3(norm_linear_depth)), 1.0);

// TODO(cmc): This assumes a pinhole camera; need to support other kinds at some point.
let intrinsics = transpose(depth_cloud_info.depth_camera_intrinsics);
let intrinsics = depth_cloud_info.depth_camera_intrinsics;
let focal_length = Vec2(intrinsics[0][0], intrinsics[1][1]);
let offset = Vec2(intrinsics[2][0], intrinsics[2][1]);

Expand All @@ -39,7 +39,7 @@ fn compute_point_data(quad_idx: i32) -> PointData {
norm_linear_depth,
);

let pos_in_world = depth_cloud_info.world_from_obj * Vec4(pos_in_obj, 1.0);
let pos_in_world = depth_cloud_info.extrinsincs * Vec4(pos_in_obj, 1.0);

var data: PointData;
data.pos_in_world = pos_in_world.xyz;
Expand All @@ -52,7 +52,8 @@ fn compute_point_data(quad_idx: i32) -> PointData {
// ---

struct DepthCloudInfo {
world_from_obj: Mat4,
/// The extrinsincs of the camera used for the projection.
extrinsincs: Mat4,

/// The intrinsics of the camera used for the projection.
///
Expand Down
31 changes: 16 additions & 15 deletions crates/re_renderer/src/renderer/depth_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ mod gpu_data {
#[repr(C, align(256))]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct DepthCloudInfoUBO {
pub world_from_obj: crate::wgpu_buffer_types::Mat4,
pub depth_camera_extrinsics: crate::wgpu_buffer_types::Mat4,
pub depth_camera_intrinsics: crate::wgpu_buffer_types::Mat3,
pub radius_scale: crate::wgpu_buffer_types::F32RowPadded,

Expand All @@ -65,7 +65,8 @@ pub enum DepthCloudDepthData {
}

pub struct DepthCloud {
pub world_from_obj: glam::Mat4,
/// The extrinsics of the camera used for the projection.
pub depth_camera_extrinsics: glam::Mat4,

/// The intrinsics of the camera used for the projection.
///
Expand All @@ -87,7 +88,7 @@ pub struct DepthCloud {
impl Default for DepthCloud {
fn default() -> Self {
Self {
world_from_obj: glam::Mat4::IDENTITY,
depth_camera_extrinsics: glam::Mat4::IDENTITY,
depth_camera_intrinsics: glam::Mat3::IDENTITY,
radius_scale: 1.0,
depth_dimensions: glam::UVec2::ZERO,
Expand All @@ -113,6 +114,17 @@ impl DepthCloudDrawData {
) -> Result<Self, ResourceManagerError> {
crate::profile_function!();

let bg_layout = ctx
.renderers
.write()
.get_or_create::<_, DepthCloudRenderer>(
&ctx.shared_renderer_data,
&mut ctx.gpu_resources,
&ctx.device,
&mut ctx.resolver,
)
.bind_group_layout;

if depth_clouds.is_empty() {
return Ok(DepthCloudDrawData {
bind_groups: Vec::new(),
Expand All @@ -123,24 +135,13 @@ impl DepthCloudDrawData {
ctx,
"depth_cloud_ubos".into(),
depth_clouds.iter().map(|info| gpu_data::DepthCloudInfoUBO {
world_from_obj: info.world_from_obj.into(),
depth_camera_extrinsics: info.depth_camera_extrinsics.into(),
depth_camera_intrinsics: info.depth_camera_intrinsics.into(),
radius_scale: info.radius_scale.into(),
end_padding: Default::default(),
}),
);

let bg_layout = ctx
.renderers
.write()
.get_or_create::<_, DepthCloudRenderer>(
&ctx.shared_renderer_data,
&mut ctx.gpu_resources,
&ctx.device,
&mut ctx.resolver,
)
.bind_group_layout;

let mut bind_groups = Vec::with_capacity(depth_clouds.len());
for (depth_cloud, ubo) in depth_clouds.iter().zip(depth_cloud_ubos.into_iter()) {
let depth_texture = match &depth_cloud.depth_data {
Expand Down
140 changes: 115 additions & 25 deletions crates/re_viewer/src/ui/selection_panel.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use egui::NumExt as _;
use re_data_store::{query_latest_single, EditableAutoValue, EntityPath, EntityProperties};
use re_log_types::{TimeType, Transform};
use re_log_types::{
component_types::{Tensor, TensorDataMeaning},
TimeType, Transform,
};

use crate::{
ui::{view_spatial::SpatialNavigationMode, Blueprint},
Expand Down Expand Up @@ -371,8 +375,6 @@ fn entity_props_ui(
entity_props: &mut EntityProperties,
view_state: &ViewState,
) {
use egui::NumExt;

ui.checkbox(&mut entity_props.visible, "Visible");
ui.checkbox(&mut entity_props.interactive, "Interactive")
.on_hover_text("If disabled, the entity will not react to any mouse interaction");
Expand Down Expand Up @@ -407,31 +409,119 @@ fn entity_props_ui(
}
ui.end_row();

// pinhole_image_plane_distance
if view_state.state_spatial.nav_mode == SpatialNavigationMode::ThreeD {
if let Some(entity_path) = entity_path {
let query = ctx.current_query();
if let Some(re_log_types::Transform::Pinhole(_)) =
query_latest_single::<Transform>(&ctx.log_db.entity_db, entity_path, &query)
{
ui.label("Image plane distance");
let mut distance = *entity_props.pinhole_image_plane_distance.get();
let speed = (distance * 0.05).at_least(0.01);
if ui
.add(
egui::DragValue::new(&mut distance)
.clamp_range(0.0..=1.0e8)
.speed(speed),
)
.on_hover_text("Controls how far away the image plane is.")
.changed()
{
entity_props.pinhole_image_plane_distance =
EditableAutoValue::UserEdited(distance);
}
ui.end_row();
}
pinhole_props_ui(ctx, ui, entity_path, entity_props);
depth_props_ui(ctx, ui, entity_path, entity_props);
}
}
});
}

fn pinhole_props_ui(
ctx: &mut ViewerContext<'_>,
ui: &mut egui::Ui,
entity_path: &EntityPath,
entity_props: &mut EntityProperties,
) {
let query = ctx.current_query();
if let Some(re_log_types::Transform::Pinhole(_)) =
query_latest_single::<Transform>(&ctx.log_db.entity_db, entity_path, &query)
{
ui.label("Image plane distance");
let mut distance = *entity_props.pinhole_image_plane_distance.get();
let speed = (distance * 0.05).at_least(0.01);
if ui
.add(
egui::DragValue::new(&mut distance)
.clamp_range(0.0..=1.0e8)
.speed(speed),
)
.on_hover_text("Controls how far away the image plane is.")
.changed()
{
entity_props.pinhole_image_plane_distance = EditableAutoValue::UserEdited(distance);
}
ui.end_row();
}
}

fn depth_props_ui(
ctx: &mut ViewerContext<'_>,
ui: &mut egui::Ui,
entity_path: &EntityPath,
entity_props: &mut EntityProperties,
) {
let query = ctx.current_query();

// Find closest pinhole transform, if any.
let mut pinhole_ent_path = None;
let mut cur_path = Some(entity_path.clone());
while let Some(path) = cur_path {
if let Some(re_log_types::Transform::Pinhole(_)) =
query_latest_single::<Transform>(&ctx.log_db.entity_db, &path, &query)
{
pinhole_ent_path = Some(path);
break;
}
cur_path = path.parent();
}

// Early out if there's no pinhole transform upwards in the tree.
let Some(pinhole_ent_path) = pinhole_ent_path else { return; };

entity_props.backproject_pinhole_ent_path = Some(pinhole_ent_path.clone());

let tensor = query_latest_single::<Tensor>(&ctx.log_db.entity_db, entity_path, &query);
if tensor.as_ref().map(|t| t.meaning) == Some(TensorDataMeaning::Depth) {
ui.checkbox(&mut entity_props.backproject_depth, "Backproject Depth")
.on_hover_text(
"If enabled, the depth texture will be backprojected into a point cloud rather \
than simply displayed as an image.",
);
ui.end_row();

if entity_props.backproject_depth {
ui.label("Pinhole");
ctx.entity_path_button(ui, None, &pinhole_ent_path)
.on_hover_text(
"The entity path of the pinhole transform being used to do the backprojection.",
);
ui.end_row();

ui.label("Backproject scale");
let mut scale = *entity_props.backproject_scale.get();
let speed = (scale * 0.05).at_least(0.01);
if ui
.add(
egui::DragValue::new(&mut scale)
.clamp_range(0.0..=1.0e8)
.speed(speed),
)
.on_hover_text("Scales the backprojected point cloud")
.changed()
{
entity_props.backproject_scale = EditableAutoValue::UserEdited(scale);
}
ui.end_row();

ui.label("Backproject radius scale");
let mut radius_scale = *entity_props.backproject_radius_scale.get();
let speed = (radius_scale * 0.001).at_least(0.001);
if ui
.add(
egui::DragValue::new(&mut radius_scale)
.clamp_range(0.0..=1.0e8)
.speed(speed),
)
.on_hover_text("Scales the radii of the points in the backprojected point cloud")
.changed()
{
entity_props.backproject_radius_scale = EditableAutoValue::UserEdited(radius_scale);
}
ui.end_row();
} else {
entity_props.backproject_pinhole_ent_path = None;
}
}
}
1 change: 1 addition & 0 deletions crates/re_viewer/src/ui/view_spatial/scene/picking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ pub fn picking(
line_strips,
points,
meshes,
depth_clouds: _, // no picking for depth clouds yet
} = primitives;

picking_points(&context, &mut state, points);
Expand Down
Loading

1 comment on commit cb48497

@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: cb48497 Previous: e6b0093 Ratio
datastore/insert/batch/rects/insert 551075 ns/iter (± 4665) 551339 ns/iter (± 4401) 1.00
datastore/latest_at/batch/rects/query 1808 ns/iter (± 5) 1811 ns/iter (± 8) 1.00
datastore/latest_at/missing_components/primary 356 ns/iter (± 2) 354 ns/iter (± 3) 1.01
datastore/latest_at/missing_components/secondaries 422 ns/iter (± 3) 419 ns/iter (± 4) 1.01
datastore/range/batch/rects/query 150240 ns/iter (± 2085) 149040 ns/iter (± 2277) 1.01
mono_points_arrow/generate_message_bundles 49053814 ns/iter (± 2443665) 48607153 ns/iter (± 1666236) 1.01
mono_points_arrow/generate_messages 136929232 ns/iter (± 1351940) 135563855 ns/iter (± 1681485) 1.01
mono_points_arrow/encode_log_msg 167189745 ns/iter (± 997675) 166230330 ns/iter (± 1257439) 1.01
mono_points_arrow/encode_total 355925554 ns/iter (± 5163442) 356341383 ns/iter (± 2474946) 1.00
mono_points_arrow/decode_log_msg 187054410 ns/iter (± 1137135) 185271778 ns/iter (± 1792388) 1.01
mono_points_arrow/decode_message_bundles 71930702 ns/iter (± 1607760) 70904496 ns/iter (± 1103813) 1.01
mono_points_arrow/decode_total 266507009 ns/iter (± 3993786) 254429693 ns/iter (± 2415708) 1.05
batch_points_arrow/generate_message_bundles 334203 ns/iter (± 587) 330002 ns/iter (± 3236) 1.01
batch_points_arrow/generate_messages 6249 ns/iter (± 17) 6272 ns/iter (± 72) 1.00
batch_points_arrow/encode_log_msg 369439 ns/iter (± 1707) 357173 ns/iter (± 2390) 1.03
batch_points_arrow/encode_total 724084 ns/iter (± 12310) 706366 ns/iter (± 8103) 1.03
batch_points_arrow/decode_log_msg 357228 ns/iter (± 1481) 347617 ns/iter (± 2123) 1.03
batch_points_arrow/decode_message_bundles 2107 ns/iter (± 5) 2039 ns/iter (± 21) 1.03
batch_points_arrow/decode_total 351783 ns/iter (± 2166) 353375 ns/iter (± 1344) 1.00
arrow_mono_points/insert 7256577931 ns/iter (± 40153605) 6881435297 ns/iter (± 19787234) 1.05
arrow_mono_points/query 1777555 ns/iter (± 19146) 1698411 ns/iter (± 23128) 1.05
arrow_batch_points/insert 2625185 ns/iter (± 12291) 2608831 ns/iter (± 24153) 1.01
arrow_batch_points/query 16024 ns/iter (± 157) 15936 ns/iter (± 166) 1.01
arrow_batch_vecs/insert 41636 ns/iter (± 305) 41717 ns/iter (± 295) 1.00
arrow_batch_vecs/query 505694 ns/iter (± 828) 505610 ns/iter (± 4832) 1.00
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.