diff --git a/crates/re_data_store/src/log_db.rs b/crates/re_data_store/src/log_db.rs index 7795c5d2214a..5821b97cac2a 100644 --- a/crates/re_data_store/src/log_db.rs +++ b/crates/re_data_store/src/log_db.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use nohash_hasher::IntMap; use re_arrow_store::{DataStoreConfig, TimeInt}; @@ -159,33 +161,31 @@ impl EntityDb { /// A in-memory database built from a stream of [`LogMsg`]es. #[derive(Default)] pub struct LogDb { - /// Messages in the order they arrived - chronological_row_ids: Vec, - log_messages: ahash::HashMap, - - /// Data that was logged with [`TimePoint::timeless`]. - /// We need to re-insert those in any new timelines - /// that are created after they were logged. - timeless_row_ids: Vec, + /// All [`EntityPathOpMsg`]s ever received. + entity_op_msgs: BTreeMap, /// Set by whomever created this [`LogDb`]. pub data_source: Option, /// Comes in a special message, [`LogMsg::BeginRecordingMsg`]. - recording_info: Option, + recording_msg: Option, /// Where we store the entities. pub entity_db: EntityDb, } impl LogDb { + pub fn recording_msg(&self) -> Option<&BeginRecordingMsg> { + self.recording_msg.as_ref() + } + pub fn recording_info(&self) -> Option<&RecordingInfo> { - self.recording_info.as_ref() + self.recording_msg().map(|msg| &msg.info) } pub fn recording_id(&self) -> RecordingId { - if let Some(info) = &self.recording_info { - info.recording_id + if let Some(msg) = &self.recording_msg { + msg.info.recording_id } else { RecordingId::ZERO } @@ -203,11 +203,16 @@ impl LogDb { self.entity_db.tree.num_timeless_messages() } + pub fn num_rows(&self) -> usize { + self.entity_db.data_store.total_timeless_rows() as usize + + self.entity_db.data_store.total_temporal_rows() as usize + } + pub fn is_empty(&self) -> bool { - self.log_messages.is_empty() + self.num_rows() == 0 } - pub fn add(&mut self, msg: LogMsg) -> Result<(), Error> { + pub fn add(&mut self, msg: &LogMsg) -> Result<(), Error> { crate::profile_function!(); match &msg { @@ -218,38 +223,27 @@ impl LogDb { time_point, path_op, } = msg; + self.entity_op_msgs.insert(*row_id, msg.clone()); self.entity_db.add_path_op(*row_id, time_point, path_op); } LogMsg::ArrowMsg(_, inner) => self.entity_db.try_add_arrow_msg(inner)?, LogMsg::Goodbye(_) => {} } - // TODO(#1619): the following only makes sense because, while we support sending and - // receiving batches, we don't actually do so yet. - // We need to stop storing raw `LogMsg`s before we can benefit from our batching. - self.chronological_row_ids.push(msg.id()); - self.log_messages.insert(msg.id(), msg); - Ok(()) } fn add_begin_recording_msg(&mut self, msg: &BeginRecordingMsg) { - self.recording_info = Some(msg.info.clone()); + self.recording_msg = Some(msg.clone()); } - pub fn len(&self) -> usize { - self.log_messages.len() + /// Returns an iterator over all [`EntityPathOpMsg`]s that have been written to this `LogDb`. + pub fn iter_entity_op_msgs(&self) -> impl Iterator { + self.entity_op_msgs.values() } - /// In the order they arrived - pub fn chronological_log_messages(&self) -> impl Iterator { - self.chronological_row_ids - .iter() - .filter_map(|id| self.get_log_msg(id)) - } - - pub fn get_log_msg(&self, row_id: &RowId) -> Option<&LogMsg> { - self.log_messages.get(row_id) + pub fn get_entity_op_msg(&self, row_id: &RowId) -> Option<&EntityPathOpMsg> { + self.entity_op_msgs.get(row_id) } /// Free up some RAM by forgetting the older parts of all timelines. @@ -263,26 +257,15 @@ impl LogDb { let cutoff_times = self.entity_db.data_store.oldest_time_per_timeline(); let Self { - chronological_row_ids, - log_messages, - timeless_row_ids, + entity_op_msgs, data_source: _, - recording_info: _, + recording_msg: _, entity_db, } = self; { - crate::profile_scope!("chronological_row_ids"); - chronological_row_ids.retain(|row_id| !drop_row_ids.contains(row_id)); - } - - { - crate::profile_scope!("log_messages"); - log_messages.retain(|row_id, _| !drop_row_ids.contains(row_id)); - } - { - crate::profile_scope!("timeless_row_ids"); - timeless_row_ids.retain(|row_id| !drop_row_ids.contains(row_id)); + crate::profile_scope!("entity_op_msgs"); + entity_op_msgs.retain(|row_id, _| !drop_row_ids.contains(row_id)); } entity_db.purge(&cutoff_times, &drop_row_ids); diff --git a/crates/re_log_encoding/src/encoder.rs b/crates/re_log_encoding/src/encoder.rs index 13cfbbdd849b..98f39603e5ac 100644 --- a/crates/re_log_encoding/src/encoder.rs +++ b/crates/re_log_encoding/src/encoder.rs @@ -98,3 +98,14 @@ pub fn encode<'a>( } encoder.finish() } + +pub fn encode_owned( + messages: impl Iterator, + write: impl std::io::Write, +) -> Result<(), EncodeError> { + let mut encoder = Encoder::new(write)?; + for message in messages { + encoder.append(&message)?; + } + encoder.finish() +} diff --git a/crates/re_log_types/src/data_table.rs b/crates/re_log_types/src/data_table.rs index 3af2aba910fb..4c54140be722 100644 --- a/crates/re_log_types/src/data_table.rs +++ b/crates/re_log_types/src/data_table.rs @@ -555,7 +555,7 @@ impl DataTable { /// Internally, time columns are (de)serialized separately from the rest of the control /// columns for efficiency/QOL concerns: that doesn't change the fact that they are control /// columns all the same! - /// * Data columns are the one that hold component data. + /// * Data columns are the ones that hold component data. /// They are optional, potentially sparse, and never deserialized on the server-side (not by /// the storage systems, at least). pub fn serialize(&self) -> DataTableResult<(Schema, Chunk>)> { diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 32062b79534d..b001c12fbbef 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -527,7 +527,7 @@ impl eframe::App for App { log_db, ) .selection_state - .on_frame_start(log_db, blueprint); + .on_frame_start(blueprint); { // TODO(andreas): store the re_renderer somewhere else. @@ -704,7 +704,7 @@ impl App { log_db.data_source = Some(self.rx.source().clone()); } - if let Err(err) = log_db.add(msg) { + if let Err(err) = log_db.add(&msg) { re_log::error!("Failed to add incoming msg: {err}"); }; @@ -916,8 +916,6 @@ fn preview_files_being_dropped(egui_ctx: &egui::Context) { enum PanelSelection { #[default] Viewport, - - EventLog, } #[derive(Default, serde::Deserialize, serde::Serialize)] @@ -940,8 +938,6 @@ struct AppState { /// Which view panel is currently being shown panel_selection: PanelSelection, - event_log_view: crate::event_log_view::EventLogView, - selection_panel: crate::selection_panel::SelectionPanel, time_panel: crate::time_panel::TimePanel, @@ -969,7 +965,6 @@ impl AppState { selected_rec_id, recording_configs, panel_selection, - event_log_view, blueprints, selection_panel, time_panel, @@ -1014,7 +1009,6 @@ impl AppState { .entry(selected_app_id) .or_insert_with(|| Blueprint::new(ui.ctx())) .blueprint_panel_and_viewport(&mut ctx, ui), - PanelSelection::EventLog => event_log_view.ui(&mut ctx, ui), }); // move time last, so we get to see the first data first! @@ -1523,7 +1517,13 @@ fn save(app: &mut App, loop_selection: Option<(re_data_store::Timeline, TimeRang .set_title(title) .save_file() { - let f = save_database_to_file(app.log_db(), path, loop_selection); + let f = match save_database_to_file(app.log_db(), path, loop_selection) { + Ok(f) => f, + Err(err) => { + re_log::error!("File saving failed: {err}"); + return; + } + }; if let Err(err) = app.spawn_threaded_promise(FILE_SAVER_PROMISE, f) { // NOTE: Shouldn't even be possible as the "Save" button is already // grayed out at this point... better safe than sorry though. @@ -1546,16 +1546,6 @@ fn main_view_selector_ui(ui: &mut egui::Ui, app: &mut App) { { ui.close_menu(); } - if ui - .selectable_value( - &mut app.state.panel_selection, - PanelSelection::EventLog, - "Event Log", - ) - .clicked() - { - ui.close_menu(); - } }); } } @@ -1760,57 +1750,56 @@ fn save_database_to_file( log_db: &LogDb, path: std::path::PathBuf, time_selection: Option<(re_data_store::Timeline, TimeRangeF)>, -) -> impl FnOnce() -> anyhow::Result { - use re_log_types::{EntityPathOpMsg, TimeInt}; - - let msgs = match time_selection { - // Fast path: no query, just dump everything. - None => log_db - .chronological_log_messages() - .cloned() - .collect::>(), - - // Query path: time to filter! - Some((timeline, range)) => { - use std::ops::RangeInclusive; - let range: RangeInclusive = range.min.floor()..=range.max.ceil(); - log_db - .chronological_log_messages() - .filter(|msg| { - match msg { - LogMsg::BeginRecordingMsg(_) | LogMsg::Goodbye(_) => { - true // timeless - } - LogMsg::EntityPathOpMsg(_, EntityPathOpMsg { time_point, .. }) => { - time_point.is_timeless() || { - let is_within_range = time_point - .get(&timeline) - .map_or(false, |t| range.contains(t)); - is_within_range - } - } - LogMsg::ArrowMsg(_, _) => { - // TODO(john) - false - } - } - }) - .cloned() - .collect::>() - } - }; +) -> anyhow::Result anyhow::Result> { + use re_arrow_store::TimeRange; + + crate::profile_scope!("dump_messages"); - move || { + let begin_rec_msg = log_db + .recording_msg() + .map(|msg| LogMsg::BeginRecordingMsg(msg.clone())); + + let ent_op_msgs = log_db + .iter_entity_op_msgs() + .map(|msg| LogMsg::EntityPathOpMsg(log_db.recording_id(), msg.clone())) + .collect_vec(); + + let time_filter = time_selection.map(|(timeline, range)| { + ( + timeline, + TimeRange::new(range.min.floor(), range.max.ceil()), + ) + }); + let data_msgs: Result, _> = log_db + .entity_db + .data_store + .to_data_tables(time_filter) + .map(|table| { + table + .to_arrow_msg() + .map(|msg| LogMsg::ArrowMsg(log_db.recording_id(), msg)) + }) + .collect(); + + use anyhow::Context as _; + let data_msgs = data_msgs.with_context(|| "Failed to export to data tables")?; + + let msgs = std::iter::once(begin_rec_msg) + .flatten() // option + .chain(ent_op_msgs) + .chain(data_msgs); + + Ok(move || { crate::profile_scope!("save_to_file"); use anyhow::Context as _; let file = std::fs::File::create(path.as_path()) .with_context(|| format!("Failed to create file at {path:?}"))?; - re_log_encoding::encoder::encode(msgs.iter(), file) + re_log_encoding::encoder::encode_owned(msgs, file) .map(|_| path) .context("Message encode") - } + }) } #[allow(unused_mut)] @@ -1821,7 +1810,7 @@ fn load_rrd_to_log_db(mut read: impl std::io::Read) -> anyhow::Result { let mut log_db = LogDb::default(); for msg in decoder { - log_db.add(msg?)?; + log_db.add(&msg?)?; } Ok(log_db) } diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index d9942fe1928c..10ceb56ce0c6 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -13,7 +13,7 @@ mod viewer_analytics; pub(crate) use misc::{mesh_loader, Item, TimeControl, TimeView, ViewerContext}; use re_log_types::PythonVersion; -pub(crate) use ui::{event_log_view, memory_panel, selection_panel, time_panel, UiVerbosity}; +pub(crate) use ui::{memory_panel, selection_panel, time_panel, UiVerbosity}; pub use app::{App, StartupOptions}; pub use remote_viewer_app::RemoteViewerApp; diff --git a/crates/re_viewer/src/misc/item.rs b/crates/re_viewer/src/misc/item.rs index 096dc815599c..a73b3c583f0d 100644 --- a/crates/re_viewer/src/misc/item.rs +++ b/crates/re_viewer/src/misc/item.rs @@ -1,6 +1,6 @@ use itertools::Itertools; -use re_data_store::{InstancePath, LogDb}; -use re_log_types::{ComponentPath, RowId}; +use re_data_store::InstancePath; +use re_log_types::ComponentPath; use crate::ui::SpaceViewId; @@ -11,7 +11,6 @@ use crate::ui::SpaceViewId; /// A set of these is a an [`ItemCollection`]. #[derive(Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)] pub enum Item { - RowId(RowId), ComponentPath(ComponentPath), SpaceView(SpaceViewId), InstancePath(Option, InstancePath), @@ -21,7 +20,6 @@ pub enum Item { impl std::fmt::Debug for Item { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Item::RowId(s) => s.fmt(f), Item::ComponentPath(s) => s.fmt(f), Item::SpaceView(s) => write!(f, "{s:?}"), Item::InstancePath(sid, path) => write!(f, "({sid:?}, {path})"), @@ -32,13 +30,12 @@ impl std::fmt::Debug for Item { impl Item { /// If `false`, the selection is referring to data that is no longer present. - pub(crate) fn is_valid(&self, log_db: &LogDb, blueprint: &crate::ui::Blueprint) -> bool { + pub(crate) fn is_valid(&self, blueprint: &crate::ui::Blueprint) -> bool { match self { Item::ComponentPath(_) => true, Item::InstancePath(space_view_id, _) => space_view_id .map(|space_view_id| blueprint.viewport.space_view(&space_view_id).is_some()) .unwrap_or(true), - Item::RowId(row_id) => log_db.get_log_msg(row_id).is_some(), Item::SpaceView(space_view_id) => { blueprint.viewport.space_view(space_view_id).is_some() } @@ -57,7 +54,6 @@ impl Item { pub fn kind(self: &Item) -> &'static str { match self { - Item::RowId(_) => "Message", Item::InstancePath(space_view_id, instance_path) => { match ( instance_path.instance_key.is_specific(), @@ -138,8 +134,7 @@ impl ItemCollection { } /// Remove all invalid selections. - pub fn purge_invalid(&mut self, log_db: &LogDb, blueprint: &crate::ui::Blueprint) { - self.items - .retain(|selection| selection.is_valid(log_db, blueprint)); + pub fn purge_invalid(&mut self, blueprint: &crate::ui::Blueprint) { + self.items.retain(|selection| selection.is_valid(blueprint)); } } diff --git a/crates/re_viewer/src/misc/selection_state.rs b/crates/re_viewer/src/misc/selection_state.rs index 2da6eed17d74..6adaacafd0a2 100644 --- a/crates/re_viewer/src/misc/selection_state.rs +++ b/crates/re_viewer/src/misc/selection_state.rs @@ -3,7 +3,7 @@ use egui::NumExt; use lazy_static::lazy_static; use nohash_hasher::IntMap; -use re_data_store::{EntityPath, InstancePath, InstancePathHash, LogDb}; +use re_data_store::{EntityPath, InstancePath, InstancePathHash}; use re_log_types::{component_types::InstanceKey, EntityPathHash}; use re_renderer::OutlineMaskPreference; @@ -194,10 +194,10 @@ pub struct SelectionState { impl SelectionState { /// Called at the start of each frame - pub fn on_frame_start(&mut self, log_db: &LogDb, blueprint: &Blueprint) { + pub fn on_frame_start(&mut self, blueprint: &Blueprint) { crate::profile_function!(); - self.history.on_frame_start(log_db, blueprint); + self.history.on_frame_start(blueprint); self.hovered_space_previous_frame = std::mem::replace(&mut self.hovered_space_this_frame, HoveredSpace::None); @@ -298,10 +298,9 @@ impl SelectionState { .hovered_previous_frame .iter() .any(|current| match current { - Item::RowId(_) - | Item::ComponentPath(_) - | Item::SpaceView(_) - | Item::DataBlueprintGroup(_, _) => current == test, + Item::ComponentPath(_) | Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => { + current == test + } Item::InstancePath(current_space_view_id, current_instance_path) => { if let Item::InstancePath(test_space_view_id, test_instance_path) = test { @@ -356,7 +355,7 @@ impl SelectionState { for current_selection in self.selection.iter() { match current_selection { - Item::RowId(_) | Item::ComponentPath(_) | Item::SpaceView(_) => {} + Item::ComponentPath(_) | Item::SpaceView(_) => {} Item::DataBlueprintGroup(group_space_view_id, group_handle) => { if *group_space_view_id == space_view_id { @@ -432,7 +431,7 @@ impl SelectionState { for current_hover in self.hovered_previous_frame.iter() { match current_hover { - Item::RowId(_) | Item::ComponentPath(_) | Item::SpaceView(_) => {} + Item::ComponentPath(_) | Item::SpaceView(_) => {} Item::DataBlueprintGroup(group_space_view_id, group_handle) => { // Unlike for selected objects/data we are more picky for data blueprints with our hover highlights diff --git a/crates/re_viewer/src/misc/viewer_context.rs b/crates/re_viewer/src/misc/viewer_context.rs index 0068e8b038e6..90fb0b40ea25 100644 --- a/crates/re_viewer/src/misc/viewer_context.rs +++ b/crates/re_viewer/src/misc/viewer_context.rs @@ -1,5 +1,5 @@ use re_data_store::{log_db::LogDb, InstancePath}; -use re_log_types::{ComponentPath, EntityPath, RowId, TimeInt, Timeline}; +use re_log_types::{ComponentPath, EntityPath, TimeInt, Timeline}; use crate::ui::{ data_ui::{ComponentUiRegistry, DataUi}, @@ -35,19 +35,6 @@ pub struct ViewerContext<'a> { } impl<'a> ViewerContext<'a> { - /// Show a [`RowId`] and make it selectable. - pub fn row_id_button(&mut self, ui: &mut egui::Ui, row_id: RowId) -> egui::Response { - let item = Item::RowId(row_id); - let response = ui - .selectable_label(self.selection().contains(&item), row_id.short_string()) - .on_hover_ui(|ui| { - ui.label(format!("Row ID: {row_id}")); - ui.separator(); - row_id.data_ui(self, ui, UiVerbosity::Small, &self.current_query()); - }); - self.cursor_interact_with_selectable(response, item) - } - /// Show an entity path and make it selectable. pub fn entity_path_button( &mut self, diff --git a/crates/re_viewer/src/ui/data_ui/annotation_context.rs b/crates/re_viewer/src/ui/data_ui/annotation_context.rs index 3100867c5671..841ebf0d19f3 100644 --- a/crates/re_viewer/src/ui/data_ui/annotation_context.rs +++ b/crates/re_viewer/src/ui/data_ui/annotation_context.rs @@ -17,7 +17,7 @@ impl DataUi for AnnotationContext { _query: &re_arrow_store::LatestAtQuery, ) { match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small => { ui.label(format!( "AnnotationContext with {} classes", self.class_map.len() diff --git a/crates/re_viewer/src/ui/data_ui/component.rs b/crates/re_viewer/src/ui/data_ui/component.rs index b2335f696ca4..b0f1a8a82893 100644 --- a/crates/re_viewer/src/ui/data_ui/component.rs +++ b/crates/re_viewer/src/ui/data_ui/component.rs @@ -43,7 +43,7 @@ impl DataUi for EntityComponentWithInstances { let num_instances = self.num_instances(); let one_line = match verbosity { - crate::ui::UiVerbosity::Small | crate::ui::UiVerbosity::MaxHeight(_) => true, + crate::ui::UiVerbosity::Small => true, crate::UiVerbosity::Reduced | crate::ui::UiVerbosity::All => false, }; diff --git a/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs b/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs index daf46b4f8a52..cd44d99972cd 100644 --- a/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs +++ b/crates/re_viewer/src/ui/data_ui/component_ui_registry.rs @@ -142,7 +142,7 @@ impl DataUi for re_log_types::component_types::TextEntry { let Self { body, level } = self; match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small => { ui.horizontal(|ui| { if let Some(level) = level { ui.label(level_to_rich_text(ui, level)); diff --git a/crates/re_viewer/src/ui/data_ui/data.rs b/crates/re_viewer/src/ui/data_ui/data.rs index 2db2899b6889..314c6dde8586 100644 --- a/crates/re_viewer/src/ui/data_ui/data.rs +++ b/crates/re_viewer/src/ui/data_ui/data.rs @@ -81,7 +81,7 @@ impl DataUi for ViewCoordinates { _query: &re_arrow_store::LatestAtQuery, ) { match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small => { ui.label(format!("ViewCoordinates: {}", self.describe())); } UiVerbosity::All | UiVerbosity::Reduced => { @@ -101,7 +101,7 @@ impl DataUi for Rigid3 { query: &re_arrow_store::LatestAtQuery, ) { match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small => { ui.label("Rigid 3D transform").on_hover_ui(|ui| { self.data_ui(ctx, ui, UiVerbosity::All, query); }); @@ -140,7 +140,7 @@ impl DataUi for Pinhole { query: &re_arrow_store::LatestAtQuery, ) { match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small => { ui.label("Pinhole transform").on_hover_ui(|ui| { self.data_ui(ctx, ui, UiVerbosity::All, query); }); @@ -269,7 +269,7 @@ impl DataUi for LineStrip2D { _query: &re_arrow_store::LatestAtQuery, ) { match verbosity { - UiVerbosity::Small | UiVerbosity::Reduced | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small | UiVerbosity::Reduced => { ui.label(format!("{} positions", self.0.len())); } UiVerbosity::All => { @@ -318,7 +318,7 @@ impl DataUi for LineStrip3D { _query: &re_arrow_store::LatestAtQuery, ) { match verbosity { - UiVerbosity::Small | UiVerbosity::Reduced | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small | UiVerbosity::Reduced => { ui.label(format!("{} positions", self.0.len())); } UiVerbosity::All => { diff --git a/crates/re_viewer/src/ui/data_ui/image.rs b/crates/re_viewer/src/ui/data_ui/image.rs index 59547aeb2380..dd55348e4c44 100644 --- a/crates/re_viewer/src/ui/data_ui/image.rs +++ b/crates/re_viewer/src/ui/data_ui/image.rs @@ -46,13 +46,12 @@ impl DataUi for Tensor { let tensor_stats = ctx.cache.tensor_stats.get(&self.id()); match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small => { ui.horizontal_centered(|ui| { if let Some(retained_img) = tensor_view.retained_image { let max_height = match verbosity { UiVerbosity::Small => 24.0, UiVerbosity::All | UiVerbosity::Reduced => 128.0, - UiVerbosity::MaxHeight(height) => height, }; retained_img .show_max_size(ui, Vec2::new(4.0 * max_height, max_height)) diff --git a/crates/re_viewer/src/ui/data_ui/instance_path.rs b/crates/re_viewer/src/ui/data_ui/instance_path.rs index d6154d980044..677493db71f7 100644 --- a/crates/re_viewer/src/ui/data_ui/instance_path.rs +++ b/crates/re_viewer/src/ui/data_ui/instance_path.rs @@ -45,7 +45,7 @@ impl DataUi for InstancePath { continue; } match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) | UiVerbosity::Reduced => { + UiVerbosity::Small | UiVerbosity::Reduced => { if HIDDEN_COMPONENTS_FOR_LOW_VERBOSITY .contains(&component_name.as_str()) { diff --git a/crates/re_viewer/src/ui/data_ui/mod.rs b/crates/re_viewer/src/ui/data_ui/mod.rs index 09580773c449..7f4136f8fe0e 100644 --- a/crates/re_viewer/src/ui/data_ui/mod.rs +++ b/crates/re_viewer/src/ui/data_ui/mod.rs @@ -14,7 +14,6 @@ mod entity_path; pub(crate) mod image; mod instance_path; mod log_msg; -mod row_id; pub(crate) use component_ui_registry::ComponentUiRegistry; @@ -24,9 +23,6 @@ pub enum UiVerbosity { /// Keep it small enough to fit on one row. Small, - /// At most this height - MaxHeight(f32), - /// Display a reduced set, used for hovering. Reduced, @@ -81,7 +77,7 @@ impl DataUi for [DataCell] { sorted.sort_by_key(|cb| cb.component_name()); match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { + UiVerbosity::Small => { ui.label(sorted.iter().map(format_cell).join(", ")); } diff --git a/crates/re_viewer/src/ui/data_ui/row_id.rs b/crates/re_viewer/src/ui/data_ui/row_id.rs deleted file mode 100644 index 603bd924a0d4..000000000000 --- a/crates/re_viewer/src/ui/data_ui/row_id.rs +++ /dev/null @@ -1,28 +0,0 @@ -use re_log_types::RowId; - -use crate::misc::ViewerContext; - -use super::{DataUi, UiVerbosity}; - -impl DataUi for RowId { - fn data_ui( - &self, - ctx: &mut ViewerContext<'_>, - ui: &mut egui::Ui, - verbosity: UiVerbosity, - query: &re_arrow_store::LatestAtQuery, - ) { - match verbosity { - UiVerbosity::Small | UiVerbosity::MaxHeight(_) => { - ctx.row_id_button(ui, *self); - } - UiVerbosity::All | UiVerbosity::Reduced => { - if let Some(msg) = ctx.log_db.get_log_msg(self) { - msg.data_ui(ctx, ui, verbosity, query); - } else { - ctx.row_id_button(ui, *self); - } - } - } - } -} diff --git a/crates/re_viewer/src/ui/event_log_view.rs b/crates/re_viewer/src/ui/event_log_view.rs deleted file mode 100644 index 306e02192dc5..000000000000 --- a/crates/re_viewer/src/ui/event_log_view.rs +++ /dev/null @@ -1,234 +0,0 @@ -use itertools::Itertools as _; - -use re_arrow_store::{LatestAtQuery, TimeInt}; -use re_format::format_number; -use re_log_types::{BeginRecordingMsg, DataTable, EntityPathOpMsg, LogMsg, RecordingInfo}; - -use crate::{UiVerbosity, ViewerContext}; - -use super::data_ui::DataUi; - -/// An event log, a table of all log messages. -#[derive(Default, serde::Deserialize, serde::Serialize)] -#[serde(default)] -pub(crate) struct EventLogView {} - -impl EventLogView { - #[allow(clippy::unused_self)] - pub fn ui(&mut self, ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui) { - crate::profile_function!(); - - let messages = { - crate::profile_scope!("Collecting messages"); - ctx.log_db.chronological_log_messages().collect_vec() - }; - - egui::Frame { - inner_margin: re_ui::ReUi::view_padding().into(), - ..egui::Frame::default() - } - .show(ui, |ui| { - ui.label(format!("{} log lines", format_number(ctx.log_db.len()))); - ui.separator(); - - egui::ScrollArea::horizontal() - .auto_shrink([false; 2]) - .show(ui, |ui| { - message_table(ctx, ui, &messages); - }); - }); - } -} - -pub(crate) fn message_table(ctx: &mut ViewerContext<'_>, ui: &mut egui::Ui, messages: &[&LogMsg]) { - crate::profile_function!(); - - use egui_extras::{Column, TableBuilder}; - - TableBuilder::new(ui) - .max_scroll_height(f32::INFINITY) // Fill up whole height - .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) - .resizable(true) - .column(Column::initial(100.0).at_least(50.0).clip(true)) // row_id - .column(Column::initial(130.0).at_least(50.0).clip(true)) // message type - .columns( - // timeline(s): - Column::auto().clip(true).at_least(50.0), - ctx.log_db.timelines().count(), - ) - .column(Column::auto().clip(true).at_least(50.0)) // path - .column(Column::remainder()) // payload - .header(re_ui::ReUi::table_header_height(), |mut header| { - re_ui::ReUi::setup_table_header(&mut header); - header.col(|ui| { - ui.strong("MsgID"); - }); - header.col(|ui| { - ui.strong("Message Type"); - }); - for timeline in ctx.log_db.timelines() { - header.col(|ui| { - ctx.timeline_button(ui, timeline); - }); - } - header.col(|ui| { - ui.strong("Path"); - }); - header.col(|ui| { - ui.strong("Payload"); - }); - }) - .body(|mut body| { - re_ui::ReUi::setup_table_body(&mut body); - - // for MANY messages, `heterogeneous_rows` is too slow. TODO(emilk): how many? - if messages.len() < 10_000_000 { - body.heterogeneous_rows( - messages.iter().copied().map(row_height), - |index, mut row| { - let msg = messages[index]; - table_row(ctx, &mut row, msg, row_height(msg)); - }, - ); - } else { - let row_height = re_ui::ReUi::table_line_height(); - body.rows(row_height, messages.len(), |index, mut row| { - table_row(ctx, &mut row, messages[index], row_height); - }); - } - }); -} - -fn row_height(_msg: &LogMsg) -> f32 { - // TODO(emilk): make rows with images (tensors) higher! - re_ui::ReUi::table_line_height() -} - -fn table_row( - ctx: &mut ViewerContext<'_>, - row: &mut egui_extras::TableRow<'_, '_>, - msg: &LogMsg, - row_height: f32, -) { - match msg { - LogMsg::BeginRecordingMsg(msg) => { - let BeginRecordingMsg { row_id, info } = msg; - let RecordingInfo { - application_id, - recording_id, - started, - recording_source, - is_official_example, - } = info; - - row.col(|ui| { - ctx.row_id_button(ui, *row_id); - }); - row.col(|ui| { - ui.monospace("BeginRecordingMsg"); - ui.label(format!("Source: {recording_source}")); - ui.label(format!("Official example: {is_official_example}")); - }); - for _ in ctx.log_db.timelines() { - row.col(|ui| { - ui.label("-"); - }); - } - row.col(|ui| { - ui.label(started.format()); - }); - row.col(|ui| { - ui.monospace(format!("{application_id} - {recording_id:?}")); - }); - } - LogMsg::EntityPathOpMsg(_, msg) => { - let EntityPathOpMsg { - row_id, - time_point, - path_op, - } = msg; - - row.col(|ui| { - ctx.row_id_button(ui, *row_id); - }); - row.col(|ui| { - ui.monospace("EntityPathOpMsg"); - }); - for timeline in ctx.log_db.timelines() { - row.col(|ui| { - if let Some(value) = time_point.get(timeline) { - ctx.time_button(ui, timeline, *value); - } - }); - } - row.col(|ui| { - ctx.entity_path_button(ui, None, path_op.entity_path()); - }); - row.col(|ui| { - let timeline = *ctx.rec_cfg.time_ctrl.timeline(); - let query = LatestAtQuery::new( - timeline, - time_point.get(&timeline).copied().unwrap_or(TimeInt::MAX), - ); - path_op.data_ui(ctx, ui, UiVerbosity::All, &query); - }); - } - // NOTE: This really only makes sense because we don't yet have batches with more than a - // single row at the moment... and by the time we do, the event log view will have - // disappeared entirely. - LogMsg::ArrowMsg(_, msg) => match DataTable::from_arrow_msg(msg) { - Ok(table) => { - for datarow in table.to_rows() { - row.col(|ui| { - ctx.row_id_button(ui, datarow.row_id()); - }); - row.col(|ui| { - ui.monospace("ArrowMsg"); - }); - for timeline in ctx.log_db.timelines() { - row.col(|ui| { - if let Some(value) = datarow.timepoint().get(timeline) { - ctx.time_button(ui, timeline, *value); - } - }); - } - row.col(|ui| { - ctx.entity_path_button(ui, None, datarow.entity_path()); - }); - - row.col(|ui| { - let timeline = *ctx.rec_cfg.time_ctrl.timeline(); - let query = LatestAtQuery::new( - timeline, - datarow - .timepoint() - .get(&timeline) - .copied() - .unwrap_or(TimeInt::MAX), - ); - datarow.cells().data_ui( - ctx, - ui, - UiVerbosity::MaxHeight(row_height), - &query, - ); - }); - } - } - Err(err) => { - re_log::error_once!("Bad arrow payload: {err}",); - row.col(|ui| { - ui.label("Bad Arrow Payload".to_owned()); - }); - } - }, - LogMsg::Goodbye(row_id) => { - row.col(|ui| { - ctx.row_id_button(ui, *row_id); - }); - row.col(|ui| { - ui.monospace("Goodbye"); - }); - } - } -} diff --git a/crates/re_viewer/src/ui/mod.rs b/crates/re_viewer/src/ui/mod.rs index 3c89fdb5bdbc..da730d21b5e7 100644 --- a/crates/re_viewer/src/ui/mod.rs +++ b/crates/re_viewer/src/ui/mod.rs @@ -16,7 +16,6 @@ mod view_time_series; mod viewport; pub(crate) mod data_ui; -pub(crate) mod event_log_view; pub(crate) mod memory_panel; pub(crate) mod selection_panel; pub(crate) mod time_panel; diff --git a/crates/re_viewer/src/ui/selection_history.rs b/crates/re_viewer/src/ui/selection_history.rs index 69b7f29d3bae..8410b5428582 100644 --- a/crates/re_viewer/src/ui/selection_history.rs +++ b/crates/re_viewer/src/ui/selection_history.rs @@ -1,5 +1,3 @@ -use re_data_store::LogDb; - use crate::misc::ItemCollection; use super::Blueprint; @@ -32,12 +30,12 @@ pub struct SelectionHistory { } impl SelectionHistory { - pub(crate) fn on_frame_start(&mut self, log_db: &LogDb, blueprint: &Blueprint) { + pub(crate) fn on_frame_start(&mut self, blueprint: &Blueprint) { crate::profile_function!(); let mut i = 0; self.stack.retain_mut(|selection| { - selection.purge_invalid(log_db, blueprint); + selection.purge_invalid(blueprint); let retain = !selection.is_empty(); if !retain && i <= self.current { self.current = self.current.saturating_sub(1); diff --git a/crates/re_viewer/src/ui/selection_history_ui.rs b/crates/re_viewer/src/ui/selection_history_ui.rs index 5ff3563482e0..e91c53966da7 100644 --- a/crates/re_viewer/src/ui/selection_history_ui.rs +++ b/crates/re_viewer/src/ui/selection_history_ui.rs @@ -201,7 +201,6 @@ fn item_to_string(blueprint: &Blueprint, item: &Item) -> String { "".to_owned() } } - Item::RowId(row_id) => row_id.short_string(), Item::ComponentPath(path) => { format!("{} {}", path.entity_path, path.component_name.short_name(),) } diff --git a/crates/re_viewer/src/ui/selection_panel.rs b/crates/re_viewer/src/ui/selection_panel.rs index 307bd3f32020..c5fe9f25f450 100644 --- a/crates/re_viewer/src/ui/selection_panel.rs +++ b/crates/re_viewer/src/ui/selection_panel.rs @@ -119,7 +119,7 @@ impl SelectionPanel { fn has_data_section(item: &Item) -> bool { match item { - Item::RowId(_) | Item::ComponentPath(_) | Item::InstancePath(_, _) => true, + Item::ComponentPath(_) | Item::InstancePath(_, _) => true, // Skip data ui since we don't know yet what to show for these. Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => false, } @@ -133,12 +133,6 @@ pub fn what_is_selected_ui( item: &Item, ) { match item { - Item::RowId(row_id) => { - ui.horizontal(|ui| { - ui.label("Row ID:"); - ctx.row_id_button(ui, *row_id); - }); - } Item::ComponentPath(re_log_types::ComponentPath { entity_path, component_name, @@ -230,9 +224,6 @@ impl DataUi for Item { // If you add something in here make sure to adjust SelectionPanel::contents accordingly. debug_assert!(!has_data_section(self)); } - Item::RowId(row_id) => { - row_id.data_ui(ctx, ui, verbosity, query); - } Item::ComponentPath(component_path) => { component_path.data_ui(ctx, ui, verbosity, query); } @@ -251,11 +242,6 @@ fn blueprint_ui( item: &Item, ) { match item { - Item::RowId(_) => { - // TODO(andreas): Show space views that contains entities that's part of this message. - ui.weak("(nothing)"); - } - Item::ComponentPath(component_path) => { list_existing_data_blueprints(ui, ctx, component_path.entity_path(), blueprint); } diff --git a/crates/rerun/src/run.rs b/crates/rerun/src/run.rs index 8e05289a61af..0cbebd6ed75b 100644 --- a/crates/rerun/src/run.rs +++ b/crates/rerun/src/run.rs @@ -441,7 +441,7 @@ fn receive_into_log_db(rx: &Receiver) -> anyhow::Result { re_log::info_once!("Received first message."); let is_goodbye = matches!(msg, re_log_types::LogMsg::Goodbye(_)); - db.add(msg)?; + db.add(&msg)?; num_messages += 1; if is_goodbye { db.entity_db.data_store.sanity_check()?;