Skip to content

Commit 3b72860

Browse files
authored
Drive blueprints off of a DataStore (#2010)
- Uses the new serde-helper from: #2004 to stick space-views in the data-store - Refactors the frame loop to follow this basic logic: - Materialize the blueprint from the store - Save a snapshot of the materialized blueprint - Run most of the legacy code as is - If the blueprint has been modified then save the modifications back to the store - In the internals of the Python, this introduces a new `global_blueprint_stream()` - Adds a few python APIs that send data to the stream. - RecordingId is now a string, and we use a special condition of RecordingId == AppId to determine that a blueprint is the "default blueprint" for an app. - The default behavior of rr.init() now uses this special recording-id, which means in the common case your blueprint API calls do almost exactly what you want, but an expert can still opt out using `add_to_app_default_blueprint=False` (this might need a better name), in which case they get complete control over a new blueprint. - SpaceViewIds generated by the app use a hash of the spaceview name to avoid repeated appends. - The "selected blueprint" is determined based on app-id. There is a "currently selected" blueprint for each app, which defaults to the special global blueprint.
1 parent c6aa325 commit 3b72860

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1880
-330
lines changed

Cargo.lock

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/re_data_store/src/entity_properties.rs

+3-39
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use re_arrow_store::LatestAtQuery;
2-
use re_log_types::{DeserializableComponent, EntityPath};
1+
#[cfg(feature = "serde")]
2+
use re_log_types::EntityPath;
33

44
#[cfg(feature = "serde")]
55
use crate::EditableAutoValue;
@@ -8,7 +8,7 @@ use crate::EditableAutoValue;
88

99
/// Properties for a collection of entities.
1010
#[cfg(feature = "serde")]
11-
#[derive(Clone, Default)]
11+
#[derive(Clone, Default, PartialEq)]
1212
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1313
pub struct EntityPropertyMap {
1414
props: nohash_hasher::IntMap<EntityPath, EntityProperties>,
@@ -186,39 +186,3 @@ impl Default for ColorMapper {
186186
Self::Colormap(Colormap::default())
187187
}
188188
}
189-
190-
// ----------------------------------------------------------------------------
191-
192-
/// Get the latest value for a given [`re_log_types::Component`].
193-
///
194-
/// This assumes that the row we get from the store only contains a single instance for this
195-
/// component; it will log a warning otherwise.
196-
///
197-
/// This should only be used for "mono-components" such as `Transform` and `Tensor`.
198-
pub fn query_latest_single<C: DeserializableComponent>(
199-
data_store: &re_arrow_store::DataStore,
200-
entity_path: &EntityPath,
201-
query: &LatestAtQuery,
202-
) -> Option<C>
203-
where
204-
for<'b> &'b C::ArrayType: IntoIterator,
205-
{
206-
crate::profile_function!();
207-
208-
// Although it would be nice to use the `re_query` helpers for this, we would need to move
209-
// this out of re_data_store to avoid a circular dep. Since we don't need to do a join for
210-
// single components this is easy enough.
211-
212-
let (_, cells) = data_store.latest_at(query, entity_path, C::name(), &[C::name()])?;
213-
let cell = cells.get(0)?.as_ref()?;
214-
215-
let mut iter = cell.try_to_native::<C>().ok()?;
216-
217-
let component = iter.next();
218-
219-
if iter.next().is_some() {
220-
re_log::warn_once!("Unexpected batch for {} at: {}", C::name(), entity_path);
221-
}
222-
223-
component
224-
}

crates/re_data_store/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ pub mod entity_properties;
1010
pub mod entity_tree;
1111
mod instance_path;
1212
pub mod log_db;
13+
mod util;
1314

1415
pub use entity_properties::*;
1516
pub use entity_tree::*;
1617
pub use instance_path::*;
1718
pub use log_db::LogDb;
19+
pub use util::*;
1820

1921
#[cfg(feature = "serde")]
2022
pub use editable_auto_value::EditableAutoValue;

crates/re_data_store/src/log_db.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use re_arrow_store::{DataStoreConfig, TimeInt};
66
use re_log_types::{
77
component_types::InstanceKey, ArrowMsg, Component as _, ComponentPath, DataCell, DataRow,
88
DataTable, EntityPath, EntityPathHash, EntityPathOpMsg, LogMsg, PathOp, RecordingId,
9-
RecordingInfo, RowId, SetRecordingInfo, TimePoint, Timeline,
9+
RecordingInfo, RecordingType, RowId, SetRecordingInfo, TimePoint, Timeline,
1010
};
1111

1212
use crate::{Error, TimesPerTimeline};
@@ -69,7 +69,8 @@ impl EntityDb {
6969
Ok(())
7070
}
7171

72-
fn try_add_data_row(&mut self, row: &DataRow) -> Result<(), Error> {
72+
// TODO(jleibs): If this shouldn't be public, chain together other setters
73+
pub fn try_add_data_row(&mut self, row: &DataRow) -> Result<(), Error> {
7374
for (&timeline, &time_int) in row.timepoint().iter() {
7475
self.times_per_timeline.insert(timeline, time_int);
7576
}
@@ -195,6 +196,10 @@ impl LogDb {
195196
self.recording_msg().map(|msg| &msg.info)
196197
}
197198

199+
pub fn recording_type(&self) -> RecordingType {
200+
self.recording_id.variant
201+
}
202+
198203
pub fn recording_id(&self) -> &RecordingId {
199204
&self.recording_id
200205
}
@@ -241,7 +246,7 @@ impl LogDb {
241246
Ok(())
242247
}
243248

244-
fn add_begin_recording_msg(&mut self, msg: &SetRecordingInfo) {
249+
pub fn add_begin_recording_msg(&mut self, msg: &SetRecordingInfo) {
245250
self.recording_msg = Some(msg.clone());
246251
}
247252

crates/re_data_store/src/util.rs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use re_arrow_store::LatestAtQuery;
2+
use re_log_types::{
3+
DataRow, DeserializableComponent, EntityPath, RowId, SerializableComponent, TimeInt, TimePoint,
4+
Timeline,
5+
};
6+
7+
use crate::LogDb;
8+
9+
// ----------------------------------------------------------------------------
10+
11+
/// Get the latest value for a given [`re_log_types::Component`].
12+
///
13+
/// This assumes that the row we get from the store only contains a single instance for this
14+
/// component; it will log a warning otherwise.
15+
///
16+
/// This should only be used for "mono-components" such as `Transform` and `Tensor`.
17+
pub fn query_latest_single<C: DeserializableComponent>(
18+
data_store: &re_arrow_store::DataStore,
19+
entity_path: &EntityPath,
20+
query: &LatestAtQuery,
21+
) -> Option<C>
22+
where
23+
for<'b> &'b C::ArrayType: IntoIterator,
24+
{
25+
crate::profile_function!();
26+
27+
// Although it would be nice to use the `re_query` helpers for this, we would need to move
28+
// this out of re_data_store to avoid a circular dep. Since we don't need to do a join for
29+
// single components this is easy enough.
30+
31+
let (_, cells) = data_store.latest_at(query, entity_path, C::name(), &[C::name()])?;
32+
let cell = cells.get(0)?.as_ref()?;
33+
34+
let mut iter = cell.try_to_native::<C>().ok()?;
35+
36+
let component = iter.next();
37+
38+
if iter.next().is_some() {
39+
re_log::warn_once!("Unexpected batch for {} at: {}", C::name(), entity_path);
40+
}
41+
42+
component
43+
}
44+
45+
/// Get the latest value for a given [`re_log_types::Component`] assuming it is timeless.
46+
///
47+
/// This assumes that the row we get from the store only contains a single instance for this
48+
/// component; it will log a warning otherwise.
49+
pub fn query_timeless_single<C: DeserializableComponent>(
50+
data_store: &re_arrow_store::DataStore,
51+
entity_path: &EntityPath,
52+
) -> Option<C>
53+
where
54+
for<'b> &'b C::ArrayType: IntoIterator,
55+
{
56+
let query = re_arrow_store::LatestAtQuery::new(Timeline::default(), TimeInt::MAX);
57+
query_latest_single(data_store, entity_path, &query)
58+
}
59+
60+
// ----------------------------------------------------------------------------
61+
62+
/// Store a single value for a given [`re_log_types::Component`].
63+
pub fn store_one_component<C: SerializableComponent>(
64+
log_db: &mut LogDb,
65+
entity_path: &EntityPath,
66+
timepoint: &TimePoint,
67+
component: C,
68+
) {
69+
let mut row = DataRow::from_cells1(
70+
RowId::random(),
71+
entity_path.clone(),
72+
timepoint.clone(),
73+
1,
74+
[component].as_slice(),
75+
);
76+
row.compute_all_size_bytes();
77+
78+
match log_db.entity_db.try_add_data_row(&row) {
79+
Ok(()) => {}
80+
Err(err) => {
81+
re_log::warn_once!(
82+
"Failed to store component {}.{}: {err}",
83+
entity_path,
84+
C::name(),
85+
);
86+
}
87+
}
88+
}

crates/re_data_ui/src/log_msg.rs

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ impl DataUi for SetRecordingInfo {
3939
started,
4040
recording_source,
4141
is_official_example,
42+
recording_type,
4243
} = info;
4344

4445
egui::Grid::new("fields").num_columns(2).show(ui, |ui| {
@@ -61,6 +62,10 @@ impl DataUi for SetRecordingInfo {
6162
ui.monospace("is_official_example:");
6263
ui.label(format!("{is_official_example}"));
6364
ui.end_row();
65+
66+
ui.monospace("recording_type:");
67+
ui.label(format!("{recording_type}"));
68+
ui.end_row();
6469
});
6570
}
6671
}

crates/re_log_encoding/src/decoder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ fn test_encode_decode() {
198198
rustc_version: String::new(),
199199
llvm_version: String::new(),
200200
},
201+
recording_type: re_log_types::RecordingType::Data,
201202
},
202203
})];
203204

