Title: [215410] trunk/Source/WebCore
Revision
215410
Author
an...@apple.com
Date
2017-04-17 08:00:33 -0700 (Mon, 17 Apr 2017)

Log Message

GraphicsLayerCA::recursiveCommitChanges should not descend into subtrees without changes
https://bugs.webkit.org/show_bug.cgi?id=170851

Reviewed by Simon Fraser.

With lots of layers this can be very slow as it always traverses the entire layer tree.
For example GIF animations on tumblr.com trigger expensive commits where almost nothing changes.

This patch adds m_hasDescendantsWithUncommittedChanges bit to GraphicsLayerCA. With this
we can avoid descending to branches without changes when committing.

This patch enabled the optimization on Mac.

* platform/graphics/ca/GraphicsLayerCA.cpp:
(WebCore::GraphicsLayerCA::GraphicsLayerCA::syncPosition):

    Set PositionChanged flag when syncing layer position. This flag does nothing except makes
    next commit to update the coverage rect (so tiling gets updated).

(WebCore::GraphicsLayerCA::setVisibleAndCoverageRects):

    Do all setting of m_uncommittedChanges bits via addUncommittedChanges function.

(WebCore::GraphicsLayerCA::recursiveCommitChanges):

    Bail out if neither the current layer nor any of its descendants have any uncommited changes
    and none of the ancestors had changes.

(WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers):
(WebCore::GraphicsLayerCA::ensureStructuralLayer):
(WebCore::GraphicsLayerCA::changeLayerTypeTo):
(WebCore::GraphicsLayerCA::addUncommittedChanges):

    Set m_hasDescendantsWithUncommittedChanges bit in ancestors when mutating m_uncommittedChanges.

(WebCore::GraphicsLayerCA::noteLayerPropertyChanged):
* platform/graphics/ca/GraphicsLayerCA.h:
(WebCore::RenderLayerCompositor::frameViewDidScroll):

    Tell the scrolling layer that it needs to recompute coverage.
    This also schedules a layer flush so no need to do that separately.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (215409 => 215410)


--- trunk/Source/WebCore/ChangeLog	2017-04-17 14:47:04 UTC (rev 215409)
+++ trunk/Source/WebCore/ChangeLog	2017-04-17 15:00:33 UTC (rev 215410)
@@ -1,3 +1,47 @@
+2017-04-17  Antti Koivisto  <an...@apple.com>
+
+        GraphicsLayerCA::recursiveCommitChanges should not descend into subtrees without changes
+        https://bugs.webkit.org/show_bug.cgi?id=170851
+
+        Reviewed by Simon Fraser.
+
+        With lots of layers this can be very slow as it always traverses the entire layer tree.
+        For example GIF animations on tumblr.com trigger expensive commits where almost nothing changes.
+
+        This patch adds m_hasDescendantsWithUncommittedChanges bit to GraphicsLayerCA. With this
+        we can avoid descending to branches without changes when committing.
+
+        This patch enabled the optimization on Mac.
+
+        * platform/graphics/ca/GraphicsLayerCA.cpp:
+        (WebCore::GraphicsLayerCA::GraphicsLayerCA::syncPosition):
+
+            Set PositionChanged flag when syncing layer position. This flag does nothing except makes
+            next commit to update the coverage rect (so tiling gets updated).
+
+        (WebCore::GraphicsLayerCA::setVisibleAndCoverageRects):
+
+            Do all setting of m_uncommittedChanges bits via addUncommittedChanges function.
+
+        (WebCore::GraphicsLayerCA::recursiveCommitChanges):
+
+            Bail out if neither the current layer nor any of its descendants have any uncommited changes
+            and none of the ancestors had changes.
+
+        (WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers):
+        (WebCore::GraphicsLayerCA::ensureStructuralLayer):
+        (WebCore::GraphicsLayerCA::changeLayerTypeTo):
+        (WebCore::GraphicsLayerCA::addUncommittedChanges):
+
+            Set m_hasDescendantsWithUncommittedChanges bit in ancestors when mutating m_uncommittedChanges.
+
+        (WebCore::GraphicsLayerCA::noteLayerPropertyChanged):
+        * platform/graphics/ca/GraphicsLayerCA.h:
+        (WebCore::RenderLayerCompositor::frameViewDidScroll):
+
+            Tell the scrolling layer that it needs to recompute coverage.
+            This also schedules a layer flush so no need to do that separately.
+
 2017-04-16  Chris Dumez  <cdu...@apple.com>
 
         CMD+R / CMD+Q is considered as user interaction and beforeunload alert is shown

