Skip to content

Commit

Permalink
Refactor SpaceViewId as a generic BlueprintId (#4248)
Browse files Browse the repository at this point in the history
### What
I needed another type very similar to a SpaceView id for DataQueries.

Refactor to make these types somewhat generic self-aware of the prefixes
in the tree where they are registered.

### 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/4248) (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/4248)
- [Docs
preview](https://rerun.io/preview/2c7799d640f868db11c90a2ec1c717367d48161e/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/2c7799d640f868db11c90a2ec1c717367d48161e/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 Nov 20, 2023
1 parent 1270e1b commit 2264567
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 76 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/re_viewer_context/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ lazy_static.workspace = true
macaw.workspace = true
ndarray.workspace = true
nohash-hasher.workspace = true
once_cell.workapce = true
parking_lot.workspace = true
serde = "1"
slotmap.workspace = true
Expand Down
158 changes: 158 additions & 0 deletions crates/re_viewer_context/src/blueprint_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use std::hash::BuildHasher;

use once_cell::sync::Lazy;
use re_log_types::{EntityPath, EntityPathPart, Index};

pub trait BlueprintIdRegistry {
fn registry() -> &'static EntityPath;
}

/// A unique id for a type of Blueprint contents.
#[derive(
Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Deserialize, serde::Serialize,
)]
pub struct BlueprintId<T: BlueprintIdRegistry> {
id: uuid::Uuid,
#[serde(skip)]
_registry: std::marker::PhantomData<T>,
}

impl<T: BlueprintIdRegistry> BlueprintId<T> {
pub fn invalid() -> Self {
Self {
id: uuid::Uuid::nil(),
_registry: std::marker::PhantomData,
}
}

pub fn random() -> Self {
Self {
id: uuid::Uuid::new_v4(),
_registry: std::marker::PhantomData,
}
}

pub fn from_entity_path(path: &EntityPath) -> Self {
if !path.is_child_of(T::registry()) {
return Self::invalid();
}

path.last()
.and_then(|last| uuid::Uuid::parse_str(last.to_string().as_str()).ok())
.map_or(Self::invalid(), |id| Self {
id,
_registry: std::marker::PhantomData,
})
}

pub fn hashed_from_str(s: &str) -> Self {
use std::hash::{Hash as _, Hasher as _};

let salt1: u64 = 0x307b_e149_0a3a_5552;
let salt2: u64 = 0x6651_522f_f510_13a4;

let hash1 = {
let mut hasher = ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher();
salt1.hash(&mut hasher);
s.hash(&mut hasher);
hasher.finish()
};

let hash2 = {
let mut hasher = ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher();
salt2.hash(&mut hasher);
s.hash(&mut hasher);
hasher.finish()
};

let uuid = uuid::Uuid::from_u64_pair(hash1, hash2);

uuid.into()
}

pub fn gpu_readback_id(self) -> re_renderer::GpuReadbackIdentifier {
re_log_types::hash::Hash64::hash(self.id).hash64()
}

#[inline]
pub fn as_entity_path(&self) -> EntityPath {
T::registry()
.iter()
.cloned()
.chain(std::iter::once(EntityPathPart::Index(Index::Uuid(self.id))))
.collect()
}

#[inline]
pub fn registry() -> &'static EntityPath {
T::registry()
}
}

impl<T: BlueprintIdRegistry> From<uuid::Uuid> for BlueprintId<T> {
#[inline]
fn from(id: uuid::Uuid) -> Self {
Self {
id,
_registry: std::marker::PhantomData,
}
}
}

impl<T: BlueprintIdRegistry> std::fmt::Display for BlueprintId<T> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}({:#})", T::registry(), self.id)
}
}

// ----------------------------------------------------------------------------
/// Helper to define a new [`BlueprintId`] type.
macro_rules! define_blueprint_id_type {
($type:ident, $registry:ident, $registry_name:expr) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $registry;

impl $registry {
const REGISTRY: &'static str = $registry_name;
}

impl BlueprintIdRegistry for $registry {
fn registry() -> &'static EntityPath {
static REGISTRY_PATH: Lazy<EntityPath> = Lazy::new(|| $registry::REGISTRY.into());
&REGISTRY_PATH
}
}

pub type $type = BlueprintId<$registry>;
};
}

// ----------------------------------------------------------------------------
// Definitions for the different [`BlueprintId`] types.
define_blueprint_id_type!(SpaceViewId, SpaceViewIdRegistry, "space_view");
define_blueprint_id_type!(DataQueryId, DataQueryIdRegistry, "data_query");

