Skip to content

Commit

Permalink
Fully migrate to DataQueryBlueprint (#4311)
Browse files Browse the repository at this point in the history
### What
- Part of the #4308 stack:
  - #4310
  - THIS PR: #4311
  - #4380
  - #4381

This activates the new expression-based DataQueries and removes the
usages of SpaceViewContents.
Also includes a very primitive query-expression editor.

### Known issues
- #4377 captures several issues, specifically:
- Add/Remove Entity logic is broken. (See:
#4381)
  - Heuristics run way too slowly for complex scenes

However, for the sake of reviewer sanity, that work will be broken out
as a follow-on PR. This PR is going to be painful enough as it is.

### 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 [demo.rerun.io](https://demo.rerun.io/pr/4311) (if
applicable)
* [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/4311)
- [Docs
preview](https://rerun.io/preview/68b22fecfb162e692db791f53e2cdbce1969b53d/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/68b22fecfb162e692db791f53e2cdbce1969b53d/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
  • Loading branch information
jleibs authored and teh-cmc committed Nov 30, 2023
1 parent 0ffa61c commit c2217d0
Show file tree
Hide file tree
Showing 63 changed files with 2,458 additions and 650 deletions.
2 changes: 1 addition & 1 deletion crates/re_data_ui/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl DataUi for Item {
query: &re_arrow_store::LatestAtQuery,
) {
match self {
Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => {
Item::SpaceView(_) | Item::DataBlueprintGroup(_, _, _) => {
// Shouldn't be reachable since SelectionPanel::contents doesn't show data ui for these.
// If you add something in here make sure to adjust SelectionPanel::contents accordingly.
}
Expand Down
7 changes: 4 additions & 3 deletions crates/re_data_ui/src/item_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use egui::Ui;
use re_data_store::InstancePath;
use re_log_types::{ComponentPath, EntityPath, TimeInt, Timeline};
use re_viewer_context::{
DataBlueprintGroupHandle, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext,
DataQueryId, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext,
};

use super::DataUi;
Expand Down Expand Up @@ -197,9 +197,10 @@ pub fn data_blueprint_group_button_to(
ui: &mut egui::Ui,
text: impl Into<egui::WidgetText>,
space_view_id: SpaceViewId,
group_handle: DataBlueprintGroupHandle,
query_id: DataQueryId,
entity_path: EntityPath,
) -> egui::Response {
let item = Item::DataBlueprintGroup(space_view_id, group_handle);
let item = Item::DataBlueprintGroup(space_view_id, query_id, entity_path);
let response = ctx
.re_ui
.selectable_label_with_icon(
Expand Down
14 changes: 14 additions & 0 deletions crates/re_log_types/src/path/entity_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,20 @@ impl From<EntityPath> for String {
}
}

impl From<re_types_core::datatypes::EntityPath> for EntityPath {
#[inline]
fn from(value: re_types_core::datatypes::EntityPath) -> Self {
EntityPath::parse_forgiving(&value.0)
}
}

impl From<&EntityPath> for re_types_core::datatypes::EntityPath {
#[inline]
fn from(value: &EntityPath) -> Self {
Self(value.to_string().into())
}
}

// ----------------------------------------------------------------------------

use re_types_core::Loggable;
Expand Down
23 changes: 22 additions & 1 deletion crates/re_log_types/src/path/entity_path_expr.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Display;

use crate::EntityPath;

/// An expression that corresponds to multiple [`EntityPath`]s within a tree.
Expand Down Expand Up @@ -44,11 +46,30 @@ impl EntityPathExpr {
}
}

impl Display for EntityPathExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Exact(path) => path.fmt(f),
Self::Recursive(path) => {
if path.is_root() {
write!(f, "/")
} else {
write!(f, "{path}/")
}
}
}
}
}

impl From<&str> for EntityPathExpr {
#[inline]
fn from(path: &str) -> Self {
if let Some(path) = path.strip_suffix('/') {
Self::Recursive(EntityPath::from(path))
if path.is_empty() {
Self::Recursive(EntityPath::root())
} else {
Self::Recursive(EntityPath::from(path))
}
} else {
Self::Exact(EntityPath::from(path))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/re_space_view/src/blueprint/query_expressions.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions crates/re_space_view/src/data_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ pub trait PropertyResolver {
fn resolve_entity_overrides(&self, ctx: &StoreContext<'_>) -> EntityOverrides;
}

pub struct NoopResolver {}

impl PropertyResolver for NoopResolver {
fn resolve_entity_overrides(&self, _ctx: &StoreContext<'_>) -> EntityOverrides {
EntityOverrides {
root: EntityProperties::default(),
individual: EntityPropertyMap::default(),
group: EntityPropertyMap::default(),
}
}
}

pub static NOOP_RESOLVER: NoopResolver = NoopResolver {};

/// The common trait implemented for data queries
///
/// Both interfaces return [`re_viewer_context::DataResult`]s, which are self-contained description of the data
Expand Down
187 changes: 176 additions & 11 deletions crates/re_space_view/src/data_query_blueprint.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use nohash_hasher::IntSet;
use once_cell::sync::Lazy;
use re_data_store::{EntityProperties, EntityTree};
use re_data_store::{
EntityProperties, EntityPropertiesComponent, EntityPropertyMap, EntityTree, StoreDb,
};
use re_log_types::{EntityPath, EntityPathExpr};
use re_viewer_context::{
DataQueryId, DataQueryResult, DataResult, DataResultHandle, DataResultNode, DataResultTree,
EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassName,
EntitiesPerSystem, EntitiesPerSystemPerClass, SpaceViewClassName, SpaceViewId, StoreContext,
};
use slotmap::SlotMap;
use smallvec::SmallVec;
Expand All @@ -23,14 +25,75 @@ use crate::{blueprint::QueryExpressions, DataQuery, EntityOverrides, PropertyRes
/// The results of recursive expressions are only included if they are found within the [`EntityTree`]
/// and for which there is a valid `ViewPart` system. This keeps recursive expressions from incorrectly
/// picking up irrelevant data within the tree.
#[derive(Clone, PartialEq, Eq)]
pub struct DataQueryBlueprint {
pub id: DataQueryId,
pub space_view_class_name: SpaceViewClassName,
pub expressions: QueryExpressions,
}

impl DataQueryBlueprint {
pub const OVERRIDES_PREFIX: &str = "overrides";
pub fn is_equivalent(&self, other: &DataQueryBlueprint) -> bool {
self.space_view_class_name.eq(&other.space_view_class_name)
&& self.expressions.eq(&other.expressions)
}
}

impl DataQueryBlueprint {
pub const INDIVIDUAL_OVERRIDES_PREFIX: &str = "individual_overrides";
pub const RECURSIVE_OVERRIDES_PREFIX: &str = "recursive_overrides";

pub fn new<'a>(
space_view_class_name: SpaceViewClassName,
queries_entities: impl Iterator<Item = &'a EntityPathExpr>,
) -> Self {
Self {
id: DataQueryId::random(),
space_view_class_name,
expressions: queries_entities
.map(|exp| exp.to_string().into())
.collect::<Vec<_>>()
.into(),
}
}

pub fn try_from_db(
path: &EntityPath,
blueprint_db: &StoreDb,
space_view_class_name: SpaceViewClassName,
) -> Option<Self> {
let expressions = blueprint_db
.store()
.query_timeless_component::<QueryExpressions>(path)
.map(|c| c.value)?;

let id = DataQueryId::from_entity_path(path);

Some(Self {
id,
space_view_class_name,
expressions,
})
}

pub fn build_resolver<'a>(
&self,
container: SpaceViewId,
auto_properties: &'a EntityPropertyMap,
) -> DataQueryPropertyResolver<'a> {
DataQueryPropertyResolver {
auto_properties,
default_stack: vec![container.as_entity_path(), self.id.as_entity_path()],
individual_override_root: self
.id
.as_entity_path()
.join(&Self::INDIVIDUAL_OVERRIDES_PREFIX.into()),
recursive_override_root: self
.id
.as_entity_path()
.join(&Self::RECURSIVE_OVERRIDES_PREFIX.into()),
}
}
}

impl DataQuery for DataQueryBlueprint {
Expand Down Expand Up @@ -93,6 +156,7 @@ impl<'a> QueryExpressionEvaluator<'a> {
.expressions
.expressions
.iter()
.filter(|exp| !exp.as_str().is_empty())
.map(|exp| EntityPathExpr::from(exp.as_str()))
.collect();

Expand Down Expand Up @@ -166,18 +230,29 @@ impl<'a> QueryExpressionEvaluator<'a> {
}

let base_entity_path = self.blueprint.id.as_entity_path().clone();
let prefix = EntityPath::from(DataQueryBlueprint::OVERRIDES_PREFIX);
let override_path = base_entity_path.join(&prefix).join(&entity_path);

let individual_override_path = base_entity_path
.join(&DataQueryBlueprint::INDIVIDUAL_OVERRIDES_PREFIX.into())
.join(&entity_path);
let recursive_override_path = base_entity_path
.join(&DataQueryBlueprint::RECURSIVE_OVERRIDES_PREFIX.into())
.join(&entity_path);

let self_leaf = if !view_parts.is_empty() || exact_match {
let individual_props = overrides.individual.get_opt(&entity_path);
let mut leaf_resolved_properties = resolved_properties.clone();

if let Some(props) = individual_props {
leaf_resolved_properties = leaf_resolved_properties.with_child(props);
}
Some(data_results.insert(DataResultNode {
data_result: DataResult {
entity_path: entity_path.clone(),
view_parts,
is_group: false,
individual_properties: overrides.individual.get_opt(&entity_path).cloned(),
resolved_properties: resolved_properties.clone(),
override_path: override_path.clone(),
resolved_properties: leaf_resolved_properties,
override_path: individual_override_path,
},
children: Default::default(),
}))
Expand All @@ -196,7 +271,7 @@ impl<'a> QueryExpressionEvaluator<'a> {
self.add_entity_tree_to_data_results_recursive(
subtree,
overrides,
inherited,
&resolved_properties,
data_results,
recursive_match, // Once we have hit a recursive match, it's always propagated
)
Expand All @@ -207,26 +282,109 @@ impl<'a> QueryExpressionEvaluator<'a> {
if children.is_empty() || children.len() == 1 && self_leaf.is_some() {
self_leaf
} else {
let individual_properties = overrides.individual.get_opt(&entity_path).cloned();
// The 'individual' properties of a group are the group overrides
let individual_properties = overrides.group.get_opt(&entity_path).cloned();
Some(data_results.insert(DataResultNode {
data_result: DataResult {
entity_path,
view_parts: Default::default(),
is_group: true,
individual_properties,
resolved_properties,
override_path,
override_path: recursive_override_path,
},
children,
}))
}
}
}

pub struct DataQueryPropertyResolver<'a> {
auto_properties: &'a EntityPropertyMap,
default_stack: Vec<EntityPath>,
individual_override_root: EntityPath,
recursive_override_root: EntityPath,
}

impl DataQueryPropertyResolver<'_> {
fn resolve_entity_overrides_for_path(
&self,
ctx: &StoreContext<'_>,
props_path: &EntityPath,
) -> EntityPropertyMap {
re_tracing::profile_function!();
let blueprint = ctx.blueprint;

let mut prop_map = self.auto_properties.clone();

if let Some(tree) = blueprint.entity_db().tree.subtree(props_path) {
tree.visit_children_recursively(&mut |path: &EntityPath| {
if let Some(props) = blueprint
.store()
.query_timeless_component_quiet::<EntityPropertiesComponent>(path)
{
let overridden_path =
EntityPath::from(&path.as_slice()[props_path.len()..path.len()]);
prop_map.update(overridden_path, props.value.props);
}
});
}
prop_map
}
}

impl<'a> PropertyResolver for DataQueryPropertyResolver<'a> {
/// Helper function to lookup the properties for a given entity path.
///
/// We start with the auto properties for the `SpaceView` as the base layer and
/// then incrementally override from there.
fn resolve_entity_overrides(&self, ctx: &StoreContext<'_>) -> EntityOverrides {
re_tracing::profile_function!();
let blueprint = ctx.blueprint;

let mut root: EntityProperties = Default::default();
for prefix in &self.default_stack {
if let Some(overrides) = ctx
.blueprint
.store()
.query_timeless_component::<EntityPropertiesComponent>(prefix)
{
root = root.with_child(&overrides.value.props);
}
}

let mut individual = self.auto_properties.clone();

if let Some(tree) = blueprint
.entity_db()
.tree
.subtree(&self.individual_override_root)
{
tree.visit_children_recursively(&mut |path: &EntityPath| {
if let Some(props) = blueprint
.store()
.query_timeless_component::<EntityPropertiesComponent>(path)
{
let overridden_path = EntityPath::from(
&path.as_slice()[self.individual_override_root.len()..path.len()],
);
individual.update(overridden_path, props.value.props);
}
});
}

EntityOverrides {
root,
individual: self.resolve_entity_overrides_for_path(ctx, &self.individual_override_root),
group: self.resolve_entity_overrides_for_path(ctx, &self.recursive_override_root),
}
}
}

#[cfg(feature = "testing")]
#[cfg(test)]
mod tests {
use re_data_store::{EntityPropertyMap, StoreDb};
use re_data_store::StoreDb;
use re_log_types::{example_components::MyPoint, DataRow, RowId, StoreId, TimePoint, Timeline};
use re_viewer_context::StoreContext;

Expand Down Expand Up @@ -334,6 +492,13 @@ mod tests {
"parent/skipped/child2",
],
),
(
vec!["not/found"],
// TODO(jleibs): Making this work requires merging the EntityTree walk with a minimal-coverage ExactMatchTree walk
// not crucial for now until we expose a free-form UI for entering paths.
// vec!["/", "not/", "not/found"]),
vec![],
),
];

for (input, outputs) in scenarios {
Expand Down
2 changes: 1 addition & 1 deletion crates/re_space_view/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod space_view_contents;
mod unreachable_transform_reason;

pub use blueprint::QueryExpressions;
pub use data_query::{DataQuery, EntityOverrides, PropertyResolver};
pub use data_query::{DataQuery, EntityOverrides, PropertyResolver, NOOP_RESOLVER};
pub use data_query_blueprint::DataQueryBlueprint;
pub use screenshot::ScreenshotMode;
pub use space_view_contents::{DataBlueprintGroup, SpaceViewContents};
Expand Down
Loading

0 comments on commit c2217d0

Please sign in to comment.