Modified: trunk/Source/WebCore/platform/graphics/GraphicsLayer.h (215409 => 215410)


--- trunk/Source/WebCore/platform/graphics/GraphicsLayer.h	2017-04-17 14:47:04 UTC (rev 215409)
+++ trunk/Source/WebCore/platform/graphics/GraphicsLayer.h	2017-04-17 15:00:33 UTC (rev 215410)
@@ -525,7 +525,8 @@
     // Some compositing systems may do internal batching to synchronize compositing updates
     // with updates drawn into the window. These methods flush internal batched state on this layer
     // and descendant layers, and this layer only.
-    virtual void flushCompositingState(const FloatRect& /* clipRect */) { }
+    enum class FlushScope { Uncommitted, All };
+    virtual void flushCompositingState(const FloatRect& /* clipRect */, FlushScope = FlushScope::All) { }
     virtual void flushCompositingStateForThisLayerOnly() { }
 
     // If the exposed rect of this layer changes, returns true if this or descendant layers need a flush,

Modified: trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp (215409 => 215410)


--- trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp	2017-04-17 14:47:04 UTC (rev 215409)
+++ trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp	2017-04-17 15:00:33 UTC (rev 215410)
@@ -581,6 +581,16 @@
     noteLayerPropertyChanged(GeometryChanged);
 }
 
+void GraphicsLayerCA::syncPosition(const FloatPoint& point)
+{
+    if (point == m_position)
+        return;
+
+    GraphicsLayer::syncPosition(point);
+    // Ensure future flushes will recompute the coverage rect and update tiling.
+    noteLayerPropertyChanged(PositionChanged, DontScheduleFlush);
+}
+
 void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point)
 {
     if (point == m_anchorPoint)
@@ -1164,12 +1174,17 @@
     return FloatPoint();
 }
 
-void GraphicsLayerCA::flushCompositingState(const FloatRect& clipRect)
+void GraphicsLayerCA::flushCompositingState(const FloatRect& visibleRect, FlushScope flushScope)
 {
-    TransformState state(TransformState::UnapplyInverseTransformDirection, FloatQuad(clipRect));
-    FloatQuad coverageQuad(clipRect);
+    TransformState state(TransformState::UnapplyInverseTransformDirection, FloatQuad(visibleRect));
+    FloatQuad coverageQuad(visibleRect);
     state.setSecondaryQuad(&coverageQuad);
-    recursiveCommitChanges(CommitState(), state);
+
+    CommitState commitState;
+    commitState.ancestorHadChanges = visibleRect != m_previousCommittedVisibleRect || flushScope == FlushScope::All;
+    m_previousCommittedVisibleRect = visibleRect;
+
+    recursiveCommitChanges(commitState, state);
 }
 
 void GraphicsLayerCA::flushCompositingStateForThisLayerOnly()
