Skip to content

Commit 7c7c712

Browse files
authored
Improve performance for scenes with many entities & transforms (#7456)
### What Not super significant unfortunately, in one badly affected scene I went from ~130ms to ~110ms. It's probably better in those that aren't using any transforms at all. We need to do some more general overhaul there and imho at that point we might as well start thinking more seriously about range queries for transforms. See among others: * #7025 * #723 ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7456?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7456?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! * [x] If have noted any breaking changes to the log API in `CHANGELOG.md` and the migration guide * [x] run checklists & samples to check if transforms still work fine - [PR Build Summary](https://build.rerun.io/pr/7456) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) To run all checks from `main`, comment on the PR with `@rerun-bot full-check`.
1 parent 199f48a commit 7c7c712

File tree

4 files changed

+102
-65
lines changed

4 files changed

+102
-65
lines changed

crates/store/re_chunk_store/src/query.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,8 @@ impl ChunkStore {
430430
entity_path: &EntityPath,
431431
component_name: ComponentName,
432432
) -> Vec<Arc<Chunk>> {
433-
re_tracing::profile_function!(format!("{query:?}"));
434-
435-
self.query_id.fetch_add(1, Ordering::Relaxed);
433+
// Don't do a profile scope here, this can have a lot of overhead when executing many small queries.
434+
//re_tracing::profile_function!(format!("{query:?}"));
436435

437436
// Reminder: if a chunk has been indexed for a given component, then it must contain at
438437
// least one non-null value for that column.
@@ -510,7 +509,8 @@ impl ChunkStore {
510509
query: &LatestAtQuery,
511510
temporal_chunk_ids_per_time: &ChunkIdSetPerTime,
512511
) -> Option<Vec<Arc<Chunk>>> {
513-
re_tracing::profile_function!();
512+
// Don't do a profile scope here, this can have a lot of overhead when executing many small queries.
513+
//re_tracing::profile_function!();
514514

515515
let upper_bound = temporal_chunk_ids_per_time
516516
.per_start_time

crates/store/re_query/src/latest_at.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,8 @@ impl LatestAtCache {
649649
entity_path: &EntityPath,
650650
component_name: ComponentName,
651651
) -> Option<UnitChunkShared> {
652-
re_tracing::profile_scope!("latest_at", format!("{component_name} @ {query:?}"));
652+
// Don't do a profile scope here, this can have a lot of overhead when executing many small queries.
653+
//re_tracing::profile_scope!("latest_at", format!("{component_name} @ {query:?}"));
653654

654655
debug_assert_eq!(query.timeline(), self.cache_key.timeline);
655656

crates/viewer/re_space_view_spatial/src/contexts/transform_context.rs

+41-27
Original file line numberDiff line numberDiff line change
@@ -529,16 +529,8 @@ fn query_and_resolve_tree_transform_at_entity(
529529
entity_path: &EntityPath,
530530
entity_db: &EntityDb,
531531
query: &LatestAtQuery,
532+
transform3d_components: impl Iterator<Item = re_types::ComponentName>,
532533
) -> Option<glam::Affine3A> {
533-
let Some(transform3d_components) =
534-
TransformComponentTracker::access(entity_db.store_id(), |tracker| {
535-
tracker.transform3d_components(entity_path).cloned()
536-
})
537-
.flatten()
538-
else {
539-
return None;
540-
};
541-
542534
// TODO(#6743): Doesn't take into account overrides.
543535
let result = entity_db.latest_at(query, entity_path, transform3d_components);
544536
if result.components.is_empty() {
@@ -575,16 +567,8 @@ fn query_and_resolve_instance_poses_at_entity(
575567
entity_path: &EntityPath,
576568
entity_db: &EntityDb,
577569
query: &LatestAtQuery,
570+
pose3d_components: impl Iterator<Item = re_types::ComponentName>,
578571
) -> Vec<glam::Affine3A> {
579-
let Some(pose3d_components) =
580-
TransformComponentTracker::access(entity_db.store_id(), |tracker| {
581-
tracker.pose3d_components(entity_path).cloned()
582-
})
583-
.flatten()
584-
else {
585-
return Vec::new();
586-
};
587-
588572
// TODO(#6743): Doesn't take into account overrides.
589573
let result = entity_db.latest_at(query, entity_path, pose3d_components);
590574

@@ -726,6 +710,7 @@ fn query_and_resolve_obj_from_pinhole_image_plane(
726710
}
727711

728712
/// Resolved transforms at an entity.
713+
#[derive(Default)]
729714
struct TransformsAtEntity {
730715
parent_from_entity_tree_transform: Option<glam::Affine3A>,
731716
entity_from_instance_poses: Vec<glam::Affine3A>,
@@ -741,23 +726,49 @@ fn transforms_at(
741726
) -> Result<TransformsAtEntity, UnreachableTransformReason> {
742727
re_tracing::profile_function!();
743728

744-
let transforms_at_entity = TransformsAtEntity {
745-
parent_from_entity_tree_transform: query_and_resolve_tree_transform_at_entity(
729+
let potential_transform_components =
730+
TransformComponentTracker::access(entity_db.store_id(), |tracker| {
731+
tracker.potential_transform_components(entity_path).cloned()
732+
})
733+
.flatten()
734+
.unwrap_or_default();
735+
736+
let parent_from_entity_tree_transform = if potential_transform_components.transform3d.is_empty()
737+
{
738+
None
739+
} else {
740+
query_and_resolve_tree_transform_at_entity(
746741
entity_path,
747742
entity_db,
748743
query,
749-
),
750-
entity_from_instance_poses: query_and_resolve_instance_poses_at_entity(
744+
potential_transform_components.transform3d.iter().copied(),
745+
)
746+
};
747+
let entity_from_instance_poses = if potential_transform_components.pose3d.is_empty() {
748+
Vec::new()
749+
} else {
750+
query_and_resolve_instance_poses_at_entity(
751751
entity_path,
752752
entity_db,
753753
query,
754-
),
755-
instance_from_pinhole_image_plane: query_and_resolve_obj_from_pinhole_image_plane(
754+
potential_transform_components.pose3d.iter().copied(),
755+
)
756+
};
757+
let instance_from_pinhole_image_plane = if potential_transform_components.pinhole {
758+
query_and_resolve_obj_from_pinhole_image_plane(
756759
entity_path,
757760
entity_db,
758761
query,
759762
pinhole_image_plane_distance,
760-
),
763+
)
764+
} else {
765+
None
766+
};
767+
768+
let transforms_at_entity = TransformsAtEntity {
769+
parent_from_entity_tree_transform,
770+
entity_from_instance_poses,
771+
instance_from_pinhole_image_plane,
761772
};
762773

763774
// Handle pinhole encounters.
@@ -773,13 +784,16 @@ fn transforms_at(
773784
}
774785

775786
// If there is any other transform, we ignore `DisconnectedSpace`.
776-
if transforms_at_entity
787+
let no_other_transforms = transforms_at_entity
777788
.parent_from_entity_tree_transform
778789
.is_none()
779790
&& transforms_at_entity.entity_from_instance_poses.is_empty()
780791
&& transforms_at_entity
781792
.instance_from_pinhole_image_plane
782-
.is_none()
793+
.is_none();
794+
795+
if no_other_transforms
796+
&& potential_transform_components.disconnected_space
783797
&& entity_db
784798
.latest_at_component::<DisconnectedSpace>(entity_path, query)
785799
.map_or(false, |(_index, res)| **res)

crates/viewer/re_space_view_spatial/src/transform_component_tracker.rs

+55-33
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,26 @@ use re_chunk_store::{
77
ChunkStoreSubscriberHandle,
88
};
99
use re_log_types::{EntityPath, EntityPathHash, StoreId};
10-
use re_types::ComponentName;
10+
use re_types::{ComponentName, Loggable as _};
1111

1212
// ---
1313

14+
/// Set of components that an entity ever had over its known lifetime.
15+
#[derive(Default, Clone)]
16+
pub struct PotentialTransformComponentSet {
17+
/// All transform components ever present.
18+
pub transform3d: IntSet<ComponentName>,
19+
20+
/// All pose transform components ever present.
21+
pub pose3d: IntSet<ComponentName>,
22+
23+
/// Whether the entity ever had a pinhole camera.
24+
pub pinhole: bool,
25+
26+
/// Whether the entity ever had a disconnected space component.
27+
pub disconnected_space: bool,
28+
}
29+
1430
/// Keeps track of which entities have had any `Transform3D`-related data on any timeline at any
1531
/// point in time.
1632
///
@@ -20,13 +36,7 @@ use re_types::ComponentName;
2036
/// This is a huge performance improvement in practice, especially in recordings with many entities.
2137
#[derive(Default)]
2238
pub struct TransformComponentTracker {
23-
/// Which entities have had any `Transform3D` component at any point in time, and which
24-
/// components they actually make use of.
25-
transform3d_entities: IntMap<EntityPathHash, IntSet<ComponentName>>,
26-
27-
/// Which entities have had any `InstancePoses3D` components at any point in time, and
28-
/// which components they actually make use of.
29-
pose3d_entities: IntMap<EntityPathHash, IntSet<ComponentName>>,
39+
components_per_entity: IntMap<EntityPathHash, PotentialTransformComponentSet>,
3040
}
3141

3242
impl TransformComponentTracker {
@@ -42,17 +52,11 @@ impl TransformComponentTracker {
4252
.flatten()
4353
}
4454

45-
#[inline]
46-
pub fn transform3d_components(
55+
pub fn potential_transform_components(
4756
&self,
4857
entity_path: &EntityPath,
49-
) -> Option<&IntSet<ComponentName>> {
50-
self.transform3d_entities.get(&entity_path.hash())
51-
}
52-
53-
#[inline]
54-
pub fn pose3d_components(&self, entity_path: &EntityPath) -> Option<&IntSet<ComponentName>> {
55-
self.pose3d_entities.get(&entity_path.hash())
58+
) -> Option<&PotentialTransformComponentSet> {
59+
self.components_per_entity.get(&entity_path.hash())
5660
}
5761
}
5862

@@ -123,37 +127,55 @@ impl ChunkStoreSubscriber for TransformComponentTrackerStoreSubscriber {
123127

124128
let entity_path_hash = event.chunk.entity_path().hash();
125129

130+
let contains_non_zero_component_array = |component_name| {
131+
event
132+
.chunk
133+
.components()
134+
.get(&component_name)
135+
.map_or(false, |list_array| {
136+
list_array.offsets().lengths().any(|len| len > 0)
137+
})
138+
};
139+
126140
for component_name in event.chunk.component_names() {
127141
if self.transform_components.contains(&component_name)
128-
&& event
129-
.chunk
130-
.components()
131-
.get(&component_name)
132-
.map_or(false, |list_array| {
133-
list_array.offsets().lengths().any(|len| len > 0)
134-
})
142+
&& contains_non_zero_component_array(component_name)
135143
{
136144
transform_component_tracker
137-
.transform3d_entities
145+
.components_per_entity
138146
.entry(entity_path_hash)
139147
.or_default()
148+
.transform3d
140149
.insert(component_name);
141150
}
142151
if self.pose_components.contains(&component_name)
143-
&& event
144-
.chunk
145-
.components()
146-
.get(&component_name)
147-
.map_or(false, |list_array| {
148-
list_array.offsets().lengths().any(|len| len > 0)
149-
})
152+
&& contains_non_zero_component_array(component_name)
150153
{
151154
transform_component_tracker
152-
.pose3d_entities
155+
.components_per_entity
153156
.entry(entity_path_hash)
154157
.or_default()
158+
.pose3d
155159
.insert(component_name);
156160
}
161+
if component_name == re_types::components::PinholeProjection::name()
162+
&& contains_non_zero_component_array(component_name)
163+
{
164+
transform_component_tracker
165+
.components_per_entity
166+
.entry(entity_path_hash)
167+
.or_default()
168+
.pinhole = true;
169+
}
170+
if component_name == re_types::components::DisconnectedSpace::name()
171+
&& contains_non_zero_component_array(component_name)
172+
{
173+
transform_component_tracker
174+
.components_per_entity
175+
.entry(entity_path_hash)
176+
.or_default()
177+
.disconnected_space = true;
178+
}
157179
}
158180
}
159181
}

0 commit comments

Comments
 (0)