[Repaint from Layout Removal] Move layer repaint rects into a map
authorhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Aug 2017 21:03:03 +0000 (21:03 +0000)
committerhyatt@apple.com <hyatt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Aug 2017 21:03:03 +0000 (21:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175393

Reviewed by Zalan Bujtas.

Move the two repaint rects held by RenderLayer into a RenderLayerModelObject -> RepaintLayoutRects hash map.
RepaintLayoutRects is a new struct that holds both rects.

Eventually more objects than just self painting layers will be caching repaint rects, so this takes a first
step towards having a common cache for these repaint rects. In addition this change saves memory, since
layers that aren't self-painting no longer have empty repaint rects taking up space in RenderLayer.

* rendering/RenderBlockLineLayout.cpp:
(WebCore::RenderBlockFlow::layoutRunsAndFloats):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::RenderLayer):
(WebCore::RenderLayer::updateLayerPositions):
(WebCore::RenderLayer::repaintRectIncludingNonCompositingDescendants):
(WebCore::RenderLayer::computeRepaintRects):
(WebCore::RenderLayer::clearRepaintRects):
(WebCore::RenderLayer::updateLayerPositionsAfterScroll):
(WebCore::RenderLayer::scrollTo):
* rendering/RenderLayer.h:
* rendering/RenderLayerModelObject.cpp:
(WebCore::RepaintLayoutRects::RepaintLayoutRects):
(WebCore::RenderLayerModelObject::willBeDestroyed):
(WebCore::RenderLayerModelObject::destroyLayer):
(WebCore::RenderLayerModelObject::styleDidChange):
(WebCore::RenderLayerModelObject::hasRepaintLayoutRects):
(WebCore::RenderLayerModelObject::setRepaintLayoutRects):
(WebCore::RenderLayerModelObject::clearRepaintLayoutRects):
(WebCore::RenderLayerModelObject::repaintLayoutRects):
(WebCore::RenderLayerModelObject::computeRepaintLayoutRects):
* rendering/RenderLayerModelObject.h:
(WebCore::RepaintLayoutRects::RepaintLayoutRects):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220479 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderBlockLineLayout.cpp
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderLayer.h
Source/WebCore/rendering/RenderLayerModelObject.cpp
Source/WebCore/rendering/RenderLayerModelObject.h

index 1a4f457..aff53c2 100644 (file)
@@ -1,3 +1,41 @@
+2017-08-09  Dave Hyatt  <hyatt@apple.com>
+
+        [Repaint from Layout Removal] Move layer repaint rects into a map
+        https://bugs.webkit.org/show_bug.cgi?id=175393
+
+        Reviewed by Zalan Bujtas.
+
+        Move the two repaint rects held by RenderLayer into a RenderLayerModelObject -> RepaintLayoutRects hash map.
+        RepaintLayoutRects is a new struct that holds both rects.
+
+        Eventually more objects than just self painting layers will be caching repaint rects, so this takes a first
+        step towards having a common cache for these repaint rects. In addition this change saves memory, since
+        layers that aren't self-painting no longer have empty repaint rects taking up space in RenderLayer.
+
+        * rendering/RenderBlockLineLayout.cpp:
+        (WebCore::RenderBlockFlow::layoutRunsAndFloats):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::RenderLayer):
+        (WebCore::RenderLayer::updateLayerPositions):
+        (WebCore::RenderLayer::repaintRectIncludingNonCompositingDescendants):
+        (WebCore::RenderLayer::computeRepaintRects):
+        (WebCore::RenderLayer::clearRepaintRects):
+        (WebCore::RenderLayer::updateLayerPositionsAfterScroll):
+        (WebCore::RenderLayer::scrollTo):
+        * rendering/RenderLayer.h:
+        * rendering/RenderLayerModelObject.cpp:
+        (WebCore::RepaintLayoutRects::RepaintLayoutRects):
+        (WebCore::RenderLayerModelObject::willBeDestroyed):
+        (WebCore::RenderLayerModelObject::destroyLayer):
+        (WebCore::RenderLayerModelObject::styleDidChange):
+        (WebCore::RenderLayerModelObject::hasRepaintLayoutRects):
+        (WebCore::RenderLayerModelObject::setRepaintLayoutRects):
+        (WebCore::RenderLayerModelObject::clearRepaintLayoutRects):
+        (WebCore::RenderLayerModelObject::repaintLayoutRects):
+        (WebCore::RenderLayerModelObject::computeRepaintLayoutRects):
+        * rendering/RenderLayerModelObject.h:
+        (WebCore::RepaintLayoutRects::RepaintLayoutRects):
+
 2017-08-09  Sam Weinig  <sam@webkit.org>
 
         WTF::Function does not allow for reference / non-default constructible return types
