Bug 1606251 - Fix invalidation for elements with inflation factors. r=kvark
authorGlenn Watson <git@intuitionlibrary.com>
Thu, 30 Jan 2020 09:44:45 +0000
changeset 512116 784fdbec47d2351b211f1fceae1d68974ea21798
parent 512115 236b8297fd5d66bebcb478e4dffe5ad7d2376a4b
child 512117 aed68d2d967db45a05cba610f7783f773864264d
push id37072
push usercsabou@mozilla.com
push dateThu, 30 Jan 2020 15:44:43 +0000
treeherdermozilla-central@f97c48da9cee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1606251
milestone74.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 1606251 - Fix invalidation for elements with inflation factors. r=kvark Differential Revision: https://phabricator.services.mozilla.com/D61261
gfx/wr/webrender/src/frame_builder.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store/mod.rs
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, DebugFlags, DocumentLayer, FontRenderMode, PremultipliedColorF};
 use api::units::*;
 use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
-use crate::clip::{ClipStore, ClipChainStack};
+use crate::clip::{ClipStore, ClipChainStack, ClipDataHandle};
 use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, CoordinateSystemId};
 use crate::composite::{CompositorKind, CompositeState};
 use crate::debug_render::DebugItem;
 use crate::gpu_cache::{GpuCache, GpuCacheHandle};
 use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
 use crate::gpu_types::TransformData;
 use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex};
 use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion};
@@ -127,16 +127,35 @@ pub struct FrameVisibilityState<'a> {
     pub gpu_cache: &'a mut GpuCache,
     pub scratch: &'a mut PrimitiveScratchBuffer,
     pub tile_cache: Option<Box<TileCacheInstance>>,
     pub retained_tiles: &'a mut RetainedTiles,
     pub data_stores: &'a mut DataStores,
     pub clip_chain_stack: ClipChainStack,
     pub render_tasks: &'a mut RenderTaskGraph,
     pub composite_state: &'a mut CompositeState,
+    /// A stack of currently active off-screen surfaces during the
+    /// visibility frame traversal.
+    pub surface_stack: Vec<SurfaceIndex>,
+}
+
+impl<'a> FrameVisibilityState<'a> {
+    pub fn push_surface(
+        &mut self,
+        surface_index: SurfaceIndex,
+        shared_clips: &[ClipDataHandle]
+    ) {
+        self.surface_stack.push(surface_index);
+        self.clip_chain_stack.push_surface(shared_clips);
+    }
+
+    pub fn pop_surface(&mut self) {
+        self.surface_stack.pop().unwrap();
+        self.clip_chain_stack.pop_surface();
+    }
 }
 
 pub struct FrameBuildingContext<'a> {
     pub global_device_pixel_scale: DevicePixelScale,
     pub scene_properties: &'a SceneProperties,
     pub global_screen_world_rect: WorldRect,
     pub spatial_tree: &'a SpatialTree,
     pub max_local_clip: LayoutRect,
