Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10050,6 +10050,7 @@ dependencies = [
"js-sys",
"parking_lot",
"poll-promise",
"rayon",
"re_analytics",
"re_auth",
"re_blueprint_tree",
Expand Down
18 changes: 9 additions & 9 deletions crates/viewer/re_test_viewport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ impl TestContextExt for TestContext {
let view_blueprint = viewport_blueprint
.view(view_id)
.expect("view is known to exist");

let class_registry = ctx.view_class_registry();
let class_identifier = view_blueprint.class_identifier();
let class = class_registry.class(class_identifier).unwrap_or_else(|| panic!("The class '{class_identifier}' must be registered beforehand"));

let visualizable_entities = ctx
.view_class_registry()
.class(class_identifier)
.unwrap_or_else(|| panic!("The class '{class_identifier}' must be registered beforehand"))
let visualizable_entities = class
.determine_visualizable_entities(
ctx.maybe_visualizable_entities_per_visualizer,
ctx.recording(),
&ctx.view_class_registry()
&class_registry
.new_visualizer_collection(class_identifier),
&view_blueprint.space_origin,
);
Expand All @@ -109,14 +109,14 @@ impl TestContextExt for TestContext {

let mut data_query_result = view_blueprint.contents.execute_query(
ctx.store_context,
ctx.view_class_registry(),
class_registry,
ctx.blueprint_query,
&visualizable_entities,
);

let resolver = DataQueryPropertyResolver::new(
view_blueprint,
ctx.view_class_registry(),
class_registry,
ctx.maybe_visualizable_entities_per_visualizer,
&visualizable_entities,
&indicated_entities_per_visualizer,
Expand All @@ -126,9 +126,9 @@ impl TestContextExt for TestContext {
ctx.store_context.blueprint,
ctx.blueprint_query,
ctx.rec_cfg.time_ctrl.read().timeline(),
ctx.view_class_registry(),
class_registry,
&mut data_query_result,
&mut self.view_states.lock(),
self.view_states.lock().get_mut_or_create(*view_id, class),
);

query_results.insert(*view_id, data_query_result);
Expand Down
1 change: 1 addition & 0 deletions crates/viewer/re_viewer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ itertools.workspace = true
jiff.workspace = true
parking_lot.workspace = true
poll-promise = { workspace = true, features = ["web"] }
rayon.workspace = true
rfd.workspace = true
ron.workspace = true
serde = { workspace = true, features = ["derive"] }
Expand Down
143 changes: 105 additions & 38 deletions crates/viewer/re_viewer/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{borrow::Cow, str::FromStr as _};
use ahash::HashMap;
use egui::{Ui, text_edit::TextEditState, text_selection::LabelSelectionState};

use re_chunk::TimelineName;
use re_chunk::{Timeline, TimelineName};
use re_chunk_store::LatestAtQuery;
use re_entity_db::EntityDb;
use re_log_types::{AbsoluteTimeRangeF, DataSourceMessage, StoreId, TableId};
Expand All @@ -14,9 +14,10 @@ use re_types::blueprint::components::PanelState;
use re_ui::{ContextExt as _, UiExt as _};
use re_viewer_context::{
AppOptions, ApplicationSelectionState, AsyncRuntimeHandle, BlueprintUndoState, CommandSender,
ComponentUiRegistry, DisplayMode, DragAndDropManager, GlobalContext, Item, PlayState,
RecordingConfig, SelectionChange, StorageContext, StoreContext, StoreHub, SystemCommand,
SystemCommandSender as _, TableStore, ViewClassRegistry, ViewStates, ViewerContext,
ComponentUiRegistry, DataQueryResult, DisplayMode, DragAndDropManager, GlobalContext,
IndicatedEntities, Item, MaybeVisualizableEntities, PerVisualizer, PlayState, RecordingConfig,
SelectionChange, StorageContext, StoreContext, StoreHub, SystemCommand,
SystemCommandSender as _, TableStore, ViewClassRegistry, ViewId, ViewStates, ViewerContext,
blueprint_timeline,
open_url::{self, ViewerOpenUrl},
};
Expand Down Expand Up @@ -278,7 +279,7 @@ impl AppState {
view_class_registry.indicated_entities_per_visualizer(recording.store_id());

// Execute the queries for every `View`
let mut query_results = {
let query_results = {
re_tracing::profile_scope!("query_results");
viewport_ui
.blueprint
Expand Down Expand Up @@ -359,39 +360,18 @@ impl AppState {
// Update the viewport. May spawn new views and handle queued requests (like screenshots).
viewport_ui.on_frame_start(&ctx);

for view in viewport_ui.blueprint.views.values() {
if let Some(query_result) = query_results.get_mut(&view.id) {
// TODO(andreas): This needs to be done in a store subscriber that exists per view (instance, not class!).
// Note that right now we determine *all* visualizable entities, not just the queried ones.
// In a store subscriber set this is fine, but on a per-frame basis it's wasteful.
let visualizable_entities = view
.class(view_class_registry)
.determine_visualizable_entities(
&maybe_visualizable_entities_per_visualizer,
recording,
&view_class_registry
.new_visualizer_collection(view.class_identifier()),
&view.space_origin,
);

let resolver = re_viewport_blueprint::DataQueryPropertyResolver::new(
view,
view_class_registry,
&maybe_visualizable_entities_per_visualizer,
&visualizable_entities,
&indicated_entities_per_visualizer,
);

resolver.update_overrides(
store_context.blueprint,
&blueprint_query,
rec_cfg.time_ctrl.read().timeline(),
view_class_registry,
query_result,
view_states,
);
}
}
let active_timeline = *rec_cfg.time_ctrl.read().timeline();
let query_results = update_overrides(
store_context,
query_results,
view_class_registry,
&viewport_ui.blueprint,
&blueprint_query,
&active_timeline,
&maybe_visualizable_entities_per_visualizer,
&indicated_entities_per_visualizer,
view_states,
);

// We need to recreate the context to appease the borrow checker. It is a bit annoying, but
// it's just a bunch of refs so not really that big of a deal in practice.
Expand Down Expand Up @@ -760,6 +740,93 @@ impl AppState {
}
}

/// Updates the query results for the given viewport UI.
///
/// Returns query results derived from the previous one.
#[expect(clippy::too_many_arguments)]
fn update_overrides(
store_context: &StoreContext<'_>,
mut query_results: HashMap<ViewId, DataQueryResult>,
view_class_registry: &ViewClassRegistry,
viewport_blueprint: &ViewportBlueprint,
blueprint_query: &LatestAtQuery,
active_timeline: &Timeline,
maybe_visualizable_entities_per_visualizer: &PerVisualizer<MaybeVisualizableEntities>,
indicated_entities_per_visualizer: &PerVisualizer<IndicatedEntities>,
view_states: &mut ViewStates,
) -> HashMap<ViewId, DataQueryResult> {
use rayon::iter::{IntoParallelIterator as _, ParallelIterator as _};

struct OverridesUpdateTask<'a> {
view: &'a re_viewport_blueprint::ViewBlueprint,
view_state: &'a dyn re_viewer_context::ViewState,
query_result: DataQueryResult,
}

for view in viewport_blueprint.views.values() {
view_states.ensure_state_exists(view.id, view.class(view_class_registry));
}

let work_items = viewport_blueprint
.views
.values()
.filter_map(|view| {
query_results.remove(&view.id).map(|query_result| {
let view_state = view_states
.get(view.id)
.expect("View state should exist, we just called ensure_state_exists on it.");
OverridesUpdateTask {
view,
view_state,
query_result,
}
})
})
.collect::<Vec<_>>();

work_items
.into_par_iter()
.map(
|OverridesUpdateTask {
view,
view_state,
mut query_result,
}| {
// TODO(andreas): This needs to be done in a store subscriber that exists per view (instance, not class!).
// Note that right now we determine *all* visualizable entities, not just the queried ones.
// In a store subscriber set this is fine, but on a per-frame basis it's wasteful.
let visualizable_entities = view
.class(view_class_registry)
.determine_visualizable_entities(
maybe_visualizable_entities_per_visualizer,
store_context.recording,
&view_class_registry.new_visualizer_collection(view.class_identifier()),
&view.space_origin,
);

let resolver = re_viewport_blueprint::DataQueryPropertyResolver::new(
view,
view_class_registry,
maybe_visualizable_entities_per_visualizer,
&visualizable_entities,
indicated_entities_per_visualizer,
);

resolver.update_overrides(
store_context.blueprint,
blueprint_query,
active_timeline,
view_class_registry,
&mut query_result,
view_state,
);

(view.id, query_result)
},
)
.collect()
}

fn table_ui(
ctx: &ViewerContext<'_>,
runtime: &AsyncRuntimeHandle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl ViewClassRegistry {
)
}

/// For each visualizer, the set of entities that have at least one matching indicator component.
/// For each visualizer, the set of entities that have at least one component with a matching archetype name.
pub fn indicated_entities_per_visualizer(
&self,
store_id: &re_log_types::StoreId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@ impl VisualizerEntitySubscriber {
.map(|mapping| &mapping.maybe_visualizable_entities)
}

/// List of entities that at some point in time had any of the indicator components advertised by this visualizer.
/// List of entities that at some point in time had a component of an archetypes matching the visualizer's query.
///
/// Useful for quickly evaluating basic "should this visualizer apply by default"-heuristic.
/// Does *not* imply that any of the given entities is also in the (maybe-)visualizable-set!
///
/// If the visualizer has no indicator components, this list will contain all entities in the store.
/// If the visualizer has no archetypes, this list will contain all entities in the store.
pub fn indicated_entities(&self, store: &StoreId) -> Option<&IndicatedEntities> {
self.per_store_mapping
.get(store)
Expand Down
8 changes: 7 additions & 1 deletion crates/viewer/re_viewport_blueprint/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,14 +764,20 @@ mod tests {
visualizable_entities,
);
let mut view_states = ViewStates::default();
let view_state = view_states.get_mut_or_create(
view.id,
ctx.view_class_registry
.class(view.class_identifier())
.expect("view class should be registered"),
);

resolver.update_overrides(
ctx.blueprint_db(),
ctx.blueprint_query,
ctx.rec_cfg.time_ctrl.read().timeline(),
ctx.view_class_registry(),
&mut query_result,
&mut view_states,
view_state,
);

result = Some(query_result.clone());
Expand Down
7 changes: 2 additions & 5 deletions crates/viewer/re_viewport_blueprint/src/view_contents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use re_types::{
use re_viewer_context::{
DataQueryResult, DataResult, DataResultHandle, DataResultNode, DataResultTree,
IndicatedEntities, MaybeVisualizableEntities, OverridePath, PerVisualizer, PropertyOverrides,
QueryRange, ViewClassRegistry, ViewId, ViewStates, ViewerContext, VisualizableEntities,
QueryRange, ViewClassRegistry, ViewId, ViewState, ViewerContext, VisualizableEntities,
};

use crate::{ViewBlueprint, ViewProperty};
Expand Down Expand Up @@ -594,14 +594,11 @@ impl<'a> DataQueryPropertyResolver<'a> {
active_timeline: &Timeline,
view_class_registry: &ViewClassRegistry,
query_result: &mut DataQueryResult,
view_states: &mut ViewStates,
view_state: &dyn ViewState,
) {
// This is called very frequently, don't put a profile scope here.

if let Some(root) = query_result.tree.root_handle() {
let class = self.view.class(view_class_registry);
let view_state = view_states.get_mut_or_create(self.view.id, class);

let default_query_range = self.view.query_range(
blueprint,
blueprint_query,
Expand Down
Loading