Skip to content

Commit

Permalink
Use gpu picking for points, streamline/share picking code some more (#…
Browse files Browse the repository at this point in the history
…1814)

* use gpu picking for picking points
* gpu based picking no longer works like a fallback but integrates with other picking sources
* fix incorrect cursor rounding for picking
* refactor picking context to be a pub struct with exposed state
* unify ui picking method for 2d & 3d space views
* less indentation for picking method
* picking rect size is dynamically chosen
* fix accidental z scaling in projection correction for picking & make cropped_projection_from_projection easier to read
  • Loading branch information
Wumpf authored Apr 12, 2023
1 parent c472b07 commit f7cdc66
Show file tree
Hide file tree
Showing 20 changed files with 598 additions and 659 deletions.
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl framework::Example for Render2D {
// Moving the windows to a high dpi screen makes the second one bigger.
// Also, it looks different under perspective projection.
// The third point is automatic thickness which is determined by the point renderer implementation.
let mut point_cloud_builder = PointCloudBuilder::<()>::new(re_ctx);
let mut point_cloud_builder = PointCloudBuilder::new(re_ctx);
point_cloud_builder
.batch("points")
.add_points_2d(
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/depth_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl RenderDepthClouds {
})
.multiunzip();

let mut builder = PointCloudBuilder::<()>::new(re_ctx);
let mut builder = PointCloudBuilder::new(re_ctx);
builder
.batch("backprojected point cloud")
.add_points(num_points as _, points.into_iter())
Expand Down
8 changes: 4 additions & 4 deletions crates/re_renderer/examples/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,10 @@ impl<E: Example + 'static> Application<E> {
Event::WindowEvent {
event: WindowEvent::CursorMoved { position, .. },
..
} => self.example.on_cursor_moved(glam::uvec2(
position.x.round() as u32,
position.y.round() as u32,
)),
} => self
.example
// Don't round the position: The entire range from 0 to excluding 1 should fall into pixel coordinate 0!
.on_cursor_moved(glam::uvec2(position.x as u32, position.y as u32)),
Event::WindowEvent {
event:
WindowEvent::ScaleFactorChanged {
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/multiview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ impl Example for Multiview {
let skybox = GenericSkyboxDrawData::new(re_ctx);
let lines = build_lines(re_ctx, seconds_since_startup);

let mut builder = PointCloudBuilder::<()>::new(re_ctx);
let mut builder = PointCloudBuilder::new(re_ctx);
builder
.batch("Random Points")
.world_from_obj(glam::Mat4::from_rotation_x(seconds_since_startup))
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/picking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl framework::Example for Picking {
.schedule_picking_rect(re_ctx, picking_rect, READBACK_IDENTIFIER, (), false)
.unwrap();

let mut point_builder = PointCloudBuilder::<()>::new(re_ctx);
let mut point_builder = PointCloudBuilder::new(re_ctx);
for (i, point_set) in self.point_sets.iter().enumerate() {
point_builder
.batch(format!("Random Points {i}"))
Expand Down
10 changes: 5 additions & 5 deletions crates/re_renderer/src/draw_phases/picking_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,18 +224,18 @@ impl PickingLayerProcessor {
DepthReadbackWorkaround::new(ctx, picking_rect.extent, picking_depth_target.handle)
});

let rect_min = picking_rect.top_left_corner.as_vec2();
let rect_min = picking_rect.left_top.as_vec2();
let rect_max = rect_min + picking_rect.extent.as_vec2();
let screen_resolution = screen_resolution.as_vec2();
// y axis is flipped in NDC, therefore we need to flip the y axis of the rect.
let rect_min_ndc =
pixel_coord_to_ndc(glam::vec2(rect_min.x, rect_max.y), screen_resolution);
let rect_max_ndc =
pixel_coord_to_ndc(glam::vec2(rect_max.x, rect_min.y), screen_resolution);
let rect_center_ndc = (rect_min_ndc + rect_max_ndc) * 0.5;
let cropped_projection_from_projection =
glam::Mat4::from_scale(2.0 / (rect_max_ndc - rect_min_ndc).extend(1.0))
* glam::Mat4::from_translation(-rect_center_ndc.extend(0.0));
let scale = 2.0 / (rect_max_ndc - rect_min_ndc);
let translation = -0.5 * (rect_min_ndc + rect_max_ndc);
let cropped_projection_from_projection = glam::Mat4::from_scale(scale.extend(1.0))
* glam::Mat4::from_translation(translation.extend(0.0));

// Setup frame uniform buffer
let previous_projection_from_world: glam::Mat4 =
Expand Down
125 changes: 27 additions & 98 deletions crates/re_renderer/src/point_cloud_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,19 @@ use crate::{
};

/// Builder for point clouds, making it easy to create [`crate::renderer::PointCloudDrawData`].
pub struct PointCloudBuilder<PerPointUserData> {
// Size of `point`/color`/`per_point_user_data` must be equal.
pub struct PointCloudBuilder {
// Size of `point`/color` must be equal.
pub vertices: Vec<PointCloudVertex>,

pub(crate) color_buffer: CpuWriteGpuReadBuffer<Color32>,
pub(crate) picking_instance_ids_buffer: CpuWriteGpuReadBuffer<PickingLayerInstanceId>,
pub user_data: Vec<PerPointUserData>,

pub(crate) batches: Vec<PointCloudBatchInfo>,

pub(crate) radius_boost_in_ui_points_for_outlines: f32,
}

impl<PerPointUserData> PointCloudBuilder<PerPointUserData>
where
PerPointUserData: Default + Copy,
{
impl PointCloudBuilder {
pub fn new(ctx: &RenderContext) -> Self {
const RESERVE_SIZE: usize = 512;

Expand All @@ -48,7 +44,6 @@ where
vertices: Vec::with_capacity(RESERVE_SIZE),
color_buffer,
picking_instance_ids_buffer,
user_data: Vec::with_capacity(RESERVE_SIZE),
batches: Vec::with_capacity(16),
radius_boost_in_ui_points_for_outlines: 0.0,
}
Expand All @@ -65,10 +60,7 @@ where

/// Start of a new batch.
#[inline]
pub fn batch(
&mut self,
label: impl Into<DebugLabel>,
) -> PointCloudBatchBuilder<'_, PerPointUserData> {
pub fn batch(&mut self, label: impl Into<DebugLabel>) -> PointCloudBatchBuilder<'_> {
self.batches.push(PointCloudBatchInfo {
label: label.into(),
world_from_obj: glam::Mat4::IDENTITY,
Expand Down Expand Up @@ -105,30 +97,6 @@ where
})
}

// Iterate over all batches, yielding the batch info and a point vertex iterator zipped with its user data.
pub fn iter_vertices_and_userdata_by_batch(
&self,
) -> impl Iterator<
Item = (
&PointCloudBatchInfo,
impl Iterator<Item = (&PointCloudVertex, &PerPointUserData)>,
),
> {
let mut vertex_offset = 0;
self.batches.iter().map(move |batch| {
let out = (
batch,
self.vertices
.iter()
.zip(self.user_data.iter())
.skip(vertex_offset)
.take(batch.point_count as usize),
);
vertex_offset += batch.point_count as usize;
out
})
}

/// Finalizes the builder and returns a point cloud draw data with all the points added so far.
pub fn to_draw_data(
self,
Expand All @@ -138,16 +106,9 @@ where
}
}

pub struct PointCloudBatchBuilder<'a, PerPointUserData>(
&'a mut PointCloudBuilder<PerPointUserData>,
)
where
PerPointUserData: Default + Copy;
pub struct PointCloudBatchBuilder<'a>(&'a mut PointCloudBuilder);

impl<'a, PerPointUserData> Drop for PointCloudBatchBuilder<'a, PerPointUserData>
where
PerPointUserData: Default + Copy,
{
impl<'a> Drop for PointCloudBatchBuilder<'a> {
fn drop(&mut self) {
// Remove batch again if it wasn't actually used.
if self.0.batches.last().unwrap().point_count == 0 {
Expand All @@ -157,10 +118,7 @@ where
}
}

impl<'a, PerPointUserData> PointCloudBatchBuilder<'a, PerPointUserData>
where
PerPointUserData: Default + Copy,
{
impl<'a> PointCloudBatchBuilder<'a> {
#[inline]
fn batch_mut(&mut self) -> &mut PointCloudBatchInfo {
self.0
Expand Down Expand Up @@ -200,13 +158,6 @@ where
self.0.vertices.len() - self.0.picking_instance_ids_buffer.num_written(),
));
}

if self.0.user_data.len() < self.0.vertices.len() {
self.0.user_data.extend(
std::iter::repeat(PerPointUserData::default())
.take(self.0.vertices.len() - self.0.user_data.len()),
);
}
}

#[inline]
Expand All @@ -222,7 +173,7 @@ where
&mut self,
size_hint: usize,
positions: impl Iterator<Item = glam::Vec3>,
) -> PointsBuilder<'_, PerPointUserData> {
) -> PointsBuilder<'_> {
// TODO(jleibs): Figure out if we can plumb-through proper support for `Iterator::size_hints()`
// or potentially make `FixedSizedIterator` work correctly. This should be possible size the
// underlying arrow structures are of known-size, but carries some complexity with the amount of
Expand All @@ -232,7 +183,6 @@ where
self.extend_defaults();

debug_assert_eq!(self.0.vertices.len(), self.0.color_buffer.num_written());
debug_assert_eq!(self.0.vertices.len(), self.0.user_data.len());

let old_size = self.0.vertices.len();

Expand All @@ -245,8 +195,6 @@ where
let num_points = self.0.vertices.len() - old_size;
self.batch_mut().point_count += num_points as u32;

self.0.user_data.reserve(num_points);

let new_range = old_size..self.0.vertices.len();

let max_points = self.0.vertices.len();
Expand All @@ -256,7 +204,6 @@ where
max_points,
colors: &mut self.0.color_buffer,
picking_instance_ids: &mut self.0.picking_instance_ids_buffer,
user_data: &mut self.0.user_data,
additional_outline_mask_ids: &mut self
.0
.batches
Expand All @@ -268,24 +215,22 @@ where
}

#[inline]
pub fn add_point(&mut self, position: glam::Vec3) -> PointBuilder<'_, PerPointUserData> {
pub fn add_point(&mut self, position: glam::Vec3) -> PointBuilder<'_> {
self.extend_defaults();

debug_assert_eq!(self.0.vertices.len(), self.0.color_buffer.num_written());
debug_assert_eq!(self.0.vertices.len(), self.0.user_data.len());

let vertex_index = self.0.vertices.len() as u32;
self.0.vertices.push(PointCloudVertex {
position,
radius: Size::AUTO,
});
self.0.user_data.push(Default::default());
self.batch_mut().point_count += 1;

PointBuilder {
vertex: self.0.vertices.last_mut().unwrap(),
color: &mut self.0.color_buffer,
user_data: self.0.user_data.last_mut().unwrap(),
picking_instance_id: &mut self.0.picking_instance_ids_buffer,
vertex_index,
additional_outline_mask_ids: &mut self
.0
Expand All @@ -308,13 +253,13 @@ where
&mut self,
size_hint: usize,
positions: impl Iterator<Item = glam::Vec2>,
) -> PointsBuilder<'_, PerPointUserData> {
) -> PointsBuilder<'_> {
self.add_points(size_hint, positions.map(|p| p.extend(0.0)))
}