@@ -1354,17 +1369,17 @@
     // FIXME: we need to take reflections into account when determining whether this layer intersects the coverage rect.
     bool intersectsCoverageRect = isViewportConstrained || rects.coverageRect.intersects(FloatRect(m_boundsOrigin, size()));
     if (intersectsCoverageRect != m_intersectsCoverageRect) {
-        m_uncommittedChanges |= CoverageRectChanged;
+        addUncommittedChanges(CoverageRectChanged);
         m_intersectsCoverageRect = intersectsCoverageRect;
     }
 
     if (visibleRectChanged) {
-        m_uncommittedChanges |= CoverageRectChanged;
+        addUncommittedChanges(CoverageRectChanged);
         m_visibleRect = rects.visibleRect;
     }
 
     if (coverageRectChanged) {
-        m_uncommittedChanges |= CoverageRectChanged;
+        addUncommittedChanges(CoverageRectChanged);
         m_coverageRect = rects.coverageRect;
     }
 }
@@ -1373,8 +1388,12 @@
 // animation that contributes maximally to the scale (on every layer with animations down the hierarchy).
 void GraphicsLayerCA::recursiveCommitChanges(const CommitState& commitState, const TransformState& state, float pageScaleFactor, const FloatPoint& positionRelativeToBase, bool affectedByPageScale)
 {
+    if (!commitState.ancestorHadChanges && !m_uncommittedChanges && !hasDescendantsWithUncommittedChanges())
+        return;
+
     TransformState localState = state;
     CommitState childCommitState = commitState;
+
     bool affectedByTransformAnimation = commitState.ancestorHasTransformAnimation;
 
     bool accumulateTransform = accumulatesTransform(*this);
@@ -1411,7 +1430,11 @@
 #endif
 
     bool hadChanges = m_uncommittedChanges;
-    
+
+    // FIXME: This could be more fine-grained. Only some types of changes have impact on sublayers.
+    if (!childCommitState.ancestorHadChanges)
+        childCommitState.ancestorHadChanges = hadChanges;
+
     if (appliesPageScale()) {
         pageScaleFactor = this->pageScaleFactor();
         affectedByPageScale = true;
@@ -1450,6 +1473,8 @@
     if (GraphicsLayerCA* maskLayer = downcast<GraphicsLayerCA>(m_maskLayer))
         maskLayer->commitLayerChangesAfterSublayers(childCommitState);
 
+    setHasDescendantsWithUncommittedChanges(false);
+
     bool hadDirtyRects = m_uncommittedChanges & DirtyRectsChanged;
     commitLayerChangesAfterSublayers(childCommitState);
 
@@ -1564,7 +1589,7 @@
     if (!m_uncommittedChanges) {
         // Ensure that we cap layer depth in commitLayerChangesAfterSublayers().
         if (commitState.treeDepth > cMaxLayerTreeDepth)
-            m_uncommittedChanges |= ChildrenChanged;
+            addUncommittedChanges(ChildrenChanged);
     }
 
     bool needTiledLayer = requiresTiledLayer(pageScaleFactor);
@@ -1706,7 +1731,7 @@
 
     // Ensure that we cap layer depth in commitLayerChangesAfterSublayers().
     if (commitState.treeDepth > cMaxLayerTreeDepth)
-        m_uncommittedChanges |= ChildrenChanged;
+        addUncommittedChanges(ChildrenChanged);
 }
 
 void GraphicsLayerCA::commitLayerChangesAfterSublayers(CommitState& commitState)
@@ -2116,7 +2141,7 @@
             // Release the structural layer.
             m_structuralLayer = nullptr;
 
-            m_uncommittedChanges |= structuralLayerChangeFlags;
+            addUncommittedChanges(structuralLayerChangeFlags);
         }
         return;
     }
@@ -2144,7 +2169,7 @@
     if (!structuralLayerChanged)
         return;
     
-    m_uncommittedChanges |= structuralLayerChangeFlags;
+    addUncommittedChanges(structuralLayerChangeFlags);
 
     // We've changed the layer that our parent added to its sublayer list, so tell it to update
     // sublayers again in its commitLayerChangesAfterSublayers().
@@ -3615,7 +3640,7 @@
         oldLayer->superlayer()->replaceSublayer(*oldLayer, *m_layer);
     }
 
