use ahash::HashMap;
use re_byte_size::SizeBytes as _;
use re_log_types::{StoreId, TimeReal};
use crate::blueprint_helpers::AppBlueprintCtx;
use crate::time_control::{PreviewRecordingsDb, TimeControlUpdateParams};
use crate::view::system_execution_output::VisualizerViewReport;
use crate::{
NeedsRepaint, SystemExecutionOutput, TimeControl, ViewClass, ViewId, ViewState,
VisualizerTypeReport,
};
type ViewStateKey = (StoreId, ViewId);
pub struct PreviewState {
time_ctrl: TimeControl,
recording_ids: ahash::HashSet<StoreId>,
}
impl re_byte_size::SizeBytes for PreviewState {
fn heap_size_bytes(&self) -> u64 {
let Self {
time_ctrl,
recording_ids,
} = self;
time_ctrl.heap_size_bytes() + recording_ids.heap_size_bytes()
}
}
impl Default for PreviewState {
fn default() -> Self {
Self {
time_ctrl: TimeControl::preview_time_control(),
recording_ids: Default::default(),
}
}
}
impl PreviewState {
pub fn register_recording(&mut self, store_id: &StoreId) {
self.recording_ids.insert(store_id.clone());
}
pub fn cleanup_recordings(&mut self, is_loaded: impl Fn(&StoreId) -> bool) {
self.recording_ids.retain(|id| is_loaded(id));
}
pub fn tick<'db>(
&mut self,
resolve: impl Fn(&StoreId) -> Option<&'db re_entity_db::EntityDb>,
stable_dt: f32,
) -> NeedsRepaint {
if self.recording_ids.is_empty() {
return NeedsRepaint::No;
}
let recordings: Vec<&re_entity_db::EntityDb> =
self.recording_ids.iter().filter_map(resolve).collect();
if recordings.is_empty() {
return NeedsRepaint::No;
}
let is_buffering = recordings.iter().any(|r| r.is_buffering());
let preview_db = PreviewRecordingsDb {
recordings: &recordings,
};
self.time_ctrl
.update(
&preview_db,
&TimeControlUpdateParams {
stable_dt,
more_data_is_streaming_in: false,
is_buffering,
should_diff_state: false,
},
None::<&AppBlueprintCtx<'_>>,
)
.needs_repaint
}
pub fn derive_recording_time_ctrl(&self, recording: &re_entity_db::EntityDb) -> TimeControl {
let mut tc = self.time_ctrl.clone();
let canonical_tl = *self.time_ctrl.timeline_name();
let Some(range) = recording.time_range_for(&canonical_tl) else {
return tc;
};
let offset = self
.time_ctrl
.time()
.unwrap_or_else(|| TimeReal::from(0.0_f64));
let time = (TimeReal::from(range.min) + offset).min(TimeReal::from(range.max));
tc.set_time_cursor_ad_hoc(canonical_tl, time);
tc
}
}
#[derive(Default)]
pub struct ViewStates {
states: HashMap<ViewStateKey, Box<dyn ViewState>>,
visualizer_reports: HashMap<ViewStateKey, VisualizerViewReport>,
pub preview: PreviewState,
}
impl re_byte_size::SizeBytes for ViewStates {
fn heap_size_bytes(&self) -> u64 {
let Self {
states,
visualizer_reports,
preview,
} = self;
states.heap_size_bytes() + visualizer_reports.heap_size_bytes() + preview.heap_size_bytes()
}
}
impl re_byte_size::MemUsageTreeCapture for ViewStates {
fn capture_mem_usage_tree(&self) -> re_byte_size::MemUsageTree {
let Self {
states,
visualizer_reports,
preview,
} = self;
let mut state_sizes = states
.iter()
.map(|((store_id, view_id), state)| {
(
format!("{store_id:?}/{view_id:?}"),
state.total_size_bytes(),
)
})
.collect::<Vec<_>>();
state_sizes.sort_by(|(lhs, _), (rhs, _)| lhs.cmp(rhs));
let mut states_node = re_byte_size::MemUsageNode::default();
for (name, size_bytes) in state_sizes {
states_node.add(name, size_bytes);
}
let mut node = re_byte_size::MemUsageNode::default();
node.add("states", states_node.into_tree());
node.add("visualizer_reports", visualizer_reports.heap_size_bytes());
node.add("preview", preview.heap_size_bytes());
node.with_total_size_bytes(self.total_size_bytes())
}
}
impl ViewStates {
pub fn get(&self, store_id: &StoreId, view_id: ViewId) -> Option<&dyn ViewState> {
self.states
.get(&(store_id.clone(), view_id))
.map(|s| s.as_ref())
}
pub fn get_mut_or_create(
&mut self,
store_id: &StoreId,
view_id: ViewId,
view_class: &dyn ViewClass,
) -> &mut dyn ViewState {
self.states
.entry((store_id.clone(), view_id))
.or_insert_with(|| view_class.new_state())
.as_mut()
}
pub fn ensure_state_exists(
&mut self,
store_id: &StoreId,
view_id: ViewId,
view_class: &dyn ViewClass,
) {
self.states
.entry((store_id.clone(), view_id))
.or_insert_with(|| view_class.new_state());
}
pub fn reset_visualizer_reports(&mut self) {
self.visualizer_reports.clear();
}
pub fn add_visualizer_reports_from_output(
&mut self,
store_id: &StoreId,
view_id: ViewId,
system_output: &SystemExecutionOutput,
) {
let per_visualizer_reports = self
.visualizer_reports
.entry((store_id.clone(), view_id))
.or_default();
per_visualizer_reports.extend(system_output.visualizer_execution_output.iter().filter_map(
|(id, result)| VisualizerTypeReport::from_result(result).map(|error| (*id, error)),
));
}
pub fn per_visualizer_type_reports(
&self,
store_id: &StoreId,
view_id: ViewId,
) -> Option<&VisualizerViewReport> {
self.visualizer_reports.get(&(store_id.clone(), view_id))
}
}