diff --git a/Cargo.lock b/Cargo.lock index a2bfeafc359d..d85440b638d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5180,6 +5180,7 @@ dependencies = [ "image", "itertools 0.11.0", "nohash-hasher", + "rayon", "re_arrow_store", "re_data_store", "re_data_ui", diff --git a/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs b/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs index f0d1bf6b4ed5..7e1c43c31c1b 100644 --- a/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs +++ b/crates/re_renderer/src/allocator/cpu_write_gpu_read_belt.rs @@ -38,7 +38,7 @@ pub enum CpuWriteGpuReadError { /// Note that the "vec like behavior" further encourages /// * not leaving holes /// * keeping writes sequential -pub struct CpuWriteGpuReadBuffer { +pub struct CpuWriteGpuReadBuffer { /// Write view into the relevant buffer portion. /// /// UNSAFE: The lifetime is transmuted to be `'static`. @@ -55,9 +55,17 @@ pub struct CpuWriteGpuReadBuffer { _type: std::marker::PhantomData, } +#[allow(unsafe_code)] +// SAFETY: TODO(gfx-rs/wgpu#4818): Upstream wgpu allows `wgpu::BufferViewMut` to be Send. +unsafe impl Send for CpuWriteGpuReadBuffer where T: bytemuck::Pod + Send + Sync {} + +#[allow(unsafe_code)] +// SAFETY: TODO(gfx-rs/wgpu#4818): Upstream wgpu allows `wgpu::BufferViewMut` to be Sync. +unsafe impl Sync for CpuWriteGpuReadBuffer where T: bytemuck::Pod + Send + Sync {} + impl CpuWriteGpuReadBuffer where - T: bytemuck::Pod + 'static, + T: bytemuck::Pod + Send + Sync, { /// Memory as slice. /// @@ -283,7 +291,7 @@ impl Chunk { } /// Caller needs to make sure that there is enough space. - fn allocate( + fn allocate( &mut self, num_elements: usize, size_in_bytes: u64, @@ -419,7 +427,7 @@ impl CpuWriteGpuReadBelt { } /// Allocates a cpu writable buffer for `num_elements` instances of type `T`. - pub fn allocate( + pub fn allocate( &mut self, device: &wgpu::Device, buffer_pool: &GpuBufferPool, diff --git a/crates/re_renderer/src/allocator/uniform_buffer_fill.rs b/crates/re_renderer/src/allocator/uniform_buffer_fill.rs index ae42aa9680ff..95b30bc5c0de 100644 --- a/crates/re_renderer/src/allocator/uniform_buffer_fill.rs +++ b/crates/re_renderer/src/allocator/uniform_buffer_fill.rs @@ -42,7 +42,7 @@ impl UniformBufferAlignmentCheck { /// For subsequent frames, this will automatically not allocate any resources (thanks to our buffer pooling mechanism). /// /// TODO(#1383): We could do this on a more complex stack allocator. -pub fn create_and_fill_uniform_buffer_batch( +pub fn create_and_fill_uniform_buffer_batch( ctx: &RenderContext, label: DebugLabel, content: impl ExactSizeIterator, @@ -97,7 +97,7 @@ pub fn create_and_fill_uniform_buffer_batch( } /// See [`create_and_fill_uniform_buffer`]. -pub fn create_and_fill_uniform_buffer( +pub fn create_and_fill_uniform_buffer( ctx: &RenderContext, label: DebugLabel, content: T, diff --git a/crates/re_space_view_bar_chart/src/space_view_class.rs b/crates/re_space_view_bar_chart/src/space_view_class.rs index 4ab8cbae2caa..99afebef569e 100644 --- a/crates/re_space_view_bar_chart/src/space_view_class.rs +++ b/crates/re_space_view_bar_chart/src/space_view_class.rs @@ -5,8 +5,7 @@ use re_space_view::controls; use re_types::datatypes::TensorBuffer; use re_viewer_context::{ auto_color, SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, - SpaceViewSystemExecutionError, ViewContextCollection, ViewPartCollection, ViewQuery, - ViewerContext, + SpaceViewSystemExecutionError, ViewQuery, ViewerContext, }; use super::view_part_system::BarChartViewPartSystem; @@ -129,14 +128,15 @@ impl SpaceViewClass for BarChartSpaceView { ui: &mut egui::Ui, _state: &mut Self::State, root_entity_properties: &EntityProperties, - _view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, _query: &ViewQuery<'_>, - _draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { use egui_plot::{Bar, BarChart, Legend, Plot}; - let charts = &parts.get::()?.charts; + let charts = &system_output + .view_systems + .get::()? + .charts; let zoom_both_axis = !ui.input(|i| i.modifiers.contains(controls::ASPECT_SCROLL_MODIFIER)); diff --git a/crates/re_space_view_spatial/src/space_view_2d.rs b/crates/re_space_view_spatial/src/space_view_2d.rs index 509ef5e2e4f4..a4e6301cc6bc 100644 --- a/crates/re_space_view_spatial/src/space_view_2d.rs +++ b/crates/re_space_view_spatial/src/space_view_2d.rs @@ -2,8 +2,7 @@ use re_data_store::EntityProperties; use re_log_types::EntityPath; use re_viewer_context::{ AutoSpawnHeuristic, PerSystemEntities, SpaceViewClass, SpaceViewClassRegistryError, - SpaceViewId, SpaceViewSystemExecutionError, ViewContextCollection, ViewPartCollection, - ViewQuery, ViewerContext, + SpaceViewId, SpaceViewSystemExecutionError, ViewQuery, ViewerContext, }; use crate::{ @@ -141,17 +140,17 @@ impl SpaceViewClass for SpatialSpaceView2D { ui: &mut egui::Ui, state: &mut Self::State, _root_entity_properties: &EntityProperties, - view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, query: &ViewQuery<'_>, - draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { - state.scene_bbox = calculate_bounding_box(parts, &mut state.scene_bbox_accum); - state.scene_num_primitives = view_ctx + state.scene_bbox = + calculate_bounding_box(&system_output.view_systems, &mut state.scene_bbox_accum); + state.scene_num_primitives = system_output + .context_systems .get::()? .num_primitives .load(std::sync::atomic::Ordering::Relaxed); - crate::ui_2d::view_2d(ctx, ui, state, view_ctx, parts, query, draw_data) + crate::ui_2d::view_2d(ctx, ui, state, query, system_output) } } diff --git a/crates/re_space_view_spatial/src/space_view_3d.rs b/crates/re_space_view_spatial/src/space_view_3d.rs index c2261491ab8d..e95ff65af6c9 100644 --- a/crates/re_space_view_spatial/src/space_view_3d.rs +++ b/crates/re_space_view_spatial/src/space_view_3d.rs @@ -2,8 +2,8 @@ use re_data_store::EntityProperties; use re_log_types::EntityPath; use re_viewer_context::{ AutoSpawnHeuristic, IdentifiedViewSystem as _, PerSystemEntities, SpaceViewClass, - SpaceViewClassRegistryError, SpaceViewId, SpaceViewSystemExecutionError, ViewContextCollection, - ViewPartCollection, ViewQuery, ViewerContext, + SpaceViewClassRegistryError, SpaceViewId, SpaceViewSystemExecutionError, ViewQuery, + ViewerContext, }; use crate::{ @@ -116,19 +116,19 @@ impl SpaceViewClass for SpatialSpaceView3D { ui: &mut egui::Ui, state: &mut Self::State, _root_entity_properties: &EntityProperties, - view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, query: &ViewQuery<'_>, - draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { re_tracing::profile_function!(); - state.scene_bbox = calculate_bounding_box(parts, &mut state.scene_bbox_accum); - state.scene_num_primitives = view_ctx + state.scene_bbox = + calculate_bounding_box(&system_output.view_systems, &mut state.scene_bbox_accum); + state.scene_num_primitives = system_output + .context_systems .get::()? .num_primitives .load(std::sync::atomic::Ordering::Relaxed); - crate::ui_3d::view_3d(ctx, ui, state, view_ctx, parts, query, draw_data) + crate::ui_3d::view_3d(ctx, ui, state, query, system_output) } } diff --git a/crates/re_space_view_spatial/src/ui_2d.rs b/crates/re_space_view_spatial/src/ui_2d.rs index a0284f2086e9..a594f0e0a3bb 100644 --- a/crates/re_space_view_spatial/src/ui_2d.rs +++ b/crates/re_space_view_spatial/src/ui_2d.rs @@ -9,8 +9,8 @@ use re_renderer::view_builder::{TargetConfiguration, ViewBuilder}; use re_space_view::controls::{DRAG_PAN2D_BUTTON, RESET_VIEW_BUTTON_TEXT, ZOOM_SCROLL_MODIFIER}; use re_types::{archetypes::Pinhole, components::ViewCoordinates}; use re_viewer_context::{ - gpu_bridge, HoveredSpace, SpaceViewSystemExecutionError, ViewContextCollection, - ViewPartCollection, ViewQuery, ViewerContext, + gpu_bridge, HoveredSpace, SpaceViewSystemExecutionError, SystemExecutionOutput, ViewQuery, + ViewerContext, }; use super::{ @@ -228,13 +228,17 @@ pub fn view_2d( ctx: &ViewerContext<'_>, ui: &mut egui::Ui, state: &mut SpatialSpaceViewState, - view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, query: &ViewQuery<'_>, - mut draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { re_tracing::profile_function!(); + let SystemExecutionOutput { + view_systems: parts, + context_systems: view_ctx, + draw_data, + } = system_output; + // Save off the available_size since this is used for some of the layout updates later let available_size = ui.available_size(); let store = ctx.store_db.store(); @@ -312,11 +316,11 @@ pub fn view_2d( // Create labels now since their shapes participate are added to scene.ui for picking. let (label_shapes, ui_rects) = create_labels( - &collect_ui_labels(parts), + &collect_ui_labels(&parts), ui_from_canvas, &eye, ui, - query.highlights, + &query.highlights, SpatialSpaceViewKind::TwoD, ); @@ -330,15 +334,15 @@ pub fn view_2d( eye, &mut view_builder, state, - view_ctx, - parts, + &view_ctx, + &parts, &ui_rects, query, SpatialSpaceViewKind::TwoD, )?; } - for draw_data in draw_data.drain(..) { + for draw_data in draw_data { view_builder.queue_draw(draw_data); } if let Ok(shared_render_builders) = view_ctx.get::() { diff --git a/crates/re_space_view_spatial/src/ui_3d.rs b/crates/re_space_view_spatial/src/ui_3d.rs index 90bcdd066856..6ba579c96fe3 100644 --- a/crates/re_space_view_spatial/src/ui_3d.rs +++ b/crates/re_space_view_spatial/src/ui_3d.rs @@ -13,8 +13,8 @@ use re_space_view::controls::{ }; use re_types::components::ViewCoordinates; use re_viewer_context::{ - gpu_bridge, HoveredSpace, Item, SpaceViewSystemExecutionError, ViewContextCollection, - ViewPartCollection, ViewQuery, ViewerContext, + gpu_bridge, HoveredSpace, Item, SpaceViewSystemExecutionError, SystemExecutionOutput, + ViewQuery, ViewerContext, }; use crate::{ @@ -307,19 +307,22 @@ pub fn help_text(re_ui: &re_ui::ReUi) -> egui::WidgetText { layout.layout_job.into() } -/// TODO(andreas): Split into smaller parts, more re-use with `ui_2d` pub fn view_3d( ctx: &ViewerContext<'_>, ui: &mut egui::Ui, state: &mut SpatialSpaceViewState, - view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, query: &ViewQuery<'_>, - mut draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { re_tracing::profile_function!(); - let highlights = query.highlights; + let SystemExecutionOutput { + view_systems: parts, + context_systems: view_ctx, + draw_data, + } = system_output; + + let highlights = &query.highlights; let space_cameras = &parts.get::()?.space_cameras; let view_coordinates = ctx .store_db @@ -412,7 +415,7 @@ pub fn view_3d( // Create labels now since their shapes participate are added to scene.ui for picking. let (label_shapes, ui_rects) = create_labels( - &collect_ui_labels(parts), + &collect_ui_labels(&parts), RectTransform::from_to(rect, rect), &eye, ui, @@ -430,8 +433,8 @@ pub fn view_3d( eye, &mut view_builder, state, - view_ctx, - parts, + &view_ctx, + &parts, &ui_rects, query, SpatialSpaceViewKind::ThreeD, @@ -586,7 +589,7 @@ pub fn view_3d( } } - for draw_data in draw_data.drain(..) { + for draw_data in draw_data { view_builder.queue_draw(draw_data); } if let Ok(shared_render_builders) = view_ctx.get::() { diff --git a/crates/re_space_view_tensor/src/space_view_class.rs b/crates/re_space_view_tensor/src/space_view_class.rs index 0664049bbd6e..1e7f0993f6a6 100644 --- a/crates/re_space_view_tensor/src/space_view_class.rs +++ b/crates/re_space_view_tensor/src/space_view_class.rs @@ -15,7 +15,7 @@ use re_types::{ use re_viewer_context::{ gpu_bridge, gpu_bridge::colormap_dropdown_button_ui, SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, SpaceViewSystemExecutionError, - TensorStatsCache, ViewContextCollection, ViewPartCollection, ViewQuery, ViewerContext, + TensorStatsCache, ViewQuery, ViewerContext, }; use crate::{tensor_dimension_mapper::dimension_mapping_ui, view_part_system::TensorSystem}; @@ -184,14 +184,12 @@ impl SpaceViewClass for TensorSpaceView { ui: &mut egui::Ui, state: &mut Self::State, _root_entity_properties: &EntityProperties, - _view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, _query: &ViewQuery<'_>, - _draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { re_tracing::profile_function!(); - let tensors = &parts.get::()?.tensors; + let tensors = &system_output.view_systems.get::()?.tensors; if tensors.is_empty() { ui.centered_and_justified(|ui| ui.label("(empty)")); diff --git a/crates/re_space_view_text_document/src/space_view_class.rs b/crates/re_space_view_text_document/src/space_view_class.rs index 88458073e7a1..f9cb06b88b42 100644 --- a/crates/re_space_view_text_document/src/space_view_class.rs +++ b/crates/re_space_view_text_document/src/space_view_class.rs @@ -3,8 +3,7 @@ use egui::Label; use re_viewer_context::external::re_data_store::EntityProperties; use re_viewer_context::{ external::re_log_types::EntityPath, SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, - SpaceViewState, SpaceViewSystemExecutionError, ViewContextCollection, ViewPartCollection, - ViewQuery, ViewerContext, + SpaceViewState, SpaceViewSystemExecutionError, ViewQuery, ViewerContext, }; use crate::view_part_system::TextDocumentEntry; @@ -99,12 +98,10 @@ impl SpaceViewClass for TextDocumentSpaceView { ui: &mut egui::Ui, state: &mut Self::State, _root_entity_properties: &EntityProperties, - _view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, _query: &ViewQuery<'_>, - _draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { - let text_document = parts.get::()?; + let text_document = system_output.view_systems.get::()?; egui::Frame { inner_margin: re_ui::ReUi::view_padding().into(), diff --git a/crates/re_space_view_text_log/src/space_view_class.rs b/crates/re_space_view_text_log/src/space_view_class.rs index 28b8d2f13f70..dc4d2d26fae9 100644 --- a/crates/re_space_view_text_log/src/space_view_class.rs +++ b/crates/re_space_view_text_log/src/space_view_class.rs @@ -7,7 +7,7 @@ use re_types::components::TextLogLevel; use re_viewer_context::{ level_to_rich_text, AutoSpawnHeuristic, PerSystemEntities, SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, SpaceViewSystemExecutionError, - ViewContextCollection, ViewPartCollection, ViewQuery, ViewerContext, + ViewQuery, ViewerContext, }; use super::view_part_system::{Entry, TextLogSystem}; @@ -136,13 +136,11 @@ impl SpaceViewClass for TextSpaceView { ui: &mut egui::Ui, state: &mut Self::State, _root_entity_properties: &EntityProperties, - _view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, _query: &ViewQuery<'_>, - _draw_data: Vec, + system_output: re_viewer_context::SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { re_tracing::profile_function!(); - let text = parts.get::()?; + let text = system_output.view_systems.get::()?; // TODO(andreas): Should filter text entries in the part-system instead. // this likely requires a way to pass state into a context. diff --git a/crates/re_space_view_time_series/src/space_view_class.rs b/crates/re_space_view_time_series/src/space_view_class.rs index af2a83b3ef8e..93e3602d8423 100644 --- a/crates/re_space_view_time_series/src/space_view_class.rs +++ b/crates/re_space_view_time_series/src/space_view_class.rs @@ -9,8 +9,7 @@ use re_viewer_context::external::re_data_store::{ }; use re_viewer_context::{ SpaceViewClass, SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, - SpaceViewSystemExecutionError, ViewContextCollection, ViewPartCollection, ViewQuery, - ViewerContext, + SpaceViewSystemExecutionError, SystemExecutionOutput, ViewQuery, ViewerContext, }; use crate::view_part_system::{PlotSeriesKind, TimeSeriesSystem}; @@ -159,10 +158,8 @@ impl SpaceViewClass for TimeSeriesSpaceView { ui: &mut egui::Ui, state: &mut Self::State, root_entity_properties: &EntityProperties, - _view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, _query: &ViewQuery<'_>, - _draw_data: Vec, + system_output: SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { re_tracing::profile_function!(); @@ -177,7 +174,7 @@ impl SpaceViewClass for TimeSeriesSpaceView { let timeline_name = timeline.name().to_string(); - let time_series = parts.get::()?; + let time_series = system_output.view_systems.get::()?; // Get the minimum time/X value for the entire plot… let min_time = time_series.min_time.unwrap_or(0); diff --git a/crates/re_tracing/src/lib.rs b/crates/re_tracing/src/lib.rs index 72062ddb1fa0..d82be40348bd 100644 --- a/crates/re_tracing/src/lib.rs +++ b/crates/re_tracing/src/lib.rs @@ -13,7 +13,9 @@ pub mod reexports { pub use puffin; } -/// Wrapper around puffin profiler on native, no-op on wasm. +/// Create a profile scope based on the function name. +/// +/// Call this at the very top of an expensive function. #[macro_export] macro_rules! profile_function { ($($arg: tt)*) => { @@ -22,7 +24,7 @@ macro_rules! profile_function { }; } -/// Wrapper around puffin profiler on native, no-op on wasm. +/// Create a profiling scope with a custom name. #[macro_export] macro_rules! profile_scope { ($($arg: tt)*) => { @@ -30,3 +32,31 @@ macro_rules! profile_scope { $crate::reexports::puffin::profile_scope!($($arg)*); }; } + +/// Create a special profiling scope that indicates that we are waiting +/// for some other thread to finish. +/// +/// You should pass in the name of the thing you are waiting for as the first argument. +/// +/// # Example +/// ```ignore +/// let normals = { +/// profile_wait!("compute_normals"); +/// things.par_iter().for_each(compute_normals) +/// }; +/// ``` +#[macro_export] +macro_rules! profile_wait { + () => { + #[cfg(not(target_arch = "wasm32"))] + $crate::reexports::puffin::profile_scope!("[WAIT]"); + }; + ($id:expr) => { + #[cfg(not(target_arch = "wasm32"))] + $crate::reexports::puffin::profile_scope!(concat!("[WAIT] ", $id)); + }; + ($id:expr, $data:expr) => { + #[cfg(not(target_arch = "wasm32"))] + $crate::reexports::puffin::profile_scope!(concat!("[WAIT] ", $id), $data); + }; +} diff --git a/crates/re_types_builder/src/codegen/cpp/mod.rs b/crates/re_types_builder/src/codegen/cpp/mod.rs index 35b0732965f7..c9dc96450621 100644 --- a/crates/re_types_builder/src/codegen/cpp/mod.rs +++ b/crates/re_types_builder/src/codegen/cpp/mod.rs @@ -120,6 +120,7 @@ impl crate::CodeGenerator for CppCodeGenerator { objects: &Objects, _arrow_registry: &ArrowRegistry, ) -> GeneratedFiles { + re_tracing::profile_wait!("generate_folder"); ObjectKind::ALL .par_iter() .flat_map(|object_kind| self.generate_folder(reporter, objects, *object_kind)) diff --git a/crates/re_types_builder/src/format/cpp.rs b/crates/re_types_builder/src/format/cpp.rs index 8007e54b17ee..111f6a6a36b5 100644 --- a/crates/re_types_builder/src/format/cpp.rs +++ b/crates/re_types_builder/src/format/cpp.rs @@ -8,7 +8,7 @@ impl CodeFormatter for CppCodeFormatter { fn format(&mut self, _reporter: &crate::Reporter, files: &mut crate::GeneratedFiles) { use rayon::prelude::*; - re_tracing::profile_function!(); + re_tracing::profile_wait!("format_code"); files.par_iter_mut().for_each(|(filepath, contents)| { *contents = if matches!(filepath.extension(), Some("cpp" | "hpp")) { diff --git a/crates/re_types_builder/src/format/rust.rs b/crates/re_types_builder/src/format/rust.rs index dc642c60aeda..c9be15d2be6c 100644 --- a/crates/re_types_builder/src/format/rust.rs +++ b/crates/re_types_builder/src/format/rust.rs @@ -8,7 +8,7 @@ impl CodeFormatter for RustCodeFormatter { fn format(&mut self, _reporter: &crate::Reporter, files: &mut crate::GeneratedFiles) { use rayon::prelude::*; - re_tracing::profile_function!(); + re_tracing::profile_wait!("format_code"); files.par_iter_mut().for_each(|(filepath, contents)| { *contents = if matches!(filepath.extension(), Some("rs")) { diff --git a/crates/re_types_builder/src/lib.rs b/crates/re_types_builder/src/lib.rs index 8d0b3aafa25d..7a380927e54a 100644 --- a/crates/re_types_builder/src/lib.rs +++ b/crates/re_types_builder/src/lib.rs @@ -409,7 +409,7 @@ fn generate_code( // 4. Write all files to filesystem. { - re_tracing::profile_scope!("write_files"); + re_tracing::profile_wait!("write_files"); files.par_iter().for_each(|(filepath, contents)| { crate::codegen::common::write_file(filepath, contents); diff --git a/crates/re_viewer/src/app_state.rs b/crates/re_viewer/src/app_state.rs index a9dcbe15c945..e4b3df042f0d 100644 --- a/crates/re_viewer/src/app_state.rs +++ b/crates/re_viewer/src/app_state.rs @@ -261,7 +261,7 @@ impl AppState { if show_welcome { welcome_screen.ui(ui, re_ui, rx, command_sender); } else { - viewport.viewport_ui(ui, &mut ctx); + viewport.viewport_ui(ui, &ctx); } }); }); diff --git a/crates/re_viewer_context/src/caches.rs b/crates/re_viewer_context/src/caches.rs index 1743762136f1..c449bf93dac0 100644 --- a/crates/re_viewer_context/src/caches.rs +++ b/crates/re_viewer_context/src/caches.rs @@ -41,7 +41,7 @@ impl Caches { } /// A cache for memoizing things in order to speed up immediate mode UI & other immediate mode style things. -pub trait Cache: std::any::Any { +pub trait Cache: std::any::Any + Send + Sync { /// Called once per frame to potentially flush the cache. fn begin_frame(&mut self); diff --git a/crates/re_viewer_context/src/component_ui_registry.rs b/crates/re_viewer_context/src/component_ui_registry.rs index 10ac0a96d9a7..8368de4ddef0 100644 --- a/crates/re_viewer_context/src/component_ui_registry.rs +++ b/crates/re_viewer_context/src/component_ui_registry.rs @@ -34,14 +34,15 @@ pub enum UiVerbosity { type ComponentUiCallback = Box< dyn Fn( - &ViewerContext<'_>, - &mut egui::Ui, - UiVerbosity, - &LatestAtQuery, - &EntityPath, - &ComponentWithInstances, - &InstanceKey, - ), + &ViewerContext<'_>, + &mut egui::Ui, + UiVerbosity, + &LatestAtQuery, + &EntityPath, + &ComponentWithInstances, + &InstanceKey, + ) + Send + + Sync, >; /// How to display components in a Ui. diff --git a/crates/re_viewer_context/src/lib.rs b/crates/re_viewer_context/src/lib.rs index 3f963d729144..34afff8d9759 100644 --- a/crates/re_viewer_context/src/lib.rs +++ b/crates/re_viewer_context/src/lib.rs @@ -46,8 +46,8 @@ pub use space_view::{ SpaceViewClass, SpaceViewClassIdentifier, SpaceViewClassLayoutPriority, SpaceViewClassRegistry, SpaceViewClassRegistryError, SpaceViewEntityHighlight, SpaceViewHighlights, SpaceViewOutlineMasks, SpaceViewState, SpaceViewSystemExecutionError, SpaceViewSystemRegistry, - ViewContextCollection, ViewContextSystem, ViewPartCollection, ViewPartSystem, ViewQuery, - ViewSystemIdentifier, + SystemExecutionOutput, ViewContextCollection, ViewContextSystem, ViewPartCollection, + ViewPartSystem, ViewQuery, ViewSystemIdentifier, }; pub use store_context::StoreContext; pub use tensor::{TensorDecodeCache, TensorStats, TensorStatsCache}; diff --git a/crates/re_viewer_context/src/space_view/dyn_space_view_class.rs b/crates/re_viewer_context/src/space_view/dyn_space_view_class.rs index cbad16b8950b..c2cc19208451 100644 --- a/crates/re_viewer_context/src/space_view/dyn_space_view_class.rs +++ b/crates/re_viewer_context/src/space_view/dyn_space_view_class.rs @@ -4,7 +4,7 @@ use re_types::ComponentName; use crate::{ AutoSpawnHeuristic, PerSystemEntities, SpaceViewClassRegistryError, SpaceViewId, - SpaceViewSystemRegistry, ViewQuery, ViewerContext, + SpaceViewSystemRegistry, SystemExecutionOutput, ViewQuery, ViewerContext, }; re_string_interner::declare_new_type!( @@ -41,7 +41,7 @@ pub enum SpaceViewClassLayoutPriority { /// Each Space View in the viewer's viewport has a single class assigned immutable at its creation time. /// The class defines all aspects of its behavior. /// It determines which entities are queried, how they are rendered, and how the user can interact with them. -pub trait DynSpaceViewClass { +pub trait DynSpaceViewClass: Send + Sync { /// Name of this space view class. /// /// Used for identification. Must be unique within a viewer session. @@ -128,8 +128,8 @@ pub trait DynSpaceViewClass { ui: &mut egui::Ui, state: &mut dyn SpaceViewState, root_entity_properties: &EntityProperties, - systems: &SpaceViewSystemRegistry, query: &ViewQuery<'_>, + system_output: SystemExecutionOutput, ); } diff --git a/crates/re_viewer_context/src/space_view/mod.rs b/crates/re_viewer_context/src/space_view/mod.rs index 01644221e76f..7e022f2e9de1 100644 --- a/crates/re_viewer_context/src/space_view/mod.rs +++ b/crates/re_viewer_context/src/space_view/mod.rs @@ -11,6 +11,7 @@ mod named_system; mod space_view_class; mod space_view_class_placeholder; mod space_view_class_registry; +mod system_execution_output; mod view_context_system; mod view_part_system; mod view_query; @@ -25,6 +26,7 @@ pub use space_view_class::SpaceViewClass; pub use space_view_class_registry::{ SpaceViewClassRegistry, SpaceViewClassRegistryError, SpaceViewSystemRegistry, }; +pub use system_execution_output::SystemExecutionOutput; pub use view_context_system::{ViewContextCollection, ViewContextSystem}; pub use view_part_system::{ default_heuristic_filter, HeuristicFilterContext, ViewPartCollection, ViewPartSystem, diff --git a/crates/re_viewer_context/src/space_view/space_view_class.rs b/crates/re_viewer_context/src/space_view/space_view_class.rs index a7cc8934db97..6ac07992dc5f 100644 --- a/crates/re_viewer_context/src/space_view/space_view_class.rs +++ b/crates/re_viewer_context/src/space_view/space_view_class.rs @@ -5,7 +5,7 @@ use re_types::ComponentName; use crate::{ AutoSpawnHeuristic, DynSpaceViewClass, PerSystemEntities, SpaceViewClassIdentifier, SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, SpaceViewSystemExecutionError, - SpaceViewSystemRegistry, ViewContextCollection, ViewPartCollection, ViewQuery, ViewerContext, + SpaceViewSystemRegistry, SystemExecutionOutput, ViewQuery, ViewerContext, }; /// Defines a class of space view. @@ -13,7 +13,7 @@ use crate::{ /// Each Space View in the viewer's viewport has a single class assigned immutable at its creation time. /// The class defines all aspects of its behavior. /// It determines which entities are queried, how they are rendered, and how the user can interact with them. -pub trait SpaceViewClass: std::marker::Sized { +pub trait SpaceViewClass: std::marker::Sized + Send + Sync { /// State of a space view. type State: SpaceViewState + Default + 'static; @@ -112,24 +112,17 @@ pub trait SpaceViewClass: std::marker::Sized { /// /// The passed state is kept frame-to-frame. /// - /// The passed systems (`view_ctx` and `parts`) are only valid for the duration of this frame and - /// were already executed upon entering this method. - /// - /// `draw_data` is all draw data gathered by executing the view part systems. /// TODO(wumpf): Right now the ui methods control when and how to create [`re_renderer::ViewBuilder`]s. /// In the future, we likely want to move view builder handling to `re_viewport` with /// minimal configuration options exposed via [`crate::SpaceViewClass`]. - #[allow(clippy::too_many_arguments)] fn ui( &self, ctx: &ViewerContext<'_>, ui: &mut egui::Ui, state: &mut Self::State, root_entity_properties: &EntityProperties, - view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, query: &ViewQuery<'_>, - draw_data: Vec, + system_output: SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError>; } @@ -231,48 +224,14 @@ impl DynSpaceViewClass for T { ui: &mut egui::Ui, state: &mut dyn SpaceViewState, root_entity_properties: &EntityProperties, - systems: &SpaceViewSystemRegistry, query: &ViewQuery<'_>, + system_output: SystemExecutionOutput, ) { re_tracing::profile_function!(); - // TODO(andreas): We should be able to parallelize both of these loops - let view_ctx = { - re_tracing::profile_scope!("ViewContextSystem::execute"); - let mut view_ctx = systems.new_context_collection(self.identifier()); - for (_name, system) in &mut view_ctx.systems { - re_tracing::profile_scope!(_name.as_str()); - system.execute(ctx, query); - } - view_ctx - }; - let (parts, draw_data) = { - re_tracing::profile_scope!("ViewPartSystem::execute"); - let mut parts = systems.new_part_collection(); - let mut draw_data = Vec::new(); - for (name, part) in &mut parts.systems { - re_tracing::profile_scope!(name.as_str()); - match part.execute(ctx, query, &view_ctx) { - Ok(part_draw_data) => draw_data.extend(part_draw_data), - Err(err) => { - re_log::error_once!("Error executing view part system {name:?}: {err}"); - } - } - } - (parts, draw_data) - }; - typed_state_wrapper_mut(state, |state| { - if let Err(err) = self.ui( - ctx, - ui, - state, - root_entity_properties, - &view_ctx, - &parts, - query, - draw_data, - ) { + if let Err(err) = self.ui(ctx, ui, state, root_entity_properties, query, system_output) + { // TODO(andreas): Draw an error message on top of the space view ui instead of logging. re_log::error_once!("Error drawing ui for space view: {err}"); } diff --git a/crates/re_viewer_context/src/space_view/space_view_class_placeholder.rs b/crates/re_viewer_context/src/space_view/space_view_class_placeholder.rs index f3c2396e939f..8b8c862404f4 100644 --- a/crates/re_viewer_context/src/space_view/space_view_class_placeholder.rs +++ b/crates/re_viewer_context/src/space_view/space_view_class_placeholder.rs @@ -1,6 +1,6 @@ use crate::{ SpaceViewClass, SpaceViewClassRegistryError, SpaceViewSystemExecutionError, - SpaceViewSystemRegistry, ViewContextCollection, ViewPartCollection, ViewQuery, ViewerContext, + SpaceViewSystemRegistry, SystemExecutionOutput, ViewQuery, ViewerContext, }; use re_data_store::EntityProperties; @@ -50,10 +50,8 @@ impl SpaceViewClass for SpaceViewClassPlaceholder { ui: &mut egui::Ui, _state: &mut Self::State, _root_entity_properties: &EntityProperties, - _view_ctx: &ViewContextCollection, - _parts: &ViewPartCollection, _query: &ViewQuery<'_>, - _draw_data: Vec, + _system_output: SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { ui.centered_and_justified(|ui| ui.label(self.help_text(ctx.re_ui))); Ok(()) diff --git a/crates/re_viewer_context/src/space_view/space_view_class_registry.rs b/crates/re_viewer_context/src/space_view/space_view_class_registry.rs index 95858c52a2be..3e679dcc38ce 100644 --- a/crates/re_viewer_context/src/space_view/space_view_class_registry.rs +++ b/crates/re_viewer_context/src/space_view/space_view_class_registry.rs @@ -26,8 +26,9 @@ pub enum SpaceViewClassRegistryError { /// for every instance of the space view class this belongs to. #[derive(Default)] pub struct SpaceViewSystemRegistry { - contexts: HashMap Box>>, - parts: HashMap Box>>, + contexts: + HashMap Box + Send + Sync>>, + parts: HashMap Box + Send + Sync>>, } impl SpaceViewSystemRegistry { @@ -87,6 +88,8 @@ impl SpaceViewSystemRegistry { &self, space_view_class_identifier: SpaceViewClassIdentifier, ) -> ViewContextCollection { + re_tracing::profile_function!(); + ViewContextCollection { systems: self .contexts @@ -101,6 +104,8 @@ impl SpaceViewSystemRegistry { } pub fn new_part_collection(&self) -> ViewPartCollection { + re_tracing::profile_function!(); + ViewPartCollection { systems: self .parts diff --git a/crates/re_viewer_context/src/space_view/system_execution_output.rs b/crates/re_viewer_context/src/space_view/system_execution_output.rs new file mode 100644 index 000000000000..d9332c205c9d --- /dev/null +++ b/crates/re_viewer_context/src/space_view/system_execution_output.rs @@ -0,0 +1,18 @@ +use crate::{ViewContextCollection, ViewPartCollection}; + +/// Output of space view system execution. +pub struct SystemExecutionOutput { + /// Executed view systems, may hold state that the ui method needs. + pub view_systems: ViewPartCollection, + + /// Executed context systems, may hold state that the ui method needs. + pub context_systems: ViewContextCollection, + + /// Draw data gathered during execution of the view part systems. + /// + /// Ui methods are supposed to use this to create [`re_renderer::ViewBuilder`]s. + // _TODO(andreas)_: In the future view builder execution should be outside of the space view ui method. + // This would allow to run the wgpu command buffer buildup in parallel. + // (This implies that we'd pass out the readily built command buffer here instead of drawables.) + pub draw_data: Vec, +} diff --git a/crates/re_viewer_context/src/space_view/view_context_system.rs b/crates/re_viewer_context/src/space_view/view_context_system.rs index 66c11d344406..cfd728e247f7 100644 --- a/crates/re_viewer_context/src/space_view/view_context_system.rs +++ b/crates/re_viewer_context/src/space_view/view_context_system.rs @@ -10,7 +10,7 @@ use crate::{ /// View context that can be used by view parts and ui methods to retrieve information about the scene as a whole. /// /// Is always populated before view part systems. -pub trait ViewContextSystem { +pub trait ViewContextSystem: Send + Sync { /// Returns all the component sets that the system is compatible with. /// /// If an entity path satisfies any of these sets, then the system will automatically run for @@ -31,8 +31,8 @@ pub trait ViewContextSystem { // TODO(jleibs): This probably needs a better name now that it includes class name pub struct ViewContextCollection { - pub(crate) systems: HashMap>, - pub(crate) space_view_class_identifier: SpaceViewClassIdentifier, + pub systems: HashMap>, + pub space_view_class_identifier: SpaceViewClassIdentifier, } impl ViewContextCollection { diff --git a/crates/re_viewer_context/src/space_view/view_part_system.rs b/crates/re_viewer_context/src/space_view/view_part_system.rs index d1dba45b8c13..408a65597ddd 100644 --- a/crates/re_viewer_context/src/space_view/view_part_system.rs +++ b/crates/re_viewer_context/src/space_view/view_part_system.rs @@ -40,7 +40,7 @@ impl HeuristicFilterContext { /// Element of a scene derived from a single archetype query. /// /// Is populated after scene contexts and has access to them. -pub trait ViewPartSystem { +pub trait ViewPartSystem: Send + Sync + 'static { // TODO(andreas): This should be able to list out the ContextSystems it needs. /// Returns the minimal set of components that the system _requires_ in order to be instantiated. @@ -127,7 +127,7 @@ pub fn default_heuristic_filter( } pub struct ViewPartCollection { - pub(crate) systems: HashMap>, + pub systems: HashMap>, } impl ViewPartCollection { diff --git a/crates/re_viewer_context/src/space_view/view_query.rs b/crates/re_viewer_context/src/space_view/view_query.rs index 9b7e0283f358..29e8f67ecb7a 100644 --- a/crates/re_viewer_context/src/space_view/view_query.rs +++ b/crates/re_viewer_context/src/space_view/view_query.rs @@ -118,7 +118,7 @@ pub struct ViewQuery<'s> { /// All queried [`DataResult`]s. /// /// Contains also invisible objects, use `iter_entities` to iterate over visible ones. - pub per_system_data_results: &'s PerSystemDataResults<'s>, + pub per_system_data_results: PerSystemDataResults<'s>, /// The timeline we're on. pub timeline: Timeline, @@ -128,8 +128,8 @@ pub struct ViewQuery<'s> { /// Hover/select highlighting information for this space view. /// - /// TODO(andreas): This should be a [`crate::ViewContextSystem`] instead. - pub highlights: &'s SpaceViewHighlights, + /// TODO(andreas): This should be the result of a [`crate::ViewContextSystem`] instead? + pub highlights: SpaceViewHighlights, } impl<'s> ViewQuery<'s> { diff --git a/crates/re_viewport/Cargo.toml b/crates/re_viewport/Cargo.toml index 6bb3dd3221ef..d16dd60ce03e 100644 --- a/crates/re_viewport/Cargo.toml +++ b/crates/re_viewport/Cargo.toml @@ -43,5 +43,6 @@ glam.workspace = true image = { workspace = true, default-features = false, features = ["png"] } itertools.workspace = true nohash-hasher.workspace = true +rayon.workspace = true rmp-serde.workspace = true tinyvec.workspace = true diff --git a/crates/re_viewport/src/lib.rs b/crates/re_viewport/src/lib.rs index 69caca258606..65b7dc9b9eaa 100644 --- a/crates/re_viewport/src/lib.rs +++ b/crates/re_viewport/src/lib.rs @@ -10,6 +10,7 @@ mod space_view; mod space_view_entity_picker; mod space_view_heuristics; mod space_view_highlights; +mod system_execution; mod viewport; mod viewport_blueprint; mod viewport_blueprint_ui; diff --git a/crates/re_viewport/src/space_view.rs b/crates/re_viewport/src/space_view.rs index e6888526134b..d562f007a3b1 100644 --- a/crates/re_viewport/src/space_view.rs +++ b/crates/re_viewport/src/space_view.rs @@ -12,9 +12,11 @@ use re_types::blueprint::SpaceViewComponent; use re_viewer_context::{ DataQueryId, DataResult, DynSpaceViewClass, PerSystemDataResults, PerSystemEntities, SpaceViewClass, SpaceViewClassIdentifier, SpaceViewHighlights, SpaceViewId, SpaceViewState, - SpaceViewSystemRegistry, StoreContext, ViewerContext, + SpaceViewSystemRegistry, StoreContext, SystemExecutionOutput, ViewQuery, ViewerContext, }; +use crate::system_execution::create_and_run_space_view_systems; + // ---------------------------------------------------------------------------- /// A view of a space. @@ -229,26 +231,17 @@ impl SpaceViewBlueprint { } } - pub(crate) fn scene_ui( - &mut self, - view_state: &mut dyn SpaceViewState, - ctx: &ViewerContext<'_>, - ui: &mut egui::Ui, + pub(crate) fn execute_systems<'a>( + &'a self, + ctx: &'a ViewerContext<'_>, latest_at: TimeInt, - highlights: &SpaceViewHighlights, - ) { - re_tracing::profile_function!(); - - let is_zero_sized_viewport = ui.available_size().min_elem() <= 0.0; - if is_zero_sized_viewport { - return; - } + highlights: SpaceViewHighlights, + ) -> (ViewQuery<'a>, SystemExecutionOutput) { + re_tracing::profile_function!(self.class_identifier.as_str()); let class = self.class(ctx.space_view_class_registry); - // TODO(jleibs): Sort out borrow-checker to avoid the need to clone here - // while still being able to pass &ViewerContext down the chain. - let query_result = ctx.lookup_query_result(self.query_id()).clone(); + let query_result = ctx.lookup_query_result(self.query_id()); let mut per_system_data_results = PerSystemDataResults::default(); { @@ -270,12 +263,30 @@ impl SpaceViewBlueprint { let query = re_viewer_context::ViewQuery { space_view_id: self.id, space_origin: &self.space_origin, - per_system_data_results: &per_system_data_results, + per_system_data_results, timeline: *ctx.rec_cfg.time_ctrl.read().timeline(), latest_at, highlights, }; + let system_output = + create_and_run_space_view_systems(ctx, class.identifier(), system_registry, &query); + + (query, system_output) + } + + pub(crate) fn scene_ui( + &self, + view_state: &mut dyn SpaceViewState, + ctx: &ViewerContext<'_>, + ui: &mut egui::Ui, + query: &ViewQuery<'_>, + system_output: SystemExecutionOutput, + ) { + re_tracing::profile_function!(); + + let class = self.class(ctx.space_view_class_registry); + let root_data_result = self.root_data_result(ctx.store_context); let props = root_data_result .individual_properties @@ -283,7 +294,7 @@ impl SpaceViewBlueprint { .unwrap_or_default(); ui.scope(|ui| { - class.ui(ctx, ui, view_state, &props, system_registry, &query); + class.ui(ctx, ui, view_state, &props, query, system_output); }); } diff --git a/crates/re_viewport/src/system_execution.rs b/crates/re_viewport/src/system_execution.rs new file mode 100644 index 000000000000..05a8d1a3a35f --- /dev/null +++ b/crates/re_viewport/src/system_execution.rs @@ -0,0 +1,80 @@ +use std::collections::BTreeMap; + +use ahash::HashMap; +use rayon::prelude::*; + +use re_viewer_context::{ + SpaceViewClassIdentifier, SpaceViewId, SpaceViewSystemRegistry, SystemExecutionOutput, + ViewQuery, ViewerContext, +}; + +use crate::{space_view_highlights::highlights_for_space_view, SpaceViewBlueprint}; + +pub fn create_and_run_space_view_systems( + ctx: &ViewerContext<'_>, + space_view_identifier: SpaceViewClassIdentifier, + systems: &SpaceViewSystemRegistry, + query: &ViewQuery<'_>, +) -> SystemExecutionOutput { + re_tracing::profile_function!(space_view_identifier.as_str()); + + let context_systems = { + re_tracing::profile_wait!("ViewContextSystem::execute"); + let mut context_systems = systems.new_context_collection(space_view_identifier); + context_systems + .systems + .par_iter_mut() + .for_each(|(_name, system)| { + re_tracing::profile_scope!("ViewContextSystem::execute", _name.as_str()); + system.execute(ctx, query); + }); + context_systems + }; + + re_tracing::profile_wait!("ViewPartSystem::execute"); + let mut view_systems = systems.new_part_collection(); + let draw_data = view_systems + .systems + .par_iter_mut() + .map(|(name, part)| { + re_tracing::profile_scope!("ViewPartSystem::execute", name.as_str()); + match part.execute(ctx, query, &context_systems) { + Ok(part_draw_data) => part_draw_data, + Err(err) => { + re_log::error_once!("Error executing view part system {name:?}: {err}"); + Vec::new() + } + } + }) + .flatten() + .collect(); + + SystemExecutionOutput { + view_systems, + context_systems, + draw_data, + } +} + +pub fn execute_systems_for_space_views<'a>( + ctx: &'a ViewerContext<'a>, + mut space_views_to_execute: Vec, + space_views: &'a BTreeMap, +) -> HashMap, SystemExecutionOutput)> { + let Some(time_int) = ctx.rec_cfg.time_ctrl.read().time_int() else { + return HashMap::default(); + }; + + re_tracing::profile_wait!("execute_systems"); + + space_views_to_execute + .par_drain(..) + .filter_map(|space_view_id| { + let space_view_blueprint = space_views.get(&space_view_id)?; + let highlights = + highlights_for_space_view(ctx.selection_state(), space_view_id, space_views); + let output = space_view_blueprint.execute_systems(ctx, time_int, highlights); + Some((space_view_id, output)) + }) + .collect::>() +} diff --git a/crates/re_viewport/src/viewport.rs b/crates/re_viewport/src/viewport.rs index 16cf5671146a..fa95c6ce8ce0 100644 --- a/crates/re_viewport/src/viewport.rs +++ b/crates/re_viewport/src/viewport.rs @@ -5,19 +5,21 @@ use std::collections::BTreeMap; use ahash::HashMap; + use egui_tiles::Behavior as _; use re_data_ui::item_ui; use re_ui::{Icon, ReUi}; use re_viewer_context::{ - CommandSender, Item, SpaceViewClassIdentifier, SpaceViewClassRegistry, SpaceViewHighlights, - SpaceViewId, SpaceViewState, ViewerContext, + CommandSender, Item, SpaceViewClassIdentifier, SpaceViewClassRegistry, SpaceViewId, + SpaceViewState, SystemExecutionOutput, ViewQuery, ViewerContext, }; use crate::{ space_view_entity_picker::SpaceViewEntityPicker, space_view_heuristics::default_created_space_views, - space_view_highlights::highlights_for_space_view, viewport_blueprint::load_viewport_blueprint, + space_view_highlights::highlights_for_space_view, + system_execution::execute_systems_for_space_views, viewport_blueprint::load_viewport_blueprint, SpaceInfoCollection, SpaceViewBlueprint, ViewportBlueprint, }; @@ -28,6 +30,11 @@ use crate::{ pub struct ViewportState { pub(crate) space_view_entity_window: Option, space_view_states: HashMap>, + + /// List of all space views that were visible *on screen* (excluding e.g. unselected tabs) the last frame. + /// + /// TODO(rerun-io/egui_tiles#34): This is needed because we don't know which space views will be visible until we have drawn them. + space_views_displayed_last_frame: Vec, } impl ViewportState { @@ -92,7 +99,7 @@ impl<'a, 'b> Viewport<'a, 'b> { self.state.space_view_entity_window = Some(SpaceViewEntityPicker { space_view_id }); } - pub fn viewport_ui(&mut self, ui: &mut egui::Ui, ctx: &'a mut ViewerContext<'_>) { + pub fn viewport_ui(&mut self, ui: &mut egui::Ui, ctx: &'a ViewerContext<'_>) { let Viewport { blueprint, state, .. } = self; @@ -140,6 +147,12 @@ impl<'a, 'b> Viewport<'a, 'b> { &mut blueprint.tree }; + let executed_systems_per_space_view = execute_systems_for_space_views( + ctx, + std::mem::take(&mut state.space_views_displayed_last_frame), + &blueprint.space_views, + ); + ui.scope(|ui| { ui.spacing_mut().item_spacing.x = re_ui::ReUi::view_padding(); @@ -148,9 +161,11 @@ impl<'a, 'b> Viewport<'a, 'b> { let mut tab_viewer = TabViewer { viewport_state: state, ctx, - space_views: &mut blueprint.space_views, + space_views: &blueprint.space_views, maximized: &mut blueprint.maximized, edited: false, + space_views_displayed_current_frame: Vec::new(), + executed_systems_per_space_view, }; tree.ui(&mut tab_viewer, ui); @@ -167,6 +182,8 @@ impl<'a, 'b> Viewport<'a, 'b> { blueprint.auto_layout = false; } + + state.space_views_displayed_last_frame = tab_viewer.space_views_displayed_current_frame; }); } @@ -234,10 +251,18 @@ impl<'a, 'b> Viewport<'a, 'b> { /// while containers are just groups of things. struct TabViewer<'a, 'b> { viewport_state: &'a mut ViewportState, - ctx: &'a mut ViewerContext<'b>, - space_views: &'a mut BTreeMap, + ctx: &'a ViewerContext<'b>, + space_views: &'a BTreeMap, maximized: &'a mut Option, + /// List of query & system execution results for each space view. + executed_systems_per_space_view: HashMap, SystemExecutionOutput)>, + + /// List of all space views drawn this frame. + /// + /// TODO(rerun-io/egui_tiles#34): It should be possible to predict which space views will be drawn. + space_views_displayed_current_frame: Vec, + /// The user edited the tree. edited: bool, } @@ -251,31 +276,52 @@ impl<'a, 'b> egui_tiles::Behavior for TabViewer<'a, 'b> { ) -> egui_tiles::UiResponse { re_tracing::profile_function!(); - let highlights = - highlights_for_space_view(self.ctx.selection_state(), *space_view_id, self.space_views); - let Some(space_view_blueprint) = self.space_views.get_mut(space_view_id) else { + let Some(space_view_blueprint) = self.space_views.get(space_view_id) else { return Default::default(); }; + let is_zero_sized_viewport = ui.available_size().min_elem() <= 0.0; + if is_zero_sized_viewport { + return Default::default(); + } + + let Some(latest_at) = self.ctx.rec_cfg.time_ctrl.read().time_int() else { + ui.centered_and_justified(|ui| { + ui.weak("No time selected"); + }); + return Default::default(); + }; + + // TODO(rerun-io/egui_tiles#34): If we haven't executed the system yet ahead of time, we should do so now. + // This is needed because we merely "guess" which systems we are going to need. + let (query, system_output) = + if let Some(result) = self.executed_systems_per_space_view.remove(space_view_id) { + result + } else { + let highlights = highlights_for_space_view( + self.ctx.selection_state(), + *space_view_id, + self.space_views, + ); + space_view_blueprint.execute_systems(self.ctx, latest_at, highlights) + }; + let space_view_state = self.viewport_state.space_view_state_mut( self.ctx.space_view_class_registry, space_view_blueprint.id, space_view_blueprint.class_identifier(), ); - space_view_ui( - self.ctx, - ui, - space_view_blueprint, - space_view_state, - &highlights, - ); + self.space_views_displayed_current_frame + .push(space_view_blueprint.id); + + space_view_blueprint.scene_ui(space_view_state, self.ctx, ui, &query, system_output); Default::default() } fn tab_title_for_pane(&mut self, space_view_id: &SpaceViewId) -> egui::WidgetText { - if let Some(space_view) = self.space_views.get_mut(space_view_id) { + if let Some(space_view) = self.space_views.get(space_view_id) { space_view.display_name.clone().into() } else { // All panes are space views, so this shouldn't happen unless we have a bug @@ -454,23 +500,6 @@ impl<'a, 'b> egui_tiles::Behavior for TabViewer<'a, 'b> { } } -fn space_view_ui( - ctx: &ViewerContext<'_>, - ui: &mut egui::Ui, - space_view_blueprint: &mut SpaceViewBlueprint, - space_view_state: &mut dyn SpaceViewState, - space_view_highlights: &SpaceViewHighlights, -) { - let Some(latest_at) = ctx.rec_cfg.time_ctrl.read().time_int() else { - ui.centered_and_justified(|ui| { - ui.weak("No time selected"); - }); - return; - }; - - space_view_blueprint.scene_ui(space_view_state, ctx, ui, latest_at, space_view_highlights); -} - /// A tab button for a tab in the viewport. /// /// The tab can contain any `egui_tiles::Tile`, diff --git a/examples/rust/custom_space_view/src/color_coordinates_space_view.rs b/examples/rust/custom_space_view/src/color_coordinates_space_view.rs index 70f5928d4afa..1be883284722 100644 --- a/examples/rust/custom_space_view/src/color_coordinates_space_view.rs +++ b/examples/rust/custom_space_view/src/color_coordinates_space_view.rs @@ -1,15 +1,13 @@ -use re_viewer::external::re_data_store::EntityProperties; use re_viewer::external::{ egui, - re_data_store::InstancePath, + re_data_store::{EntityProperties, InstancePath}, re_data_ui::{item_ui, DataUi}, re_log_types::EntityPath, - re_renderer, re_ui, + re_ui, re_viewer_context::{ HoverHighlight, Item, SelectionHighlight, SpaceViewClass, SpaceViewClassLayoutPriority, SpaceViewClassRegistryError, SpaceViewId, SpaceViewState, SpaceViewSystemExecutionError, - SpaceViewSystemRegistry, UiVerbosity, ViewContextCollection, ViewPartCollection, ViewQuery, - ViewerContext, + SpaceViewSystemRegistry, SystemExecutionOutput, UiVerbosity, ViewQuery, ViewerContext, }, }; @@ -133,12 +131,10 @@ impl SpaceViewClass for ColorCoordinatesSpaceView { ui: &mut egui::Ui, state: &mut Self::State, _root_entity_properties: &EntityProperties, - _view_ctx: &ViewContextCollection, - parts: &ViewPartCollection, query: &ViewQuery<'_>, - _draw_data: Vec, + system_output: SystemExecutionOutput, ) -> Result<(), SpaceViewSystemExecutionError> { - let colors = parts.get::()?; + let colors = system_output.view_systems.get::()?; egui::Frame::default().show(ui, |ui| { let color_at = match state.mode {