re_tf/transform_resolution_cache/
cache.rs1use std::sync::Arc;
2
3use ahash::HashMap;
4use parking_lot::{ArcRwLockReadGuard, RawRwLock, RwLock};
5use re_byte_size::SizeBytes;
6use re_chunk_store::ChunkStore;
7use re_entity_db::EntityDb;
8use re_log::{debug_assert, debug_assert_eq};
9use re_log_types::{TimeInt, TimelineName};
10use re_sdk_types::archetypes;
11
12use crate::frame_id_registry::FrameIdRegistry;
13use crate::transform_aspect::TransformAspect;
14
15use super::cached_transforms_for_timeline::CachedTransformsForTimeline;
16use super::iter_child_frames_in_chunk;
17
18type ArcRwLock<T> = Arc<RwLock<T>>;
19
20pub struct TransformResolutionCache {
34 frame_id_registry: ArcRwLock<FrameIdRegistry>,
38
39 per_timeline: HashMap<TimelineName, ArcRwLock<CachedTransformsForTimeline>>,
44
45 static_timeline: ArcRwLock<CachedTransformsForTimeline>,
46}
47
48impl Default for TransformResolutionCache {
49 #[inline]
50 fn default() -> Self {
51 Self {
52 frame_id_registry: Default::default(),
53 per_timeline: Default::default(),
54 static_timeline: Arc::new(RwLock::new(CachedTransformsForTimeline::new_static())),
55 }
56 }
57}
58
59impl TransformResolutionCache {
60 pub fn new(entity_db: &EntityDb) -> Self {
64 re_tracing::profile_function!();
65
66 let mut cache = Self::default();
67
68 for chunk in entity_db.storage_engine().store().iter_physical_chunks() {
69 cache
71 .frame_id_registry
72 .write()
73 .register_all_frames_in_chunk(chunk);
74
75 let aspects = TransformAspect::transform_aspects_of(chunk);
76 if aspects.is_empty() {
77 continue;
78 }
79
80 if chunk.is_static() {
82 cache.add_static_chunk(chunk, aspects);
83 }
84 }
85
86 cache
87 }
88}
89
90impl SizeBytes for TransformResolutionCache {
91 fn heap_size_bytes(&self) -> u64 {
92 re_tracing::profile_function!();
93
94 let Self {
95 frame_id_registry,
96 per_timeline,
97 static_timeline,
98 } = self;
99
100 frame_id_registry.heap_size_bytes()
101 + per_timeline.heap_size_bytes()
102 + static_timeline.heap_size_bytes()
103 }
104}
105
106impl re_byte_size::MemUsageTreeCapture for TransformResolutionCache {
107 fn capture_mem_usage_tree(&self) -> re_byte_size::MemUsageTree {
108 re_tracing::profile_function!();
109
110 let Self {
111 frame_id_registry,
112 per_timeline,
113 static_timeline,
114 } = self;
115
116 let mut per_timeline_node = re_byte_size::MemUsageNode::new();
117 for (timeline, cached_transforms) in per_timeline {
118 per_timeline_node = per_timeline_node.with_child(
119 timeline.to_string(),
120 cached_transforms.read().capture_mem_usage_tree(),
121 );
122 }
123
124 re_byte_size::MemUsageNode::new()
125 .with_child("frame_id_registry", frame_id_registry.total_size_bytes())
126 .with_child("per_timeline", per_timeline_node.into_tree())
127 .with_child("static_timeline", static_timeline.total_size_bytes())
128 .into_tree()
129 }
130}
131
132impl TransformResolutionCache {
133 #[inline]
135 pub fn frame_id_registry(&self) -> ArcRwLockReadGuard<RawRwLock, FrameIdRegistry> {
136 self.frame_id_registry.read_arc()
137 }
138
139 #[inline]
141 pub fn transforms_for_timeline(
142 &self,
143 timeline: TimelineName,
144 ) -> ArcRwLockReadGuard<RawRwLock, CachedTransformsForTimeline> {
145 if let Some(per_timeline) = self.per_timeline.get(&timeline) {
146 per_timeline.read_arc()
147 } else {
148 self.static_timeline.read_arc()
149 }
150 }
151
152 #[inline]
154 pub fn cached_timelines(&self) -> impl Iterator<Item = TimelineName> + '_ {
155 self.per_timeline.keys().copied()
156 }
157
158 pub fn ensure_timeline_is_initialized(
160 &mut self,
161 chunk_store: &ChunkStore,
162 timeline: TimelineName,
163 ) {
164 re_tracing::profile_function!(timeline);
165
166 let static_timeline = self.static_timeline.read();
167 let frame_id_registry = self.frame_id_registry.read();
168
169 self.per_timeline.entry(timeline).or_insert_with(|| {
170 Arc::new(RwLock::new(CachedTransformsForTimeline::new_temporal(
171 timeline,
172 &static_timeline,
173 &frame_id_registry,
174 chunk_store,
175 )))
176 });
177 }
178
179 pub fn evict_timeline_cache(&mut self, timeline: TimelineName) {
181 re_tracing::profile_function!(); self.per_timeline.remove(&timeline);
183 }
184
185 pub fn process_store_events<'a>(
196 &mut self,
197 events: impl Iterator<Item = &'a re_chunk_store::ChunkStoreEvent>,
198 ) {
199 re_tracing::profile_function!();
200
201 for event in events {
202 let Some(delta_chunk) = event.delta_chunk() else {
206 continue; };
208
209 self.frame_id_registry
212 .write()
213 .register_all_frames_in_chunk(delta_chunk);
214
215 let aspects = TransformAspect::transform_aspects_of(delta_chunk);
216 if aspects.is_empty() {
217 continue;
218 }
219
220 if event.is_deletion() {
221 self.remove_chunk(delta_chunk, aspects);
222 } else if delta_chunk.is_static() {
223 self.add_static_chunk(delta_chunk, aspects);
224 } else {
225 self.add_temporal_chunk(delta_chunk, aspects);
226 }
227 }
228 }
229
230 fn add_temporal_chunk(&self, chunk: &re_chunk_store::Chunk, aspects: TransformAspect) {
231 re_tracing::profile_function!(format!(
232 "{} rows, {}",
233 chunk.num_rows(),
234 chunk.entity_path()
235 ));
236
237 debug_assert!(!chunk.is_static());
238
239 let static_timeline = self.static_timeline.read();
240 let frame_id_registry = self.frame_id_registry.read();
241
242 for timeline in chunk.timelines().keys() {
243 let Some(per_timeline) = self.per_timeline.get(timeline) else {
245 continue;
246 };
247 let mut per_timeline = per_timeline.write();
248
249 per_timeline.add_temporal_chunk(
250 chunk,
251 aspects,
252 *timeline,
253 &static_timeline,
254 &frame_id_registry,
255 );
256 }
257 }
258
259 fn add_static_chunk(&mut self, chunk: &re_chunk_store::Chunk, aspects: TransformAspect) {
260 re_tracing::profile_function!();
261
262 debug_assert!(chunk.is_static());
263
264 let entity_path = chunk.entity_path();
265 let place_holder_timeline = TimelineName::new("ignored for static chunk");
266
267 let transform_child_frame_component =
268 archetypes::Transform3D::descriptor_child_frame().component;
269 let pinhole_child_frame_component = archetypes::Pinhole::descriptor_child_frame().component;
270
271 let mut static_timeline = self.static_timeline.write();
272 let frame_id_registry = self.frame_id_registry.read();
273
274 if aspects.contains(TransformAspect::Frame) {
277 for (time, frame) in iter_child_frames_in_chunk(
278 chunk,
279 place_holder_timeline,
280 transform_child_frame_component,
281 ) {
282 debug_assert_eq!(time, TimeInt::STATIC);
283
284 let frame_transforms = static_timeline.get_or_create_tree_transforms_static(
285 entity_path,
286 frame,
287 &frame_id_registry,
288 );
289 frame_transforms.invalidate_transform_at(TimeInt::STATIC);
290
291 #[cfg_attr(not(debug_assertions), expect(clippy::for_kv_map))]
292 for (_timeline, per_timeline) in &mut self.per_timeline {
293 let mut per_timeline_guard = per_timeline.write();
296 let transforms = per_timeline_guard.get_or_create_tree_transforms_static(
297 entity_path,
298 frame,
299 &frame_id_registry,
300 );
301 transforms.invalidate_transform_at(TimeInt::STATIC);
302
303 #[cfg(debug_assertions)]
305 {
306 transforms.timeline = Some(*_timeline);
307 }
308 }
309 }
310 }
311 if aspects.contains(TransformAspect::Pose) {
312 let frame_transforms =
313 static_timeline.get_or_create_pose_transforms_static(entity_path);
314 frame_transforms.invalidate_at(TimeInt::STATIC);
315
316 for per_timeline in self.per_timeline.values_mut() {
317 per_timeline
318 .write()
319 .get_or_create_pose_transforms_temporal(entity_path, &static_timeline)
320 .invalidate_at(TimeInt::STATIC);
321 }
322 }
323 if aspects.contains(TransformAspect::PinholeOrViewCoordinates) {
324 for (time, frame) in iter_child_frames_in_chunk(
325 chunk,
326 place_holder_timeline,
327 pinhole_child_frame_component,
328 ) {
329 debug_assert_eq!(time, TimeInt::STATIC);
330
331 let frame_transforms = static_timeline.get_or_create_tree_transforms_static(
332 entity_path,
333 frame,
334 &frame_id_registry,
335 );
336 frame_transforms.invalidate_pinhole_projection_at(TimeInt::STATIC);
337
338 #[cfg_attr(not(debug_assertions), expect(clippy::for_kv_map))]
339 for (_timeline, per_timeline) in &mut self.per_timeline {
340 let mut per_timeline_guard = per_timeline.write();
343 let transforms = per_timeline_guard.get_or_create_tree_transforms_static(
344 entity_path,
345 frame,
346 &frame_id_registry,
347 );
348 transforms.invalidate_pinhole_projection_at(TimeInt::STATIC);
349
350 #[cfg(debug_assertions)]
352 {
353 transforms.timeline = Some(*_timeline);
354 }
355 }
356 }
357 }
358
359 }
361
362 fn remove_chunk(&mut self, chunk: &re_chunk_store::Chunk, aspects: TransformAspect) {
363 re_tracing::profile_function!();
364
365 for timeline in chunk.timelines().keys() {
367 let Some(per_timeline_rw) = self.per_timeline.get_mut(timeline) else {
368 continue;
369 };
370
371 let mut per_timeline = per_timeline_rw.write();
372 per_timeline.remove_chunk(chunk, aspects, *timeline);
373
374 let is_empty = per_timeline.per_child_frame_transforms.is_empty();
376 drop(per_timeline);
377 if is_empty {
378 self.per_timeline.remove(timeline);
379 }
380 }
381 }
382}