Skip to content

Commit 2f90674

Browse files
authored
Get rid of sync_blueprint_changes (#4524)
### What Resolves: - #4155 - #4440 Lots of refactoring and untangling here: - `ViewportBlueprint` is a thin wrapper around the Archetype -- it is now always read-only. - All the runtime-mutable stuff (specifically the tree / deferred-tree-actions) are moved out of ViewportBlueprint and into Viewport - Subsequently a couple of the UI eliminate now take a `&mut Viewport` instead of a &mut ViewportBlueprint - Almost all modifications are now made by calling `set_<prop>` APIs which emit a deferred component-write directly to the blueprint. - The one remaining complex type is the ViewportLayout which is still stored as a single component. The deferred update logic for the tree is now done at the end of the frame, followed by a single component-update. ### 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): * Full build: [app.rerun.io](https://app.rerun.io/pr/4524/index.html) * Partial build: [app.rerun.io](https://app.rerun.io/pr/4524/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) - Useful for quick testing when changes do not affect examples in any way * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG - [PR Build Summary](https://build.rerun.io/pr/4524) - [Docs preview](https://rerun.io/preview/db702308b1658fe691878c7976b445ba5623b72a/docs) <!--DOCS-PREVIEW--> - [Examples preview](https://rerun.io/preview/db702308b1658fe691878c7976b445ba5623b72a/examples) <!--EXAMPLES-PREVIEW--> - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
1 parent 8ebaf8d commit 2f90674

File tree

19 files changed

+738
-629
lines changed

19 files changed

+738
-629
lines changed

crates/re_space_view/src/data_query_blueprint.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use re_data_store::{
44
EntityProperties, EntityPropertiesComponent, EntityPropertyMap, EntityTree, StoreDb,
55
};
66
use re_log_types::{DataRow, EntityPath, EntityPathExpr, RowId, TimePoint};
7+
use re_types_core::archetypes::Clear;
78
use re_viewer_context::{
89
DataQueryId, DataQueryResult, DataResult, DataResultHandle, DataResultNode, DataResultTree,
910
EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassIdentifier, SpaceViewId,
@@ -28,7 +29,14 @@ use crate::{
2829
/// The results of recursive expressions are only included if they are found within the [`EntityTree`]
2930
/// and for which there is a valid `ViewPart` system. This keeps recursive expressions from incorrectly
3031
/// picking up irrelevant data within the tree.
31-
#[derive(Clone, PartialEq, Eq)]
32+
///
33+
/// Note: [`DataQueryBlueprint`] doesn't implement Clone because it stores an internal
34+
/// uuid used for identifying the path of its data in the blueprint store. It's ambiguous
35+
/// whether the intent is for a clone to write to the same place.
36+
///
37+
/// If you want a new space view otherwise identical to an existing one, use
38+
/// [`DataQueryBlueprint::duplicate`].
39+
#[derive(PartialEq, Eq)]
3240
pub struct DataQueryBlueprint {
3341
pub id: DataQueryId,
3442
pub space_view_class_identifier: SpaceViewClassIdentifier,
@@ -47,6 +55,10 @@ impl DataQueryBlueprint {
4755
pub const INDIVIDUAL_OVERRIDES_PREFIX: &'static str = "individual_overrides";
4856
pub const RECURSIVE_OVERRIDES_PREFIX: &'static str = "recursive_overrides";
4957

58+
/// Creates a new [`DataQueryBlueprint`].
59+
///
60+
/// This [`DataQueryBlueprint`] is ephemeral. It must be saved by calling
61+
/// `save_to_blueprint_store` on the enclosing `SpaceViewBlueprint`.
5062
pub fn new(
5163
space_view_class_identifier: SpaceViewClassIdentifier,
5264
queries_entities: impl Iterator<Item = EntityPathExpr>,
@@ -58,25 +70,48 @@ impl DataQueryBlueprint {
5870
}
5971
}
6072

73+
/// Attempt to load a [`DataQueryBlueprint`] from the blueprint store.
6174
pub fn try_from_db(
62-
path: &EntityPath,
75+
id: DataQueryId,
6376
blueprint_db: &StoreDb,
6477
space_view_class_identifier: SpaceViewClassIdentifier,
6578
) -> Option<Self> {
6679
let expressions = blueprint_db
6780
.store()
68-
.query_timeless_component::<QueryExpressions>(path)
81+
.query_timeless_component::<QueryExpressions>(&id.as_entity_path())
6982
.map(|c| c.value)?;
7083

71-
let id = DataQueryId::from_entity_path(path);
72-
7384
Some(Self {
7485
id,
7586
space_view_class_identifier,
7687
expressions,
7788
})
7889
}
7990

91+
/// Persist the entire [`DataQueryBlueprint`] to the blueprint store.
92+
///
93+
/// This only needs to be called if the [`DataQueryBlueprint`] was created with [`Self::new`].
94+
///
95+
/// Otherwise, incremental calls to `set_` functions will write just the necessary component
96+
/// update directly to the store.
97+
pub fn save_to_blueprint_store(&self, ctx: &ViewerContext<'_>) {
98+
ctx.save_blueprint_component(&self.id.as_entity_path(), self.expressions.clone());
99+
}
100+
101+
/// Creates a new [`DataQueryBlueprint`] with a the same contents, but a different [`DataQueryId`]
102+
pub fn duplicate(&self) -> Self {
103+
Self {
104+
id: DataQueryId::random(),
105+
space_view_class_identifier: self.space_view_class_identifier,
106+
expressions: self.expressions.clone(),
107+
}
108+
}
109+
110+
pub fn clear(&self, ctx: &ViewerContext<'_>) {
111+
let clear = Clear::recursive();
112+
ctx.save_blueprint_component(&self.id.as_entity_path(), clear.is_recursive);
113+
}
114+
80115
pub fn build_resolver<'a>(
81116
&self,
82117
container: SpaceViewId,

crates/re_types/definitions/rerun/blueprint/archetypes/viewport_blueprint.fbs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ table ViewportBlueprint (
1919
/// All of the space-views that belong to the viewport.
2020
space_views: rerun.blueprint.components.IncludedSpaceViews ("attr.rerun.component_required", order: 1000);
2121

22-
/// The layout of the space-views
23-
layout: rerun.blueprint.components.ViewportLayout ("attr.rerun.component_required", order: 2000);
24-
2522
// --- Optional ---
2623

24+
/// The layout of the space-views
25+
layout: rerun.blueprint.components.ViewportLayout ("attr.rerun.component_optional", nullable, order: 2000);
26+
2727
/// Show one tab as maximized?
2828
maximized: rerun.blueprint.components.SpaceViewMaximized ("attr.rerun.component_optional", nullable, order: 3000);
2929

crates/re_viewer/src/app.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ impl App {
368368
SystemCommand::ResetBlueprint => {
369369
// By clearing the blueprint it will be re-populated with the defaults
370370
// at the beginning of the next frame.
371+
re_log::debug!("Reset blueprint");
371372
store_hub.clear_blueprint();
372373
}
373374
SystemCommand::UpdateBlueprint(blueprint_id, updates) => {

crates/re_viewer/src/app_state.rs

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use re_smart_channel::ReceiveSet;
66
use re_space_view::DataQuery as _;
77
use re_viewer_context::{
88
AppOptions, Caches, CommandSender, ComponentUiRegistry, PlayState, RecordingConfig,
9-
SelectionState, SpaceViewClassRegistry, StoreContext, ViewerContext,
9+
SelectionState, SpaceViewClassRegistry, StoreContext, SystemCommandSender as _, ViewerContext,
1010
};
1111
use re_viewport::{
12-
identify_entities_per_system_per_class, SpaceInfoCollection, Viewport, ViewportState,
12+
identify_entities_per_system_per_class, SpaceInfoCollection, Viewport, ViewportBlueprint,
13+
ViewportState,
1314
};
1415

1516
use crate::ui::recordings_panel_ui;
@@ -104,7 +105,25 @@ impl AppState {
104105
viewport_state,
105106
} = self;
106107

107-
let mut viewport = Viewport::from_db(store_context.blueprint, viewport_state);
108+
let viewport_blueprint = ViewportBlueprint::try_from_db(store_context.blueprint);
109+
let mut viewport = Viewport::new(
110+
&viewport_blueprint,
111+
viewport_state,
112+
space_view_class_registry,
113+
);
114+
115+
// If the blueprint is invalid, reset it.
116+
if viewport.blueprint.is_invalid() {
117+
re_log::warn!("Incompatible blueprint detected. Resetting to default.");
118+
command_sender.send_system(re_viewer_context::SystemCommand::ResetBlueprint);
119+
120+
// The blueprint isn't valid so nothing past this is going to work properly.
121+
// we might as well return and it will get fixed on the next frame.
122+
123+
// TODO(jleibs): If we move viewport loading up to a context where the StoreDb is mutable
124+
// we can run the clear and re-load.
125+
return;
126+
}
108127

109128
recording_config_entry(recording_configs, store_db.store_id().clone(), store_db)
110129
.selection_state
@@ -125,11 +144,11 @@ impl AppState {
125144
viewport
126145
.blueprint
127146
.space_views
128-
.values_mut()
147+
.values()
129148
.flat_map(|space_view| {
130149
space_view.queries.iter().map(|query| {
131-
let resolver =
132-
query.build_resolver(space_view.id, &space_view.auto_properties);
150+
let props = viewport.state.space_view_props(space_view.id);
151+
let resolver = query.build_resolver(space_view.id, props);
133152
(
134153
query.id,
135154
query.execute_query(
@@ -163,12 +182,6 @@ impl AppState {
163182
// have the latest information.
164183
let spaces_info = SpaceInfoCollection::new(ctx.store_db);
165184

166-
// If the blueprint is invalid, reset it.
167-
if viewport.blueprint.is_invalid() {
168-
re_log::warn!("Incompatible blueprint detected. Resetting to default.");
169-
viewport.blueprint.reset(&ctx, &spaces_info);
170-
}
171-
172185
viewport.on_frame_start(&ctx, &spaces_info);
173186

174187
// TODO(jleibs): Running the queries a second time is annoying, but we need
@@ -179,11 +192,11 @@ impl AppState {
179192
viewport
180193
.blueprint
181194
.space_views
182-
.values_mut()
195+
.values()
183196
.flat_map(|space_view| {
184197
space_view.queries.iter().map(|query| {
185-
let resolver =
186-
query.build_resolver(space_view.id, &space_view.auto_properties);
198+
let props = viewport.state.space_view_props(space_view.id);
199+
let resolver = query.build_resolver(space_view.id, props);
187200
(
188201
query.id,
189202
query.execute_query(
@@ -243,7 +256,7 @@ impl AppState {
243256
ui.add_space(4.0);
244257
}
245258

246-
blueprint_panel_ui(&mut viewport.blueprint, &ctx, ui, &spaces_info);
259+
blueprint_panel_ui(&mut viewport, &ctx, ui, &spaces_info);
247260
},
248261
);
249262

@@ -266,7 +279,8 @@ impl AppState {
266279
});
267280
});
268281

269-
viewport.sync_blueprint_changes(command_sender);
282+
// Process deferred layout operations and apply updates back to blueprint
283+
viewport.update_and_sync_tile_tree_to_blueprint(&ctx);
270284

271285
{
272286
// We move the time at the very end of the frame,

crates/re_viewer/src/ui/blueprint_panel.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use re_viewer_context::{SystemCommandSender as _, ViewerContext};
2-
use re_viewport::{SpaceInfoCollection, ViewportBlueprint};
2+
use re_viewport::{SpaceInfoCollection, Viewport};
33

4-
/// Show the Blueprint section of the left panel based on the current [`ViewportBlueprint`]
4+
/// Show the Blueprint section of the left panel based on the current [`Viewport`]
55
pub fn blueprint_panel_ui(
6-
blueprint: &mut ViewportBlueprint<'_>,
6+
viewport: &mut Viewport<'_, '_>,
77
ctx: &ViewerContext<'_>,
88
ui: &mut egui::Ui,
99
spaces_info: &SpaceInfoCollection,
@@ -14,15 +14,15 @@ pub fn blueprint_panel_ui(
1414
"Blueprint",
1515
Some("The Blueprint is where you can configure the Rerun Viewer"),
1616
|ui| {
17-
blueprint.add_new_spaceview_button_ui(ctx, ui, spaces_info);
17+
viewport.add_new_spaceview_button_ui(ctx, ui, spaces_info);
1818
reset_blueprint_button_ui(ctx, ui);
1919
},
2020
);
2121
});
2222

2323
// This call is excluded from `panel_content` because it has a ScrollArea, which should not be
2424
// inset. Instead, it calls panel_content itself inside the ScrollArea.
25-
blueprint.tree_ui(ctx, ui);
25+
viewport.tree_ui(ctx, ui);
2626
}
2727

2828
fn reset_blueprint_button_ui(ctx: &ViewerContext<'_>, ui: &mut egui::Ui) {

crates/re_viewer/src/ui/selection_history_ui.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ impl SelectionHistoryUi {
1515
&mut self,
1616
re_ui: &re_ui::ReUi,
1717
ui: &mut egui::Ui,
18-
blueprint: &ViewportBlueprint<'_>,
18+
blueprint: &ViewportBlueprint,
1919
history: &mut SelectionHistory,
2020
) -> Option<ItemCollection> {
2121
let next = self.next_button_ui(re_ui, ui, blueprint, history);
@@ -27,7 +27,7 @@ impl SelectionHistoryUi {
2727
&mut self,
2828
re_ui: &re_ui::ReUi,
2929
ui: &mut egui::Ui,
30-
blueprint: &ViewportBlueprint<'_>,
30+
blueprint: &ViewportBlueprint,
3131
history: &mut SelectionHistory,
3232
) -> Option<ItemCollection> {
3333
// undo selection
@@ -77,7 +77,7 @@ impl SelectionHistoryUi {
7777
&mut self,
7878
re_ui: &re_ui::ReUi,
7979
ui: &mut egui::Ui,
80-
blueprint: &ViewportBlueprint<'_>,
80+
blueprint: &ViewportBlueprint,
8181
history: &mut SelectionHistory,
8282
) -> Option<ItemCollection> {
8383
// redo selection
@@ -126,7 +126,7 @@ impl SelectionHistoryUi {
126126
#[allow(clippy::unused_self)]
127127
fn history_item_ui(
128128
&mut self,
129-
blueprint: &ViewportBlueprint<'_>,
129+
blueprint: &ViewportBlueprint,
130130
ui: &mut egui::Ui,
131131
index: usize,
132132
history: &mut SelectionHistory,
@@ -157,7 +157,7 @@ fn item_kind_ui(ui: &mut egui::Ui, sel: &Item) {
157157
ui.weak(RichText::new(format!("({})", sel.kind())));
158158
}
159159

160-
fn item_collection_to_string(blueprint: &ViewportBlueprint<'_>, items: &ItemCollection) -> String {
160+
fn item_collection_to_string(blueprint: &ViewportBlueprint, items: &ItemCollection) -> String {
161161
assert!(!items.is_empty()); // history never contains empty selections.
162162
if items.len() == 1 {
163163
item_to_string(blueprint, items.iter().next().unwrap())
@@ -168,7 +168,7 @@ fn item_collection_to_string(blueprint: &ViewportBlueprint<'_>, items: &ItemColl
168168
}
169169
}
170170

171-
fn item_to_string(blueprint: &ViewportBlueprint<'_>, item: &Item) -> String {
171+
fn item_to_string(blueprint: &ViewportBlueprint, item: &Item) -> String {
172172
match item {
173173
Item::SpaceView(sid) => {
174174
if let Some(space_view) = blueprint.space_view(sid) {

0 commit comments

Comments
 (0)