Skip to content

Commit

Permalink
Fix too many points crash (#1822)
Browse files Browse the repository at this point in the history
* Simplify point cloud builder and fix crash on too many points
Fixes #1779
* faster point cloud population by not chaining iterators with default values
  • Loading branch information
Wumpf authored Apr 13, 2023
1 parent fd870f0 commit 9716235
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 204 deletions.
41 changes: 19 additions & 22 deletions crates/re_renderer/examples/2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,28 +150,25 @@ impl framework::Example for Render2D {
// 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);
point_cloud_builder
.batch("points")
.add_points_2d(
4,
[
glam::vec2(500.0, 120.0),
glam::vec2(520.0, 120.0),
glam::vec2(540.0, 120.0),
glam::vec2(560.0, 120.0),
]
.into_iter(),
)
.radii(
[
Size::new_scene(4.0),
Size::new_points(4.0),
Size::AUTO,
Size::AUTO_LARGE,
]
.into_iter(),
)
.colors(std::iter::repeat(Color32::from_rgb(55, 180, 1)).take(4));
point_cloud_builder.batch("points").add_points_2d(
4,
[
glam::vec2(500.0, 120.0),
glam::vec2(520.0, 120.0),
glam::vec2(540.0, 120.0),
glam::vec2(560.0, 120.0),
]
.into_iter(),
[
Size::new_scene(4.0),
Size::new_points(4.0),
Size::AUTO,
Size::AUTO_LARGE,
]
.into_iter(),
std::iter::repeat(Color32::from_rgb(55, 180, 1)),
std::iter::repeat(re_renderer::PickingLayerInstanceId::default()),
);

// Pile stuff to test for overlap handling
{
Expand Down
12 changes: 7 additions & 5 deletions crates/re_renderer/examples/depth_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,13 @@ impl RenderDepthClouds {
.multiunzip();

let mut builder = PointCloudBuilder::new(re_ctx);
builder
.batch("backprojected point cloud")
.add_points(num_points as _, points.into_iter())
.colors(colors.into_iter())
.radii(radii.into_iter());
builder.batch("backprojected point cloud").add_points(
num_points as _,
points.into_iter(),
radii.into_iter(),
colors.into_iter(),
std::iter::empty::<re_renderer::PickingLayerInstanceId>(),
);

builder.to_draw_data(re_ctx).unwrap()
};
Expand Down
7 changes: 4 additions & 3 deletions crates/re_renderer/examples/multiview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,10 @@ impl Example for Multiview {
.add_points(
self.random_points_positions.len(),
self.random_points_positions.iter().cloned(),
)
.radii(self.random_points_radii.iter().cloned())
.colors(self.random_points_colors.iter().cloned());
self.random_points_radii.iter().cloned(),
self.random_points_colors.iter().cloned(),
std::iter::empty::<re_renderer::PickingLayerInstanceId>(),
);

let point_cloud = builder.to_draw_data(re_ctx).unwrap();
let meshes = build_mesh_instances(
Expand Down
8 changes: 4 additions & 4 deletions crates/re_renderer/examples/picking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ impl framework::Example for Picking {
.add_points(
point_set.positions.len(),
point_set.positions.iter().cloned(),
)
.radii(point_set.radii.iter().cloned())
.colors(point_set.colors.iter().cloned())
.picking_instance_ids(point_set.picking_ids.iter().cloned());
point_set.radii.iter().cloned(),
point_set.colors.iter().cloned(),
point_set.picking_ids.iter().cloned(),
);
}
view_builder.queue_draw(&point_builder.to_draw_data(re_ctx).unwrap());

Expand Down
6 changes: 5 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 @@ -65,11 +65,15 @@ where
/// Pushes several elements into the buffer.
///
/// Panics if there are more elements than there is space in the buffer.
///
/// Returns number of elements pushed.
#[inline]
pub fn extend(&mut self, elements: impl Iterator<Item = T>) {
pub fn extend(&mut self, elements: impl Iterator<Item = T>) -> usize {
let num_written_before = self.num_written();
for element in elements {
self.push(element);
}
self.num_written() - num_written_before
}

/// Pushes a single element into the buffer and advances the write pointer.
Expand Down
208 changes: 85 additions & 123 deletions crates/re_renderer/src/point_cloud_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ impl<'a> Drop for PointCloudBatchBuilder<'a> {
if self.0.batches.last().unwrap().point_count == 0 {
self.0.batches.pop();
}
self.extend_defaults();
}
}

Expand All @@ -141,92 +140,110 @@ impl<'a> PointCloudBatchBuilder<'a> {
self
}

/// Each time we `add_points`, or upon builder drop, make sure that we
/// fill in any additional colors and user-data to have matched vectors.
fn extend_defaults(&mut self) {
if self.0.color_buffer.num_written() < self.0.vertices.len() {
self.0.color_buffer.extend(
std::iter::repeat(Color32::WHITE)
.take(self.0.vertices.len() - self.0.color_buffer.num_written()),
);
}

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

#[inline]
/// Add several 3D points
///
/// Returns a `PointBuilder` which can be used to set the colors, radii, and user-data for the points.
///
/// Params:
/// - `size_hint`: The `PointBuilder` will pre-allocate buffers to accommodate up to this number of points.
/// The resulting point batch, will still be determined by the length of the iterator.
/// - `positions`: An iterable of the positions of the collection of points
/// Will *always* add `num_points`, no matter how many elements are in the iterators.
/// Missing elements will be filled up with defaults (in case of positions that's the origin)
///
/// TODO(#957): Clamps number of points to the allowed per-builder maximum.
#[inline]
pub fn add_points(
&mut self,
size_hint: usize,
mut self,
mut num_points: usize,
positions: impl Iterator<Item = glam::Vec3>,
) -> PointsBuilder<'_> {
radii: impl Iterator<Item = Size>,
colors: impl Iterator<Item = Color32>,
picking_instance_ids: impl Iterator<Item = PickingLayerInstanceId>,
) -> Self {
// 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
// chaining, joining, filtering, etc. that happens along the way.
crate::profile_function!();

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.picking_instance_ids_buffer.num_written()
);

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

self.0.vertices.reserve(size_hint);
self.0.vertices.extend(positions.map(|p| PointCloudVertex {
position: p,
radius: Size::AUTO,
}));

let num_points = self.0.vertices.len() - old_size;
if num_points + self.0.vertices.len() > PointCloudDrawData::MAX_NUM_POINTS {
re_log::error_once!(
"Reached maximum number of supported points of {}.
See also https://github.com/rerun-io/rerun/issues/957",
PointCloudDrawData::MAX_NUM_POINTS
);
num_points = PointCloudDrawData::MAX_NUM_POINTS - self.0.vertices.len();
}
if num_points == 0 {
return self;
}
self.batch_mut().point_count += num_points as u32;

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

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

PointsBuilder {
vertices: &mut self.0.vertices[new_range],
max_points,
colors: &mut self.0.color_buffer,
picking_instance_ids: &mut self.0.picking_instance_ids_buffer,
additional_outline_mask_ids: &mut self
{
crate::profile_scope!("positions");
let num_before = self.0.vertices.len();
self.0.vertices.extend(
positions
.take(num_points)
.zip(radii.take(num_points))
.map(|(position, radius)| PointCloudVertex { position, radius }),
);
// Fill up with defaults. Doing this in a separate step is faster than chaining the iterator.
let num_default = num_points - (self.0.vertices.len() - num_before);
self.0.vertices.extend(
std::iter::repeat(PointCloudVertex {
position: glam::Vec3::ZERO,
radius: Size::AUTO,
})
.take(num_default),
);
}
{
crate::profile_scope!("colors");
let num_written = self.0.color_buffer.extend(colors.take(num_points));
// Fill up with defaults. Doing this in a separate step is faster than chaining the iterator.
self.0
.color_buffer
.extend(std::iter::repeat(Color32::TRANSPARENT).take(num_points - num_written));
}
{
crate::profile_scope!("picking_instance_ids");
let num_written = self
.0
.batches
.last_mut()
.unwrap()
.additional_outline_mask_ids_vertex_ranges,
start_vertex_index: old_size as _,
.picking_instance_ids_buffer
.extend(picking_instance_ids.take(num_points));
// Fill up with defaults. Doing this in a separate step is faster than chaining the iterator.
self.0.picking_instance_ids_buffer.extend(
std::iter::repeat(PickingLayerInstanceId::default()).take(num_points - num_written),
);
}

self
}

/// Adds several 2D points. Uses an autogenerated depth value, the same for all points passed.
///
/// Params:
/// - `size_hint`: The `PointBuilder` will pre-allocate buffers to accommodate up to this number of points.
/// The resulting point batch, will be the size of the length of the `positions` iterator.
/// - `positions`: An iterable of the positions of the collection of points
/// Will *always* add `num_points`, no matter how many elements are in the iterators.
/// Missing elements will be filled up with defaults (in case of positions that's the origin)
#[inline]
pub fn add_points_2d(
&mut self,
size_hint: usize,
self,
num_points: usize,
positions: impl Iterator<Item = glam::Vec2>,
) -> PointsBuilder<'_> {
self.add_points(size_hint, positions.map(|p| p.extend(0.0)))
radii: impl Iterator<Item = Size>,
colors: impl Iterator<Item = Color32>,
picking_instance_ids: impl Iterator<Item = PickingLayerInstanceId>,
) -> Self {
self.add_points(
num_points,
positions.map(|p| p.extend(0.0)),
radii,
colors,
picking_instance_ids,
)
}

/// Set flags for this batch.
Expand All @@ -235,80 +252,25 @@ impl<'a> PointCloudBatchBuilder<'a> {
self
}

/// Sets the picking object id for the current batch.
pub fn picking_object_id(mut self, picking_object_id: PickingLayerObjectId) -> Self {
self.batch_mut().picking_object_id = picking_object_id;
self
}
}

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>,
additional_outline_mask_ids: &'a mut Vec<(std::ops::Range<u32>, OutlineMaskPreference)>,
start_vertex_index: u32,
}

