re_renderer/
point_cloud_builder.rs1use re_log::{ResultExt as _, debug_assert_eq};
2
3use crate::allocator::DataTextureSource;
4use crate::draw_phases::PickingLayerObjectId;
5use crate::renderer::gpu_data::PositionRadius;
6use crate::renderer::{
7 PointCloudBatchFlags, PointCloudBatchInfo, PointCloudDrawData, PointCloudDrawDataError,
8};
9use crate::{
10 Color32, CpuWriteGpuReadError, DepthOffset, Label, OutlineMaskPreference,
11 PickingLayerInstanceId, RenderContext, Size,
12};
13
14pub struct PointCloudBuilder<'ctx> {
16 pub(crate) ctx: &'ctx RenderContext,
17
18 pub(crate) position_radius_buffer: DataTextureSource<'ctx, PositionRadius>,
20
21 pub(crate) color_buffer: DataTextureSource<'ctx, Color32>,
22 pub(crate) picking_instance_ids_buffer: DataTextureSource<'ctx, PickingLayerInstanceId>,
23
24 pub(crate) batches: Vec<PointCloudBatchInfo>,
25
26 pub(crate) radius_boost_in_ui_points_for_outlines: f32,
27}
28
29impl<'ctx> PointCloudBuilder<'ctx> {
30 pub fn new(ctx: &'ctx RenderContext) -> Self {
31 Self {
32 ctx,
33 position_radius_buffer: DataTextureSource::new(ctx),
34 color_buffer: DataTextureSource::new(ctx),
35 picking_instance_ids_buffer: DataTextureSource::new(ctx),
36 batches: Vec::with_capacity(16),
37 radius_boost_in_ui_points_for_outlines: 0.0,
38 }
39 }
40
41 pub fn reserve(
44 &mut self,
45 expected_number_of_additional_points: usize,
46 ) -> Result<usize, CpuWriteGpuReadError> {
47 re_tracing::profile_function_if!(100_000 < expected_number_of_additional_points);
48
49 self.position_radius_buffer
51 .reserve(expected_number_of_additional_points)?;
52 self.color_buffer
53 .reserve(expected_number_of_additional_points)?;
54 self.picking_instance_ids_buffer
55 .reserve(expected_number_of_additional_points)
56 }
57
58 pub fn radius_boost_in_ui_points_for_outlines(
60 &mut self,
61 radius_boost_in_ui_points_for_outlines: f32,
62 ) {
63 self.radius_boost_in_ui_points_for_outlines = radius_boost_in_ui_points_for_outlines;
64 }
65
66 #[inline]
68 pub fn batch(&mut self, label: impl Into<Label>) -> PointCloudBatchBuilder<'_, 'ctx> {
69 self.batches.push(PointCloudBatchInfo {
70 label: label.into(),
71 ..PointCloudBatchInfo::default()
72 });
73
74 PointCloudBatchBuilder(self)
75 }
76
77 #[inline]
78 pub fn batch_with_info(
79 &mut self,
80 info: PointCloudBatchInfo,
81 ) -> PointCloudBatchBuilder<'_, 'ctx> {
82 self.batches.push(info);
83
84 PointCloudBatchBuilder(self)
85 }
86
87 pub fn into_draw_data(self) -> Result<PointCloudDrawData, PointCloudDrawDataError> {
89 PointCloudDrawData::new(self)
90 }
91}
92
93pub struct PointCloudBatchBuilder<'a, 'ctx>(&'a mut PointCloudBuilder<'ctx>);
94
95impl Drop for PointCloudBatchBuilder<'_, '_> {
96 fn drop(&mut self) {
97 if self.0.batches.last().unwrap().point_count == 0 {
99 self.0.batches.pop();
100 }
101 }
102}
103
104impl PointCloudBatchBuilder<'_, '_> {
105 #[inline]
106 fn batch_mut(&mut self) -> &mut PointCloudBatchInfo {
107 self.0
108 .batches
109 .last_mut()
110 .expect("batch should have been added on PointCloudBatchBuilder creation")
111 }
112
113 #[inline]
115 pub fn world_from_obj(mut self, world_from_obj: glam::Affine3A) -> Self {
116 self.batch_mut().world_from_obj = world_from_obj;
117 self
118 }
119
120 #[inline]
122 pub fn outline_mask_ids(mut self, outline_mask_ids: OutlineMaskPreference) -> Self {
123 self.batch_mut().overall_outline_mask_ids = outline_mask_ids;
124 self
125 }
126
127 #[inline]
129 pub fn depth_offset(mut self, depth_offset: DepthOffset) -> Self {
130 self.batch_mut().depth_offset = depth_offset;
131 self
132 }
133
134 #[inline]
145 pub fn add_points_slow(
146 self,
147 positions: &[glam::Vec3],
148 radii: &[Size],
149 colors: &[Color32],
150 picking_ids: &[PickingLayerInstanceId],
151 ) -> Self {
152 re_tracing::profile_function!();
153
154 let positions_and_radii = PositionRadius::from_many(positions, radii);
155 self.add_points(&positions_and_radii, colors, picking_ids)
156 }
157
158 #[inline]
165 pub fn add_points(
166 mut self,
167 positions_and_radii: &[PositionRadius],
168 colors: &[Color32],
169 picking_ids: &[PickingLayerInstanceId],
170 ) -> Self {
171 re_tracing::profile_function!();
172
173 debug_assert_eq!(
174 self.0.position_radius_buffer.len(),
175 self.0.color_buffer.len()
176 );
177 debug_assert_eq!(
178 self.0.position_radius_buffer.len(),
179 self.0.picking_instance_ids_buffer.len()
180 );
181
182 let num_points = positions_and_radii.len();
183
184 let Some(num_available_points) = self
187 .0
188 .position_radius_buffer
189 .reserve(num_points)
190 .ok_or_log_error()
191 else {
192 return self;
193 };
194
195 let num_points = if num_points > num_available_points {
196 re_log::error_once!(
197 "Reached maximum number of points for point cloud of {}. Ignoring all excess points.",
198 self.0.position_radius_buffer.len() + num_available_points
199 );
200 num_available_points
201 } else {
202 num_points
203 };
204
205 if num_points == 0 {
206 return self;
207 }
208
209 let positions_and_radii =
211 &positions_and_radii[0..num_points.min(positions_and_radii.len())];
212 let colors = &colors[0..num_points.min(colors.len())];
213 let picking_ids = &picking_ids[0..num_points.min(picking_ids.len())];
214
215 self.batch_mut().point_count += num_points as u32;
216
217 {
218 re_tracing::profile_scope!("positions_and_radii");
219 self.0
220 .position_radius_buffer
221 .extend_from_slice(positions_and_radii)
222 .ok_or_log_error();
223 }
224 {
225 re_tracing::profile_scope!("colors");
226
227 self.0
228 .color_buffer
229 .extend_from_slice(colors)
230 .ok_or_log_error();
231 self.0
232 .color_buffer
233 .add_n(Color32::WHITE, num_points.saturating_sub(colors.len())) .ok_or_log_error();
235 }
236 {
237 re_tracing::profile_scope!("picking_ids");
238
239 self.0
240 .picking_instance_ids_buffer
241 .extend_from_slice(picking_ids)
242 .ok_or_log_error();
243 self.0
244 .picking_instance_ids_buffer
245 .add_n(
246 PickingLayerInstanceId::default(),
247 num_points.saturating_sub(picking_ids.len()),
248 )
249 .ok_or_log_error();
250 }
251
252 self
253 }
254
255 #[inline]
261 pub fn add_points_2d(
262 self,
263 positions: &[glam::Vec3],
264 radii: &[Size],
265 colors: &[Color32],
266 picking_ids: &[PickingLayerInstanceId],
267 ) -> Self {
268 re_tracing::profile_function!();
269 self.add_points_slow(positions, radii, colors, picking_ids)
270 .flags(PointCloudBatchFlags::FLAG_DRAW_AS_CIRCLES)
271 }
272
273 #[inline]
275 pub fn flags(mut self, flags: PointCloudBatchFlags) -> Self {
276 self.batch_mut().flags |= flags;
277 self
278 }
279
280 #[inline]
282 pub fn picking_object_id(mut self, picking_object_id: PickingLayerObjectId) -> Self {
283 self.batch_mut().picking_object_id = picking_object_id;
284 self
285 }
286
287 #[inline]
292 pub fn push_additional_outline_mask_ids_for_range(
293 mut self,
294 range: std::ops::Range<u32>,
295 ids: OutlineMaskPreference,
296 ) -> Self {
297 self.batch_mut()
298 .additional_outline_mask_ids_vertex_ranges
299 .push((range, ids));
300 self
301 }
302}