Skip to content

Commit

Permalink
Handle static text messages in TextLogView gracefully, handle overrid…
Browse files Browse the repository at this point in the history
…es (rerun-io#6712)

### What

First commit wires the `TextLogView` to the override system. Making this
part of:

* rerun-io#6595

Second commit makes us handle static text without errors. Allowing
non-catastrophic behavior when changing the text message:

Text run using:
```py
import rerun as rr

rr.init("rerun_example_text_log", spawn=True)

rr.set_time_seconds("t", 0.0)
rr.log("log", rr.TextLog("Application started.", level=rr.TextLogLevel.INFO))
rr.set_time_seconds("t", 0.1)
rr.log("log", rr.TextLog("Application wumpfed.", level=rr.TextLogLevel.WARN))
rr.set_time_seconds("t", 100.0)
rr.log("log", rr.TextLog("Thing happening", color=0xFF9001FF, level=rr.TextLogLevel.ERROR))
```


https://github.com/rerun-io/rerun/assets/1220815/5c364ca2-65d2-4626-862e-e60ce70cfdaf
  • Loading branch information
Wumpf authored Jul 2, 2024
1 parent b201e06 commit 917f24f
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 103 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4976,6 +4976,7 @@ dependencies = [
"re_log_types",
"re_query",
"re_renderer",
"re_space_view",
"re_tracing",
"re_types",
"re_ui",
Expand Down
1 change: 1 addition & 0 deletions crates/re_space_view_text_log/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ re_log_types.workspace = true
re_log.workspace = true
re_query.workspace = true
re_renderer.workspace = true
re_space_view.workspace = true
re_tracing.workspace = true
re_types.workspace = true
re_ui.workspace = true
Expand Down
53 changes: 23 additions & 30 deletions crates/re_space_view_text_log/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,9 @@ fn get_time_point(ctx: &ViewerContext<'_>, entry: &Entry) -> Option<TimePoint> {
if let Some((time_point, _)) = ctx.recording_store().row_metadata(&entry.row_id) {
Some(time_point.clone())
} else {
re_log::warn_once!("Missing metadata for {:?}", entry.entity_path);
if !entry.time.is_static() {
re_log::warn_once!("Missing metadata for {:?}", entry.entity_path);
}
None
}
}
Expand Down Expand Up @@ -361,39 +363,30 @@ fn table_ui(
body.heterogeneous_rows(row_heights, |mut row| {
let entry = &entries[row.index()];

// NOTE: `try_from_props` is where we actually fetch data from the underlying
// store, which is a costly operation.
// Doing this here guarantees that it only happens for visible rows.
let Some(time_point) = get_time_point(ctx, entry) else {
row.col(|ui| {
ui.colored_label(
egui::Color32::RED,
"<failed to load TextLog from data store>",
);
});
return;
};

// timeline(s)
let time_point = get_time_point(ctx, entry);
for timeline in &timelines {
row.col(|ui| {
if let Some(row_time) = time_point.get(timeline).copied() {
item_ui::time_button(ctx, ui, timeline, row_time);

if let Some(global_time) = global_time {
if *timeline == &global_timeline {
#[allow(clippy::comparison_chain)]
if global_time < row_time {
// We've past the global time - it is thus above this row.
if current_time_y.is_none() {
current_time_y = Some(ui.max_rect().top());
}
} else if global_time == row_time {
// This row is exactly at the current time.
// We could draw the current time exactly onto this row, but that would look bad,
// so let's draw it under instead. It looks better in the "following" mode.
current_time_y = Some(ui.max_rect().bottom());
let row_time = time_point
.as_ref()
.and_then(|t| t.get(timeline))
.copied()
.unwrap_or(re_log_types::TimeInt::STATIC);
item_ui::time_button(ctx, ui, timeline, row_time);

if let Some(global_time) = global_time {
if *timeline == &global_timeline {
#[allow(clippy::comparison_chain)]
if global_time < row_time {
// We've past the global time - it is thus above this row.
if current_time_y.is_none() {
current_time_y = Some(ui.max_rect().top());
}
} else if global_time == row_time {
// This row is exactly at the current time.
// We could draw the current time exactly onto this row, but that would look bad,
// so let's draw it under instead. It looks better in the "following" mode.
current_time_y = Some(ui.max_rect().bottom());
}
}
}
Expand Down
138 changes: 65 additions & 73 deletions crates/re_space_view_text_log/src/visualizer_system.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use re_data_store::ResolvedTimeRange;
use re_entity_db::EntityPath;
use re_log_types::{RowId, TimeInt};
use re_query::{clamped_zip_1x2, range_zip_1x2, PromiseResult, RangeData};
use re_query::{clamped_zip_1x2, range_zip_1x2};
use re_space_view::{range_with_blueprint_resolved_data, RangeResultsExt};
use re_types::{
archetypes::TextLog,
components::{Color, Text, TextLogLevel},
Component, Loggable as _,
Loggable as _,
};
use re_viewer_context::{
IdentifiedViewSystem, SpaceViewSystemExecutionError, ViewContext, ViewContextCollection,
Expand All @@ -14,7 +15,6 @@ use re_viewer_context::{

#[derive(Debug, Clone)]
pub struct Entry {
// props
pub row_id: RowId,

pub entity_path: EntityPath,
Expand Down Expand Up @@ -51,64 +51,18 @@ impl VisualizerSystem for TextLogSystem {
view_query: &ViewQuery<'_>,
_context_systems: &ViewContextCollection,
) -> Result<Vec<re_renderer::QueueableDrawData>, SpaceViewSystemExecutionError> {
let resolver = ctx.recording().resolver();
re_tracing::profile_function!();

let query =
re_data_store::RangeQuery::new(view_query.timeline, ResolvedTimeRange::EVERYTHING);

for data_result in view_query.iter_visible_data_results(ctx, Self::identifier()) {
re_tracing::profile_scope!("primary", &data_result.entity_path.to_string());

let results = ctx.recording().query_caches().range(
ctx.recording_store(),
&query,
&data_result.entity_path,
[Text::name(), TextLogLevel::name(), Color::name()],
);

let all_bodies = {
let Some(all_bodies) = results.get(Text::name()) else {
continue;
};
all_bodies.to_dense::<Text>(resolver)
};
check_range(&all_bodies)?;

let all_levels = results
.get_or_empty(TextLogLevel::name())
.to_dense::<TextLogLevel>(resolver);
check_range(&all_levels)?;

let all_colors = results
.get_or_empty(Color::name())
.to_dense::<Color>(resolver);
check_range(&all_colors)?;

let all_frames = range_zip_1x2(
all_bodies.range_indexed(),
all_levels.range_indexed(),
all_colors.range_indexed(),
);

for (&(data_time, row_id), bodies, levels, colors) in all_frames {
let levels = levels.unwrap_or(&[]).iter().cloned().map(Some);
let colors = colors.unwrap_or(&[]).iter().copied().map(Some);

let level_default_fn = || None;
let color_default_fn = || None;

let results =
clamped_zip_1x2(bodies, levels, level_default_fn, colors, color_default_fn);

for (body, level, color) in results {
self.entries.push(Entry {
row_id,
entity_path: data_result.entity_path.clone(),
time: data_time,
color,
body: body.clone(),
level,
});
}
if let Err(err) = self.process_entity(ctx, &query, data_result) {
re_log::error_once!(
"Error visualizing text logs for {:?}: {:?}",
data_result.entity_path,
err
);
}
}

Expand All @@ -130,22 +84,60 @@ impl VisualizerSystem for TextLogSystem {
}
}

re_viewer_context::impl_component_fallback_provider!(TextLogSystem => []);
impl TextLogSystem {
fn process_entity(
&mut self,
ctx: &ViewContext<'_>,
query: &re_data_store::RangeQuery,
data_result: &re_viewer_context::DataResult,
) -> Result<(), SpaceViewSystemExecutionError> {
re_tracing::profile_function!();
let resolver = ctx.recording().resolver();

// TODO(#5607): what should happen if the promise is still pending?
#[inline]
fn check_range<'a, C: Component>(results: &'a RangeData<'a, C>) -> re_query::Result<()> {
let (front_status, back_status) = results.status();
match front_status {
PromiseResult::Pending => return Ok(()),
PromiseResult::Error(err) => return Err(re_query::QueryError::Other(err.into())),
PromiseResult::Ready(_) => {}
}
match back_status {
PromiseResult::Pending => return Ok(()),
PromiseResult::Error(err) => return Err(re_query::QueryError::Other(err.into())),
PromiseResult::Ready(_) => {}
}
let results = range_with_blueprint_resolved_data(
ctx,
None,
query,
data_result,
[Text::name(), TextLogLevel::name(), Color::name()],
);

let Some(all_texts) = results.get_dense::<Text>(resolver).transpose()? else {
return Ok(());
};

let all_levels = results.get_or_empty_dense::<TextLogLevel>(resolver)?;
let all_colors = results.get_or_empty_dense::<Color>(resolver)?;
let all_frames = range_zip_1x2(
all_texts.range_indexed(),
all_levels.range_indexed(),
all_colors.range_indexed(),
);

for (&(data_time, row_id), bodies, levels, colors) in all_frames {
let levels = levels.unwrap_or(&[]).iter().cloned().map(Some);
let colors = colors.unwrap_or(&[]).iter().copied().map(Some);

let level_default_fn = || None;
let color_default_fn = || None;

let results =
clamped_zip_1x2(bodies, levels, level_default_fn, colors, color_default_fn);

for (text, level, color) in results {
self.entries.push(Entry {
row_id,
entity_path: data_result.entity_path.clone(),
time: data_time,
color,
body: text.clone(),
level,
});
}
}

Ok(())
Ok(())
}
}

re_viewer_context::impl_component_fallback_provider!(TextLogSystem => []);

0 comments on commit 917f24f

Please sign in to comment.