-    m_uncommittedChanges |= ChildrenChanged
+    addUncommittedChanges(ChildrenChanged
         | GeometryChanged
         | TransformChanged
         | ChildrenTransformChanged
@@ -3631,10 +3656,10 @@
         | MaskLayerChanged
         | OpacityChanged
         | NameChanged
-        | DebugIndicatorsChanged;
+        | DebugIndicatorsChanged);
     
     if (isTiledLayer)
-        m_uncommittedChanges |= CoverageRectChanged;
+        addUncommittedChanges(CoverageRectChanged);
 
     moveAnimations(oldLayer.get(), m_layer.get());
     
@@ -4008,12 +4033,33 @@
     return !(m_uncommittedChanges & TilesAdded);
 }
 
+void GraphicsLayerCA::addUncommittedChanges(LayerChangeFlags flags)
+{
+    m_uncommittedChanges |= flags;
+
+    if (m_isCommittingChanges)
+        return;
+
+    for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
+        auto& ancestorCA = static_cast<GraphicsLayerCA&>(*ancestor);
+        ASSERT(!ancestorCA.m_isCommittingChanges);
+        if (ancestorCA.hasDescendantsWithUncommittedChanges())
+            return;
+        ancestorCA.setHasDescendantsWithUncommittedChanges(true);
+    }
+}
+
+void GraphicsLayerCA::setHasDescendantsWithUncommittedChanges(bool value)
+{
+    m_hasDescendantsWithUncommittedChanges = value;
+}
+
 void GraphicsLayerCA::noteLayerPropertyChanged(LayerChangeFlags flags, ScheduleFlushOrNot scheduleFlush)
 {
     bool hadUncommittedChanges = !!m_uncommittedChanges;
     bool oldCanThrottleLayerFlush = canThrottleLayerFlush();
 
-    m_uncommittedChanges |= flags;
+    addUncommittedChanges(flags);
 
     if (scheduleFlush == ScheduleFlush) {
         bool needsFlush = !hadUncommittedChanges || oldCanThrottleLayerFlush != canThrottleLayerFlush();

Modified: trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h (215409 => 215410)


--- trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h	2017-04-17 14:47:04 UTC (rev 215409)
+++ trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h	2017-04-17 15:00:33 UTC (rev 215410)
@@ -75,6 +75,7 @@
     WEBCORE_EXPORT void setReplicatedLayer(GraphicsLayer*) override;
 
     WEBCORE_EXPORT void setPosition(const FloatPoint&) override;
+    WEBCORE_EXPORT void syncPosition(const FloatPoint&) override;
     WEBCORE_EXPORT void setAnchorPoint(const FloatPoint3D&) override;
     WEBCORE_EXPORT void setSize(const FloatSize&) override;
     WEBCORE_EXPORT void setBoundsOrigin(const FloatPoint&) override;
@@ -152,12 +153,13 @@
 
     struct CommitState {
         int treeDepth { 0 };
+        bool ancestorHadChanges { false };
         bool ancestorHasTransformAnimation { false };
         bool ancestorIsViewportConstrained { false };
     };
     void recursiveCommitChanges(const CommitState&, const TransformState&, float pageScaleFactor = 1, const FloatPoint& positionRelativeToBase = FloatPoint(), bool affectedByPageScale = false);
 
-    WEBCORE_EXPORT void flushCompositingState(const FloatRect&) override;
+    WEBCORE_EXPORT void flushCompositingState(const FloatRect&, FlushScope) override;
     WEBCORE_EXPORT void flushCompositingStateForThisLayerOnly() override;
 
     WEBCORE_EXPORT bool visibleRectChangeRequiresFlush(const FloatRect& visibleRect) const override;
@@ -494,8 +496,12 @@
         ShapeChanged                            = 1LLU << 36,
         WindRuleChanged                         = 1LLU << 37,
         UserInteractionEnabledChanged           = 1LLU << 38,
+        PositionChanged                         = 1LLU << 39,
     };
     typedef uint64_t LayerChangeFlags;
+    void addUncommittedChanges(LayerChangeFlags);
+    bool hasDescendantsWithUncommittedChanges() const { return m_hasDescendantsWithUncommittedChanges; }
+    void setHasDescendantsWithUncommittedChanges(bool);
     enum ScheduleFlushOrNot { ScheduleFlush, DontScheduleFlush };
     void noteLayerPropertyChanged(LayerChangeFlags, ScheduleFlushOrNot = ScheduleFlush);
     void noteSublayersChanged(ScheduleFlushOrNot = ScheduleFlush);
@@ -585,7 +591,7 @@
     AnimationsMap m_runningAnimations;
 
     Vector<FloatRect> m_dirtyRects;
-    
+
     std::unique_ptr<DisplayList::DisplayList> m_displayList;
 
     FloatSize m_pixelAlignmentOffset;
@@ -596,8 +602,10 @@
 #else
     LayerChangeFlags m_uncommittedChanges { CoverageRectChanged };
 #endif
+    bool m_hasDescendantsWithUncommittedChanges { false };
 
     bool m_isCommittingChanges { false };
+    FloatRect m_previousCommittedVisibleRect;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp (215409 => 215410)


--- trunk/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp	2017-04-17 14:47:04 UTC (rev 215409)
+++ trunk/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp	2017-04-17 15:00:33 UTC (rev 215410)
@@ -572,7 +572,7 @@
     didChangeLayerState();
 }
 