// ----------------------------------------------------------------------------
// Tests
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_blueprint_id() {
let id = SpaceViewId::random();
let path = id.as_entity_path();
assert!(path.is_child_of(&EntityPath::parse_forgiving("space_view/")));

let id = DataQueryId::random();
let path = id.as_entity_path();
assert!(path.is_child_of(&EntityPath::parse_forgiving("data_query/")));

let roundtrip = DataQueryId::from_entity_path(&id.as_entity_path());
assert_eq!(roundtrip, id);

let crossed = DataQueryId::from_entity_path(&SpaceViewId::random().as_entity_path());
assert_eq!(crossed, DataQueryId::invalid());
}
}
75 changes: 3 additions & 72 deletions crates/re_viewer_context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
mod annotations;
mod app_options;
mod blueprint_id;
mod caches;
mod command_sender;
mod component_ui_registry;
Expand All @@ -20,21 +21,20 @@ mod viewer_context;
// TODO(andreas): Move to its own crate?
pub mod gpu_bridge;

use std::hash::BuildHasher;

pub use annotations::{
AnnotationMap, Annotations, ResolvedAnnotationInfo, ResolvedAnnotationInfos,
MISSING_ANNOTATIONS,
};
pub use app_options::AppOptions;
pub use blueprint_id::{DataQueryId, SpaceViewId};
pub use caches::{Cache, Caches};
pub use command_sender::{
command_channel, CommandReceiver, CommandSender, SystemCommand, SystemCommandSender,
};
pub use component_ui_registry::{ComponentUiRegistry, UiVerbosity};
pub use item::{resolve_mono_instance_path, resolve_mono_instance_path_item, Item, ItemCollection};
use nohash_hasher::{IntMap, IntSet};
use re_log_types::{EntityPath, EntityPathPart, Index};
use re_log_types::EntityPath;
pub use selection_history::SelectionHistory;
pub use selection_state::{
HoverHighlight, HoveredSpace, InteractionHighlight, SelectionHighlight, SelectionState,
Expand Down Expand Up @@ -70,75 +70,6 @@ pub type EntitiesPerSystem = IntMap<ViewSystemName, IntSet<EntityPath>>;

pub type EntitiesPerSystemPerClass = IntMap<SpaceViewClassName, EntitiesPerSystem>;

/// A unique id for each space view.
#[derive(
Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Deserialize, serde::Serialize,
)]

pub struct SpaceViewId(uuid::Uuid);

impl SpaceViewId {
// TODO(jleibs): Can we make this an EntityPath instead?
pub const SPACEVIEW_PREFIX: &str = "space_view";

pub fn invalid() -> Self {
Self(uuid::Uuid::nil())
}

pub fn random() -> Self {
Self(uuid::Uuid::new_v4())
}

pub fn from_entity_path(path: &EntityPath) -> Self {
path.last()
.and_then(|last| uuid::Uuid::parse_str(last.to_string().as_str()).ok())
.map_or(Self::invalid(), Self)
}

pub fn hashed_from_str(s: &str) -> Self {
use std::hash::{Hash as _, Hasher as _};

let salt1: u64 = 0x307b_e149_0a3a_5552;
let salt2: u64 = 0x6651_522f_f510_13a4;

let hash1 = {
let mut hasher = ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher();
salt1.hash(&mut hasher);
s.hash(&mut hasher);
hasher.finish()
};

let hash2 = {
let mut hasher = ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher();
salt2.hash(&mut hasher);
s.hash(&mut hasher);
hasher.finish()
};

let uuid = uuid::Uuid::from_u64_pair(hash1, hash2);

Self(uuid)
}

pub fn gpu_readback_id(self) -> re_renderer::GpuReadbackIdentifier {
re_log_types::hash::Hash64::hash(self).hash64()
}

#[inline]
pub fn as_entity_path(&self) -> EntityPath {
let prefix = EntityPathPart::Name(Self::SPACEVIEW_PREFIX.into());
let uuid = EntityPathPart::Index(Index::Uuid(self.0));
EntityPath::from([prefix, uuid].as_slice())
}
}

impl std::fmt::Display for SpaceViewId {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#}", self.0)
}
}

slotmap::new_key_type! {
/// Identifier for a blueprint group.
pub struct DataBlueprintGroupHandle;
Expand Down
6 changes: 2 additions & 4 deletions crates/re_viewport/src/viewport_blueprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,8 @@ pub fn load_viewport_blueprint(blueprint_db: &re_data_store::StoreDb) -> Viewpor
blueprint_db
.entity_db()
.tree
.children
.get(&re_data_store::EntityPathPart::Name(
SpaceViewId::SPACEVIEW_PREFIX.into(),
)) {
.subtree(SpaceViewId::registry())
{
space_views
.children
.values()
Expand Down

0 comments on commit 2264567

Please sign in to comment.