1- use puffin:: { GlobalProfiler , NanoSecond , ScopeDetails , StreamInfo , ThreadInfo } ;
1+ use std:: collections:: HashMap ;
2+
3+ use puffin:: { GlobalProfiler , NanoSecond , ScopeDetails , ScopeId , StreamInfo , ThreadInfo } ;
24
35use crate :: GpuTimerQueryResult ;
46
7+ /// Cache for registered puffin scope IDs to avoid memory leaks.
8+ #[ derive( Default ) ]
9+ pub struct PuffinScopeCache {
10+ scope_ids : HashMap < String , ScopeId > ,
11+ }
12+
513/// Visualize the query results in a `puffin::GlobalProfiler`.
6- pub fn output_frame_to_puffin ( profiler : & mut GlobalProfiler , query_result : & [ GpuTimerQueryResult ] ) {
14+ pub fn output_frame_to_puffin (
15+ profiler : & mut GlobalProfiler ,
16+ cache : & mut PuffinScopeCache ,
17+ query_result : & [ GpuTimerQueryResult ] ,
18+ ) {
719 let mut stream_info = StreamInfo :: default ( ) ;
8- collect_stream_info_recursive ( profiler, & mut stream_info, query_result, 0 ) ;
20+ build_stream_info ( profiler, cache , & mut stream_info, query_result, 0 ) ;
921
1022 profiler. report_user_scopes (
1123 ThreadInfo {
@@ -16,29 +28,41 @@ pub fn output_frame_to_puffin(profiler: &mut GlobalProfiler, query_result: &[Gpu
1628 ) ;
1729}
1830
19- fn collect_stream_info_recursive (
31+ fn build_stream_info (
2032 profiler : & mut GlobalProfiler ,
33+ cache : & mut PuffinScopeCache ,
2134 stream_info : & mut StreamInfo ,
2235 query_result : & [ GpuTimerQueryResult ] ,
2336 depth : usize ,
2437) {
25- let details: Vec < _ > = query_result
26- . iter ( )
27- . map ( |query| ScopeDetails :: from_scope_name ( query. label . clone ( ) ) )
28- . collect ( ) ;
29- let ids = profiler. register_user_scopes ( & details) ;
30- for ( query, id) in query_result. iter ( ) . zip ( ids) {
38+ for query in query_result {
39+ // Use get() first to avoid cloning the label on cache hits.
40+ let id = if let Some ( & id) = cache. scope_ids . get ( & query. label ) {
41+ id
42+ } else {
43+ let details = [ ScopeDetails :: from_scope_name ( query. label . clone ( ) ) ] ;
44+ let id = profiler. register_user_scopes ( & details) [ 0 ] ;
45+ cache. scope_ids . insert ( query. label . clone ( ) , id) ;
46+ id
47+ } ;
48+
3149 if let Some ( time) = & query. time {
3250 let start = ( time. start * 1e9 ) as NanoSecond ;
3351 let end = ( time. end * 1e9 ) as NanoSecond ;
3452
3553 stream_info. depth = stream_info. depth . max ( depth) ;
3654 stream_info. num_scopes += 1 ;
3755 stream_info. range_ns . 0 = stream_info. range_ns . 0 . min ( start) ;
38- stream_info. range_ns . 1 = stream_info. range_ns . 0 . max ( end) ;
56+ stream_info. range_ns . 1 = stream_info. range_ns . 1 . max ( end) ;
3957
4058 let ( offset, _) = stream_info. stream . begin_scope ( || start, id, "" ) ;
41- collect_stream_info_recursive ( profiler, stream_info, & query. nested_queries , depth + 1 ) ;
59+ build_stream_info (
60+ profiler,
61+ cache,
62+ stream_info,
63+ & query. nested_queries ,
64+ depth + 1 ,
65+ ) ;
4266 stream_info. stream . end_scope ( offset, end as NanoSecond ) ;
4367 }
4468 }
0 commit comments