crates/re_log_types/src/lib.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ impl std::fmt::Display for RecordingType {
120120
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
121121
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
122122
pub struct RecordingId {
123-
variant: RecordingType,
124-
id: Arc<String>,
123+
pub variant: RecordingType,
124+
pub id: Arc<String>,
125125
}
126126

127127
impl RecordingId {
@@ -148,6 +148,11 @@ impl RecordingId {
148148
id: Arc::new(str),
149149
}
150150
}
151+
152+
#[inline]
153+
pub fn as_str(&self) -> &str {
154+
self.id.as_str()
155+
}
151156
}
152157

153158
impl std::fmt::Display for RecordingId {
@@ -187,6 +192,10 @@ impl ApplicationId {
187192
pub fn unknown() -> Self {
188193
Self("unknown_app_id".to_owned())
189194
}
195+
196+
pub fn as_str(&self) -> &str {
197+
self.0.as_str()
198+
}
190199
}
191200

192201
impl std::fmt::Display for ApplicationId {
@@ -261,6 +270,16 @@ pub struct RecordingInfo {
261270
pub started: Time,
262271

263272
pub recording_source: RecordingSource,
273+
274+
pub recording_type: RecordingType,
275+
}
276+
277+
impl RecordingInfo {
278+
/// Whether this `RecordingInfo` is the default used when a user is not explicitly
279+
/// creating their own blueprint.
280+
pub fn is_app_default_blueprint(&self) -> bool {
281+
self.application_id.as_str() == self.recording_id.as_str()
282+
}
264283
}
265284

266285
#[derive(Clone, Debug, PartialEq, Eq)]

crates/re_memory/src/memory_history.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ pub struct MemoryHistory {
2424

2525
/// Bytes used by the datastore according to its own accounting.
2626
pub counted_store: History<i64>,
27+
28+
/// Bytes used by the blueprint store according to its own accounting.
29+
pub counted_blueprint: History<i64>,
2730
}
2831

2932
impl Default for MemoryHistory {
@@ -35,6 +38,7 @@ impl Default for MemoryHistory {
3538
counted: History::new(0..max_elems, max_seconds),
3639
counted_gpu: History::new(0..max_elems, max_seconds),
3740
counted_store: History::new(0..max_elems, max_seconds),
41+
counted_blueprint: History::new(0..max_elems, max_seconds),
3842
}
3943
}
4044
}
@@ -46,15 +50,22 @@ impl MemoryHistory {
4650
counted,
4751
counted_gpu,
4852
counted_store,
53+
counted_blueprint,
4954
} = self;
5055
resident.is_empty()
5156
&& counted.is_empty()
5257
&& counted_gpu.is_empty()
5358
&& counted_store.is_empty()
59+
&& counted_blueprint.is_empty()
5460
}
5561

