Bug 1521420 - Skip some redundant work during picture caching updates. r=jrmuizel
authorGlenn Watson <github@intuitionlibrary.com>
Tue, 22 Jan 2019 02:10:01 +0000
changeset 454761 c25d82a5b24c0b66321417121f6e1f80d8e62004
parent 454760 989238f82c8949cbdb9a664d93f9313c0b5ab8e2
child 454785 62fee61d444bc1ad93d24cc3f27f62831100086a
push id76513
push usergwatson@mozilla.com
push dateTue, 22 Jan 2019 02:50:09 +0000
treeherderautoland@c25d82a5b24c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1521420
milestone66.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 1521420 - Skip some redundant work during picture caching updates. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D17085
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store/mod.rs
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -530,22 +530,16 @@ pub struct TileCache {
     map_local_to_world: SpaceMapper<LayoutPixel, WorldPixel>,
     /// A list of tiles to draw during batching.
     pub tiles_to_draw: Vec<TileIndex>,
     /// List of opacity bindings, with some extra information
     /// about whether they changed since last frame.
     opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>,
     /// The current dirty region tracker for this picture.
     pub dirty_region: DirtyRegion,
-    /// If true, we need to update the prim dependencies, due
-    /// to relative transforms changing. The dependencies are
-    /// stored in each tile, and are a list of things that
-    /// force the tile to re-rasterize if they change (e.g.
-    /// images, transforms).
-    needs_update: bool,
     /// The current world reference point that tiles are created around.
     world_origin: WorldPoint,
     /// Current size of tiles in world units.
     world_tile_size: WorldSize,
     /// Current number of tiles in the allocated grid.
     tile_count: TileSize,
     /// The current scroll offset for this frame builder. Reset when
     /// a new scene arrives.
@@ -675,17 +669,16 @@ impl TileCache {
             tiles: Vec::new(),
             map_local_to_world: SpaceMapper::new(
                 ROOT_SPATIAL_NODE_INDEX,
                 WorldRect::zero(),
             ),
             tiles_to_draw: Vec::new(),
             opacity_bindings: FastHashMap::default(),
             dirty_region: DirtyRegion::new(),
-            needs_update: true,
             world_origin: WorldPoint::zero(),
             world_tile_size: WorldSize::zero(),
             tile_count: TileSize::zero(),
             scroll_offset: None,
             pending_blits: Vec::new(),
             world_bounding_rect: WorldRect::zero(),
             root_clip_rect: WorldRect::max_rect(),
             reference_prims,
@@ -921,20 +914,16 @@ impl TileCache {
                 self.tiles.push(tile);
             }
         }
 
         if !old_tiles.is_empty() {
             // TODO(gw): Should we explicitly drop the tile texture cache handles here?
         }
 
-        // TODO(gw): We don't actually need to update the prim dependencies each frame.
-        //           For common cases, such as only being one main scroll root, we could
-        //           detect this and skip the dependency update on scroll frames.
-        self.needs_update = true;
         self.world_bounding_rect = WorldRect::zero();
         self.root_clip_rect = WorldRect::max_rect();
 
         // Calculate the world space of the root clip node, that every primitive has
         // at the root of its clip chain (this is enforced by the per-pipeline-root
         // clip node added implicitly during display list flattening). Doing it once
         // here saves doing it for every primitive during update_prim_dependencies.
         let root_clip_chain_node = &frame_state
@@ -976,77 +965,60 @@ impl TileCache {
                     };
                     if changed {
                         tile.is_same_content = false;
                         break;
                     }
                 }
             }
 
