-
Notifications
You must be signed in to change notification settings - Fork 538
Make time and timeline controlled by blueprint #11405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Web viewer built successfully.
Note: This comment is updated whenever you push a commit. |
c446bca
to
c8610b0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements time and timeline control via blueprints, allowing the time cursor and current timeline to be persisted in the blueprint store instead of just being ephemeral UI state.
Key changes:
- Time cursor and timeline selection are now stored in blueprints using a new
TimePanelBlueprint
archetype - Time is logged as static data to avoid affecting undo state
- Added blueprint context helpers for reading/writing time-related blueprint components
Reviewed Changes
Copilot reviewed 84 out of 84 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
rerun_py/rerun_sdk/rerun/recording_stream.py |
Move import to TYPE_CHECKING block to avoid runtime import |
rerun_py/rerun_sdk/rerun/blueprint/components/time_cell.py |
New auto-generated component for storing time references in blueprints |
rerun_py/rerun_sdk/rerun/blueprint/archetypes/time_panel_blueprint.py |
New auto-generated archetype for time panel blueprint state including timeline and time cursor |
rerun_py/rerun_sdk/rerun/blueprint/api.py |
Enhanced TimePanel class with timeline and time cursor parameters |
crates/viewer/re_viewer_context/src/time_control.rs |
Major refactor to integrate blueprint-based time control with helper traits and context |
crates/viewer/re_viewer/src/app.rs |
Updated to use blueprint context for time control operations |
crates/store/re_types/definitions/rerun/blueprint/archetypes/panel_blueprint.fbs |
Added TimePanelBlueprint definition and fixed documentation |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
use crate::{NeedsRepaint, blueprint_helpers::BlueprintContext}; | ||
|
||
pub const TIME_PANEL_PATH: &str = "time_panel"; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] This constant is duplicated in crates/viewer/re_viewer/src/app_blueprint.rs
. Consider moving this to a shared location to avoid duplication and potential inconsistencies.
use crate::{NeedsRepaint, blueprint_helpers::BlueprintContext}; | |
pub const TIME_PANEL_PATH: &str = "time_panel"; | |
use crate::{NeedsRepaint, blueprint_helpers::{BlueprintContext, TIME_PANEL_PATH}}; |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not duplicated. And is moved to a lower crate, possibly there could be some better place to have all of these constants though?
1c132d7
to
bd286d2
Compare
463d941
to
ba81443
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, this didn't look easy 😆 Good job on wading through all this mess.
I'm worried we're making already complicated code even more complicated.
I never liked how TimeControl
was behind an RwLock
. Ideally, all our UI code reads immutable state and then emits commands, and TimeControl
is and remains the main holdout in all this (not coincidentally, it is also among the oldest code in the codebase).
Is there a design we could have that is more data-centric and less reliant on traits
?
In my ideal world we'd set up the current state of TimeControl at the start of the frame (based on the blueprint for as much as we can), then in the UI functions we return some delta of what happens as idempotent commands ("change timeline to log_time, turn off looping, …"). It's then up to the caller to pipe those commands to the correct sink (e.g. depending on if this is the real data time panel, or the dev blueprint time panel).
Some commands will end up as writes in the blueprint store, others not (we haven't blueprintified everything yet).
I think such a design would also make testing simpler (we could do a unit-snapshot-test of the time control buttons, without having any larger test context), FWIW.
WDYT? After this PR I'm pretty certain you have the best understanding of this mess :)
timeline: rerun.blueprint.components.TimelineName ("attr.rerun.component_optional", nullable, order: 2000); | ||
|
||
/// What time the time cursor should be on. | ||
time: rerun.blueprint.components.TimeCell ("attr.rerun.component_optional", nullable, order: 2100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note for self: I wonder if we should somehow mark its "staticness" here? 🤔
"attr.rust.repr": "transparent", | ||
"attr.rust.tuple_struct" | ||
) { | ||
time: rerun.datatypes.TimeInt (order: 100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be a bit confusing. We already hav e a struct TimeCell
that has the TimeType
in it as well.
I feel like this should either have a TimeType
, or simply be called TimeInt
(it's quite often we have a datatype and a component of the same name)
blueprint_query: &blueprint_query, | ||
}; | ||
|
||
// ctx.set_timeline("log_tick".into()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rm?
time_control: &mut impl TimeControlExt, | ||
ui: &mut egui::Ui, | ||
) { | ||
ui.horizontal(|ui| { | ||
ui.spacing_mut().item_spacing.x = 5.0; // from figma | ||
self.play_button_ui(ctx, time_control, ui); | ||
self.follow_button_ui(ctx, time_control, ui); | ||
self.pause_button_ui(ctx, time_control, ui); | ||
self.play_button_ui(ctx, time_control.get(), ui); | ||
self.follow_button_ui(ctx, time_control.get(), ui); | ||
self.pause_button_ui(ctx, time_control.get(), ui); | ||
self.step_time_button_ui(ctx, ui); | ||
self.loop_button_ui(time_control, ui); | ||
self.loop_button_ui(time_control.get_mut(), ui); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This smells. Why not just pass in &mut TimeControl
directly?
&self, | ||
time_control: &mut TimeControl, | ||
ctx: &ViewerContext<'_>, | ||
time_control: &mut impl TimeControlExt, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will add unnecessary monomorphization, increasing binary size and compilation times.
time_control: &mut impl TimeControlExt, | |
time_control: &mut dyn TimeControlExt, |
here and elsewhere
file_handle.write(&bytes).await.context("Failed to save") | ||
} | ||
|
||
pub struct AppBlueprintCtx<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please move this to its own file. app.rs
is already way too big 🙈
/// Replaces the current timeline with the automatic one. | ||
fn clear_timeline(&self); | ||
|
||
fn clear_time(&self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this do?
&TimePanelBlueprint::descriptor_time(), | ||
)?; | ||
|
||
Some(TimeInt::saturated_temporal_i64(time.0.0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some(TimeInt::saturated_temporal_i64(time.0.0)) | |
Some(TimeInt::ZERO) |
/// | ||
/// If `should_diff_state` is true, then the response also contains any changes in state | ||
/// between last frame and the current one. | ||
#[must_use] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intentionally removed? Maybe it should be moved onto TimeControlResponse
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most likely got lost in copying it, yeah having it on TimeControlResponse
makes more sense!
if let Some(time) = blueprint_ctx.get_time() { | ||
if self.time_int() != Some(time) { | ||
self.set_time(time.into()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if let Some(time) = blueprint_ctx.get_time() { | |
if self.time_int() != Some(time) { | |
self.set_time(time.into()); | |
} | |
} | |
if let Some(time) = blueprint_ctx.get_time() && self.time_int() != Some(time) { | |
self.set_time(time.into()); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, this didn't look easy 😆 Good job on wading through all this mess.
I'm worried we're making already complicated code even more complicated.
I never liked how TimeControl
was behind an RwLock
. Ideally, all our UI code reads immutable state and then emits commands, and TimeControl
is and remains the main holdout in all this (not coincidentally, it is also among the oldest code in the codebase).
Is there a design we could have that is more data-centric and less reliant on traits
?
In my ideal world we'd set up the current state of TimeControl at the start of the frame (based on the blueprint for as much as we can), then in the UI functions we return some delta of what happens as idempotent commands ("change timeline to log_time, turn off looping, …"). It's then up to the caller to pipe those commands to the correct sink (e.g. depending on if this is the real data time panel, or the dev blueprint time panel).
Some commands will end up as writes in the blueprint store, others not (we haven't blueprintified everything yet).
I think such a design would also make testing simpler (we could do a unit-snapshot-test of the time control buttons, without having any larger test context), FWIW.
WDYT? After this PR I'm pretty certain you have the best understanding of this mess :)
Related
What
Makes time cursor and current timeline be controlled by the blueprint.
Details
We log the time as static for blueprints for it not to affect undo state.
This includes some refactors that felt necessary to make tests still work and not be overly complicated:
TestContext
doesn't require a mutable reference to run.TestContext
blueprints viaTestContext::with_blueprint_ctx
.AppBlueprintCtx
, andViewerContext
also implements this trait.The time panel was refactored a bit to allow the normal time panel and blueprint inspector time panel to still share code. This is mostly changing
&mut TimeControl
to&mut impl TimeControlCtx
which handles writing to blueprints/just updating time control whether this is the blueprint inspector time panel or not.TODO