Bug 1642629 - Recycle the surface info vector in frame building. r=gw
☠☠ backed out by a184a8ccba8b ☠ ☠
authorNicolas Silva <nsilva@mozilla.com>
Tue, 16 Jun 2020 20:11:39 +0000
changeset 535959 217914372d03fefa9da57b87d646dff54df351ea
parent 535958 149ca53d3549df2e8e6ad1a3b8985d549095b5f3
child 535960 b8ad0fa3519db2b169c38312f78244a89d583960
push id37513
push userrmaries@mozilla.com
push dateWed, 17 Jun 2020 03:41:56 +0000
treeherdermozilla-central@0e023da23571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1642629
milestone79.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1642629 - Recycle the surface info vector in frame building. r=gw This vector is usually small, but on some pages it can be fairly large (500+ in https://forum.xda-developers.com/redmi-note-3/development for example). Differential Revision: https://phabricator.services.mozilla.com/D79855
gfx/wr/webrender/src/frame_builder.rs
gfx/wr/webrender/src/render_backend.rs
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -116,16 +116,20 @@ impl FrameGlobalResources {
 
 /// Produces the frames that are sent to the renderer.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 pub struct FrameBuilder {
     /// Cache of surface tiles from the previous frame builder
     /// that can optionally be consumed by this frame builder.
     pending_retained_tiles: RetainedTiles,
     pub globals: FrameGlobalResources,
+    // A vector that is cleared and re-built each frame. We keep it
+    // here to avoid reallocations.
+    #[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
+    surfaces: Vec<SurfaceInfo>,
 }
 
 pub struct FrameVisibilityContext<'a> {
     pub spatial_tree: &'a SpatialTree,
     pub global_screen_world_rect: WorldRect,
     pub global_device_pixel_scale: DevicePixelScale,
     pub surfaces: &'a [SurfaceInfo],
     pub debug_flags: DebugFlags,
@@ -232,16 +236,17 @@ pub struct PictureState {
     pub plane_splitter: Option<PlaneSplitter>,
 }
 
 impl FrameBuilder {
     pub fn new() -> Self {
         FrameBuilder {
             pending_retained_tiles: RetainedTiles::new(),
             globals: FrameGlobalResources::empty(),
+            surfaces: Vec::new(),
         }
     }
 
     /// Provide any cached surface tiles from the previous frame builder
     /// to a new frame builder. These will be consumed or dropped the
     /// first time a new frame builder creates a frame.
     pub fn set_retained_resources(&mut self, retained_tiles: RetainedTiles) {
         // In general, the pending retained tiles are consumed by the frame
@@ -250,31 +255,34 @@ impl FrameBuilder {
         // frame builder may not have had a chance to build a frame and
         // consume the pending tiles. In this case, the pending tiles will
         // be lost, causing a full invalidation of the entire screen. To
         // avoid this, if there are still pending tiles, include them in
         // the retained tiles passed to the next frame builder.
         self.pending_retained_tiles.merge(retained_tiles);
     }
 
+    pub fn memory_pressure(&mut self) {
+        self.surfaces = Vec::new();
+    }
+
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(
         &mut self,
         scene: &mut BuiltScene,
         global_screen_world_rect: WorldRect,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskGraph,
         profile_counters: &mut FrameProfileCounters,
         global_device_pixel_scale: DevicePixelScale,
         scene_properties: &SceneProperties,
         transform_palette: &mut TransformPalette,
         data_stores: &mut DataStores,
-        surfaces: &mut Vec<SurfaceInfo>,
         scratch: &mut PrimitiveScratchBuffer,
         debug_flags: DebugFlags,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         composite_state: &mut CompositeState,
         tile_cache_logger: &mut TileCacheLogger,
     ) -> Option<RenderTaskId> {
         profile_scope!("build_layer_screen_rects_and_cull_layers");
 
@@ -322,32 +330,32 @@ impl FrameBuilder {
             ROOT_SPATIAL_NODE_INDEX,
             ROOT_SPATIAL_NODE_INDEX,
             0.0,
             global_screen_world_rect,
             &scene.spatial_tree,
             global_device_pixel_scale,
             (1.0, 1.0),
         );
-        surfaces.push(root_surface);
+        self.surfaces.push(root_surface);
 
         let mut retained_tiles = mem::replace(
             &mut self.pending_retained_tiles,
             RetainedTiles::new(),
         );
 
         // The first major pass of building a frame is to walk the picture
         // tree. This pass must be quick (it should never touch individual
         // primitives). For now, all we do here is determine which pictures
         // will create surfaces. In the future, this will be expanded to
         // set up render tasks, determine scaling of surfaces, and detect
         // which surfaces have valid cached surfaces that don't need to
         // be rendered this frame.
         PictureUpdateState::update_all(
-            surfaces,
+            &mut self.surfaces,
             scene.root_pic_index,
             &mut scene.prim_store.pictures,
             &frame_context,
             gpu_cache,
             &scene.clip_store,
             data_stores,
             composite_state,
         );
@@ -355,17 +363,17 @@ impl FrameBuilder {
         {
             profile_scope!("UpdateVisibility");
             profile_marker!("UpdateVisibility");
 
             let visibility_context = FrameVisibilityContext {
                 global_device_pixel_scale,
                 spatial_tree: &scene.spatial_tree,
                 global_screen_world_rect,
-                surfaces,
+                surfaces: &self.surfaces,
                 debug_flags,
                 scene_properties,
                 config: scene.config,
             };
 
             let mut visibility_state = FrameVisibilityState {
                 resource_cache,
                 gpu_cache,
@@ -421,17 +429,17 @@ impl FrameBuilder {
         let mut frame_state = FrameBuildingState {
             render_tasks,
             profile_counters,
             clip_store: &mut scene.clip_store,
             resource_cache,
             gpu_cache,
             transforms: transform_palette,
             segment_builder: SegmentBuilder::new(),
-            surfaces,
+            surfaces: &mut self.surfaces,
             dirty_region_stack: Vec::new(),
             composite_state,
         };
 
         frame_state
             .surfaces
             .first_mut()
             .unwrap()
@@ -522,16 +530,18 @@ impl FrameBuilder {
         scratch: &mut PrimitiveScratchBuffer,
         render_task_counters: &mut RenderTaskGraphCounters,
         debug_flags: DebugFlags,
         tile_cache_logger: &mut TileCacheLogger,
     ) -> Frame {
         profile_scope!("build");
         profile_marker!("BuildFrame");
 
+        self.surfaces.clear();
+
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters
             .total_primitives
             .set(scene.prim_store.prim_count());
         resource_profile.content_slices.set(scene.content_slice_count);
         resource_cache.begin_frame(stamp);
         gpu_cache.begin_frame(stamp);
 
@@ -544,17 +554,16 @@ impl FrameBuilder {
         );
         let mut transform_palette = scene.spatial_tree.build_transform_palette();
         scene.clip_store.clear_old_instances();
 
         let mut render_tasks = RenderTaskGraph::new(
             stamp.frame_id(),
             render_task_counters,
         );
-        let mut surfaces = Vec::new();
 
         let output_size = scene.output_rect.size.to_i32();
         let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
 
         // Determine if we will draw this frame with picture caching enabled. This depends on:
         // (1) If globally enabled when WR was initialized
         // (2) If current debug flags allow picture caching
         // (3) Whether we are currently pinch zooming
@@ -582,17 +591,16 @@ impl FrameBuilder {
             resource_cache,
             gpu_cache,
             &mut render_tasks,
             &mut profile_counters,
             global_device_pixel_scale,
             scene_properties,
             &mut transform_palette,
             data_stores,
-            &mut surfaces,
             scratch,
             debug_flags,
             &mut resource_profile.texture_cache,
             &mut composite_state,
             tile_cache_logger,
         );
 
         let mut passes;
@@ -620,17 +628,17 @@ impl FrameBuilder {
                     prim_store: &scene.prim_store,
                     resource_cache,
                     use_dual_source_blending,
                     use_advanced_blending: scene.config.gpu_supports_advanced_blend,
                     break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
                     batch_lookback_count: scene.config.batch_lookback_count,
                     spatial_tree: &scene.spatial_tree,
                     data_stores,
-                    surfaces: &surfaces,
+                    surfaces: &self.surfaces,
                     scratch,
                     screen_world_rect,
                     globals: &self.globals,
                 };
 
                 build_render_pass(
                     pass,
                     &mut ctx,
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -1135,16 +1135,20 @@ impl RenderBackend {
                 // that we risk crashing if we don't do something about it.
                 // The advantage of clearing the cache completely is that it gets rid of any
                 // remaining fragmentation that could have persisted if we kept around the most
                 // recently used resources.
                 self.resource_cache.clear(ClearCache::all());
 
                 self.gpu_cache.clear();
 
+                for (_, doc) in &mut self.documents {
+                    doc.frame_builder.memory_pressure();
+                }
+
                 let resource_updates = self.resource_cache.pending_updates();
                 let msg = ResultMsg::UpdateResources {
                     resource_updates,
                     memory_pressure: true,
                 };
                 self.result_tx.send(msg).unwrap();
                 self.notifier.wake_up();
             }