/// Adds a single 2D point. Uses an autogenerated depth value.
#[inline]
pub fn add_point_2d(&mut self, position: glam::Vec2) -> PointBuilder<'_, PerPointUserData> {
pub fn add_point_2d(&mut self, position: glam::Vec2) -> PointBuilder<'_> {
self.add_point(position.extend(0.0))
}

Expand All @@ -331,19 +276,17 @@ where
}

// TODO(andreas): Should remove single-point builder, practically this never makes sense as we're almost always dealing with arrays of points.
pub struct PointBuilder<'a, PerPointUserData> {
pub struct PointBuilder<'a> {
vertex: &'a mut PointCloudVertex,
color: &'a mut CpuWriteGpuReadBuffer<Color32>,
user_data: &'a mut PerPointUserData,
picking_instance_id: &'a mut CpuWriteGpuReadBuffer<PickingLayerInstanceId>,
vertex_index: u32,

additional_outline_mask_ids: &'a mut Vec<(std::ops::Range<u32>, OutlineMaskPreference)>,
outline_mask_id: OutlineMaskPreference,
}

impl<'a, PerPointUserData> PointBuilder<'a, PerPointUserData>
where
PerPointUserData: Clone,
{
impl<'a> PointBuilder<'a> {
#[inline]
pub fn radius(self, radius: Size) -> Self {
self.vertex.radius = radius;
Expand All @@ -357,21 +300,24 @@ where
self
}

pub fn user_data(self, data: PerPointUserData) -> Self {
*self.user_data = data;
self
}

/// Pushes additional outline mask ids for this point
///
/// Prefer the `overall_outline_mask_ids` setting to set the outline mask ids for the entire batch whenever possible!
#[inline]
pub fn outline_mask_id(mut self, outline_mask_id: OutlineMaskPreference) -> Self {
self.outline_mask_id = outline_mask_id;
self
}

/// This mustn't call this more than once.
#[inline]
pub fn picking_instance_id(self, picking_instance_id: PickingLayerInstanceId) -> Self {
self.picking_instance_id.push(picking_instance_id);
self
}
}

impl<'a, PerPointUserData> Drop for PointBuilder<'a, PerPointUserData> {
impl<'a> Drop for PointBuilder<'a> {
fn drop(&mut self) {
if self.outline_mask_id.is_some() {
self.additional_outline_mask_ids.push((
Expand All @@ -382,21 +328,17 @@ impl<'a, PerPointUserData> Drop for PointBuilder<'a, PerPointUserData> {
}
}

pub struct PointsBuilder<'a, PerPointUserData> {
pub struct PointsBuilder<'a> {
// Vertices is a slice, which radii will update
vertices: &'a mut [PointCloudVertex],
max_points: usize,
colors: &'a mut CpuWriteGpuReadBuffer<Color32>,
picking_instance_ids: &'a mut CpuWriteGpuReadBuffer<PickingLayerInstanceId>,
user_data: &'a mut Vec<PerPointUserData>,
additional_outline_mask_ids: &'a mut Vec<(std::ops::Range<u32>, OutlineMaskPreference)>,
start_vertex_index: u32,
}

impl<'a, PerPointUserData> PointsBuilder<'a, PerPointUserData>
where
PerPointUserData: Clone,
{
impl<'a> PointsBuilder<'a> {
/// Assigns radii to all points.
///
/// This mustn't call this more than once.
Expand Down Expand Up @@ -440,19 +382,6 @@ where
self
}

/// Assigns user data for all points in this builder.
///
/// This mustn't call this more than once.
///
/// User data is currently not available on the GPU.
#[inline]
pub fn user_data(self, data: impl Iterator<Item = PerPointUserData>) -> Self {
crate::profile_function!();
self.user_data
.extend(data.take(self.max_points - self.user_data.len()));
self
}

/// Pushes additional outline mask ids for a specific range of points.
/// The range is relative to this builder's range, not the entire batch.
///
Expand Down
Loading

1 comment on commit f7cdc66

@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: f7cdc66 Previous: 5da248d Ratio
datastore/num_rows=1000/num_instances=1000/packed=false/insert/default 12647423 ns/iter (± 771443) 10871026 ns/iter (± 350957) 1.16
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/default 1832 ns/iter (± 20) 1825 ns/iter (± 20) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/default 287 ns/iter (± 0) 280 ns/iter (± 1) 1.02
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/default 437 ns/iter (± 0) 434 ns/iter (± 1) 1.01
datastore/num_rows=1000/num_instances=1000/packed=false/range/default 12619455 ns/iter (± 957541) 11345752 ns/iter (± 529709) 1.11
mono_points_arrow/generate_message_bundles 49349168 ns/iter (± 956671) 45871565 ns/iter (± 793224) 1.08
mono_points_arrow/generate_messages 170422381 ns/iter (± 1445254) 152002884 ns/iter (± 1349616) 1.12
mono_points_arrow/encode_log_msg 197428348 ns/iter (± 1536666) 181778304 ns/iter (± 845219) 1.09
mono_points_arrow/encode_total 418867271 ns/iter (± 2157925) 378345137 ns/iter (± 2345952) 1.11
mono_points_arrow/decode_log_msg 250596891 ns/iter (± 1424614) 234493797 ns/iter (± 1013798) 1.07
mono_points_arrow/decode_message_bundles 85571845 ns/iter (± 1184302) 71050291 ns/iter (± 652376) 1.20
mono_points_arrow/decode_total 342919689 ns/iter (± 2500778) 311528052 ns/iter (± 1317039) 1.10
mono_points_arrow_batched/generate_message_bundles 43919041 ns/iter (± 1367501) 38035383 ns/iter (± 1042329) 1.15
mono_points_arrow_batched/generate_messages 9270537 ns/iter (± 989843) 7698709 ns/iter (± 533096) 1.20
mono_points_arrow_batched/encode_log_msg 1476663 ns/iter (± 7486) 1486983 ns/iter (± 3295) 0.99
mono_points_arrow_batched/encode_total 54538211 ns/iter (± 1869911) 48290705 ns/iter (± 1138444) 1.13
mono_points_arrow_batched/decode_log_msg 855861 ns/iter (± 3205) 861353 ns/iter (± 2424) 0.99
mono_points_arrow_batched/decode_message_bundles 13096938 ns/iter (± 880697) 11916978 ns/iter (± 559697) 1.10
mono_points_arrow_batched/decode_total 14899865 ns/iter (± 1022613) 13015582 ns/iter (± 598282) 1.14
batch_points_arrow/generate_message_bundles 333770 ns/iter (± 574) 344593 ns/iter (± 2060) 0.97
batch_points_arrow/generate_messages 6465 ns/iter (± 17) 6401 ns/iter (± 38) 1.01
batch_points_arrow/encode_log_msg 398721 ns/iter (± 1813) 401666 ns/iter (± 2424) 0.99
batch_points_arrow/encode_total 762505 ns/iter (± 3700) 765135 ns/iter (± 4155) 1.00
batch_points_arrow/decode_log_msg 351797 ns/iter (± 1023) 344846 ns/iter (± 1445) 1.02
batch_points_arrow/decode_message_bundles 2327 ns/iter (± 7) 2284 ns/iter (± 17) 1.02
batch_points_arrow/decode_total 360113 ns/iter (± 1637) 351331 ns/iter (± 1725) 1.02
arrow_mono_points/insert 7424209949 ns/iter (± 17492043) 6524020995 ns/iter (± 31705062) 1.14
arrow_mono_points/query 1808539 ns/iter (± 17588) 1794474 ns/iter (± 16825) 1.01
arrow_batch_points/insert 3188138 ns/iter (± 24782) 3230521 ns/iter (± 12475) 0.99
arrow_batch_points/query 16354 ns/iter (± 25) 16291 ns/iter (± 83) 1.00
arrow_batch_vecs/insert 44563 ns/iter (± 71) 45236 ns/iter (± 292) 0.99
arrow_batch_vecs/query 389254 ns/iter (± 4163) 388092 ns/iter (± 2317) 1.00
tuid/Tuid::random 34 ns/iter (± 0) 41 ns/iter (± 0) 0.83

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

Please sign in to comment.