-void CoordinatedGraphicsLayer::flushCompositingState(const FloatRect& rect)
+void CoordinatedGraphicsLayer::flushCompositingState(const FloatRect& rect, FlushScope)
 {
     if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer()))
         mask->flushCompositingStateForThisLayerOnly();

Modified: trunk/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h (215409 => 215410)


--- trunk/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h	2017-04-17 14:47:04 UTC (rev 215409)
+++ trunk/Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h	2017-04-17 15:00:33 UTC (rev 215410)
@@ -100,7 +100,7 @@
     void setNeedsDisplayInRect(const FloatRect&, ShouldClipToLayer = ClipToLayer) override;
     void setContentsNeedsDisplay() override;
     void deviceOrPageScaleFactorChanged() override;
-    void flushCompositingState(const FloatRect&) override;
+    void flushCompositingState(const FloatRect&, FlushScope) override;
     void flushCompositingStateForThisLayerOnly() override;
     bool setFilters(const FilterOperations&) override;
     bool addAnimation(const KeyframeValueList&, const FloatSize&, const Animation*, const String&, double) override;

Modified: trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp (215409 => 215410)


--- trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp	2017-04-17 14:47:04 UTC (rev 215409)
+++ trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp	2017-04-17 15:00:33 UTC (rev 215410)
@@ -441,6 +441,8 @@
 #if PLATFORM(IOS)
         FloatRect exposedRect = frameView.exposedContentRect();
         LOG_WITH_STREAM(Compositing, stream << "\nRenderLayerCompositor " << this << " flushPendingLayerChanges (root " << isFlushRoot << ") exposedRect " << exposedRect);
+
+        // FIXME: Use optimized flushes.
         rootLayer->flushCompositingState(exposedRect);
 #else
         // Having a m_clipLayer indicates that we're doing scrolling via GraphicsLayers.
@@ -450,7 +452,7 @@
             visibleRect.intersect(frameView.viewExposedRect().value());
 
         LOG_WITH_STREAM(Compositing,  stream << "\nRenderLayerCompositor " << this << " flushPendingLayerChanges(" << isFlushRoot << ") " << visibleRect);
-        rootLayer->flushCompositingState(visibleRect);
+        rootLayer->flushCompositingState(visibleRect, GraphicsLayer::FlushScope::Uncommitted);
         LOG_WITH_STREAM(Compositing,  stream << "RenderLayerCompositor " << this << " flush complete\n");
 #endif
     }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to