Bug 1511245 - Update webrender to commit d771bae9f824769c73419fdc3ccffa2bdc47c3e4 (WR PR #3368). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Fri, 30 Nov 2018 02:07:23 +0000
changeset 505457 1a7497dad85e5674cf508b93f22bb0301e013074
parent 505456 7725c317d72320510a3c792a60185179a4183632
child 505458 f4cb63cf5e47447de99ca1d236696a3a63e09629
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1511245
milestone65.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 1511245 - Update webrender to commit d771bae9f824769c73419fdc3ccffa2bdc47c3e4 (WR PR #3368). r=kats https://github.com/servo/webrender/pull/3368 Differential Revision: https://phabricator.services.mozilla.com/D13497
gfx/webrender_bindings/revision.txt
gfx/wr/webrender/src/clip_scroll_tree.rs
gfx/wr/webrender/src/display_list_flattener.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/spatial_node.rs
gfx/wr/webrender/src/util.rs
gfx/wr/wrench/reftests/clip/fixed-position-clipping-ref.yaml
gfx/wr/wrench/reftests/clip/fixed-position-clipping.yaml
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-a48f34e09901a62ab12164c8e539011ed04d41bd
+d771bae9f824769c73419fdc3ccffa2bdc47c3e4
--- a/gfx/wr/webrender/src/clip_scroll_tree.rs
+++ b/gfx/wr/webrender/src/clip_scroll_tree.rs
@@ -5,17 +5,17 @@
 use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D};
 use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation};
 use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint};
 use gpu_types::TransformPalette;
 use internal_types::{FastHashMap, FastHashSet};
 use print_tree::{PrintTree, PrintTreePrinter};
 use scene::SceneProperties;
 use smallvec::SmallVec;
-use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo};
+use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo, ScrollFrameKind};
 use util::{LayoutToWorldFastTransform, ScaleOffset};
 
 pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
 
 /// An id that identifies coordinate systems in the ClipScrollTree. Each
 /// coordinate system has an id and those ids will be shared when the coordinates
 /// system are the same or are in the same axis-aligned space. This allows
 /// for optimizing mask generation.
@@ -321,24 +321,26 @@ impl ClipScrollTree {
     pub fn add_scroll_frame(
         &mut self,
         parent_index: SpatialNodeIndex,
         external_id: Option<ExternalScrollId>,
         pipeline_id: PipelineId,
         frame_rect: &LayoutRect,
         content_size: &LayoutSize,
         scroll_sensitivity: ScrollSensitivity,
+        frame_kind: ScrollFrameKind,
     ) -> SpatialNodeIndex {
         let node = SpatialNode::new_scroll_frame(
             pipeline_id,
             parent_index,
             external_id,
             frame_rect,
             content_size,
             scroll_sensitivity,
+            frame_kind,
         );
         self.add_spatial_node(node)
     }
 
     pub fn add_reference_frame(
         &mut self,
         parent_index: Option<SpatialNodeIndex>,
         source_transform: Option<PropertyBinding<LayoutTransform>>,
@@ -430,16 +432,63 @@ impl ClipScrollTree {
         }
     }
 
     pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
         if !self.spatial_nodes.is_empty() {
             self.print_node(self.root_reference_frame_index(), pt);
         }
     }
+
+    /// Return true if this is a guaranteed identity transform. This
+    /// is conservative, it assumes not identity if a property
+    /// binding animation, or scroll frame is found, for example.
+    pub fn node_is_identity(
+        &self,
+        spatial_node_index: SpatialNodeIndex,
+    ) -> bool {
+        let mut current = spatial_node_index;
+
+        while current != ROOT_SPATIAL_NODE_INDEX {
+            let node = &self.spatial_nodes[current.0];
+
+            match node.node_type {
+                SpatialNodeType::ReferenceFrame(ref info) => {
+                    if !info.source_perspective.is_identity() {
+                        return false;
+                    }
+
+                    match info.source_transform {
+                        PropertyBinding::Value(transform) => {
+                            if transform != LayoutTransform::identity() {
+                                return false;
+                            }
+                        }
+                        PropertyBinding::Binding(..) => {
+                            // Assume not identity since it may change with animation.
+                            return false;
+                        }
+                    }
+                }
+                SpatialNodeType::ScrollFrame(ref info) => {
+                    // Assume not identity since it may change with scrolling.
+                    if let ScrollFrameKind::Explicit = info.frame_kind {
+                        return false;
+                    }
+                }
+                SpatialNodeType::StickyFrame(..) => {
+                    // Assume not identity since it may change with scrolling.
+                    return false;
+                }
+            }
+            current = node.parent.unwrap();
+        }
+
+        true
+    }
 }
 
 #[cfg(test)]
 fn add_reference_frame(
     cst: &mut ClipScrollTree,
     parent: Option<SpatialNodeIndex>,
     transform: LayoutTransform,
     origin_in_parent_reference_frame: LayoutVector2D,
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -25,17 +25,17 @@ use picture::{Picture3DContext, PictureC
 use prim_store::{PrimitiveInstance, PrimitiveDataInterner, PrimitiveKeyKind, RadialGradientParams};
 use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, GradientStopKey, NinePatchDescriptor};
 use prim_store::{PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats, LineDecorationCacheKey};
 use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, get_line_decoration_sizes};
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, ScenePipeline, StackingContextHelpers};
 use scene_builder::DocumentResources;
