Title: [238178] trunk/Source/WebKit
Revision
238178
Author
an...@apple.com
Date
2018-11-14 08:53:25 -0800 (Wed, 14 Nov 2018)

Log Message

Align Mac WK2 layer flush throttling with iOS
https://bugs.webkit.org/show_bug.cgi?id=191632

Reviewed by Zalan Bujtas.

Currently Mac WK2 uses WebCore side throttling implementation in RenderLayerCompositor. This code has
throttling timer per-frame while the actual decision making and layer flushes itself are per page. These
timers generate way more flushes than expected in presence of multiple frames. There are also bugs in how
flushing state is updated when frames are created dynamically.

On iOS WK2 throttling is implemented on WebKit side and controlled by a per-page timer. Recent fixes also
make this implementation visually fast. We should align the Mac implementation and eventually unify them.

This patch implements throttling in TiledCoreAnimationDrawingArea mirroring the iOS RemoteLayerTreeDrawingArea
implementation. There are some adjustments for platform differences (local vs remote layers) and we continue
using runloop observer for the actual flushes. Timings are as in the existing Mac code.

The patch appears to be a significant performance progression.

* WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h:
* WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
(WebKit::TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea):
(WebKit::TiledCoreAnimationDrawingArea::setLayerTreeStateIsFrozen):

Schedule an immediate flush when layers are unfrozen.

(WebKit::TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlush):
(WebKit::TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlushImmediately):

Track pending flushes with m_hasPendingFlush bit.
Delay flush if the flush throttling timer is active. Start it if we throttling but it is not active yet.

(WebKit::TiledCoreAnimationDrawingArea::flushLayers):

Clear m_hasPendingFlush if the flush succeeded.
Restart the throttling timer if we are still throttling.
Manage runloop observer invalidation here instead of the caller (and stop returning value).

(WebKit::TiledCoreAnimationDrawingArea::layerFlushRunLoopCallback):
(WebKit::TiledCoreAnimationDrawingArea::adjustLayerFlushThrottling):

Returning 'true' here disables WebCore side throttling code.

Start or stop the throttling timer on state changes.

(WebKit::TiledCoreAnimationDrawingArea::layerFlushThrottlingIsActive const):

This is used to control style and layout timers on WebCore side. Return false on frozen
state since unfreezing depends on style and layout.

(WebKit::TiledCoreAnimationDrawingArea::startLayerFlushThrottlingTimer):
(WebKit::TiledCoreAnimationDrawingArea::layerFlushThrottlingTimerFired):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (238177 => 238178)


--- trunk/Source/WebKit/ChangeLog	2018-11-14 16:24:59 UTC (rev 238177)
+++ trunk/Source/WebKit/ChangeLog	2018-11-14 16:53:25 UTC (rev 238178)
@@ -1,3 +1,58 @@
+2018-11-14  Antti Koivisto  <an...@apple.com>
+
+        Align Mac WK2 layer flush throttling with iOS
+        https://bugs.webkit.org/show_bug.cgi?id=191632
+
+        Reviewed by Zalan Bujtas.
+
+        Currently Mac WK2 uses WebCore side throttling implementation in RenderLayerCompositor. This code has
+        throttling timer per-frame while the actual decision making and layer flushes itself are per page. These
+        timers generate way more flushes than expected in presence of multiple frames. There are also bugs in how
+        flushing state is updated when frames are created dynamically.
+
+        On iOS WK2 throttling is implemented on WebKit side and controlled by a per-page timer. Recent fixes also
+        make this implementation visually fast. We should align the Mac implementation and eventually unify them.
+
+        This patch implements throttling in TiledCoreAnimationDrawingArea mirroring the iOS RemoteLayerTreeDrawingArea
+        implementation. There are some adjustments for platform differences (local vs remote layers) and we continue
+        using runloop observer for the actual flushes. Timings are as in the existing Mac code.
+
+        The patch appears to be a significant performance progression.
+
+        * WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h:
+        * WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
+        (WebKit::TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea):
+        (WebKit::TiledCoreAnimationDrawingArea::setLayerTreeStateIsFrozen):
+
+        Schedule an immediate flush when layers are unfrozen.
+
+        (WebKit::TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlush):
+        (WebKit::TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlushImmediately):
+
+        Track pending flushes with m_hasPendingFlush bit.
+        Delay flush if the flush throttling timer is active. Start it if we throttling but it is not active yet.
+
+        (WebKit::TiledCoreAnimationDrawingArea::flushLayers):
+
+        Clear m_hasPendingFlush if the flush succeeded.
+        Restart the throttling timer if we are still throttling.
+        Manage runloop observer invalidation here instead of the caller (and stop returning value).
+
+        (WebKit::TiledCoreAnimationDrawingArea::layerFlushRunLoopCallback):
+        (WebKit::TiledCoreAnimationDrawingArea::adjustLayerFlushThrottling):
+
+        Returning 'true' here disables WebCore side throttling code.
+
+        Start or stop the throttling timer on state changes.
+
+        (WebKit::TiledCoreAnimationDrawingArea::layerFlushThrottlingIsActive const):
+
+        This is used to control style and layout timers on WebCore side. Return false on frozen
+        state since unfreezing depends on style and layout.
+
+        (WebKit::TiledCoreAnimationDrawingArea::startLayerFlushThrottlingTimer):
+        (WebKit::TiledCoreAnimationDrawingArea::layerFlushThrottlingTimerFired):
+
 2018-11-14  Chris Dumez  <cdu...@apple.com>
 
         WebKit.WKHTTPCookieStoreWithoutProcessPool API test is failing with process prewarming is enabled