index e2e3507..ac7cfbe 100644 (file)
@@ -1281,12 +1281,12 @@ void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool has
     // determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html.
     if (layoutState.isFullLayout() && hasInlineChild && !selfNeedsLayout()) {
         setNeedsLayout(MarkOnlyThis); // Mark as needing a full layout to force us to repaint.
-        if (!view().doingFullRepaint() && hasSelfPaintingLayer() && layer()->hasComputedRepaintRect()) {
+        if (!view().doingFullRepaint() && hasSelfPaintingLayer() && hasRepaintLayoutRects()) {
             // Because we waited until we were already inside layout to discover
             // that the block really needed a full layout, we missed our chance to repaint the layer
             // before layout started.  Luckily the layer has cached the repaint rect for its original
             // position and size, and so we can use that to make a repaint happen now.
-            repaintUsingContainer(containerForRepaint(), layer()->repaintRect());
+            repaintUsingContainer(containerForRepaint(), repaintLayoutRects().m_repaintRect);
         }
     }
 
index 92ddde6..1212c76 100644 (file)
@@ -310,7 +310,6 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject)
     , m_layerListMutationAllowed(true)
 #endif
     , m_hasFilterInfo(false)
-    , m_hasComputedRepaintRect(false)
 #if ENABLE(CSS_COMPOSITING)
     , m_blendMode(BlendModeNormal)
     , m_hasNotIsolatedCompositedBlendingDescendants(false)
@@ -516,23 +515,29 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay
         ASSERT(!renderer().view().layoutStateEnabled());
 
         RenderLayerModelObject* repaintContainer = renderer().containerForRepaint();
-        LayoutRect oldRepaintRect = m_repaintRect;
-        LayoutRect oldOutlineBox = m_outlineBox;
+        
+        auto hadRepaintLayoutRects = renderer().hasRepaintLayoutRects();
+        RepaintLayoutRects oldRects = hadRepaintLayoutRects ? renderer().repaintLayoutRects() : RepaintLayoutRects();
         computeRepaintRects(repaintContainer, geometryMap);
-
+        
+        auto hasRepaintLayoutRects = renderer().hasRepaintLayoutRects();
+        RepaintLayoutRects newRects = hasRepaintLayoutRects ? renderer().repaintLayoutRects() : RepaintLayoutRects();
         // FIXME: Should ASSERT that value calculated for m_outlineBox using the cached offset is the same
         // as the value not using the cached offset, but we can't due to https://bugs.webkit.org/show_bug.cgi?id=37048