5662
/// Add data to history
57-
pub fn capture(&mut self, counted_gpu: Option<i64>, counted_store: Option<i64>) {
63+
pub fn capture(
64+
&mut self,
65+
counted_gpu: Option<i64>,
66+
counted_store: Option<i64>,
67+
counted_blueprint: Option<i64>,
68+
) {
5869
let mem_use = crate::MemoryUse::capture();
5970
let now = crate::util::sec_since_start();
6071

@@ -70,5 +81,8 @@ impl MemoryHistory {
7081
if let Some(counted_store) = counted_store {
7182
self.counted_store.add(now, counted_store);
7283
}
84+
if let Some(counted_blueprint) = counted_blueprint {
85+
self.counted_blueprint.add(now, counted_blueprint);
86+
}
7387
}
7488
}

crates/re_renderer/src/view_builder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ pub enum Projection {
133133
}
134134

135135
/// How [`Size::AUTO`] is interpreted.
136-
#[derive(Clone, Copy, Debug)]
136+
#[derive(Clone, Copy, Debug, PartialEq)]
137137
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
138138
pub struct AutoSizeConfig {
139139
/// Determines the point radius when [`crate::Size::AUTO`].

crates/re_sdk/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ pub fn new_recording_info(
167167
rustc_version: env!("RE_BUILD_RUSTC_VERSION").into(),
168168
llvm_version: env!("RE_BUILD_LLVM_VERSION").into(),
169169
},
170+
recording_type: re_log_types::RecordingType::Data,
170171
}
171172
}
172173

0 commit comments

Comments
 (0)