-use spatial_node::{StickyFrameInfo};
+use spatial_node::{StickyFrameInfo, ScrollFrameKind};
 use std::{f32, mem};
 use std::collections::vec_deque::VecDeque;
 use tiling::{CompositeOps};
 use util::{MaxRect, VecHelper};
 
 #[derive(Debug, Copy, Clone)]
 struct ClipNode {
     id: ClipChainId,
@@ -377,16 +377,17 @@ impl<'a> DisplayListFlattener<'a> {
         self.add_scroll_frame(
             info.scroll_frame_id,
             info.clip_id,
             info.external_id,
             pipeline_id,
             &frame_rect,
             &content_rect.size,
             info.scroll_sensitivity,
+            ScrollFrameKind::Explicit,
         );
     }
 
     fn flatten_reference_frame(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
         item: &DisplayItemRef,
@@ -494,16 +495,17 @@ impl<'a> DisplayListFlattener<'a> {
         self.add_scroll_frame(
             ClipId::root_scroll_node(iframe_pipeline_id),
             ClipId::root_reference_frame(iframe_pipeline_id),
             Some(ExternalScrollId(0, iframe_pipeline_id)),
             iframe_pipeline_id,
             &iframe_rect,
             &pipeline.content_size,
             ScrollSensitivity::ScriptAndInputEvents,
+            ScrollFrameKind::PipelineRoot,
         );
 
         self.flatten_root(
             pipeline,
             &iframe_rect.size,
         );
 
         self.pipeline_clip_chain_stack.pop();
@@ -1073,16 +1075,35 @@ impl<'a> DisplayListFlattener<'a> {
             transform_style,
             context_3d,
         });
     }
 
     pub fn pop_stacking_context(&mut self) {
         let stacking_context = self.sc_stack.pop().unwrap();
 
+        // If we encounter a stacking context that is effectively a no-op, then instead
+        // of creating a picture, just append the primitive list to the parent stacking
+        // context as a short cut. This serves two purposes:
+        // (a) It's an optimization to reduce picture count and allocations, as display lists
+        //     often contain a lot of these stacking contexts that don't require pictures or
+        //     off-screen surfaces.
+        // (b) It's useful for the initial version of picture caching in gecko, by enabling
+        //     is to just look for interesting scroll roots on the root stacking context,
+        //     without having to consider cuts at stacking context boundaries.
+        if let Some(parent_sc) = self.sc_stack.last_mut() {
+            if stacking_context.is_redundant(
+                parent_sc,
+                self.clip_scroll_tree,
+            ) {
+                parent_sc.primitives.extend(stacking_context.primitives);
+                return;
+            }
+        }
+
         // An arbitrary large clip rect. For now, we don't
         // specify a clip specific to the stacking context.
         // However, now that they are represented as Picture
         // primitives, we can apply any kind of clip mask
         // to them, as for a normal primitive. This is needed
         // to correctly handle some CSS cases (see #1957).
         let max_clip = LayoutRect::max_rect();
 
@@ -1339,16 +1360,17 @@ impl<'a> DisplayListFlattener<'a> {
         self.add_scroll_frame(
             ClipId::root_scroll_node(pipeline_id),
             ClipId::root_reference_frame(pipeline_id),
             Some(ExternalScrollId(0, pipeline_id)),
             pipeline_id,
             &LayoutRect::new(LayoutPoint::zero(), *viewport_size),
             content_size,
             ScrollSensitivity::ScriptAndInputEvents,
+            ScrollFrameKind::PipelineRoot,
         );
     }
 
     pub fn add_clip_node<I>(
         &mut self,
         new_node_id: ClipId,
         parent: &ClipAndScrollInfo,
         clip_region: ClipRegion<I>,
@@ -1450,25 +1472,27 @@ impl<'a> DisplayListFlattener<'a> {
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
         external_id: Option<ExternalScrollId>,
         pipeline_id: PipelineId,
         frame_rect: &LayoutRect,
         content_size: &LayoutSize,
         scroll_sensitivity: ScrollSensitivity,
+        frame_kind: ScrollFrameKind,
     ) -> SpatialNodeIndex {
         let parent_node_index = self.id_to_index_mapper.get_spatial_node_index(parent_id);
         let node_index = self.clip_scroll_tree.add_scroll_frame(
             parent_node_index,
             external_id,
             pipeline_id,
             frame_rect,
             content_size,
             scroll_sensitivity,
+            frame_kind,
         );
         self.id_to_index_mapper.map_spatial_node(new_node_id, node_index);
         self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
         node_index
     }
 
     pub fn push_shadow(
         &mut self,
@@ -2180,16 +2204,61 @@ struct FlattenedStackingContext {
 }
 
 impl FlattenedStackingContext {
     /// Return true if the stacking context has a valid preserve-3d property
     pub fn is_3d(&self) -> bool {
         self.transform_style == TransformStyle::Preserve3D && self.composite_ops.is_empty()
     }
 
+    /// Return true if the stacking context isn't needed.
+    pub fn is_redundant(
+        &self,
+        parent: &FlattenedStackingContext,
+        clip_scroll_tree: &ClipScrollTree,
+    ) -> bool {
+        // Any 3d context is required
+        if let Picture3DContext::In { .. } = self.context_3d {
+            return false;
+        }
+
+        // If there are filters / mix-blend-mode
+        if !self.composite_ops.is_empty() {
+            return false;
+        }
+
+        // If backface visibility is different
+        if self.primitive_data_handle.uid() != parent.primitive_data_handle.uid() {
+            return false;
+        }
+
+        // If rasterization space is different
+        if self.requested_raster_space != parent.requested_raster_space {
+            return false;
+        }
+
+        // If different clipp chains
+        if self.clip_chain_id != parent.clip_chain_id {
+            return false;
+        }
+
+        // If need to isolate in surface due to clipping / mix-blend-mode
+        if self.should_isolate {
+            return false;
+        }
+
+        // If represents a transform, it may affect backface visibility of children
+        if !clip_scroll_tree.node_is_identity(self.spatial_node_index) {
+            return false;
+        }
+
+        // It is redundant!
+        true
+    }
+
     /// For a Preserve3D context, cut the sequence of the immediate flat children
     /// recorded so far and generate a picture from them.
     pub fn cut_flat_item_sequence(
         &mut self,
         prim_store: &mut PrimitiveStore,
         prim_interner: &PrimitiveDataInterner,
         clip_store: &ClipStore,
     ) -> Option<PrimitiveInstance> {
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -770,17 +770,17 @@ impl TileCache {
 
                 // Try to reuse cached tiles from the previous scene in this new
                 // scene, if possible.
                 if !resource_cache.texture_cache.is_allocated(&tile.handle) {
                     // See if we have a retained tile from last scene that matches the
                     // exact content of this tile.
                     if let Some(handle) = retained_tiles.remove(&tile.descriptor) {
                         // Only use if not evicted from texture cache in the meantime.
-                        if !resource_cache.texture_cache.request(&handle, gpu_cache) {
+                        if resource_cache.texture_cache.is_allocated(&handle) {
                             // We found a matching tile from the previous scene, so use it!
                             tile.handle = handle;
                             tile.is_valid = true;
                             // We know that the hash key of the descriptor validates that
                             // the local transforms in this tile exactly match the value
                             // of the current relative transforms needed for this tile,
                             // so we can mark those transforms as valid to avoid the
                             // retained tile being invalidated below.
@@ -842,22 +842,29 @@ impl TileCache {
                 }
 
                 // Check if this tile is actually visible.
                 let tile_world_rect = world_mapper
                     .map(&tile_rect)
                     .expect("bug: unable to map tile to world coords");
                 tile.is_visible = frame_context.screen_world_rect.intersects(&tile_world_rect);
 
-                // If we have an invalid tile, which is also visible, add it to the
-                // dirty rect we will need to draw.
-                if !tile.is_valid && tile.is_visible && tile.in_use {
-                    dirty_rect = dirty_rect.union(&tile_rect);
-                    tile_offset.x = tile_offset.x.min(x);
-                    tile_offset.y = tile_offset.y.min(y);
+                if tile.is_visible && tile.in_use {
+                    // Ensure we request the texture cache handle for this tile
+                    // each frame it will be used so the texture cache doesn't
+                    // decide to evict tiles that we currently want to use.
+                    resource_cache.texture_cache.request(&tile.handle, gpu_cache);
+
+                    // If we have an invalid tile, which is also visible, add it to the
+                    // dirty rect we will need to draw.
+                    if !tile.is_valid {
+                        dirty_rect = dirty_rect.union(&tile_rect);
+                        tile_offset.x = tile_offset.x.min(x);
+                        tile_offset.y = tile_offset.y.min(y);
+                    }
                 }
             }
         }
 
         self.dirty_region = if dirty_rect.is_empty() {
             None
         } else {
             let dirty_world_rect = world_mapper.map(&dirty_rect).expect("todo");
--- a/gfx/wr/webrender/src/spatial_node.rs
+++ b/gfx/wr/webrender/src/spatial_node.rs
@@ -91,25 +91,27 @@ impl SpatialNode {
 
     pub fn new_scroll_frame(
         pipeline_id: PipelineId,
         parent_index: SpatialNodeIndex,
         external_id: Option<ExternalScrollId>,
         frame_rect: &LayoutRect,
         content_size: &LayoutSize,
         scroll_sensitivity: ScrollSensitivity,
+        frame_kind: ScrollFrameKind,
     ) -> Self {
         let node_type = SpatialNodeType::ScrollFrame(ScrollFrameInfo::new(
                 *frame_rect,
                 scroll_sensitivity,
                 LayoutSize::new(
                     (content_size.width - frame_rect.size.width).max(0.0),
                     (content_size.height - frame_rect.size.height).max(0.0)
                 ),
                 external_id,
+                frame_kind,
             )
         );
 
         Self::new(pipeline_id, Some(parent_index), node_type)
     }
 
     pub fn new_reference_frame(
         parent_index: Option<SpatialNodeIndex>,
@@ -553,16 +555,24 @@ impl SpatialNode {
     pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool {
         match self.node_type {
             SpatialNodeType::ScrollFrame(info) if info.external_id == Some(external_id) => true,
             _ => false,
         }
     }
 }
 
+/// Defines whether we have an implicit scroll frame for a pipeline root,
+/// or an explicitly defined scroll frame from the display list.
+#[derive(Copy, Clone, Debug)]
+pub enum ScrollFrameKind {
+    PipelineRoot,
+    Explicit,
+}
+
 #[derive(Copy, Clone, Debug)]
 pub struct ScrollFrameInfo {
     /// The rectangle of the viewport of this scroll frame. This is important for
     /// positioning of items inside child StickyFrames.
     pub viewport_rect: LayoutRect,
 
     pub offset: LayoutVector2D,
     pub scroll_sensitivity: ScrollSensitivity,
@@ -570,32 +580,42 @@ pub struct ScrollFrameInfo {
     /// Amount that this ScrollFrame can scroll in both directions.
     pub scrollable_size: LayoutSize,
 
     /// An external id to identify this scroll frame to API clients. This
     /// allows setting scroll positions via the API without relying on ClipsIds
     /// which may change between frames.
     pub external_id: Option<ExternalScrollId>,
 
+    /// Stores whether this is a scroll frame added implicitly by WR when adding
+    /// a pipeline (either the root or an iframe). We need to exclude these
+    /// when searching for scroll roots we care about for picture caching.
+    /// TODO(gw): I think we can actually completely remove the implicit
+    ///           scroll frame being added by WR, and rely on the embedder
+    ///           to define scroll frames. However, that involves API changes
+    ///           so we will use this as a temporary hack!
+    pub frame_kind: ScrollFrameKind,
 }
 
 /// Manages scrolling offset.
 impl ScrollFrameInfo {
     pub fn new(
         viewport_rect: LayoutRect,
         scroll_sensitivity: ScrollSensitivity,
         scrollable_size: LayoutSize,
         external_id: Option<ExternalScrollId>,
+        frame_kind: ScrollFrameKind,
     ) -> ScrollFrameInfo {
         ScrollFrameInfo {
             viewport_rect,
             offset: LayoutVector2D::zero(),
             scroll_sensitivity,
             scrollable_size,
             external_id,
+            frame_kind,
         }
     }
 
     pub fn sensitive_to_input_events(&self) -> bool {
         match self.scroll_sensitivity {
             ScrollSensitivity::ScriptAndInputEvents => true,
             ScrollSensitivity::Script => false,
         }
@@ -606,16 +626,17 @@ impl ScrollFrameInfo {
         old_scroll_info: &ScrollFrameInfo
     ) -> ScrollFrameInfo {
         ScrollFrameInfo {
             viewport_rect: self.viewport_rect,
             offset: old_scroll_info.offset,
             scroll_sensitivity: self.scroll_sensitivity,
             scrollable_size: self.scrollable_size,
             external_id: self.external_id,
+            frame_kind: self.frame_kind,
         }
     }
 }
 
 /// Contains information about reference frames.
 #[derive(Copy, Clone, Debug)]
 pub struct ReferenceFrameInfo {
     /// The source transform and perspective matrices provided by the stacking context
--- a/gfx/wr/webrender/src/util.rs
+++ b/gfx/wr/webrender/src/util.rs
@@ -577,16 +577,28 @@ impl<Src, Dst> FastTransform<Src, Dst> {
 
     pub fn is_invertible(&self) -> bool {
         match *self {
             FastTransform::Offset(..) => true,
             FastTransform::Transform { ref inverse, .. } => inverse.is_some(),
         }
     }
 
+    /// Return true if this is an identity transform
+    pub fn is_identity(&self)-> bool {
+        match *self {
+            FastTransform::Offset(offset) => {
+                offset == TypedVector2D::zero()
+            }
+            FastTransform::Transform { ref transform, .. } => {
+                *transform == TypedTransform3D::identity()
+            }
+        }
+    }
+
     #[inline(always)]
     pub fn pre_mul<NewSrc>(
         &self,
         other: &FastTransform<NewSrc, Src>
     ) -> FastTransform<NewSrc, Dst> {
         match (self, other) {
             (&FastTransform::Offset(ref offset), &FastTransform::Offset(ref other_offset)) => {
                 let offset = TypedVector2D::from_untyped(&offset.to_untyped());
--- a/gfx/wr/wrench/reftests/clip/fixed-position-clipping-ref.yaml
+++ b/gfx/wr/wrench/reftests/clip/fixed-position-clipping-ref.yaml
@@ -1,15 +1,15 @@
 ---
 root:
   items:
     -
       bounds: [10, 10, 100, 100]
       clip-rect: [10, 10, 100, 100]
       type: rect
-      color: 0 256 0 1.0
+      color: 0 255 0 1.0
     -
       bounds: [110, 10, 100, 100]
       clip-rect: [110, 10, 100, 100]
       type: rect
-      color: 0 256 0 1.0
+      color: 0 255 0 1.0
   id: [0, 1]
 pipelines: []
--- a/gfx/wr/wrench/reftests/clip/fixed-position-clipping.yaml
+++ b/gfx/wr/wrench/reftests/clip/fixed-position-clipping.yaml
@@ -17,17 +17,17 @@ root:
         clip-and-scroll: root-reference-frame
         type: stacking-context
         items:
           -
             bounds: [0, 0, 100, 100]
             clip-rect: [0, 0, 100, 100]
             clip-and-scroll: root-reference-frame
             type: rect
-            color: 0 256 0 1.0
+            color: 0 255 0 1.0
     # The same test as above, except this time the stacking context also starts its
     # own reference frame.
     -
       clip-rect: [115, 15, 30, 30]
       type: scroll-frame
       content-size: [60, 60]
       bounds: [115, 15, 30, 30]
       items:
@@ -39,11 +39,11 @@ root:
         type: stacking-context
         transform: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
         items:
           -
             bounds: [0, 0, 100, 100]
             clip-rect: [0, 0, 100, 100]
             clip-and-scroll: 4
             type: rect
-            color: 0 256 0 1.0
+            color: 0 255 0 1.0
   id: [0, 1]
 pipelines: []