-        if ((flags & CheckForRepaint) && m_hasComputedRepaintRect) {
+        if ((flags & CheckForRepaint) && hasRepaintLayoutRects) {
             if (!renderer().view().printing()) {
                 bool didRepaint = false;
                 if (m_repaintStatus & NeedsFullRepaint) {
-                    renderer().repaintUsingContainer(repaintContainer, oldRepaintRect);
-                    if (m_repaintRect != oldRepaintRect) {
-                        renderer().repaintUsingContainer(repaintContainer, m_repaintRect);
+                    if (hadRepaintLayoutRects)
+                        renderer().repaintUsingContainer(repaintContainer, oldRects.m_repaintRect);
+                    if (!hadRepaintLayoutRects || newRects.m_repaintRect != oldRects.m_repaintRect) {
+                        renderer().repaintUsingContainer(repaintContainer, newRects.m_repaintRect);
                         didRepaint = true;
                     }
                 } else if (shouldRepaintAfterLayout()) {
-                    renderer().repaintAfterLayoutIfNeeded(repaintContainer, oldRepaintRect, oldOutlineBox, &m_repaintRect, &m_outlineBox);
+                    // FIXME: We will convert this to just take the old and new RepaintLayoutRects once
+                    // we change other callers to use RepaintLayoutRects.
+                    renderer().repaintAfterLayoutIfNeeded(repaintContainer, oldRects.m_repaintRect, oldRects.m_outlineBox, &newRects.m_repaintRect, &newRects.m_outlineBox);
                     didRepaint = true;
                 }
 
@@ -601,7 +606,7 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay
 
 LayoutRect RenderLayer::repaintRectIncludingNonCompositingDescendants() const
 {
-    LayoutRect repaintRect = m_repaintRect;
+    LayoutRect repaintRect = renderer().repaintLayoutRects().m_repaintRect;
     for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
         // Don't include repaint rects for composited child layers; they will paint themselves and have a different origin.
         if (child->isComposited())
@@ -797,18 +802,9 @@ void RenderLayer::updateDescendantsAreContiguousInStackingOrderRecursive(const H
 void RenderLayer::computeRepaintRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap)
 {
     ASSERT(!m_visibleContentStatusDirty);
-
-    if (!isSelfPaintingLayer()) {
-        clearRepaintRects();
-        return;
-    }
-    
-    m_hasComputedRepaintRect = true;
-    m_repaintRect = renderer().clippedOverflowRectForRepaint(repaintContainer);
-    m_outlineBox = renderer().outlineBoundsForRepaint(repaintContainer, geometryMap);
+    renderer().computeRepaintLayoutRects(repaintContainer, geometryMap);
 }
 
-
 void RenderLayer::computeRepaintRectsIncludingDescendants()
 {
     // FIXME: computeRepaintRects() has to walk up the parent chain for every layer to compute the rects.
@@ -824,9 +820,7 @@ void RenderLayer::clearRepaintRects()
 {
     ASSERT(!m_visibleContentStatusDirty);
 
-    m_hasComputedRepaintRect = false;
-    m_repaintRect = LayoutRect();
-    m_outlineBox = LayoutRect();
+    renderer().clearRepaintLayoutRects();
 }
 
 void RenderLayer::updateLayerPositionsAfterDocumentScroll()
@@ -889,8 +883,8 @@ void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap
             computeRepaintRects(renderer().containerForRepaint(), geometryMap);
     } else {
         // Check that our cached rects are correct.
-        ASSERT(!m_hasComputedRepaintRect || (m_repaintRect == renderer().clippedOverflowRectForRepaint(renderer().containerForRepaint())));
-        ASSERT(!m_hasComputedRepaintRect || m_outlineBox == renderer().outlineBoundsForRepaint(renderer().containerForRepaint()));
+        ASSERT(!renderer().hasRepaintLayoutRects() || renderer().repaintLayoutRects().m_repaintRect == renderer().clippedOverflowRectForRepaint(renderer().containerForRepaint()));
+        ASSERT(!renderer().hasRepaintLayoutRects() || renderer().repaintLayoutRects().m_outlineBox == renderer().outlineBoundsForRepaint(renderer().containerForRepaint()));
     }
     
     for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
@@ -2440,7 +2434,7 @@ void RenderLayer::scrollTo(const ScrollPosition& position)
     // The caret rect needs to be invalidated after scrolling
     frame.selection().setCaretRectNeedsUpdate();
     
-    LayoutRect rectForRepaint = m_hasComputedRepaintRect ? m_repaintRect : renderer().clippedOverflowRectForRepaint(repaintContainer);
+    LayoutRect rectForRepaint = renderer().hasRepaintLayoutRects() ? renderer().repaintLayoutRects().m_repaintRect : renderer().clippedOverflowRectForRepaint(repaintContainer);
 
     FloatQuad quadForFakeMouseMoveEvent = FloatQuad(rectForRepaint);
     if (repaintContainer)
index 53b350c..184199e 100644 (file)
@@ -581,8 +581,7 @@ public:
     LayoutRect calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, CalculateLayerBoundsFlags = DefaultCalculateLayerBoundsFlags) const;
     
     // Return a cached repaint rect, computed relative to the layer renderer's containerForRepaint.
-    bool hasComputedRepaintRect() const { return m_hasComputedRepaintRect; }
-    LayoutRect repaintRect() const { ASSERT(hasComputedRepaintRect()); return m_repaintRect; }
+    bool hasComputedRepaintRects() const { return renderer().hasRepaintLayoutRects(); }
     LayoutRect repaintRectIncludingNonCompositingDescendants() const;
 
     void setRepaintStatus(RepaintStatus status) { m_repaintStatus = status; }
@@ -1115,8 +1114,6 @@ private:
 #endif
 
     bool m_hasFilterInfo : 1;
-    
-    bool m_hasComputedRepaintRect : 1;
 
 #if ENABLE(CSS_COMPOSITING)
     unsigned m_blendMode : 5;
@@ -1133,9 +1130,6 @@ private:
     RenderLayer* m_first;
     RenderLayer* m_last;
 
-    LayoutRect m_repaintRect; // Cached repaint rects. Used by layout.
-    LayoutRect m_outlineBox;
-
     // Our current relative position offset.
     LayoutSize m_offsetForInFlowPosition;
 
index 343d141..45c052e 100644 (file)
@@ -38,6 +38,15 @@ bool RenderLayerModelObject::s_hadLayer = false;
 bool RenderLayerModelObject::s_hadTransform = false;
 bool RenderLayerModelObject::s_layerWasSelfPainting = false;
 
+typedef WTF::HashMap<const RenderLayerModelObject*, RepaintLayoutRects> RepaintLayoutRectsMap;
+static RepaintLayoutRectsMap* gRepaintLayoutRectsMap = nullptr;
+
+RepaintLayoutRects::RepaintLayoutRects(const RenderLayerModelObject& renderer, const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap)
+    : m_repaintRect(renderer.clippedOverflowRectForRepaint(repaintContainer))
+    , m_outlineBox(renderer.outlineBoundsForRepaint(repaintContainer, geometryMap))
+{
+}
+
 RenderLayerModelObject::RenderLayerModelObject(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
     : RenderElement(element, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag)
 {
@@ -61,7 +70,9 @@ void RenderLayerModelObject::willBeDestroyed()
     }
 
     RenderElement::willBeDestroyed();
-
+    
+    clearRepaintLayoutRects();
+    
     // Our layer should have been destroyed and cleared by now
     ASSERT(!hasLayer());
     ASSERT(!m_layer);
@@ -71,6 +82,8 @@ void RenderLayerModelObject::destroyLayer()
 {
     ASSERT(!hasLayer()); // Callers should have already called setHasLayer(false)
     ASSERT(m_layer);
+    if (m_layer->isSelfPaintingLayer())
+        clearRepaintLayoutRects();
     m_layer = nullptr;
 }
 
@@ -166,8 +179,8 @@ void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderSt
         setHasTransformRelatedProperty(false); // All transform-related propeties force layers, so we know we don't have one or the object doesn't support them.
         setHasReflection(false);
         // Repaint the about to be destroyed self-painting layer when style change also triggers repaint.
-        if (layer()->isSelfPaintingLayer() && layer()->repaintStatus() == NeedsFullRepaint && layer()->hasComputedRepaintRect())
-            repaintUsingContainer(containerForRepaint(), layer()->repaintRect());
+        if (layer()->isSelfPaintingLayer() && layer()->repaintStatus() == NeedsFullRepaint && hasRepaintLayoutRects())
+            repaintUsingContainer(containerForRepaint(), repaintLayoutRects().m_repaintRect);
         layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
         if (s_wasFloating && isFloating())
             setChildNeedsLayout();
@@ -235,5 +248,38 @@ bool RenderLayerModelObject::shouldPlaceBlockDirectionScrollbarOnLeft() const
 #endif
 }
 
+bool RenderLayerModelObject::hasRepaintLayoutRects() const
+{
+    return gRepaintLayoutRectsMap && gRepaintLayoutRectsMap->contains(this);
+}
+
+void RenderLayerModelObject::setRepaintLayoutRects(const RepaintLayoutRects& rects)
+{
+    if (!gRepaintLayoutRectsMap)
+        gRepaintLayoutRectsMap = new RepaintLayoutRectsMap();
+    gRepaintLayoutRectsMap->set(this, rects);
+}
+
+void RenderLayerModelObject::clearRepaintLayoutRects()
+{
+    if (gRepaintLayoutRectsMap)
+        gRepaintLayoutRectsMap->remove(this);
+}
+
+RepaintLayoutRects RenderLayerModelObject::repaintLayoutRects() const
+{
+    if (!hasRepaintLayoutRects())
+        return RepaintLayoutRects();
+    return gRepaintLayoutRectsMap->get(this);
+}
+
+void RenderLayerModelObject::computeRepaintLayoutRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap)
+{
+    if (!m_layer || !m_layer->isSelfPaintingLayer())
+        clearRepaintLayoutRects();
+    else
+        setRepaintLayoutRects(RepaintLayoutRects(*this, repaintContainer, geometryMap));
+}
+
 } // namespace WebCore
 
index 590aa44..c1d6723 100644 (file)
@@ -28,6 +28,14 @@ namespace WebCore {
 
 class RenderLayer;
 
+struct RepaintLayoutRects {
+    LayoutRect m_repaintRect; // This rect is clipped by enclosing objects (e.g., overflow:hidden).
+    LayoutRect m_outlineBox; // This rect is unclipped.
+
+    RepaintLayoutRects(const RenderLayerModelObject& renderer, const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* = nullptr);
+    RepaintLayoutRects() { };
+};
+
 class RenderLayerModelObject : public RenderElement {
 public:
     virtual ~RenderLayerModelObject();
@@ -51,6 +59,14 @@ public:
     virtual bool isScrollableOrRubberbandableBox() const { return false; }
 
     bool shouldPlaceBlockDirectionScrollbarOnLeft() const;
+    
+    void computeRepaintLayoutRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* = nullptr);
+
+    RepaintLayoutRects repaintLayoutRects() const;
+    
+    bool hasRepaintLayoutRects() const;
+    void setRepaintLayoutRects(const RepaintLayoutRects&);
+    void clearRepaintLayoutRects();
 
 protected:
     RenderLayerModelObject(Element&, RenderStyle&&, BaseTypeFlags);