-            if self.needs_update {
-                // Clear any dependencies so that when we rebuild them we
-                // can compare if the tile has the same content.
-                tile.clear();
-            }
+            // Clear any dependencies so that when we rebuild them we
+            // can compare if the tile has the same content.
+            tile.clear();
         }
     }
 
     /// Update the dependencies for each tile for a given primitive instance.
     pub fn update_prim_dependencies(
         &mut self,
         prim_instance: &PrimitiveInstance,
+        prim_rect: LayoutRect,
         clip_scroll_tree: &ClipScrollTree,
         data_stores: &DataStores,
         clip_chain_nodes: &[ClipChainNode],
         pictures: &[PicturePrimitive],
         resource_cache: &ResourceCache,
         opacity_binding_store: &OpacityBindingStorage,
         image_instances: &ImageInstanceStorage,
-    ) {
-        if !self.needs_update {
-            return;
-        }
-
+    ) -> bool {
         self.map_local_to_world.set_target_spatial_node(
             prim_instance.spatial_node_index,
             clip_scroll_tree,
         );
 
-        let prim_data = &data_stores.as_common_data(&prim_instance);
-
-        let prim_rect = match prim_instance.kind {
-            PrimitiveInstanceKind::Picture { pic_index, .. } => {
-                let pic = &pictures[pic_index.0];
-                pic.local_rect
-            }
-            _ => {
-                LayoutRect::new(
-                    prim_instance.prim_origin,
-                    prim_data.prim_size,
-                )
-            }
-        };
-
         // Map the primitive local rect into world space.
         let world_rect = match self.map_local_to_world.map(&prim_rect) {
             Some(rect) => rect,
-            None => {
-                return;
-            }
+            None => return false,
         };
 
         // If the rect is invalid, no need to create dependencies.
-        // TODO(gw): Need to handle pictures with filters here.
         if world_rect.size.width <= 0.0 || world_rect.size.height <= 0.0 {
-            return;
+            return false;
         }
 
         // Get the tile coordinates in the picture space.
         let (p0, p1) = self.get_tile_coords_for_rect(&world_rect);
 
+        // If the primitive is outside the tiling rects, it's known to not
+        // be visible.
+        if p0.x == p1.x || p0.y == p1.y {
+            return false;
+        }
+
         // Build the list of resources that this primitive has dependencies on.
         let mut opacity_bindings: SmallVec<[OpacityBinding; 4]> = SmallVec::new();
         let mut clip_chain_uids: SmallVec<[ItemUid; 8]> = SmallVec::new();
         let mut clip_vertices: SmallVec<[WorldPoint; 8]> = SmallVec::new();
         let mut image_keys: SmallVec<[ImageKey; 8]> = SmallVec::new();
         let mut clip_spatial_nodes = FastHashSet::default();
 
         // TODO(gw): We only care about world clip rects that don't have the main
@@ -1293,16 +1265,18 @@ impl TileCache {
                 for spatial_node_index in &clip_spatial_nodes {
                     tile.transforms.insert(*spatial_node_index);
                 }
                 for (world_rect, spatial_node_index) in &world_clips {
                     tile.potential_clips.insert(world_rect.clone(), *spatial_node_index);
                 }
             }
         }
+
+        true
     }
 
     /// Apply any updates after prim dependency updates. This applies
     /// any late tile invalidations, and sets up the dirty rect and
     /// set of tile blits.
     pub fn post_update(
         &mut self,
         resource_cache: &mut ResourceCache,
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -1914,16 +1914,33 @@ impl PrimitiveStore {
                         if prim_instance.is_chased() {
                             println!("\tculled for being out of the local clip rectangle: {:?}",
                                 prim_instance.local_clip_rect);
                         }
                         continue;
                     }
                 };
 
+                if let Some(ref mut tile_cache) = frame_state.tile_cache {
+                    if !tile_cache.update_prim_dependencies(
+                        prim_instance,
+                        prim_local_rect,
+                        frame_context.clip_scroll_tree,
+                        frame_state.data_stores,
+                        &frame_state.clip_store.clip_chain_nodes,
+                        &self.pictures,
+                        frame_state.resource_cache,
+                        &self.opacity_bindings,
+                        &self.images,
+                    ) {
+                        prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
+                        continue;
+                    }
+                }
+
                 let clip_chain = frame_state
                     .clip_store
                     .build_clip_chain_instance(
                         prim_instance,
                         local_rect,
                         prim_instance.local_clip_rect,
                         prim_context.spatial_node_index,
                         &map_local_to_surface,
@@ -1932,29 +1949,16 @@ impl PrimitiveStore {
                         frame_state.gpu_cache,
                         frame_state.resource_cache,
                         frame_context.device_pixel_scale,
                         &frame_context.screen_world_rect,
                         clip_node_collector.as_ref(),
                         &mut frame_state.data_stores.clip,
                     );
 
-                if let Some(ref mut tile_cache) = frame_state.tile_cache {
-                    tile_cache.update_prim_dependencies(
-                        prim_instance,
-                        frame_context.clip_scroll_tree,
-                        frame_state.data_stores,
-                        &frame_state.clip_store.clip_chain_nodes,
-                        &self.pictures,
-                        frame_state.resource_cache,
-                        &self.opacity_bindings,
-                        &self.images,
-                    );
-                }
-
                 let clip_chain = match clip_chain {
                     Some(clip_chain) => clip_chain,
                     None => {
                         if prim_instance.is_chased() {
                             println!("\tunable to build the clip chain, skipping");
                         }
                         prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
                         continue;