1- use std:: { borrow:: Cow , str:: FromStr as _} ;
1+ use std:: {
2+ borrow:: Cow ,
3+ str:: FromStr as _,
4+ } ;
25
36use ahash:: HashMap ;
47use egui:: { Ui , text_edit:: TextEditState , text_selection:: LabelSelectionState } ;
58
69use re_chunk:: { Timeline , TimelineName } ;
710use re_chunk_store:: LatestAtQuery ;
811use re_entity_db:: EntityDb ;
9- use re_log_types:: { AbsoluteTimeRangeF , DataSourceMessage , StoreId , TableId } ;
12+ use re_log_types:: { AbsoluteTimeRangeF , DataSourceMessage , EntityPath , StoreId , TableId } ;
1013use re_redap_browser:: RedapServers ;
1114use re_redap_client:: ConnectionRegistryHandle ;
1215use re_smart_channel:: ReceiveSet ;
@@ -18,7 +21,7 @@ use re_viewer_context::{
1821 DragAndDropManager , FallbackProviderRegistry , GlobalContext , IndicatedEntities , Item ,
1922 MaybeVisualizableEntities , PerVisualizer , SelectionChange , StorageContext , StoreContext ,
2023 StoreHub , SystemCommand , SystemCommandSender as _, TableStore , TimeControl , TimeControlCommand ,
21- ViewClassRegistry , ViewId , ViewStates , ViewerContext , blueprint_timeline,
24+ ViewClassRegistry , ViewId , ViewStates , ViewerContext , VisualizableEntities , blueprint_timeline,
2225 open_url:: { self , ViewerOpenUrl } ,
2326} ;
2427use re_viewport:: ViewportUi ;
@@ -72,6 +75,12 @@ pub struct AppState {
7275 #[ serde( skip) ]
7376 pub ( crate ) share_modal : crate :: ui:: ShareModal ,
7477
78+ #[ serde( skip) ]
79+ view_query_cache : HashMap < ViewId , ViewQueryCacheEntry > ,
80+
81+ #[ serde( skip) ]
82+ visualizable_entities_cache : HashMap < ViewId , VisualizableEntitiesCacheEntry > ,
83+
7584 /// Test-only: single-shot callback to run at the end of the frame. Used in integration tests
7685 /// to interact with the `ViewerContext`.
7786 #[ cfg( feature = "testing" ) ]
@@ -127,6 +136,8 @@ impl Default for AppState {
127136 view_states : Default :: default ( ) ,
128137 selection_state : Default :: default ( ) ,
129138 focused_item : Default :: default ( ) ,
139+ view_query_cache : Default :: default ( ) ,
140+ visualizable_entities_cache : Default :: default ( ) ,
130141
131142 #[ cfg( feature = "testing" ) ]
132143 test_hook : None ,
@@ -142,6 +153,58 @@ pub(crate) struct WelcomeScreenState {
142153 pub opacity : f32 ,
143154}
144155
156+ struct ViewQueryCacheEntry {
157+ key : ViewQueryCacheKey ,
158+ result : DataQueryResult ,
159+ }
160+
161+ #[ derive( Clone , PartialEq , Eq ) ]
162+ struct ViewQueryCacheKey {
163+ recording_generation : re_chunk_store:: ChunkStoreGeneration ,
164+ blueprint_generation : re_chunk_store:: ChunkStoreGeneration ,
165+ blueprint_query : LatestAtQuery ,
166+ }
167+
168+ impl ViewQueryCacheKey {
169+ fn new (
170+ recording_generation : re_chunk_store:: ChunkStoreGeneration ,
171+ blueprint_generation : re_chunk_store:: ChunkStoreGeneration ,
172+ blueprint_query : & LatestAtQuery ,
173+ ) -> Self {
174+ Self {
175+ recording_generation,
176+ blueprint_generation,
177+ blueprint_query : blueprint_query. clone ( ) ,
178+ }
179+ }
180+ }
181+
182+ struct VisualizableEntitiesCacheEntry {
183+ key : VisualizableEntitiesCacheKey ,
184+ result : PerVisualizer < VisualizableEntities > ,
185+ }
186+
187+ #[ derive( Clone , PartialEq , Eq , Hash ) ]
188+ struct VisualizableEntitiesCacheKey {
189+ recording_generation : re_chunk_store:: ChunkStoreGeneration ,
190+ blueprint_generation : re_chunk_store:: ChunkStoreGeneration ,
191+ space_origin : EntityPath ,
192+ }
193+
194+ impl VisualizableEntitiesCacheKey {
195+ fn new (
196+ recording_generation : re_chunk_store:: ChunkStoreGeneration ,
197+ blueprint_generation : re_chunk_store:: ChunkStoreGeneration ,
198+ space_origin : & EntityPath ,
199+ ) -> Self {
200+ Self {
201+ recording_generation,
202+ blueprint_generation,
203+ space_origin : space_origin. clone ( ) ,
204+ }
205+ }
206+ }
207+
145208impl AppState {
146209 pub fn set_examples_manifest_url ( & mut self , egui_ctx : & egui:: Context , url : String ) {
147210 self . welcome_screen . set_examples_manifest_url ( egui_ctx, url) ;
@@ -291,39 +354,97 @@ impl AppState {
291354 . maybe_visualizable_entities_for_visualizer_systems ( recording. store_id ( ) ) ;
292355 let indicated_entities_per_visualizer =
293356 view_class_registry. indicated_entities_per_visualizer ( recording. store_id ( ) ) ;
357+ let recording_generation = recording. generation ( ) ;
358+ let blueprint_generation = store_context. blueprint . generation ( ) ;
359+
360+ self . view_query_cache
361+ . retain ( |view_id, _| viewport_ui. blueprint . views . contains_key ( view_id) ) ;
362+ self . visualizable_entities_cache
363+ . retain ( |view_id, _| viewport_ui. blueprint . views . contains_key ( view_id) ) ;
364+
365+ // Determine visualizable entities for each view (cached)
366+ let visualizable_entities_per_view: HashMap < ViewId , PerVisualizer < VisualizableEntities > > = {
367+ re_tracing:: profile_scope!( "visualizable_entities_per_view" ) ;
368+ viewport_ui
369+ . blueprint
370+ . views
371+ . values ( )
372+ . map ( |view| {
373+ let cache_key = VisualizableEntitiesCacheKey :: new (
374+ recording_generation. clone ( ) ,
375+ blueprint_generation. clone ( ) ,
376+ & view. space_origin ,
377+ ) ;
378+
379+ let visualizable_entities = if let Some ( entry) = self . visualizable_entities_cache . get ( & view. id )
380+ && entry. key == cache_key
381+ {
382+ PerVisualizer ( entry. result . 0 . clone ( ) )
383+ } else {
384+ // This is the expensive operation we're caching
385+ view. class ( view_class_registry)
386+ . determine_visualizable_entities (
387+ & maybe_visualizable_entities_per_visualizer,
388+ recording,
389+ & view_class_registry
390+ . new_visualizer_collection ( view. class_identifier ( ) ) ,
391+ & view. space_origin ,
392+ )
393+ } ;
394+
395+ self . visualizable_entities_cache . insert (
396+ view. id ,
397+ VisualizableEntitiesCacheEntry {
398+ key : cache_key,
399+ result : PerVisualizer ( visualizable_entities. 0 . clone ( ) ) ,
400+ } ,
401+ ) ;
402+
403+ ( view. id , visualizable_entities)
404+ } )
405+ . collect ( )
406+ } ;
294407
295408 // Execute the queries for every `View`
296- let query_results = {
409+ let query_results: HashMap < _ , _ > = {
297410 re_tracing:: profile_scope!( "query_results" ) ;
298411 viewport_ui
299412 . blueprint
300413 . views
301414 . values ( )
302415 . map ( |view| {
303- // TODO(andreas): This needs to be done in a store subscriber that exists per view (instance, not class!).
304- // Note that right now we determine *all* visualizable entities, not just the queried ones.
305- // In a store subscriber set this is fine, but on a per-frame basis it's wasteful.
306- let visualizable_entities = view
307- . class ( view_class_registry)
308- . determine_visualizable_entities (
309- & maybe_visualizable_entities_per_visualizer,
310- recording,
311- & view_class_registry
312- . new_visualizer_collection ( view. class_identifier ( ) ) ,
313- & view. space_origin ,
314- ) ;
315-
316- (
317- view. id ,
416+ let visualizable_entities = & visualizable_entities_per_view[ & view. id ] ;
417+
418+ let cache_key = ViewQueryCacheKey :: new (
419+ recording_generation. clone ( ) ,
420+ blueprint_generation. clone ( ) ,
421+ & blueprint_query,
422+ ) ;
423+
424+ let result = if let Some ( entry) = self . view_query_cache . get ( & view. id )
425+ && entry. key == cache_key
426+ {
427+ entry. result . clone ( )
428+ } else {
318429 view. contents . execute_query (
319430 store_context,
320431 view_class_registry,
321432 & blueprint_query,
322- & visualizable_entities,
323- ) ,
324- )
433+ visualizable_entities,
434+ )
435+ } ;
436+
437+ self . view_query_cache . insert (
438+ view. id ,
439+ ViewQueryCacheEntry {
440+ key : cache_key,
441+ result : result. clone ( ) ,
442+ } ,
443+ ) ;
444+
445+ ( view. id , result)
325446 } )
326- . collect :: < _ > ( )
447+ . collect ( )
327448 } ;
328449
329450 let app_blueprint_ctx = AppBlueprintCtx {
@@ -361,6 +482,7 @@ impl AppState {
361482 maybe_visualizable_entities_per_visualizer :
362483 & maybe_visualizable_entities_per_visualizer,
363484 indicated_entities_per_visualizer : & indicated_entities_per_visualizer,
485+ visualizable_entities_per_view : & visualizable_entities_per_view,
364486 query_results : & query_results,
365487 time_ctrl,
366488 blueprint_time_ctrl : blueprint_time_control,
@@ -389,9 +511,16 @@ impl AppState {
389511 time_ctrl. timeline ( ) ,
390512 & maybe_visualizable_entities_per_visualizer,
391513 & indicated_entities_per_visualizer,
514+ & visualizable_entities_per_view,
392515 view_states,
393516 ) ;
394517
518+ for ( view_id, result) in & query_results {
519+ if let Some ( entry) = self . view_query_cache . get_mut ( view_id) {
520+ entry. result = result. clone ( ) ;
521+ }
522+ }
523+
395524 // We need to recreate the context to appease the borrow checker. It is a bit annoying, but
396525 // it's just a bunch of refs so not really that big of a deal in practice.
397526 let ctx = ViewerContext {
@@ -417,6 +546,7 @@ impl AppState {
417546 maybe_visualizable_entities_per_visualizer :
418547 & maybe_visualizable_entities_per_visualizer,
419548 indicated_entities_per_visualizer : & indicated_entities_per_visualizer,
549+ visualizable_entities_per_view : & visualizable_entities_per_view,
420550 query_results : & query_results,
421551 time_ctrl,
422552 blueprint_time_ctrl : blueprint_time_control,
@@ -806,6 +936,7 @@ fn update_overrides(
806936 active_timeline : & Timeline ,
807937 maybe_visualizable_entities_per_visualizer : & PerVisualizer < MaybeVisualizableEntities > ,
808938 indicated_entities_per_visualizer : & PerVisualizer < IndicatedEntities > ,
939+ visualizable_entities_per_view : & HashMap < ViewId , PerVisualizer < VisualizableEntities > > ,
809940 view_states : & mut ViewStates ,
810941) -> HashMap < ViewId , DataQueryResult > {
811942 use rayon:: iter:: { IntoParallelIterator as _, ParallelIterator as _} ;
@@ -814,6 +945,7 @@ fn update_overrides(
814945 view : & ' a re_viewport_blueprint:: ViewBlueprint ,
815946 view_state : & ' a dyn re_viewer_context:: ViewState ,
816947 query_result : DataQueryResult ,
948+ visualizable_entities : & ' a PerVisualizer < VisualizableEntities > ,
817949 }
818950
819951 for view in viewport_blueprint. views . values ( ) {
@@ -828,10 +960,12 @@ fn update_overrides(
828960 let view_state = view_states
829961 . get ( view. id )
830962 . expect ( "View state should exist, we just called ensure_state_exists on it." ) ;
963+ let visualizable_entities = & visualizable_entities_per_view[ & view. id ] ;
831964 OverridesUpdateTask {
832965 view,
833966 view_state,
834967 query_result,
968+ visualizable_entities,
835969 }
836970 } )
837971 } )
@@ -844,24 +978,15 @@ fn update_overrides(
844978 view,
845979 view_state,
846980 mut query_result,
981+ visualizable_entities,
847982 } | {
848- // TODO(andreas): This needs to be done in a store subscriber that exists per view (instance, not class!).
849- // Note that right now we determine *all* visualizable entities, not just the queried ones.
850- // In a store subscriber set this is fine, but on a per-frame basis it's wasteful.
851- let visualizable_entities = view
852- . class ( view_class_registry)
853- . determine_visualizable_entities (
854- maybe_visualizable_entities_per_visualizer,
855- store_context. recording ,
856- & view_class_registry. new_visualizer_collection ( view. class_identifier ( ) ) ,
857- & view. space_origin ,
858- ) ;
859-
983+ // Now using the pre-computed visualizable_entities instead of recomputing them.
984+ // This avoids the expensive determine_visualizable_entities call that was happening twice per frame.
860985 let resolver = re_viewport_blueprint:: DataQueryPropertyResolver :: new (
861986 view,
862987 view_class_registry,
863988 maybe_visualizable_entities_per_visualizer,
864- & visualizable_entities,
989+ visualizable_entities,
865990 indicated_entities_per_visualizer,
866991 ) ;
867992
0 commit comments