Modified: trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h (238177 => 238178)


--- trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h	2018-11-14 16:24:59 UTC (rev 238177)
+++ trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h	2018-11-14 16:53:25 UTC (rev 238178)
@@ -88,7 +88,7 @@
 
     bool dispatchDidReachLayoutMilestone(OptionSet<WebCore::LayoutMilestone>) override;
 
-    bool flushLayers();
+    void flushLayers();
 
     // Message handlers.
     void updateGeometry(const WebCore::IntSize& viewSize, bool flushSynchronously, const WTF::MachSendRight& fencePort) override;
@@ -130,6 +130,12 @@
     void invalidateLayerFlushRunLoopObserver();
     void scheduleLayerFlushRunLoopObserver();
 
+    bool adjustLayerFlushThrottling(WebCore::LayerFlushThrottleState::Flags) override;
+    bool layerFlushThrottlingIsActive() const override;
+
+    void startLayerFlushThrottlingTimer();
+    void layerFlushThrottlingTimerFired();
+
     bool m_layerTreeStateIsFrozen;
 
     std::unique_ptr<LayerHostingContext> m_layerHostingContext;
@@ -168,6 +174,12 @@
     Vector<CallbackID> m_pendingCallbackIDs;
 
     std::unique_ptr<WebCore::RunLoopObserver> m_layerFlushRunLoopObserver;
+
+    bool m_isThrottlingLayerFlushes { false };
+    bool m_isLayerFlushThrottlingTemporarilyDisabledForInteraction { false };
+    bool m_hasPendingFlush { false };
+
+    WebCore::Timer m_layerFlushThrottlingTimer;
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm (238177 => 238178)


--- trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm	2018-11-14 16:24:59 UTC (rev 238177)
+++ trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm	2018-11-14 16:53:25 UTC (rev 238178)
@@ -80,6 +80,7 @@
     , m_transientZoomScale(1)
     , m_sendDidUpdateActivityStateTimer(RunLoop::main(), this, &TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired)
     , m_viewOverlayRootLayer(nullptr)
+    , m_layerFlushThrottlingTimer(*this, &TiledCoreAnimationDrawingArea::layerFlushThrottlingTimerFired)
 {
     m_webPage.corePage()->settings().setForceCompositingMode(true);
 
@@ -174,10 +175,14 @@
 
     m_layerTreeStateIsFrozen = layerTreeStateIsFrozen;
 
-    if (m_layerTreeStateIsFrozen)
+    if (m_layerTreeStateIsFrozen) {
         invalidateLayerFlushRunLoopObserver();
-    else
-        scheduleLayerFlushRunLoopObserver();
+        m_layerFlushThrottlingTimer.stop();
+    } else {
+        // Immediate flush as any delay in unfreezing can result in flashes.
+        if (m_hasPendingFlush)
+            scheduleLayerFlushRunLoopObserver();
+    }
 }
 
 bool TiledCoreAnimationDrawingArea::layerTreeStateIsFrozen() const