@@ -338,16 +357,19 @@ impl FrameBuilder {
                 clip_store: &mut scene.clip_store,
                 scratch,
                 tile_cache: None,
                 retained_tiles: &mut retained_tiles,
                 data_stores,
                 clip_chain_stack: ClipChainStack::new(),
                 render_tasks,
                 composite_state,
+                /// Try to avoid allocating during frame traversal - it's unlikely to have a
+                /// surface stack depth of > 16 in most cases.
+                surface_stack: Vec::with_capacity(16),
             };
 
             scene.prim_store.update_visibility(
                 scene.root_pic_index,
                 ROOT_SURFACE_INDEX,
                 &global_screen_world_rect,
                 &visibility_context,
                 &mut visibility_state,
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -2294,63 +2294,89 @@ impl TileCacheInstance {
 
     /// Update the dependencies for each tile for a given primitive instance.
     pub fn update_prim_dependencies(
         &mut self,
         prim_instance: &PrimitiveInstance,
         prim_spatial_node_index: SpatialNodeIndex,
         prim_clip_chain: Option<&ClipChainInstance>,
         local_prim_rect: LayoutRect,
-        spatial_tree: &SpatialTree,
+        frame_context: &FrameVisibilityContext,
         data_stores: &DataStores,
         clip_store: &ClipStore,
         pictures: &[PicturePrimitive],
         resource_cache: &ResourceCache,
         opacity_binding_store: &OpacityBindingStorage,
         image_instances: &ImageInstanceStorage,
-        surface_index: SurfaceIndex,
-        surface_spatial_node_index: SpatialNodeIndex,
+        surface_stack: &[SurfaceIndex],
     ) -> bool {
+        // This primitive exists on the last element on the current surface stack.
+        let prim_surface_index = *surface_stack.last().unwrap();
+
         // If the primitive is completely clipped out by the clip chain, there
         // is no need to add it to any primitive dependencies.
         let prim_clip_chain = match prim_clip_chain {
             Some(prim_clip_chain) => prim_clip_chain,
             None => return false,
         };
 
         self.map_local_to_surface.set_target_spatial_node(
             prim_spatial_node_index,
-            spatial_tree,
+            frame_context.spatial_tree,
         );
 
         // Map the primitive local rect into picture space.
         let prim_rect = match self.map_local_to_surface.map(&local_prim_rect) {
             Some(rect) => rect,
             None => return false,
         };
 
         // If the rect is invalid, no need to create dependencies.
         if prim_rect.size.is_empty_or_negative() {
             return false;
         }
 
         // If the primitive is directly drawn onto this picture cache surface, then
         // the pic_clip_rect is in the same space. If not, we need to map it from
         // the surface space into the picture cache space.
-        let on_picture_surface = surface_index == self.surface_index;
+        let on_picture_surface = prim_surface_index == self.surface_index;
         let pic_clip_rect = if on_picture_surface {
             prim_clip_chain.pic_clip_rect
         } else {
-            self.map_child_pic_to_surface.set_target_spatial_node(
-                surface_spatial_node_index,
-                spatial_tree,
-            );
-            self.map_child_pic_to_surface
-                .map(&prim_clip_chain.pic_clip_rect)
-                .expect("bug: unable to map clip rect to picture cache space")
+            // We want to get the rect in the tile cache surface space that this primitive
+            // occupies, in order to enable correct invalidation regions. Each surface
+            // that exists in the chain between this primitive and the tile cache surface
+            // may have an arbitrary inflation factor (for example, in the case of a series
+            // of nested blur elements). To account for this, step through the current
+            // surface stack, mapping the primitive rect into each surface space, including
+            // the inflation factor from each intermediate surface.
+            let mut current_pic_clip_rect = prim_clip_chain.pic_clip_rect;
+            let mut current_spatial_node_index = frame_context
+                .surfaces[prim_surface_index.0]
+                .surface_spatial_node_index;
+
+            for surface_index in surface_stack.iter().rev() {
+                let surface = &frame_context.surfaces[surface_index.0];
+
+                let map_local_to_surface = SpaceMapper::new_with_target(
+                    surface.surface_spatial_node_index,
+                    current_spatial_node_index,
+                    surface.rect,
+                    frame_context.spatial_tree,
+                );
+
+                current_pic_clip_rect = map_local_to_surface
+                    .map(&current_pic_clip_rect)
+                    .expect("bug: unable to map")
+                    .inflate(surface.inflation_factor, surface.inflation_factor);
+
+                current_spatial_node_index = surface.surface_spatial_node_index;
+            }
+
+            current_pic_clip_rect
         };
 
         // Get the tile coordinates in the picture space.
         let (p0, p1) = self.get_tile_coords_for_rect(&pic_clip_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 {
@@ -2526,19 +2552,19 @@ impl TileCacheInstance {
                     // primitive as an opaque backdrop rect. Several of these are conservative
                     // checks and could be relaxed in future. However, these checks
                     // are quick and capture the common cases of background rects and images.
                     // Specifically, we currently require:
                     //  - The primitive is on the main picture cache surface.
                     //  - Same coord system as picture cache (ensures rects are axis-aligned).
                     //  - No clip masks exist.
                     let same_coord_system = {
-                        let prim_spatial_node = &spatial_tree
+                        let prim_spatial_node = &frame_context.spatial_tree
                             .spatial_nodes[prim_spatial_node_index.0 as usize];
-                        let surface_spatial_node = &spatial_tree
+                        let surface_spatial_node = &frame_context.spatial_tree
                             .spatial_nodes[self.spatial_node_index.0 as usize];
 
                         prim_spatial_node.coordinate_system_id == surface_spatial_node.coordinate_system_id
                     };
 
                     same_coord_system && on_picture_surface
                 }
             };
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -1904,22 +1904,22 @@ impl PrimitiveStore {
                         PictureRect::from_untyped(&pic.estimated_local_rect.to_untyped()),
                         surface_index,
                         frame_context,
                         frame_state,
                     );
 
                     // Push a new surface, supplying the list of clips that should be
                     // ignored, since they are handled by clipping when drawing this surface.
-                    frame_state.clip_chain_stack.push_surface(&tile_cache.shared_clips);
+                    frame_state.push_surface(surface_index, &tile_cache.shared_clips);
                     frame_state.tile_cache = Some(tile_cache);
                 }
                 _ => {
                     if is_composite {
-                        frame_state.clip_chain_stack.push_surface(&[]);
+                        frame_state.push_surface(surface_index, &[]);
                     }
                 }
             }
 
             (prim_list, surface_index, pic.apply_local_clip_rect, world_culling_rect, is_composite)
         };
 
         let surface = &frame_context.surfaces[surface_index.0 as usize];
@@ -2128,25 +2128,24 @@ impl PrimitiveStore {
                         );
 
                     if let Some(ref mut tile_cache) = frame_state.tile_cache {
                         if !tile_cache.update_prim_dependencies(
                             prim_instance,
                             cluster.spatial_node_index,
                             clip_chain.as_ref(),
                             prim_local_rect,
-                            frame_context.spatial_tree,
+                            frame_context,
                             frame_state.data_stores,
                             frame_state.clip_store,
                             &self.pictures,
                             frame_state.resource_cache,
                             &self.opacity_bindings,
                             &self.images,
-                            surface_index,
-                            surface.surface_spatial_node_index,
+                            &frame_state.surface_stack,
                         ) {
                             prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
                             // Ensure the primitive clip is popped - perhaps we can use
                             // some kind of scope to do this automatically in future.
                             frame_state.clip_chain_stack.pop_clip();
                             continue;
                         }
                     }
@@ -2287,17 +2286,17 @@ impl PrimitiveStore {
                         frame_state,
                     );
                 }
             }
         }
 
         // Similar to above, pop either the clip chain or root entry off the current clip stack.
         if is_composite {
-            frame_state.clip_chain_stack.pop_surface();
+            frame_state.pop_surface();
         }
 
         let pic = &mut self.pictures[pic_index.0];
         pic.prim_list = prim_list;
 
         // If the local rect changed (due to transforms in child primitives) then
         // invalidate the GPU cache location to re-upload the new local rect
         // and stretch size. Drop shadow filters also depend on the local rect