@@ -46,26 +46,27 @@ use api::{APZScrollGeneration, HasScrollLinkedEffect, Shadow, SpatialId, StickyF
46
46
use api:: { ClipMode , PrimitiveKeyKind , TransformStyle , YuvColorSpace , ColorRange , YuvData , TempFilterData } ;
47
47
use api:: { ReferenceTransformBinding , Rotation , FillRule , SpatialTreeItem , ReferenceFrameDescriptor } ;
48
48
use api:: FilterOpGraphPictureBufferId ;
49
+ use api:: channel:: { unbounded_channel, Receiver , Sender } ;
49
50
use api:: units:: * ;
50
51
use crate :: image_tiling:: simplify_repeated_primitive;
51
52
use crate :: box_shadow:: BLUR_SAMPLE_SCALE ;
52
- use crate :: clip:: { ClipItemKey , ClipStore , ClipItemKeyKind , ClipIntern } ;
53
+ use crate :: clip:: { ClipIntern , ClipItemKey , ClipItemKeyKind , ClipStore } ;
53
54
use crate :: clip:: { ClipInternData , ClipNodeId , ClipLeafId } ;
54
55
use crate :: clip:: { PolygonDataHandle , ClipTreeBuilder } ;
55
56
use crate :: segment:: EdgeAaSegmentMask ;
56
57
use crate :: spatial_tree:: { SceneSpatialTree , SpatialNodeContainer , SpatialNodeIndex , get_external_scroll_offset} ;
57
- use crate :: frame_builder:: { FrameBuilderConfig } ;
58
+ use crate :: frame_builder:: FrameBuilderConfig ;
58
59
use glyph_rasterizer:: { FontInstance , SharedFontResources } ;
59
60
use crate :: hit_test:: HitTestingScene ;
60
61
use crate :: intern:: Interner ;
61
62
use crate :: internal_types:: { FastHashMap , LayoutPrimitiveInfo , Filter , FilterGraphNode , FilterGraphOp , FilterGraphPictureReference , PlaneSplitterIndex , PipelineInstanceId } ;
62
63
use crate :: picture:: { Picture3DContext , PictureCompositeMode , PicturePrimitive } ;
63
64
use crate :: picture:: { BlitReason , OrderedPictureChild , PrimitiveList , SurfaceInfo , PictureFlags } ;
64
65
use crate :: picture_graph:: PictureGraph ;
65
- use crate :: prim_store:: { PrimitiveInstance } ;
66
+ use crate :: prim_store:: { PrimitiveInstance , PrimitiveStoreStats } ;
66
67
use crate :: prim_store:: { PrimitiveInstanceKind , NinePatchDescriptor , PrimitiveStore } ;
67
68
use crate :: prim_store:: { InternablePrimitive , PictureIndex } ;
68
- use crate :: prim_store:: { PolygonKey } ;
69
+ use crate :: prim_store:: PolygonKey ;
69
70
use crate :: prim_store:: backdrop:: { BackdropCapture , BackdropRender } ;
70
71
use crate :: prim_store:: borders:: { ImageBorder , NormalBorderPrim } ;
71
72
use crate :: prim_store:: gradient:: {
@@ -79,7 +80,7 @@ use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey};
79
80
use crate :: prim_store:: text_run:: TextRun ;
80
81
use crate :: render_backend:: SceneView ;
81
82
use crate :: resource_cache:: ImageRequest ;
82
- use crate :: scene:: { Scene , ScenePipeline , BuiltScene , SceneStats , StackingContextHelpers } ;
83
+ use crate :: scene:: { BuiltScene , Scene , ScenePipeline , SceneStats , StackingContextHelpers } ;
83
84
use crate :: scene_builder_thread:: Interners ;
84
85
use crate :: space:: SpaceSnapper ;
85
86
use crate :: spatial_node:: {
@@ -539,6 +540,7 @@ impl<'a> SceneBuilder<'a> {
539
540
frame_builder_config : & FrameBuilderConfig ,
540
541
interners : & mut Interners ,
541
542
spatial_tree : & mut SceneSpatialTree ,
543
+ recycler : & mut SceneRecycler ,
542
544
stats : & SceneStats ,
543
545
debug_flags : DebugFlags ,
544
546
) -> BuiltScene {
@@ -560,17 +562,17 @@ impl<'a> SceneBuilder<'a> {
560
562
spatial_tree,
561
563
fonts,
562
564
config : * frame_builder_config,
563
- id_to_index_mapper_stack : Vec :: new ( ) ,
564
- hit_testing_scene : HitTestingScene :: new ( & stats. hit_test_stats ) ,
565
- pending_shadow_items : VecDeque :: new ( ) ,
566
- sc_stack : Vec :: new ( ) ,
567
- containing_block_stack : Vec :: new ( ) ,
568
- raster_space_stack : vec ! [ RasterSpace :: Screen ] ,
569
- prim_store : PrimitiveStore :: new ( & stats . prim_store_stats ) ,
570
- clip_store : ClipStore :: new ( ) ,
565
+ id_to_index_mapper_stack : mem :: take ( & mut recycler . id_to_index_mapper_stack ) ,
566
+ hit_testing_scene : recycler . hit_testing_scene . take ( ) . unwrap_or_else ( || HitTestingScene :: new ( & stats. hit_test_stats ) ) ,
567
+ pending_shadow_items : mem :: take ( & mut recycler . pending_shadow_items ) ,
568
+ sc_stack : mem :: take ( & mut recycler . sc_stack ) ,
569
+ containing_block_stack : mem :: take ( & mut recycler . containing_block_stack ) ,
570
+ raster_space_stack : mem :: take ( & mut recycler . raster_space_stack ) ,
571
+ prim_store : mem :: take ( & mut recycler . prim_store ) ,
572
+ clip_store : mem :: take ( & mut recycler . clip_store ) ,
571
573
interners,
572
574
external_scroll_mapper : ScrollOffsetMapper :: new ( ) ,
573
- iframe_size : Vec :: new ( ) ,
575
+ iframe_size : mem :: take ( & mut recycler . iframe_size ) ,
574
576
root_iframe_clip : None ,
575
577
quality_settings : view. quality_settings ,
576
578
tile_cache_builder : TileCacheBuilder :: new (
@@ -579,14 +581,32 @@ impl<'a> SceneBuilder<'a> {
579
581
debug_flags,
580
582
) ,
581
583
snap_to_device,
582
- picture_graph : PictureGraph :: new ( ) ,
584
+ picture_graph : mem :: take ( & mut recycler . picture_graph ) ,
583
585
next_plane_splitter_index : 0 ,
584
- prim_instances : Vec :: new ( ) ,
586
+ prim_instances : mem :: take ( & mut recycler . prim_instances ) ,
585
587
pipeline_instance_ids : FastHashMap :: default ( ) ,
586
- surfaces : Vec :: new ( ) ,
587
- clip_tree_builder : ClipTreeBuilder :: new ( ) ,
588
+ surfaces : mem :: take ( & mut recycler . surfaces ) ,
589
+ clip_tree_builder : recycler . clip_tree_builder . take ( ) . unwrap_or_else ( || ClipTreeBuilder :: new ( ) ) ,
588
590
} ;
589
591
592
+ // Reset
593
+ builder. hit_testing_scene . reset ( ) ;
594
+ builder. prim_store . reset ( ) ;
595
+ builder. clip_store . reset ( ) ;
596
+ builder. picture_graph . reset ( ) ;
597
+ builder. prim_instances . clear ( ) ;
598
+ builder. surfaces . clear ( ) ;
599
+ builder. sc_stack . clear ( ) ;
600
+ builder. containing_block_stack . clear ( ) ;
601
+ builder. id_to_index_mapper_stack . clear ( ) ;
602
+ builder. pending_shadow_items . clear ( ) ;
603
+ builder. iframe_size . clear ( ) ;
604
+
605
+ builder. raster_space_stack . clear ( ) ;
606
+ builder. raster_space_stack . push ( RasterSpace :: Screen ) ;
607
+
608
+ builder. clip_tree_builder . begin ( ) ;
609
+
590
610
builder. build_all (
591
611
root_pipeline_id,
592
612
& root_pipeline,
@@ -617,6 +637,14 @@ impl<'a> SceneBuilder<'a> {
617
637
618
638
let clip_tree = builder. clip_tree_builder . finalize ( ) ;
619
639
640
+ recycler. clip_tree_builder = Some ( builder. clip_tree_builder ) ;
641
+ recycler. sc_stack = builder. sc_stack ;
642
+ recycler. id_to_index_mapper_stack = builder. id_to_index_mapper_stack ;
643
+ recycler. containing_block_stack = builder. containing_block_stack ;
644
+ recycler. raster_space_stack = builder. raster_space_stack ;
645
+ recycler. pending_shadow_items = builder. pending_shadow_items ;
646
+ recycler. iframe_size = builder. iframe_size ;
647
+
620
648
BuiltScene {
621
649
has_root_pipeline : scene. has_root_pipeline ( ) ,
622
650
pipeline_epochs : scene. pipeline_epochs . clone ( ) ,
@@ -632,6 +660,7 @@ impl<'a> SceneBuilder<'a> {
632
660
prim_instances : builder. prim_instances ,
633
661
surfaces : builder. surfaces ,
634
662
clip_tree,
663
+ recycler_tx : Some ( recycler. tx . clone ( ) ) ,
635
664
}
636
665
}
637
666
@@ -4722,3 +4751,94 @@ fn read_gradient_stops(stops: ItemRange<GradientStop>) -> Vec<GradientStopKey> {
4722
4751
}
4723
4752
} ) . collect ( )
4724
4753
}
4754
+
4755
+ /// A helper for reusing the scene builder's memory allocations and dropping
4756
+ /// scene allocations on the scene builder thread to avoid lock contention in
4757
+ /// jemalloc.
4758
+ pub struct SceneRecycler {
4759
+ pub tx : Sender < BuiltScene > ,
4760
+ rx : Receiver < BuiltScene > ,
4761
+
4762
+ // Allocations recycled from BuiltScene:
4763
+
4764
+ pub prim_store : PrimitiveStore ,
4765
+ pub clip_store : ClipStore ,
4766
+ pub picture_graph : PictureGraph ,
4767
+ pub prim_instances : Vec < PrimitiveInstance > ,
4768
+ pub surfaces : Vec < SurfaceInfo > ,
4769
+ pub hit_testing_scene : Option < HitTestingScene > ,
4770
+ pub clip_tree_builder : Option < ClipTreeBuilder > ,
4771
+ //Could also attempt to recycle the following:
4772
+ //pub tile_cache_config: TileCacheConfig,
4773
+ //pub pipeline_epochs: FastHashMap<PipelineId, Epoch>,
4774
+ //pub tile_cache_pictures: Vec<PictureIndex>,
4775
+
4776
+
4777
+ // Allocations recycled from SceneBuilder
4778
+
4779
+ id_to_index_mapper_stack : Vec < NodeIdToIndexMapper > ,
4780
+ sc_stack : Vec < FlattenedStackingContext > ,
4781
+ containing_block_stack : Vec < SpatialNodeIndex > ,
4782
+ raster_space_stack : Vec < RasterSpace > ,
4783
+ pending_shadow_items : VecDeque < ShadowItem > ,
4784
+ iframe_size : Vec < LayoutSize > ,
4785
+ }
4786
+
4787
+ impl SceneRecycler {
4788
+ pub fn new ( ) -> Self {
4789
+ let ( tx, rx) = unbounded_channel ( ) ;
4790
+ SceneRecycler {
4791
+ tx,
4792
+ rx,
4793
+
4794
+ prim_instances : Vec :: new ( ) ,
4795
+ surfaces : Vec :: new ( ) ,
4796
+ prim_store : PrimitiveStore :: new ( & PrimitiveStoreStats :: empty ( ) ) ,
4797
+ clip_store : ClipStore :: new ( ) ,
4798
+ picture_graph : PictureGraph :: new ( ) ,
4799
+ hit_testing_scene : None ,
4800
+ clip_tree_builder : None ,
4801
+
4802
+ id_to_index_mapper_stack : Vec :: new ( ) ,
4803
+ sc_stack : Vec :: new ( ) ,
4804
+ containing_block_stack : Vec :: new ( ) ,
4805
+ raster_space_stack : Vec :: new ( ) ,
4806
+ pending_shadow_items : VecDeque :: new ( ) ,
4807
+ iframe_size : Vec :: new ( ) ,
4808
+ }
4809
+ }
4810
+
4811
+ /// Do some bookkeeping of past memory allocations, retaining some of them for
4812
+ /// reuse and dropping the rest.
4813
+ ///
4814
+ /// Should be called once between scene builds, ideally outside of the critical
4815
+ /// path since deallocations can take some time.
4816
+ #[ inline( never) ]
4817
+ pub fn recycle_built_scene ( & mut self ) {
4818
+ let Ok ( scene) = self . rx . try_recv ( ) else {
4819
+ return ;
4820
+ } ;
4821
+
4822
+ self . prim_store = scene. prim_store ;
4823
+ self . clip_store = scene. clip_store ;
4824
+ // We currently retain top-level allocations but don't attempt to retain leaf
4825
+ // allocations in the prim store and clip store. We don't have to reset it here
4826
+ // but doing so avoids dropping the leaf allocations in the
4827
+ self . prim_store . reset ( ) ;
4828
+ self . clip_store . reset ( ) ;
4829
+ self . hit_testing_scene = Arc :: try_unwrap ( scene. hit_testing_scene ) . ok ( ) ;
4830
+ self . picture_graph = scene. picture_graph ;
4831
+ self . prim_instances = scene. prim_instances ;
4832
+ self . surfaces = scene. surfaces ;
4833
+ if let Some ( clip_tree_builder) = & mut self . clip_tree_builder {
4834
+ clip_tree_builder. recycle_tree ( scene. clip_tree ) ;
4835
+ }
4836
+
4837
+ while let Ok ( _) = self . rx . try_recv ( ) {
4838
+ // If for some reason more than one scene accumulated in the queue, drop
4839
+ // the rest.
4840
+ }
4841
+
4842
+ // Note: fields of the scene we don't recycle get dropped here.
4843
+ }
4844
+ }
0 commit comments