impl<'a> PointsBuilder<'a> {
/// Assigns radii to all points.
///
/// This mustn't call this more than once.
///
/// If the iterator doesn't cover all points, some will not be assigned.
/// If the iterator provides more values than there are points, the extra values will be ignored.
#[inline]
pub fn radii(self, radii: impl Iterator<Item = Size>) -> Self {
// TODO(andreas): This seems like an argument for moving radius
// to a separate storage
crate::profile_function!();
for (point, radius) in self.vertices.iter_mut().zip(radii) {
point.radius = radius;
}
self
}

/// Assigns colors to all points.
///
/// This mustn't call this more than once.
///
/// If the iterator doesn't cover all points, some will not be assigned.
/// If the iterator provides more values than there are points, the extra values will be ignored.
#[inline]
pub fn colors(self, colors: impl Iterator<Item = Color32>) -> Self {
crate::profile_function!();
self.colors
.extend(colors.take(self.max_points - self.colors.num_written()));
self
}

#[inline]
pub fn picking_instance_ids(
self,
picking_instance_ids: impl Iterator<Item = PickingLayerInstanceId>,
) -> Self {
crate::profile_function!();
self.picking_instance_ids.extend(
picking_instance_ids.take(self.max_points - self.picking_instance_ids.num_written()),
);
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.
/// The range is relative to this batch.
///
/// Prefer the `overall_outline_mask_ids` setting to set the outline mask ids for the entire batch whenever possible!
#[inline]
pub fn push_additional_outline_mask_ids_for_range(
self,
mut self,
range: std::ops::Range<u32>,
ids: OutlineMaskPreference,
) -> Self {
self.additional_outline_mask_ids.push((
(range.start + self.start_vertex_index)..(range.end + self.start_vertex_index),
ids,
));
self.batch_mut()
.additional_outline_mask_ids_vertex_ranges
.push((range, ids));
self
}
}
3 changes: 2 additions & 1 deletion crates/re_renderer/src/renderer/point_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ pub struct PointCloudBatchInfo {
}

/// Description of a point cloud.
#[derive(Clone)]
pub struct PointCloudVertex {
/// Connected points. Must be at least 2.
pub position: glam::Vec3,
Expand Down Expand Up @@ -225,7 +226,7 @@ impl PointCloudDrawData {
0
);

let vertices = if vertices.len() >= Self::MAX_NUM_POINTS {
let vertices = if vertices.len() > Self::MAX_NUM_POINTS {
re_log::error_once!(
"Reached maximum number of supported points. Clamping down to {}, passed were {}.
See also https://github.com/rerun-io/rerun/issues/957",
Expand Down
Loading

1 comment on commit 9716235

@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: 9716235 Previous: cb95f5e Ratio
datastore/num_rows=1000/num_instances=1000/packed=false/insert/default 2919746 ns/iter (± 117627) 2800753 ns/iter (± 17865) 1.04
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/default 371 ns/iter (± 2) 371 ns/iter (± 1) 1
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/default 264 ns/iter (± 0) 274 ns/iter (± 1) 0.96
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/default 421 ns/iter (± 0) 422 ns/iter (± 17) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/range/default 3021808 ns/iter (± 77617) 2949675 ns/iter (± 37931) 1.02
datastore/num_rows=1000/num_instances=1000/gc/default 2372352 ns/iter (± 2752) 2366760 ns/iter (± 11699) 1.00
mono_points_arrow/generate_message_bundles 30048583 ns/iter (± 860350) 25690583 ns/iter (± 1360299) 1.17
mono_points_arrow/generate_messages 126572200 ns/iter (± 1053707) 111183648 ns/iter (± 879567) 1.14
mono_points_arrow/encode_log_msg 157797886 ns/iter (± 2029752) 140163972 ns/iter (± 609784) 1.13
mono_points_arrow/encode_total 313926959 ns/iter (± 2267498) 280532139 ns/iter (± 1640498) 1.12
mono_points_arrow/decode_log_msg 190531535 ns/iter (± 836099) 177118254 ns/iter (± 1235545) 1.08
mono_points_arrow/decode_message_bundles 69927957 ns/iter (± 740183) 54411011 ns/iter (± 1216228) 1.29
mono_points_arrow/decode_total 258381263 ns/iter (± 2079058) 233024184 ns/iter (± 1631503) 1.11
mono_points_arrow_batched/generate_message_bundles 21567474 ns/iter (± 1560597) 18224506 ns/iter (± 293461) 1.18
mono_points_arrow_batched/generate_messages 4384761 ns/iter (± 243680) 4082562 ns/iter (± 139756) 1.07
mono_points_arrow_batched/encode_log_msg 1353438 ns/iter (± 7106) 1388313 ns/iter (± 3959) 0.97
mono_points_arrow_batched/encode_total 29874763 ns/iter (± 1552578) 24871569 ns/iter (± 776979) 1.20
mono_points_arrow_batched/decode_log_msg 783985 ns/iter (± 3504) 779621 ns/iter (± 2203) 1.01
mono_points_arrow_batched/decode_message_bundles 7644076 ns/iter (± 200293) 7545789 ns/iter (± 51407) 1.01
mono_points_arrow_batched/decode_total 8612627 ns/iter (± 304594) 8368617 ns/iter (± 56581) 1.03
batch_points_arrow/generate_message_bundles 239275 ns/iter (± 594) 237398 ns/iter (± 1455) 1.01
batch_points_arrow/generate_messages 5026 ns/iter (± 17) 5029 ns/iter (± 34) 1.00
batch_points_arrow/encode_log_msg 262049 ns/iter (± 1974) 255540 ns/iter (± 1386) 1.03
batch_points_arrow/encode_total 529482 ns/iter (± 2881) 530531 ns/iter (± 3390) 1.00
batch_points_arrow/decode_log_msg 212874 ns/iter (± 726) 208755 ns/iter (± 1143) 1.02
batch_points_arrow/decode_message_bundles 1853 ns/iter (± 6) 1817 ns/iter (± 14) 1.02
batch_points_arrow/decode_total 218081 ns/iter (± 677) 216531 ns/iter (± 1230) 1.01
arrow_mono_points/insert 2543943526 ns/iter (± 5962253) 2315994449 ns/iter (± 3540663) 1.10
arrow_mono_points/query 1189291 ns/iter (± 13750) 1174175 ns/iter (± 9928) 1.01
arrow_batch_points/insert 1160594 ns/iter (± 4770) 1140866 ns/iter (± 6596) 1.02
arrow_batch_points/query 14751 ns/iter (± 57) 14673 ns/iter (± 120) 1.01
arrow_batch_vecs/insert 26293 ns/iter (± 49) 26138 ns/iter (± 141) 1.01
arrow_batch_vecs/query 325169 ns/iter (± 418) 323075 ns/iter (± 2524) 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.