@@ -191,15 +196,36 @@
 
 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlush()
 {
-    if (m_layerTreeStateIsFrozen)
+    m_hasPendingFlush = true;
+
+    if (m_layerTreeStateIsFrozen) {
+        m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
         return;
+    }
 
+    if (m_isLayerFlushThrottlingTemporarilyDisabledForInteraction) {
+        m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
+        scheduleLayerFlushRunLoopObserver();
+        m_layerFlushThrottlingTimer.stop();
+        return;
+    }
+
+    if (m_layerFlushThrottlingTimer.isActive()) {
+        ASSERT(m_isThrottlingLayerFlushes);
+        return;
+    }
+
+    if (m_isThrottlingLayerFlushes) {
+        startLayerFlushThrottlingTimer();
+        return;
+    }
+
     scheduleLayerFlushRunLoopObserver();
 }
 
 void TiledCoreAnimationDrawingArea::scheduleCompositingLayerFlushImmediately()
 {
-    scheduleCompositingLayerFlush();
+    scheduleLayerFlushRunLoopObserver();
 }
 
 void TiledCoreAnimationDrawingArea::updatePreferences(const WebPreferencesStore&)
@@ -424,10 +450,10 @@
     scheduleCompositingLayerFlush();
 }
 
-bool TiledCoreAnimationDrawingArea::flushLayers()
+void TiledCoreAnimationDrawingArea::flushLayers()
 {
     if (layerTreeStateIsFrozen())
-        return false;
+        return;
 
     @autoreleasepool {
         scaleViewToFitDocumentIfNeeded();
@@ -461,7 +487,8 @@
                 drawingArea->sendPendingNewlyReachedLayoutMilestones();
         } forPhase:kCATransactionPhasePostCommit];
 
-        bool returnValue = m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
+        m_hasPendingFlush = !m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
+
 #if ENABLE(ASYNC_SCROLLING)
         if (ScrollingCoordinator* scrollingCoordinator = m_webPage.corePage()->scrollingCoordinator())
             scrollingCoordinator->commitTreeStateIfNeeded();
@@ -477,7 +504,13 @@
             m_pendingCallbackIDs.clear();
         }
 
-        return returnValue;
+        if (!m_hasPendingFlush)
+            invalidateLayerFlushRunLoopObserver();
+
+        if (m_isThrottlingLayerFlushes)
+            startLayerFlushThrottlingTimer();
+        else
+            m_layerFlushThrottlingTimer.stop();
     }
 }
 
@@ -902,8 +935,7 @@
 
 void TiledCoreAnimationDrawingArea::layerFlushRunLoopCallback()
 {
-    if (flushLayers())
-        invalidateLayerFlushRunLoopObserver();
+    flushLayers();
 }
 
 void TiledCoreAnimationDrawingArea::invalidateLayerFlushRunLoopObserver()
@@ -916,6 +948,51 @@
     m_layerFlushRunLoopObserver->schedule(CFRunLoopGetCurrent());
 }
 
+bool TiledCoreAnimationDrawingArea::adjustLayerFlushThrottling(WebCore::LayerFlushThrottleState::Flags flags)
+{
+    bool wasThrottlingLayerFlushes = m_isThrottlingLayerFlushes;
+    m_isThrottlingLayerFlushes = flags & WebCore::LayerFlushThrottleState::Enabled;
+    m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = flags & WebCore::LayerFlushThrottleState::UserIsInteracting;
+
+    if (wasThrottlingLayerFlushes == m_isThrottlingLayerFlushes)
+        return true;
+
+    m_layerFlushThrottlingTimer.stop();
+
+    if (m_layerTreeStateIsFrozen)
+        return true;
+
+    if (m_isThrottlingLayerFlushes) {
+        invalidateLayerFlushRunLoopObserver();
+        startLayerFlushThrottlingTimer();
+    } else if (m_hasPendingFlush)
+        scheduleLayerFlushRunLoopObserver();
+
+    return true;
+}
+
+bool TiledCoreAnimationDrawingArea::layerFlushThrottlingIsActive() const
+{
+    return m_isThrottlingLayerFlushes && !m_layerTreeStateIsFrozen;
+}
+
+void TiledCoreAnimationDrawingArea::startLayerFlushThrottlingTimer()
+{
+    ASSERT(m_isThrottlingLayerFlushes);
+
+    const auto throttledFlushDelay = 500_ms;
+    m_layerFlushThrottlingTimer.startOneShot(throttledFlushDelay);
+}
+
+void TiledCoreAnimationDrawingArea::layerFlushThrottlingTimerFired()
+{
+    if (m_layerTreeStateIsFrozen)
+        return;
+    if (!m_hasPendingFlush)
+        return;
+    scheduleLayerFlushRunLoopObserver();
+}
+
 } // namespace WebKit
 
 #endif // !PLATFORM(IOS_FAMILY)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to