use std::collections::hash_map::Entry;
use std::sync::Arc;
use ahash::HashMap;
use bit_vec::BitVec;
use nohash_hasher::{IntMap, IntSet};
use re_arrow_combinators::extract_nested_fields;
use re_chunk::{ArchetypeName, ComponentIdentifier, ComponentType};
use re_chunk_store::{ChunkStoreEvent, ChunkStoreSubscriber};
use re_log::{debug_assert, debug_panic};
use re_log_types::{EntityPath, EntityPathHash, StoreId};
use re_sdk_types::ComponentSet;
use crate::typed_entity_collections::DatatypeMatch;
use crate::view::visualizer_system::{AnyPhysicalDatatypeRequirement, DatatypeSet};
use crate::{
IdentifiedViewSystem, IndicatedEntities, RequiredComponents, ViewSystemIdentifier,
VisualizableEntities, VisualizerSystem, typed_entity_collections::VisualizableReason,
};
pub struct VisualizerEntitySubscriber {
visualizer: ViewSystemIdentifier,
relevant_archetype: Option<ArchetypeName>,
requirement: Requirement,
known_builtin_enum_components: Arc<IntSet<ComponentType>>,
per_store_mapping: HashMap<StoreId, VisualizerEntityMapping>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct AllComponentsRequirement {
required_components_indices: IntMap<ComponentIdentifier, usize>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct AnyComponentRequirement {
relevant_components: ComponentSet,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Requirement {
None,
AllComponents(AllComponentsRequirement),
AnyComponent(AnyComponentRequirement),
AnyPhysicalDatatype(AnyPhysicalDatatypeRequirement),
}
#[derive(Default)]
struct VisualizerEntityMapping {
required_component_and_filter_bitmap_per_entity: IntMap<EntityPathHash, BitVec>,
visualizable_entities: VisualizableEntities,
indicated_entities: IndicatedEntities,
}
impl From<ComponentSet> for AllComponentsRequirement {
fn from(value: ComponentSet) -> Self {
Self {
required_components_indices: value
.into_iter()
.enumerate()
.map(|(i, name)| (name, i))
.collect(),
}
}
}
impl From<ComponentSet> for AnyComponentRequirement {
fn from(value: ComponentSet) -> Self {
Self {
relevant_components: value,
}
}
}
impl From<RequiredComponents> for Requirement {
fn from(value: RequiredComponents) -> Self {
match value {
RequiredComponents::None => Self::None,
RequiredComponents::AllComponents(components) => Self::AllComponents(components.into()),
RequiredComponents::AnyComponent(components) => Self::AnyComponent(components.into()),
RequiredComponents::AnyPhysicalDatatype(requirement) => {
Self::AnyPhysicalDatatype(requirement)
}
}
}
}
impl VisualizerEntitySubscriber {
pub fn new<T: IdentifiedViewSystem + VisualizerSystem>(
visualizer: &T,
known_builtin_enum_components: Arc<IntSet<ComponentType>>,
app_options: &crate::AppOptions,
) -> Self {
let visualizer_query_info = visualizer.visualizer_query_info(app_options);
Self {
visualizer: T::identifier(),
relevant_archetype: visualizer_query_info.relevant_archetype,
requirement: visualizer_query_info.required.into(),
known_builtin_enum_components,
per_store_mapping: Default::default(),
}
}
#[inline]
pub fn visualizable_entities(&self, store: &StoreId) -> Option<&VisualizableEntities> {
self.per_store_mapping
.get(store)
.map(|mapping| &mapping.visualizable_entities)
}
pub fn indicated_entities(&self, store: &StoreId) -> Option<&IndicatedEntities> {
self.per_store_mapping
.get(store)
.map(|mapping| &mapping.indicated_entities)
}
}
fn process_entity_components(
relevant_archetype: Option<ArchetypeName>,
requirement: &Requirement,
visualizer: &ViewSystemIdentifier,
known_enum_types: &IntSet<ComponentType>,
store_mapping: &mut VisualizerEntityMapping,
store_id: &StoreId,
re_chunk_store::ChunkMeta {
entity_path,
components,
}: re_chunk_store::ChunkMeta,
) {
if relevant_archetype.is_none()
|| relevant_archetype.is_some_and(|archetype| {
components
.iter()
.any(|c| c.descriptor.archetype == Some(archetype))
})
{
store_mapping
.indicated_entities
.0
.insert(entity_path.clone());
}
match requirement {
Requirement::None => {
re_log::trace!(
"Entity {entity_path:?} in store {store_id:?} may now be visualizable by {visualizer:?} (no requirements)",
);
store_mapping
.visualizable_entities
.0
.insert(entity_path.clone(), VisualizableReason::Always);
}
Requirement::AllComponents(AllComponentsRequirement {
required_components_indices,
}) => {
let required_components_bitmap = store_mapping
.required_component_and_filter_bitmap_per_entity
.entry(entity_path.hash())
.or_insert_with(|| {
debug_assert!(
!required_components_indices.is_empty(),
"[DEBUG ASSERT] encountered empty set of required components for `RequiredComponentMode::All`"
);
BitVec::from_elem(required_components_indices.len(), false)
});
if required_components_bitmap.all() {
return;
}
for c in components {
if let Some(index) = required_components_indices.get(&c.descriptor.component)
&& c.has_data
{
required_components_bitmap.set(*index, true);
}
}
if required_components_bitmap.all() {
re_log::trace!(
"Entity {entity_path:?} in store {store_id:?} may now be visualizable by {visualizer:?}",
);
store_mapping
.visualizable_entities
.0
.insert(entity_path.clone(), VisualizableReason::ExactMatchAll);
}
}
Requirement::AnyComponent(AnyComponentRequirement {
relevant_components,
}) => {
let has_any_component = components
.iter()
.any(|c| relevant_components.contains(&c.descriptor.component) && c.has_data);
if has_any_component {
re_log::trace!(
"Entity {entity_path:?} in store {store_id:?} may now be visualizable by {visualizer:?} (has any required component)",
);
store_mapping
.visualizable_entities
.0
.insert(entity_path.clone(), VisualizableReason::ExactMatchAny);
}
}
Requirement::AnyPhysicalDatatype(AnyPhysicalDatatypeRequirement {
target_component,
semantic_type,
physical_types,
allow_static_data,
}) => {
let mut has_any_datatype = false;
for c in components {
if !allow_static_data && c.is_static_only {
continue;
}
let Some(arrow_datatype) = &c.inner_arrow_datatype else {
continue;
};
if let Some(match_info) = check_datatype_match(
known_enum_types,
arrow_datatype,
c.descriptor.component_type,
semantic_type,
physical_types,
c.descriptor.component,
) && c.has_data
{
has_any_datatype = true;
insert_datatype_match(
&mut store_mapping.visualizable_entities,
&entity_path,
c.descriptor.component,
*target_component,
match_info,
visualizer,
);
}
}
if has_any_datatype {
re_log::trace!(
"Entity {entity_path:?} in store {store_id:?} may now be visualizable by {visualizer:?} (has any required datatype)",
);
}
}
}
}
fn check_datatype_match(
known_enum_types: &IntSet<ComponentType>,
arrow_datatype: &arrow::datatypes::DataType,
component_type: Option<ComponentType>,
semantic_type: &ComponentType,
physical_types: &DatatypeSet,
component: ComponentIdentifier,
) -> Option<DatatypeMatch> {
let is_physical_match = physical_types.contains(arrow_datatype);
let is_semantic_match = component_type == Some(*semantic_type);
let is_known_enum = component_type.is_some_and(|ct| known_enum_types.contains(&ct));
if is_known_enum && !is_semantic_match {
return None;
}
match (is_physical_match, is_semantic_match) {
(false, false) => {
extract_nested_fields(arrow_datatype, |dt| physical_types.contains(dt)).map(
|selectors| DatatypeMatch::PhysicalDatatypeOnly {
arrow_datatype: arrow_datatype.clone(),
component_type,
selectors: selectors.into(),
},
)
}
(true, false) => Some(DatatypeMatch::PhysicalDatatypeOnly {
arrow_datatype: arrow_datatype.clone(),
component_type,
selectors: Vec::new(),
}),
(true, true) => Some(DatatypeMatch::NativeSemantics {
arrow_datatype: arrow_datatype.clone(),
component_type,
}),
(false, true) => {
re_log::warn_once!(
"Component {component:?} matched semantic type {semantic_type:?} but none of the expected physical arrow types {arrow_datatype:?} for this semantic type.",
);
None
}
}
}
fn insert_datatype_match(
visualizable_entities: &mut VisualizableEntities,
entity_path: &EntityPath,
component: ComponentIdentifier,
target_component: ComponentIdentifier,
match_info: DatatypeMatch,
visualizer: &ViewSystemIdentifier,
) {
match visualizable_entities.0.entry(entity_path.clone()) {
Entry::Occupied(mut occupied_entry) => {
if let VisualizableReason::DatatypeMatchAny {
matches,
target_component: previous_target,
} = occupied_entry.get_mut()
{
re_log::debug_assert_eq!(&target_component, previous_target);
matches.insert(component, match_info);
} else {
debug_panic!(
"entity {entity_path:?} already marked visualizable for visualizer {visualizer:?} with a different reason than `DatatypeMatchAny`",
);
}
}
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(VisualizableReason::DatatypeMatchAny {
target_component,
matches: std::iter::once((component, match_info)).collect(),
});
}
}
}
impl ChunkStoreSubscriber for VisualizerEntitySubscriber {
#[inline]
fn name(&self) -> String {
self.visualizer.as_str().to_owned()
}
#[inline]
fn as_any(&self) -> &dyn std::any::Any {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn on_events(&mut self, events: &[ChunkStoreEvent]) {
re_tracing::profile_function!(self.visualizer);
for event in events {
let store_mapping = self
.per_store_mapping
.entry(event.store_id.clone())
.or_default();
match &event.diff {
re_chunk_store::ChunkStoreDiff::Addition(add) => {
process_entity_components(
self.relevant_archetype,
&self.requirement,
&self.visualizer,
&self.known_builtin_enum_components,
store_mapping,
&event.store_id,
add.chunk_meta(),
);
}
re_chunk_store::ChunkStoreDiff::VirtualAddition(virtual_add) => {
for meta in virtual_add.chunk_metas() {
process_entity_components(
self.relevant_archetype,
&self.requirement,
&self.visualizer,
&self.known_builtin_enum_components,
store_mapping,
&event.store_id,
meta,
);
}
}
re_chunk_store::ChunkStoreDiff::Deletion(_) => {
}
}
}
}
}