Changeset 261113 in webkit


Ignore:
Timestamp:
May 4, 2020 2:24:49 PM (4 years ago)
Author:
commit-queue@webkit.org
Message:

Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
https://bugs.webkit.org/show_bug.cgi?id=204713

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2020-05-04
Reviewed by Simon Fraser.

Source/WebCore:

rAF and Page rendering were managed by two different timers. Throttling
rAF was implemented by changing its timer. After r242624, RenderingUpdate
steps have been managed by RenderingUpdateScheduler. This means rAF is
now serviced by the preferredFramesPerSecond which is 60 fps regardless
it's throttled or not. Moreover the rAF throttling timer was mistakenly
kept and it has been running under the old assumption which is: rAF is
serviced by a timer only. This means rAF will be serviced by its timer
and by the RenderingUpdate steps at the same time when it is supposed to
throttle. This will make it fire more than 60 fps in cases which it is
supposed to run less than 60 fps.

The solution is to have two throttling types:

1) Page throttling (or full throttling): This slows down all the steps

of RenderingUpdate for the main document and all the sub-documents.
Page throttling reasons are:
-- VisuallyIdle: Aggressive throttling.
-- LowPowerMode: Half speed throttling.

2) Document throttling (or partial throttling): This only slows down the

rAF of a certain document. Document throttling reasons are:
-- OutsideViewport: Aggressive throttling.
-- NonInteractedCrossOriginFrame: Half speed throttling.

RenderingUpdate steps will still be managed by RenderingUpdateScheduler
which can be throttled. The assumption is none of these steps will need
to run faster than the Page preferredFramesPerSecond. If rAF wants to
run slower than the Page because of a Document throttling reason, no rAF
callbacks will be serviced before its preferredFrameInterval has elapsed.

In this patch, "Half speed throttling" is only implemented for the Page
and the Document throttling. The "Aggressive throttling" will be done in
following patches. Page rendering was never throttled before. We need to
make sure this is not going to affect PLT. Some tests need to be changed
and new tests need to be written. All of the throttling tests checks the
state of the code but none of them checks the real user's experience.

  • Headers.cmake:
  • WebCore.xcodeproj/project.pbxproj:
  • animation/DocumentTimeline.cpp:

(WebCore::DocumentTimeline::animationInterval const):
(WebCore::DocumentTimeline::updateThrottlingState): Deleted.

  • animation/DocumentTimeline.h:

There is no need to have DocumentTimeline throttling. It is already
throttled when the page RenderingUpdate is throttled.

  • dom/Document.cpp:

(WebCore::Document::requestAnimationFrame):
(WebCore::Document::updateLastHandledUserGestureTimestamp):
LowPowerMode throttling is now handled by the Page. So remove its handling
from the Document.

  • dom/ScriptedAnimationController.cpp:

(WebCore::ScriptedAnimationController::ScriptedAnimationController):
(WebCore::ScriptedAnimationController::page const):
(WebCore::ScriptedAnimationController::interval const):
(WebCore::ScriptedAnimationController::preferredScriptedAnimationInterval const):
(WebCore::ScriptedAnimationController::throttlingReasons const):
(WebCore::ScriptedAnimationController::isThrottledRelativeToPage const):
(WebCore::ScriptedAnimationController::shouldRescheduleRequestAnimationFrame const):
(WebCore::ScriptedAnimationController::registerCallback):
(WebCore::ScriptedAnimationController::cancelCallback):
(WebCore::ScriptedAnimationController::serviceRequestAnimationFrameCallbacks):
(WebCore::ScriptedAnimationController::scheduleAnimation):
(WebCore::throttlingReasonToString): Deleted.
(WebCore::throttlingReasonsToString): Deleted.
(WebCore::ScriptedAnimationController::addThrottlingReason): Deleted.
(WebCore::ScriptedAnimationController::removeThrottlingReason): Deleted.
(WebCore::ScriptedAnimationController::isThrottled const): Deleted.
(WebCore::ScriptedAnimationController::animationTimerFired): Deleted.

  • dom/ScriptedAnimationController.h:

(WebCore::ScriptedAnimationController::addThrottlingReason):
(WebCore::ScriptedAnimationController::removeThrottlingReason):
Get rid of the rAF throttling timer. Service the rAF callback only when
the period from the current time stamp till the last service time stamp
is greater than the preferred rAF interval.

  • page/FrameView.cpp:

(WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
ThrottlingReason is now defined outside ScriptedAnimationController.

  • page/Page.cpp:

(WebCore::m_loadsFromNetwork):
(WebCore::Page::setLowPowerModeEnabledOverrideForTesting):

(WebCore::Page::preferredRenderingUpdateInterval const):
Calculate the preferred RenderingUpdate interval from the throttling
reasons.

(WebCore::Page::setIsVisuallyIdleInternal):
(WebCore::Page::handleLowModePowerChange):
Call adjustRenderingUpdateFrequency() when isLowPowerModeEnabled or
IsVisuallyIdle is toggled.

(WebCore::Page::isLowPowerModeEnabled const): Deleted.
(WebCore::updateScriptedAnimationsThrottlingReason): Deleted.

  • page/Page.h:

(WebCore::Page::isLowPowerModeEnabled const):
(WebCore::Page::throttlingReasons const):
(WebCore::Page::canUpdateThrottlingReason const):

  • page/RenderingUpdateScheduler.cpp:

(WebCore::RenderingUpdateScheduler::setPreferredFramesPerSecond):
(WebCore::RenderingUpdateScheduler::scheduleAnimation):
(WebCore::RenderingUpdateScheduler::adjustRenderingUpdateFrequency):
Change the preferredFramesPerSecond of the DisplayRefreshMonitor if the
throttling is not aggressive e.g. 10_s. Otherwise use the timer.

(WebCore::RenderingUpdateScheduler::scheduleTimedRenderingUpdate):
Call adjustFramesPerSecond() when DisplayRefreshMonitor is created.

(WebCore::RenderingUpdateScheduler::startTimer):

  • page/RenderingUpdateScheduler.h:
  • platform/graphics/AnimationFrameRate.h: Added.

(WebCore::preferredFrameInterval):
(WebCore::preferredFramesPerSecond):
(WebCore::operator<<):
Push names of ThrottlingReasons to a TextStream.

  • platform/graphics/DisplayRefreshMonitor.h:

(WebCore::DisplayRefreshMonitor::setPreferredFramesPerSecond):

  • platform/graphics/DisplayRefreshMonitorManager.cpp:

(WebCore::DisplayRefreshMonitorManager::monitorForClient):
Rename createMonitorForClient() to monitorForClient() since it may return
a cached DisplayRefreshMonitor.

(WebCore::DisplayRefreshMonitorManager::setPreferredFramesPerSecond):
(WebCore::DisplayRefreshMonitorManager::scheduleAnimation):
(WebCore::DisplayRefreshMonitorManager::windowScreenDidChange):
No need to call registerClient(). This function was just ensuring the
DisplayRefreshMonitor is created. scheduleAnimation() does the same thing.

(WebCore::DisplayRefreshMonitorManager::createMonitorForClient): Deleted.
(WebCore::DisplayRefreshMonitorManager::registerClient): Deleted.

  • platform/graphics/DisplayRefreshMonitorManager.h:

(WebCore::DisplayRefreshMonitorManager::DisplayRefreshMonitorManager): Deleted.

  • platform/graphics/GraphicsLayerUpdater.cpp:

(WebCore::GraphicsLayerUpdater::GraphicsLayerUpdater):

  • platform/graphics/ios/DisplayRefreshMonitorIOS.mm:

(-[WebDisplayLinkHandler setPreferredFramesPerSecond:]):
Set the preferredFramesPerSecond of the CADisplayLink.

  • testing/Internals.cpp:

(WebCore::Internals::requestAnimationFrameThrottlingReasons const):
(WebCore::Internals::isRequestAnimationFrameThrottled const): Deleted.

  • testing/Internals.h:
  • testing/Internals.idl:

Replace isRequestAnimationFrameThrottled() which returns a boolean by
requestAnimationFrameThrottlingReasons() which returns a string. The
string represents the throttling reasons.

Source/WebKit:

Create an IPC message on the DrawingArea to send a message from the
WebProcess to the UIProcess to setPreferredFramesPerSecond of the
DisplayRefreshMonitor.

  • UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.h:
  • UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.messages.in:
  • UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm:

(-[WKOneShotDisplayLinkHandler setPreferredFramesPerSecond:]):
(WebKit::RemoteLayerTreeDrawingAreaProxy::setPreferredFramesPerSecond):
Set the preferredFramesPerSecond of the CADisplayLink.

  • WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h:
  • WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm:

(WebKit::RemoteLayerTreeDisplayRefreshMonitor::setPreferredFramesPerSecond):
Forward the call to RemoteLayerTreeDrawingArea.

  • WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.h:
  • WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm:

(WebKit::RemoteLayerTreeDrawingArea::setPreferredFramesPerSecond):
Send the IPC message from the WebProcess to the UIProcess.

LayoutTests:

  • fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt:
  • fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html:
  • fast/animation/request-animation-frame-throttle-subframe-display-none-expected.txt:
  • fast/animation/request-animation-frame-throttle-subframe-display-none.html:
  • fast/animation/request-animation-frame-throttle-subframe-expected.txt:
  • fast/animation/request-animation-frame-throttle-subframe-zero-size-expected.txt:
  • fast/animation/request-animation-frame-throttle-subframe-zero-size.html:
  • fast/animation/request-animation-frame-throttle-subframe.html:
  • fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt:
  • fast/animation/request-animation-frame-throttling-detached-iframe.html:

Replace the call isRequestAnimationFrameThrottled() by requestAnimationFrameThrottlingReasons().

  • fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt:
  • fast/animation/request-animation-frame-throttling-lowPowerMode.html:

Ensure the actual rAF interval is > 30ms for lowPowerMode.

  • http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe-expected.txt:
  • http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe.html:
  • http/tests/frame-throttling/resources/requestAnimationFrame-frame.html:

Replace the call isRequestAnimationFrameThrottled() by requestAnimationFrameThrottlingReasons().

Location:
trunk
Files:
1 added
45 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r261106 r261113  
     12020-05-04  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
     4        https://bugs.webkit.org/show_bug.cgi?id=204713
     5
     6        Reviewed by Simon Fraser.
     7
     8        * fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt:
     9        * fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html:
     10        * fast/animation/request-animation-frame-throttle-subframe-display-none-expected.txt:
     11        * fast/animation/request-animation-frame-throttle-subframe-display-none.html:
     12        * fast/animation/request-animation-frame-throttle-subframe-expected.txt:
     13        * fast/animation/request-animation-frame-throttle-subframe-zero-size-expected.txt:
     14        * fast/animation/request-animation-frame-throttle-subframe-zero-size.html:
     15        * fast/animation/request-animation-frame-throttle-subframe.html:
     16        * fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt:
     17        * fast/animation/request-animation-frame-throttling-detached-iframe.html:
     18        Replace the call isRequestAnimationFrameThrottled() by requestAnimationFrameThrottlingReasons().
     19
     20        * fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt:
     21        * fast/animation/request-animation-frame-throttling-lowPowerMode.html:
     22        Ensure the actual rAF interval is > 30ms for lowPowerMode.
     23
     24        * http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe-expected.txt:
     25        * http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe.html:
     26        * http/tests/frame-throttling/resources/requestAnimationFrame-frame.html:
     27        Replace the call isRequestAnimationFrameThrottled() by requestAnimationFrameThrottlingReasons().
     28
    1292020-05-04  Eric Carlson  <eric.carlson@apple.com>
    230
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt

    r225554 r261113  
    55
    66Frame is initially inside the viewport so requestAnimationFrame should not be throttled
    7 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
     7PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    88Scrolling overflow.
    99requestAnimationFrame should still not be throttled
    10 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
     10PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    1111PASS successfullyParsed is true
    1212
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html

    r225554 r261113  
    1515
    1616    debug("requestAnimationFrame should still not be throttled");
    17     shouldBeFalse("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     17    shouldBeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    1818   
    1919    finishJSTest();
     
    2424    testFrame = document.getElementById("testFrame");
    2525    debug("Frame is initially inside the viewport so requestAnimationFrame should not be throttled");
    26     shouldBeFalse("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     26    shouldBeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    2727    scrollOverflow();
    2828}
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-subframe-display-none-expected.txt

    r186195 r261113  
    44
    55
    6 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
     6PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    77PASS successfullyParsed is true
    88
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-subframe-display-none.html

    r186195 r261113  
    1414    document.body.offsetTop;
    1515
    16     shouldBeFalse("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     16    shouldBeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    1717    finishJSTest();
    1818}
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-subframe-expected.txt

    r183998 r261113  
    55
    66Frame is initially outside the viewport so requestAnimationFrame should be throttled
    7 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() became true
    8 PASS internals.isRequestAnimationFrameThrottled() is false
    9 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
    10 PASS grandChildFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
     7PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() became "OutsideViewport"
     8PASS internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
     9PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "OutsideViewport"
     10PASS grandChildFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "OutsideViewport"
    1111Scrolling frame into view.
    1212RequestAnimationFrame should no longer be throttled
    13 PASS internals.isRequestAnimationFrameThrottled() is false
    14 PASS grandChildFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
    15 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
     13PASS internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
     14PASS grandChildFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
     15PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    1616Scrolling frame out of view again.
    17 PASS internals.isRequestAnimationFrameThrottled() is false
    18 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() became true
    19 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
    20 PASS grandChildFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
     17PASS internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
     18PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() became "OutsideViewport"
     19PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "OutsideViewport"
     20PASS grandChildFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "OutsideViewport"
    2121PASS successfullyParsed is true
    2222
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-subframe-zero-size-expected.txt

    r186195 r261113  
    44
    55
    6 PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
     6PASS testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    77PASS successfullyParsed is true
    88
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-subframe-zero-size.html

    r186195 r261113  
    1414    document.body.offsetTop;
    1515
    16     shouldBeFalse("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     16    shouldBeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    1717    finishJSTest();
    1818}
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttle-subframe.html

    r256512 r261113  
    99function checkSubframesThrottled()
    1010{
    11     shouldBeTrue("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
    12     shouldBeTrue("grandChildFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     11    shouldBeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "OutsideViewport");
     12    shouldBeEqualToString("grandChildFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "OutsideViewport");
    1313
    1414    finishJSTest();
     
    2020    window.scroll(0, 0);
    2121
    22     shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
    23     shouldBecomeEqual("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()", "true", checkSubframesThrottled);
     22    shouldBeEqualToString("internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
     23    shouldBecomeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "OutsideViewport", checkSubframesThrottled);
    2424}
    2525
    2626function scrollFrameIntoView()
    2727{
    28     shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
    29     shouldBeTrue("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
    30     shouldBeTrue("grandChildFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     28    shouldBeEqualToString("internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
     29    shouldBeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "OutsideViewport");
     30    shouldBeEqualToString("grandChildFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "OutsideViewport");
    3131
    3232    debug("Scrolling frame into view.");
     
    3434
    3535    debug("RequestAnimationFrame should no longer be throttled");
    36     shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
    37     shouldBeFalse("grandChildFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
    38     shouldBeFalse("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     36    shouldBeEqualToString("internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
     37    shouldBeEqualToString("grandChildFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
     38    shouldBeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    3939
    4040    scrollFrameOutOfView();
     
    4646    grandChildFrame = testFrame.contentDocument.getElementById("grandChildFrame");
    4747    debug("Frame is initially outside the viewport so requestAnimationFrame should be throttled");
    48     shouldBecomeEqual("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()", "true", scrollFrameIntoView);
     48    shouldBecomeEqualToString("testFrame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "OutsideViewport", scrollFrameIntoView);
    4949}
    5050
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt

    r213169 r261113  
    44
    55
    6 PASS internals.isRequestAnimationFrameThrottled() is false
     6PASS internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    77PASS internals.requestAnimationFrameInterval is 0.015
    8 PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
     8PASS frame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    99PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.015
    1010internals.setLowPowerModeEnabled(true);
    11 PASS internals.isRequestAnimationFrameThrottled() is true
     11PASS internals.requestAnimationFrameThrottlingReasons() is "LowPowerMode"
    1212PASS internals.requestAnimationFrameInterval is 0.030
    13 PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
     13PASS frame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "LowPowerMode"
    1414PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.030
    1515frame.remove()
    1616document.body.appendChild(frame)
    17 PASS internals.isRequestAnimationFrameThrottled() is true
     17PASS internals.requestAnimationFrameThrottlingReasons() is "LowPowerMode"
    1818PASS internals.requestAnimationFrameInterval is 0.030
    19 PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
     19PASS frame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "LowPowerMode"
    2020PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.030
    2121frame.remove()
    2222internals.setLowPowerModeEnabled(false);
    23 PASS internals.isRequestAnimationFrameThrottled() is false
     23PASS internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    2424PASS internals.requestAnimationFrameInterval is 0.015
    2525document.body.appendChild(frame)
    26 PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
     26PASS frame.contentWindow.internals.requestAnimationFrameThrottlingReasons() is "[Unthrottled]"
    2727PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.015
    2828PASS successfullyParsed is true
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe.html

    r256512 r261113  
    1919frame.src = "resources/frame-with-animation.html";
    2020frame.onload = function() {
    21     shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
     21    shouldBeEqualToString("internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    2222    shouldBe("internals.requestAnimationFrameInterval", "0.015");
    23     shouldBeFalse("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     23    shouldBeEqualToString("frame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    2424    shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.015");
    2525
    2626    evalAndLog("internals.setLowPowerModeEnabled(true);");
    27     shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
     27    shouldBeEqualToString("internals.requestAnimationFrameThrottlingReasons()", "LowPowerMode");
    2828    shouldBe("internals.requestAnimationFrameInterval", "0.030");
    29     shouldBeTrue("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     29    shouldBeEqualToString("frame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "LowPowerMode");
    3030    shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.030");
    3131    evalAndLog("frame.remove()");
     
    3333    evalAndLog("document.body.appendChild(frame)");
    3434    frame.onload = function() {
    35         shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
     35        shouldBeEqualToString("internals.requestAnimationFrameThrottlingReasons()", "LowPowerMode");
    3636        shouldBe("internals.requestAnimationFrameInterval", "0.030");
    37         shouldBeTrue("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     37        shouldBeEqualToString("frame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "LowPowerMode");
    3838        shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.030");
    3939
    4040        evalAndLog("frame.remove()");
    4141        evalAndLog("internals.setLowPowerModeEnabled(false);");
    42         shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
     42        shouldBeEqualToString("internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    4343        shouldBe("internals.requestAnimationFrameInterval", "0.015");
    4444
    4545        evalAndLog("document.body.appendChild(frame)");
    4646        frame.onload = function() {
    47             shouldBeFalse("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
     47            shouldBeEqualToString("frame.contentWindow.internals.requestAnimationFrameThrottlingReasons()", "[Unthrottled]");
    4848            shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.015");
    4949            finishJSTest();
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt

    r256512 r261113  
    44
    55
    6 PASS internals.isRequestAnimationFrameThrottled() is false
    7 PASS internals.requestAnimationFrameInterval is Infinity
    8 rAFHandle = requestAnimationFrame(doWork);
    9 PASS internals.isRequestAnimationFrameThrottled() is false
    10 PASS internals.requestAnimationFrameInterval is 0.015
    11 internals.setLowPowerModeEnabled(true);
    12 PASS internals.isRequestAnimationFrameThrottled() is true
    13 PASS internals.requestAnimationFrameInterval is 0.030
    14 cancelAnimationFrame(rAFHandle);
    15 PASS internals.isRequestAnimationFrameThrottled() is true
    16 PASS internals.requestAnimationFrameInterval is 0.030
    17 rAFHandle = requestAnimationFrame(doWork);
    18 PASS internals.isRequestAnimationFrameThrottled() is true
    19 PASS internals.requestAnimationFrameInterval is 0.030
    20 internals.setLowPowerModeEnabled(false);
    21 PASS internals.isRequestAnimationFrameThrottled() is false
    22 PASS internals.requestAnimationFrameInterval is 0.015
     6PASS farmesPerSecond < 35 is true
    237PASS successfullyParsed is true
    248
  • trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode.html

    r256512 r261113  
    22<html>
    33<body>
    4 <script src="../../resources/js-test-pre.js"></script>
    5 <script>
    6 description("Test that requestAnimationFrame gets throttled in low power mode.");
     4    <script src="../../resources/js-test-pre.js"></script>
     5    <script>
     6        description("Test that requestAnimationFrame gets throttled in low power mode.");
     7        window.jsTestIsAsync = true;
    78
    8 let rAFHandle;
    9 let i = 0;
    10 function doWork()
    11 {
    12     i++;
    13     rAFHandle = requestAnimationFrame(doWork);
    14 }
     9        if (window.internals)
     10            internals.setLowPowerModeEnabled(true);
    1511
    16 shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
    17 shouldBe("internals.requestAnimationFrameInterval", "Infinity");
    18 evalAndLog("rAFHandle = requestAnimationFrame(doWork);");
    19 shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
    20 shouldBe("internals.requestAnimationFrameInterval", "0.015");
    21 evalAndLog("internals.setLowPowerModeEnabled(true);");
    22 shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
    23 shouldBe("internals.requestAnimationFrameInterval", "0.030");
    24 evalAndLog("cancelAnimationFrame(rAFHandle);");
    25 shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
    26 shouldBe("internals.requestAnimationFrameInterval", "0.030");
    27 evalAndLog("rAFHandle = requestAnimationFrame(doWork);");
    28 shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
    29 shouldBe("internals.requestAnimationFrameInterval", "0.030");
    30 evalAndLog("internals.setLowPowerModeEnabled(false);");
    31 shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
    32 shouldBe("internals.requestAnimationFrameInterval", "0.015");
    33 </script>
    34 <script src="../../resources/js-test-post.js"></script>
     12        var start = null;
     13        var farmesPerSecond = 0;
     14        function doWork(timestamp) {
     15            if (!start)
     16                start = timestamp;
     17            if (timestamp - start < 1000) {
     18                ++farmesPerSecond;
     19                window.requestAnimationFrame(doWork);
     20            }
     21            else {
     22                // The LowPowerMode throttling interval = 30_ms. The frame rate ~= 33.3 fps.
     23                shouldBeTrue("farmesPerSecond < 35");
     24                finishJSTest();
     25            }
     26        }
     27        window.requestAnimationFrame(doWork);
     28    </script>
     29    <script src="../../resources/js-test-post.js"></script>
    3530</body>
    3631</html>
  • trunk/LayoutTests/http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe-expected.txt

    r215070 r261113  
    77Received message: frameload
    88Checking that requestAnimationFrame is throttled in cross origin frame
    9 Received message: throttled[cross]: true
    10 Received message: throttled[same]: false
    11 PASS throttledState['cross'] is "true"
    12 PASS throttledState['same'] is "false"
     9Received message: throttled[cross]: NonInteractiveCrossOriginFrame
     10Received message: throttled[same]: [Unthrottled]
     11PASS throttledState['cross'] is "NonInteractiveCrossOriginFrame"
     12PASS throttledState['same'] is "[Unthrottled]"
    1313Interacted with cross-origin frame
    1414Interacted with same-origin frame
    15 Received message: throttled[cross]: false
    16 Received message: throttled[same]: false
    17 PASS throttledState['cross'] is "false"
    18 PASS throttledState['same'] is "false"
     15Received message: throttled[cross]: [Unthrottled]
     16Received message: throttled[same]: [Unthrottled]
     17PASS throttledState['cross'] is "[Unthrottled]"
     18PASS throttledState['same'] is "[Unthrottled]"
    1919PASS successfullyParsed is true
    2020
  • trunk/LayoutTests/http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe.html

    r256512 r261113  
    5555    function checkInitiallyThrottled()
    5656    {
    57         shouldBeEqualToString("throttledState['cross']", "true");
    58         shouldBeEqualToString("throttledState['same']", "false");
     57        shouldBeEqualToString("throttledState['cross']", "NonInteractiveCrossOriginFrame");
     58        shouldBeEqualToString("throttledState['same']", "[Unthrottled]");
    5959        interactWithSubframes();
    6060    }
     
    6262    function checkUnthrottledAfterInteraction()
    6363    {
    64         shouldBeEqualToString("throttledState['cross']", "false");
    65         shouldBeEqualToString("throttledState['same']", "false");
     64        shouldBeEqualToString("throttledState['cross']", "[Unthrottled]");
     65        shouldBeEqualToString("throttledState['same']", "[Unthrottled]");
    6666        finishJSTest();
    6767    }
     
    7676        }
    7777
    78         var re = /throttled\[(\w+)\]: (true|false)/;
     78        var re = /throttled\[(\w+)\]: ((\w+)(\|\w+)*|\[Unthrottled\])/;
    7979        var match = re.exec(message.data);
    8080        if (match) {
  • trunk/LayoutTests/http/tests/frame-throttling/resources/requestAnimationFrame-frame.html

    r215820 r261113  
    1919                var frameId = match[1];
    2020                if (window.internals)
    21                     parent.window.postMessage("throttled[" + frameId + "]: " + internals.isRequestAnimationFrameThrottled(), "*");
     21                    parent.window.postMessage("throttled[" + frameId + "]: " + internals.requestAnimationFrameThrottlingReasons(), "*");
    2222            }
    2323        }
  • trunk/Source/WebCore/ChangeLog

    r261110 r261113  
     12020-05-04  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
     4        https://bugs.webkit.org/show_bug.cgi?id=204713
     5
     6        Reviewed by Simon Fraser.
     7
     8        rAF and Page rendering were managed by two different timers. Throttling
     9        rAF was implemented by changing its timer. After r242624, RenderingUpdate
     10        steps have been managed by RenderingUpdateScheduler. This means rAF is
     11        now serviced by the preferredFramesPerSecond which is 60 fps regardless
     12        it's throttled or not. Moreover the rAF throttling timer was mistakenly
     13        kept and it has been running under the old assumption which is: rAF is
     14        serviced by a timer only. This means rAF will be serviced by its timer
     15        and by the RenderingUpdate steps at the same time when it is supposed to
     16        throttle. This will make it fire more than 60 fps in cases which it is
     17        supposed to run less than 60 fps.
     18
     19        The solution is to have two throttling types:
     20
     21        1) Page throttling (or full throttling): This slows down all the steps
     22           of RenderingUpdate for the main document and all the sub-documents.
     23           Page throttling reasons are:
     24           -- VisuallyIdle: Aggressive throttling.
     25           -- LowPowerMode: Half speed throttling.
     26        2) Document throttling (or partial throttling): This only slows down the
     27           rAF of a certain document. Document throttling reasons are:
     28           -- OutsideViewport: Aggressive throttling.
     29           -- NonInteractedCrossOriginFrame: Half speed throttling.
     30
     31        RenderingUpdate steps will still be managed by RenderingUpdateScheduler
     32        which can be throttled. The assumption is none of these steps will need
     33        to run faster than the Page preferredFramesPerSecond. If rAF wants to
     34        run slower than the Page because of a Document throttling reason, no rAF
     35        callbacks will be serviced before its preferredFrameInterval has elapsed.
     36
     37        In this patch, "Half speed throttling" is only implemented for the Page
     38        and the Document throttling. The "Aggressive throttling" will be done in
     39        following patches. Page rendering was never throttled before. We need to
     40        make sure this is not going to affect PLT. Some tests need to be changed
     41        and new tests need to be written.  All of the throttling tests checks the
     42        state of the code but none of them checks the real user's experience.
     43
     44        * Headers.cmake:
     45        * WebCore.xcodeproj/project.pbxproj:
     46
     47        * animation/DocumentTimeline.cpp:
     48        (WebCore::DocumentTimeline::animationInterval const):
     49        (WebCore::DocumentTimeline::updateThrottlingState): Deleted.
     50        * animation/DocumentTimeline.h:
     51        There is no need to have DocumentTimeline throttling. It is already
     52        throttled when the page RenderingUpdate is throttled.
     53
     54        * dom/Document.cpp:
     55        (WebCore::Document::requestAnimationFrame):
     56        (WebCore::Document::updateLastHandledUserGestureTimestamp):
     57        LowPowerMode throttling is now handled by the Page. So remove its handling
     58        from the Document.
     59
     60        * dom/ScriptedAnimationController.cpp:
     61        (WebCore::ScriptedAnimationController::ScriptedAnimationController):
     62        (WebCore::ScriptedAnimationController::page const):
     63        (WebCore::ScriptedAnimationController::interval const):
     64        (WebCore::ScriptedAnimationController::preferredScriptedAnimationInterval const):
     65        (WebCore::ScriptedAnimationController::throttlingReasons const):
     66        (WebCore::ScriptedAnimationController::isThrottledRelativeToPage const):
     67        (WebCore::ScriptedAnimationController::shouldRescheduleRequestAnimationFrame const):
     68        (WebCore::ScriptedAnimationController::registerCallback):
     69        (WebCore::ScriptedAnimationController::cancelCallback):
     70        (WebCore::ScriptedAnimationController::serviceRequestAnimationFrameCallbacks):
     71        (WebCore::ScriptedAnimationController::scheduleAnimation):
     72        (WebCore::throttlingReasonToString): Deleted.
     73        (WebCore::throttlingReasonsToString): Deleted.
     74        (WebCore::ScriptedAnimationController::addThrottlingReason): Deleted.
     75        (WebCore::ScriptedAnimationController::removeThrottlingReason): Deleted.
     76        (WebCore::ScriptedAnimationController::isThrottled const): Deleted.
     77        (WebCore::ScriptedAnimationController::animationTimerFired): Deleted.
     78        * dom/ScriptedAnimationController.h:
     79        (WebCore::ScriptedAnimationController::addThrottlingReason):
     80        (WebCore::ScriptedAnimationController::removeThrottlingReason):
     81        Get rid of the rAF throttling timer. Service the rAF callback only when
     82        the period from the current time stamp till the last service time stamp
     83        is greater than the preferred rAF interval.
     84
     85        * page/FrameView.cpp:
     86        (WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
     87        ThrottlingReason is now defined outside ScriptedAnimationController.
     88
     89        * page/Page.cpp:
     90        (WebCore::m_loadsFromNetwork):
     91        (WebCore::Page::setLowPowerModeEnabledOverrideForTesting):
     92
     93        (WebCore::Page::preferredRenderingUpdateInterval const):
     94        Calculate the preferred RenderingUpdate interval from the throttling
     95        reasons.
     96
     97        (WebCore::Page::setIsVisuallyIdleInternal):
     98        (WebCore::Page::handleLowModePowerChange):
     99        Call adjustRenderingUpdateFrequency() when isLowPowerModeEnabled or
     100        IsVisuallyIdle is toggled.
     101
     102        (WebCore::Page::isLowPowerModeEnabled const): Deleted.
     103        (WebCore::updateScriptedAnimationsThrottlingReason): Deleted.
     104        * page/Page.h:
     105        (WebCore::Page::isLowPowerModeEnabled const):
     106        (WebCore::Page::throttlingReasons const):
     107        (WebCore::Page::canUpdateThrottlingReason const):
     108
     109        * page/RenderingUpdateScheduler.cpp:
     110        (WebCore::RenderingUpdateScheduler::setPreferredFramesPerSecond):
     111        (WebCore::RenderingUpdateScheduler::scheduleAnimation):
     112        (WebCore::RenderingUpdateScheduler::adjustRenderingUpdateFrequency):
     113        Change the preferredFramesPerSecond of the DisplayRefreshMonitor if the
     114        throttling is not aggressive e.g. 10_s. Otherwise use the timer.
     115
     116        (WebCore::RenderingUpdateScheduler::scheduleTimedRenderingUpdate):
     117        Call adjustFramesPerSecond() when DisplayRefreshMonitor is created.
     118
     119        (WebCore::RenderingUpdateScheduler::startTimer):
     120        * page/RenderingUpdateScheduler.h:
     121        * platform/graphics/AnimationFrameRate.h: Added.
     122        (WebCore::preferredFrameInterval):
     123        (WebCore::preferredFramesPerSecond):
     124        (WebCore::operator<<):
     125        Push names of ThrottlingReasons to a TextStream.
     126
     127        * platform/graphics/DisplayRefreshMonitor.h:
     128        (WebCore::DisplayRefreshMonitor::setPreferredFramesPerSecond):
     129        * platform/graphics/DisplayRefreshMonitorManager.cpp:
     130        (WebCore::DisplayRefreshMonitorManager::monitorForClient):
     131        Rename createMonitorForClient() to monitorForClient() since it may return
     132        a cached DisplayRefreshMonitor.
     133
     134        (WebCore::DisplayRefreshMonitorManager::setPreferredFramesPerSecond):
     135        (WebCore::DisplayRefreshMonitorManager::scheduleAnimation):
     136        (WebCore::DisplayRefreshMonitorManager::windowScreenDidChange):
     137        No need to call registerClient(). This function was just ensuring the
     138        DisplayRefreshMonitor is created. scheduleAnimation() does the same thing.
     139
     140        (WebCore::DisplayRefreshMonitorManager::createMonitorForClient): Deleted.
     141        (WebCore::DisplayRefreshMonitorManager::registerClient): Deleted.
     142        * platform/graphics/DisplayRefreshMonitorManager.h:
     143        (WebCore::DisplayRefreshMonitorManager::DisplayRefreshMonitorManager): Deleted.
     144
     145        * platform/graphics/GraphicsLayerUpdater.cpp:
     146        (WebCore::GraphicsLayerUpdater::GraphicsLayerUpdater):
     147        * platform/graphics/ios/DisplayRefreshMonitorIOS.mm:
     148        (-[WebDisplayLinkHandler setPreferredFramesPerSecond:]):
     149        Set the preferredFramesPerSecond of the CADisplayLink.
     150
     151        * testing/Internals.cpp:
     152        (WebCore::Internals::requestAnimationFrameThrottlingReasons const):
     153        (WebCore::Internals::isRequestAnimationFrameThrottled const): Deleted.
     154        * testing/Internals.h:
     155        * testing/Internals.idl:
     156        Replace isRequestAnimationFrameThrottled() which returns a boolean by
     157        requestAnimationFrameThrottlingReasons() which returns a string. The
     158        string represents the throttling reasons.
     159
    11602020-05-04  Darin Adler  <darin@apple.com>
    2161
  • trunk/Source/WebCore/Headers.cmake

    r260736 r261113  
    10731073    platform/graphics/AlphaPremultiplication.h
    10741074    platform/graphics/ANGLEWebKitBridge.h
     1075    platform/graphics/AnimationFrameRate.h
    10751076    platform/graphics/AudioTrackPrivate.h
    10761077    platform/graphics/BitmapImage.h
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r261077 r261113  
    21562156                7299BC6723D6A53200CC6883 /* AlphaPremultiplication.h in Headers */ = {isa = PBXBuildFile; fileRef = 7299BC6423D686A600CC6883 /* AlphaPremultiplication.h */; settings = {ATTRIBUTES = (Private, ); }; };
    21572157                7299BC6823D6A53E00CC6883 /* RenderingMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 7299BC6623D686C600CC6883 /* RenderingMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
     2158                72A73BEF245A3F90001C9D03 /* AnimationFrameRate.h in Headers */ = {isa = PBXBuildFile; fileRef = 722A815C238FD50500C00583 /* AnimationFrameRate.h */; settings = {ATTRIBUTES = (Private, ); }; };
    21582159                72BAC3AE23E1F0B0008D741C /* ImageBufferBackend.h in Headers */ = {isa = PBXBuildFile; fileRef = 72BAC3A523E17328008D741C /* ImageBufferBackend.h */; settings = {ATTRIBUTES = (Private, ); }; };
    21592160                7553CFE8108F473F00EA281E /* TimelineRecordFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 7553CFE6108F473F00EA281E /* TimelineRecordFactory.h */; };
     
    97149715                721443452240C8BA00F12FF7 /* SVGAnimatedValueProperty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGAnimatedValueProperty.h; sourceTree = "<group>"; };
    97159716                721443462240CAD200F12FF7 /* SVGValueProperty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGValueProperty.h; sourceTree = "<group>"; };
     9717                722A815C238FD50500C00583 /* AnimationFrameRate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimationFrameRate.h; sourceTree = "<group>"; };
    97169718                724ED3291A3A7E5400F5F13C /* EXTBlendMinMax.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EXTBlendMinMax.cpp; sourceTree = "<group>"; };
    97179719                724ED32A1A3A7E5400F5F13C /* EXTBlendMinMax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTBlendMinMax.h; sourceTree = "<group>"; };
     
    2532525327                                490707E41219C04300D90E51 /* ANGLEWebKitBridge.cpp */,
    2532625328                                490707E51219C04300D90E51 /* ANGLEWebKitBridge.h */,
     25329                                722A815C238FD50500C00583 /* AnimationFrameRate.h */,
    2532725330                                BEF29EE91715DD0900C4B4C9 /* AudioTrackPrivate.h */,
    2532825331                                A89943270B42338700D7C802 /* BitmapImage.cpp */,
     
    2951529518                                319848011A1D817B00A13318 /* AnimationEvent.h in Headers */,
    2951629519                                711AD126236D86E5006FF37C /* AnimationEventBase.h in Headers */,
     29520                                72A73BEF245A3F90001C9D03 /* AnimationFrameRate.h in Headers */,
    2951729521                                49E912AD0EFAC906009D0CAF /* AnimationList.h in Headers */,
    2951829522                                714C7C661FDAD2A100F2BEE1 /* AnimationPlaybackEvent.h in Headers */,
  • trunk/Source/WebCore/animation/DocumentTimeline.cpp

    r260736 r261113  
    4747#include <JavaScriptCore/VM.h>
    4848
    49 static const Seconds defaultAnimationInterval { 15_ms };
    50 static const Seconds throttledAnimationInterval { 30_ms };
    51 
    5249namespace WebCore {
    5350
     
    215212}
    216213
    217 void DocumentTimeline::updateThrottlingState()
    218 {
    219     scheduleAnimationResolution();
    220 }
    221 
    222214Seconds DocumentTimeline::animationInterval() const
    223215{
    224216    if (!m_document || !m_document->page())
    225217        return Seconds::infinity();
    226     return m_document->page()->isLowPowerModeEnabled() ? throttledAnimationInterval : defaultAnimationInterval;
     218    return m_document->page()->preferredRenderingUpdateInterval();
    227219}
    228220
  • trunk/Source/WebCore/animation/DocumentTimeline.h

    r260736 r261113  
    8181    void documentDidUpdateAnimationsAndSendEvents();
    8282
    83     void updateThrottlingState();
    8483    WEBCORE_EXPORT Seconds animationInterval() const;
    8584    WEBCORE_EXPORT void suspendAnimations();
  • trunk/Source/WebCore/dom/Document.cpp

    r261028 r261113  
    65546554            m_scriptedAnimationController->suspend();
    65556555
    6556         if (page() && page()->isLowPowerModeEnabled())
    6557             m_scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::LowPowerMode);
    6558 
    65596556        if (!topOrigin().canAccess(securityOrigin()) && !hasHadUserInteraction())
    6560             m_scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::NonInteractedCrossOriginFrame);
     6557            m_scriptedAnimationController->addThrottlingReason(ThrottlingReason::NonInteractedCrossOriginFrame);
    65616558    }
    65626559
     
    67976794    if (static_cast<bool>(time) && m_scriptedAnimationController) {
    67986795        // It's OK to always remove NonInteractedCrossOriginFrame even if this frame isn't cross-origin.
    6799         m_scriptedAnimationController->removeThrottlingReason(ScriptedAnimationController::ThrottlingReason::NonInteractedCrossOriginFrame);
     6796        m_scriptedAnimationController->removeThrottlingReason(ThrottlingReason::NonInteractedCrossOriginFrame);
    68006797    }
    68016798
  • trunk/Source/WebCore/dom/ScriptedAnimationController.cpp

    r260870 r261113  
    11/*
    22 * Copyright (C) 2011 Google Inc. All Rights Reserved.
     3 * Copyright (C) 2020 Apple Inc.  All rights reserved.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    2728#include "ScriptedAnimationController.h"
    2829
    29 #include "Chrome.h"
    30 #include "ChromeClient.h"
    31 #include "CustomHeaderFields.h"
    32 #include "DOMWindow.h"
    33 #include "Document.h"
    34 #include "DocumentLoader.h"
    35 #include "Frame.h"
    36 #include "FrameView.h"
    3730#include "InspectorInstrumentation.h"
    38 #include "Logging.h"
    3931#include "Page.h"
    40 #include "Performance.h"
    4132#include "Quirks.h"
    4233#include "RequestAnimationFrameCallback.h"
    4334#include "Settings.h"
    44 #include <algorithm>
    4535#include <wtf/Ref.h>
    4636#include <wtf/SystemTracing.h>
    47 #include <wtf/text/StringBuilder.h>
    48 
    49 // Allow a little more than 60fps to make sure we can at least hit that frame rate.
    50 static const Seconds fullSpeedAnimationInterval { 15_ms };
    51 // Allow a little more than 30fps to make sure we can at least hit that frame rate.
    52 static const Seconds halfSpeedThrottlingAnimationInterval { 30_ms };
    53 static const Seconds aggressiveThrottlingAnimationInterval { 10_s };
    54 
    55 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(page() && page()->isAlwaysOnLoggingAllowed(), PerformanceLogging, "%p - ScriptedAnimationController::" fmt, this, ##__VA_ARGS__)
    5637
    5738namespace WebCore {
     
    5940ScriptedAnimationController::ScriptedAnimationController(Document& document)
    6041    : m_document(makeWeakPtr(document))
    61     , m_animationTimer(*this, &ScriptedAnimationController::animationTimerFired)
    6242{
    6343}
     
    8666}
    8767
    88 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) && !RELEASE_LOG_DISABLED
    89 
    90 static const char* throttlingReasonToString(ScriptedAnimationController::ThrottlingReason reason)
     68Page* ScriptedAnimationController::page() const
    9169{
    92     switch (reason) {
    93     case ScriptedAnimationController::ThrottlingReason::VisuallyIdle:
    94         return "VisuallyIdle";
    95     case ScriptedAnimationController::ThrottlingReason::OutsideViewport:
    96         return "OutsideViewport";
    97     case ScriptedAnimationController::ThrottlingReason::LowPowerMode:
    98         return "LowPowerMode";
    99     case ScriptedAnimationController::ThrottlingReason::NonInteractedCrossOriginFrame:
    100         return "NonInteractiveCrossOriginFrame";
    101     }
    102     RELEASE_ASSERT_NOT_REACHED();
    103     return "";
     70    return m_document ? m_document->page() : nullptr;
    10471}
    10572
    106 static String throttlingReasonsToString(OptionSet<ScriptedAnimationController::ThrottlingReason> reasons)
     73Seconds ScriptedAnimationController::interval() const
    10774{
    108     if (reasons.isEmpty())
    109         return "[Unthrottled]"_s;
    110 
    111     StringBuilder builder;
    112     for (auto reason : reasons) {
    113         if (!builder.isEmpty())
    114             builder.append('|');
    115         builder.append(throttlingReasonToString(reason));
    116     }
    117     return builder.toString();
     75    if (auto* page = this->page())
     76        return std::max(preferredScriptedAnimationInterval(), page->preferredRenderingUpdateInterval());
     77    return FullSpeedAnimationInterval;
    11878}
    11979
    120 #endif
    121 
    122 void ScriptedAnimationController::addThrottlingReason(ThrottlingReason reason)
     80Seconds ScriptedAnimationController::preferredScriptedAnimationInterval() const
    12381{
    124 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    125     if (m_throttlingReasons.contains(reason))
    126         return;
    127 
    128     m_throttlingReasons.add(reason);
    129 
    130     RELEASE_LOG_IF_ALLOWED("addThrottlingReason(%s) -> %s", throttlingReasonToString(reason), throttlingReasonsToString(m_throttlingReasons).utf8().data());
    131 
    132     if (m_animationTimer.isActive()) {
    133         m_animationTimer.stop();
    134         scheduleAnimation();
    135     }
    136 #else
    137     UNUSED_PARAM(reason);
    138 #endif
     82    if (auto* page = this->page())
     83        return preferredFrameInterval(m_throttlingReasons);
     84    return FullSpeedAnimationInterval;
    13985}
    14086
    141 void ScriptedAnimationController::removeThrottlingReason(ThrottlingReason reason)
     87OptionSet<ThrottlingReason> ScriptedAnimationController::throttlingReasons() const
    14288{
    143 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    144     if (!m_throttlingReasons.contains(reason))
    145         return;
    146 
    147     m_throttlingReasons.remove(reason);
    148 
    149     RELEASE_LOG_IF_ALLOWED("removeThrottlingReason(%s) -> %s", throttlingReasonToString(reason), throttlingReasonsToString(m_throttlingReasons).utf8().data());
    150 
    151     if (m_animationTimer.isActive()) {
    152         m_animationTimer.stop();
    153         scheduleAnimation();
    154     }
    155 #else
    156     UNUSED_PARAM(reason);
    157 #endif
     89    if (auto* page = this->page())
     90        return page->throttlingReasons() | m_throttlingReasons;
     91    return { };
    15892}
    15993
    160 bool ScriptedAnimationController::isThrottled() const
     94bool ScriptedAnimationController::isThrottledRelativeToPage() const
    16195{
    162 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    163     return !m_throttlingReasons.isEmpty();
    164 #else
     96    if (auto* page = this->page())
     97        return preferredScriptedAnimationInterval() > page->preferredRenderingUpdateInterval();
    16598    return false;
    166 #endif
     99}
     100
     101bool ScriptedAnimationController::shouldRescheduleRequestAnimationFrame(ReducedResolutionSeconds timestamp) const
     102{
     103    return isThrottledRelativeToPage() && (timestamp - m_lastAnimationFrameTimestamp < preferredScriptedAnimationInterval());
    167104}
    168105
    169106ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(Ref<RequestAnimationFrameCallback>&& callback)
    170107{
    171     ScriptedAnimationController::CallbackId id = ++m_nextCallbackId;
     108    CallbackId callbackId = ++m_nextCallbackId;
    172109    callback->m_firedOrCancelled = false;
    173     callback->m_id = id;
     110    callback->m_id = callbackId;
    174111    m_callbacks.append(WTFMove(callback));
    175112
    176113    if (m_document)
    177         InspectorInstrumentation::didRequestAnimationFrame(*m_document, id);
     114        InspectorInstrumentation::didRequestAnimationFrame(*m_document, callbackId);
    178115
    179116    if (!m_suspendCount)
    180117        scheduleAnimation();
    181     return id;
     118    return callbackId;
    182119}
    183120
    184 void ScriptedAnimationController::cancelCallback(CallbackId id)
     121void ScriptedAnimationController::cancelCallback(CallbackId callbackId)
    185122{
    186     for (size_t i = 0; i < m_callbacks.size(); ++i) {
    187         if (m_callbacks[i]->m_id == id) {
    188             m_callbacks[i]->m_firedOrCancelled = true;
    189             InspectorInstrumentation::didCancelAnimationFrame(*m_document, id);
    190             m_callbacks.remove(i);
    191             return;
    192         }
    193     }
     123    bool cancelled = m_callbacks.removeFirstMatching([callbackId](auto& callback) {
     124        if (callback->m_id != callbackId)
     125            return false;
     126        callback->m_firedOrCancelled = true;
     127        return true;
     128    });
     129
     130    if (cancelled && m_document)
     131        InspectorInstrumentation::didCancelAnimationFrame(*m_document, callbackId);
    194132}
    195133
     
    199137        return;
    200138
     139    if (shouldRescheduleRequestAnimationFrame(timestamp)) {
     140        scheduleAnimation();
     141        return;
     142    }
     143   
    201144    TraceScope tracingScope(RAFCallbackStart, RAFCallbackEnd);
    202145
     
    229172    });
    230173
     174    m_lastAnimationFrameTimestamp = timestamp;
     175
    231176    if (m_callbacks.size())
    232177        scheduleAnimation();
    233 }
    234 
    235 Seconds ScriptedAnimationController::interval() const
    236 {
    237 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    238     if (m_throttlingReasons.contains(ThrottlingReason::VisuallyIdle) || m_throttlingReasons.contains(ThrottlingReason::OutsideViewport))
    239         return aggressiveThrottlingAnimationInterval;
    240 
    241     if (m_throttlingReasons.contains(ThrottlingReason::LowPowerMode))
    242         return halfSpeedThrottlingAnimationInterval;
    243 
    244     if (m_throttlingReasons.contains(ThrottlingReason::NonInteractedCrossOriginFrame))
    245         return halfSpeedThrottlingAnimationInterval;
    246 
    247     ASSERT(m_throttlingReasons.isEmpty());
    248 #endif
    249     return fullSpeedAnimationInterval;
    250 }
    251 
    252 Page* ScriptedAnimationController::page() const
    253 {
    254     return m_document ? m_document->page() : nullptr;
    255178}
    256179
     
    260183        return;
    261184
    262 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    263     if (!m_isUsingTimer && !isThrottled()) {
    264         if (auto* page = this->page()) {
    265             page->renderingUpdateScheduler().scheduleTimedRenderingUpdate();
    266             return;
    267         }
    268 
    269         m_isUsingTimer = true;
    270     }
    271 #endif
    272     if (m_animationTimer.isActive())
    273         return;
    274 
    275     Seconds animationInterval = interval();
    276     Seconds scheduleDelay = std::max(animationInterval - (m_document->domWindow()->nowTimestamp() - m_lastAnimationFrameTimestamp), 0_s);
    277 
    278     if (isThrottled()) {
    279         // FIXME: not ideal to snapshot time both in now() and nowTimestamp(), the latter of which also has reduced resolution.
    280         MonotonicTime now = MonotonicTime::now();
    281 
    282         MonotonicTime fireTime = now + scheduleDelay;
    283         Seconds alignmentInterval = 10_ms;
    284         // Snap to the nearest alignmentInterval.
    285         Seconds alignment = (fireTime + alignmentInterval / 2) % alignmentInterval;
    286         MonotonicTime alignedFireTime = fireTime - alignment;
    287         scheduleDelay = alignedFireTime - now;
    288     }
    289 
    290     m_animationTimer.startOneShot(scheduleDelay);
    291 }
    292 
    293 void ScriptedAnimationController::animationTimerFired()
    294 {
    295     m_lastAnimationFrameTimestamp = m_document->domWindow()->nowTimestamp();
    296     serviceRequestAnimationFrameCallbacks(m_lastAnimationFrameTimestamp);
     185    if (auto* page = this->page())
     186        page->renderingUpdateScheduler().scheduleTimedRenderingUpdate();
    297187}
    298188
  • trunk/Source/WebCore/dom/ScriptedAnimationController.h

    r260818 r261113  
    11/*
    22 * Copyright (C) 2011 Google Inc. All Rights Reserved.
     3 * Copyright (C) 2020 Apple Inc.  All rights reserved.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    2627#pragma once
    2728
    28 #include "DOMHighResTimeStamp.h"
     29#include "AnimationFrameRate.h"
    2930#include "ReducedResolutionSeconds.h"
    3031#include "Timer.h"
     
    5152    bool requestAnimationFrameEnabled() const;
    5253
    53     typedef int CallbackId;
     54    WEBCORE_EXPORT Seconds interval() const;
     55    WEBCORE_EXPORT OptionSet<ThrottlingReason> throttlingReasons() const;
    5456
     57    void suspend();
     58    void resume();
     59
     60    void addThrottlingReason(ThrottlingReason reason) { m_throttlingReasons.add(reason); }
     61    void removeThrottlingReason(ThrottlingReason reason) { m_throttlingReasons.remove(reason); }
     62
     63    using CallbackId = int;
    5564    CallbackId registerCallback(Ref<RequestAnimationFrameCallback>&&);
    5665    void cancelCallback(CallbackId);
    5766    void serviceRequestAnimationFrameCallbacks(ReducedResolutionSeconds);
    5867
    59     void suspend();
    60     void resume();
    61 
    62     enum class ThrottlingReason {
    63         VisuallyIdle                    = 1 << 0,
    64         OutsideViewport                 = 1 << 1,
    65         LowPowerMode                    = 1 << 2,
    66         NonInteractedCrossOriginFrame   = 1 << 3,
    67     };
    68     void addThrottlingReason(ThrottlingReason);
    69     void removeThrottlingReason(ThrottlingReason);
    70 
    71     WEBCORE_EXPORT bool isThrottled() const;
    72     WEBCORE_EXPORT Seconds interval() const;
    73 
    7468private:
    7569    ScriptedAnimationController(Document&);
    7670
     71    Page* page() const;
     72    Seconds preferredScriptedAnimationInterval() const;
     73    bool isThrottledRelativeToPage() const;
     74    bool shouldRescheduleRequestAnimationFrame(ReducedResolutionSeconds) const;
    7775    void scheduleAnimation();
    78     void animationTimerFired();
    7976
    80     Page* page() const;
    81 
    82     typedef Vector<RefPtr<RequestAnimationFrameCallback>> CallbackList;
     77    using CallbackList = Vector<RefPtr<RequestAnimationFrameCallback>>;
    8378    CallbackList m_callbacks;
    8479
     
    8782    int m_suspendCount { 0 };
    8883
    89     Timer m_animationTimer;
    90     ReducedResolutionSeconds m_lastAnimationFrameTimestamp { 0 };
    91 
    92 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
     84    ReducedResolutionSeconds m_lastAnimationFrameTimestamp;
    9385    OptionSet<ThrottlingReason> m_throttlingReasons;
    94     bool m_isUsingTimer { false };
    95 #endif
    9686};
    9787
  • trunk/Source/WebCore/page/FrameView.cpp

    r261044 r261113  
    25312531    if (auto* scriptedAnimationController = document->scriptedAnimationController()) {
    25322532        if (shouldThrottle)
    2533             scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
     2533            scriptedAnimationController->addThrottlingReason(ThrottlingReason::OutsideViewport);
    25342534        else
    2535             scriptedAnimationController->removeThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
     2535            scriptedAnimationController->removeThrottlingReason(ThrottlingReason::OutsideViewport);
    25362536    }
    25372537
  • trunk/Source/WebCore/page/Page.cpp

    r261054 r261113  
    2323#include "ActivityStateChangeObserver.h"
    2424#include "AlternativeTextClient.h"
     25#include "AnimationFrameRate.h"
    2526#include "ApplicationCacheStorage.h"
    2627#include "AuthenticatorCoordinator.h"
     
    334335    m_libWebRTCProvider->supportsH265(RuntimeEnabledFeatures::sharedFeatures().webRTCH265CodecEnabled());
    335336#endif
    336    
     337
    337338    if (!pageConfiguration.userScriptsShouldWaitUntilNotification)
    338339        m_hasBeenNotifiedToInjectUserScripts = true;
     340
     341    if (m_lowPowerModeNotifier->isLowPowerModeEnabled())
     342        m_throttlingReasons.add(ThrottlingReason::LowPowerMode);
    339343}
    340344
     
    11711175}
    11721176
    1173 bool Page::isLowPowerModeEnabled() const
    1174 {
    1175     if (m_lowPowerModeEnabledOverrideForTesting)
    1176         return m_lowPowerModeEnabledOverrideForTesting.value();
    1177 
    1178     return m_lowPowerModeNotifier->isLowPowerModeEnabled();
    1179 }
    1180 
    11811177void Page::setLowPowerModeEnabledOverrideForTesting(Optional<bool> isEnabled)
    11821178{
    1183     m_lowPowerModeEnabledOverrideForTesting = isEnabled;
    1184     handleLowModePowerChange(m_lowPowerModeEnabledOverrideForTesting.valueOr(false));
     1179    // Remove ThrottlingReason::LowPowerMode so handleLowModePowerChange() can do its work.
     1180    m_throttlingReasonsOverridenForTesting.remove(ThrottlingReason::LowPowerMode);
     1181
     1182    // Use the current low power mode value of the device.
     1183    if (!isEnabled) {
     1184        handleLowModePowerChange(m_lowPowerModeNotifier->isLowPowerModeEnabled());
     1185        return;
     1186    }
     1187
     1188    // Override the value and add ThrottlingReason::LowPowerMode so it override the device state.
     1189    handleLowModePowerChange(isEnabled.value());
     1190    m_throttlingReasonsOverridenForTesting.add(ThrottlingReason::LowPowerMode);
    11851191}
    11861192
     
    14751481}
    14761482
    1477 enum class ThrottlingReasonOperation { Add, Remove };
    1478 static void updateScriptedAnimationsThrottlingReason(Page& page, ThrottlingReasonOperation operation, ScriptedAnimationController::ThrottlingReason reason)
    1479 {
    1480     page.forEachDocument([&] (Document& document) {
    1481         if (auto* controller = document.scriptedAnimationController()) {
    1482             if (operation == ThrottlingReasonOperation::Add)
    1483                 controller->addThrottlingReason(reason);
    1484             else
    1485                 controller->removeThrottlingReason(reason);
    1486         }
    1487     });
     1483Seconds Page::preferredRenderingUpdateInterval() const
     1484{
     1485    return preferredFrameInterval(m_throttlingReasons);
    14881486}
    14891487
    14901488void Page::setIsVisuallyIdleInternal(bool isVisuallyIdle)
    14911489{
    1492     updateScriptedAnimationsThrottlingReason(*this, isVisuallyIdle ? ThrottlingReasonOperation::Add : ThrottlingReasonOperation::Remove, ScriptedAnimationController::ThrottlingReason::VisuallyIdle);
     1490    if (isVisuallyIdle == m_throttlingReasons.contains(ThrottlingReason::VisuallyIdle))
     1491        return;
     1492
     1493    m_throttlingReasons = m_throttlingReasons ^ ThrottlingReason::VisuallyIdle;
     1494    renderingUpdateScheduler().adjustRenderingUpdateFrequency();
    14931495}
    14941496
    14951497void Page::handleLowModePowerChange(bool isLowPowerModeEnabled)
    14961498{
    1497     updateScriptedAnimationsThrottlingReason(*this, isLowPowerModeEnabled ? ThrottlingReasonOperation::Add : ThrottlingReasonOperation::Remove, ScriptedAnimationController::ThrottlingReason::LowPowerMode);
    1498     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
    1499         forEachDocument([] (Document& document) {
    1500             if (auto timeline = document.existingTimeline())
    1501                 timeline->updateThrottlingState();
    1502         });
    1503     } else
     1499    if (!canUpdateThrottlingReason(ThrottlingReason::LowPowerMode))
     1500        return;
     1501
     1502    if (isLowPowerModeEnabled == m_throttlingReasons.contains(ThrottlingReason::LowPowerMode))
     1503        return;
     1504
     1505    m_throttlingReasons = m_throttlingReasons ^ ThrottlingReason::LowPowerMode;
     1506    renderingUpdateScheduler().adjustRenderingUpdateFrequency();
     1507
     1508    if (!RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled())
    15041509        mainFrame().legacyAnimation().updateThrottlingState();
     1510
    15051511    updateDOMTimerAlignmentInterval();
    15061512}
  • trunk/Source/WebCore/page/Page.h

    r261031 r261113  
    2222
    2323#include "ActivityState.h"
     24#include "AnimationFrameRate.h"
    2425#include "DisabledAdaptations.h"
    2526#include "Document.h"
     
    721722    bool loadsFromNetwork() const { return m_loadsFromNetwork; }
    722723
    723     bool isLowPowerModeEnabled() const;
     724    bool isLowPowerModeEnabled() const { return m_throttlingReasons.contains(ThrottlingReason::LowPowerMode); }
    724725    WEBCORE_EXPORT void setLowPowerModeEnabledOverrideForTesting(Optional<bool>);
     726
     727    OptionSet<ThrottlingReason> throttlingReasons() const { return m_throttlingReasons; }
     728    Seconds preferredRenderingUpdateInterval() const;
    725729
    726730    WEBCORE_EXPORT void applicationWillResignActive();
     
    790794    void updateDOMTimerAlignmentInterval();
    791795    void domTimerAlignmentIntervalIncreaseTimerFired();
     796
     797    bool canUpdateThrottlingReason(ThrottlingReason reason) const { return !m_throttlingReasonsOverridenForTesting.contains(reason); }
    792798
    793799    void doAfterUpdateRendering();
     
    10011007    std::unique_ptr<PerformanceMonitor> m_performanceMonitor;
    10021008    std::unique_ptr<LowPowerModeNotifier> m_lowPowerModeNotifier;
    1003     Optional<bool> m_lowPowerModeEnabledOverrideForTesting;
     1009    OptionSet<ThrottlingReason> m_throttlingReasons;
     1010    OptionSet<ThrottlingReason> m_throttlingReasonsOverridenForTesting;
    10041011
    10051012    Optional<Navigation> m_navigationToLogWhenVisible;
  • trunk/Source/WebCore/page/RenderingUpdateScheduler.cpp

    r256834 r261113  
    11/*
    2  * Copyright (C) 2019 Apple Inc. All rights reserved.
     2 * Copyright (C) 2019-2020 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    4343}
    4444
     45
     46#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
     47void RenderingUpdateScheduler::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
     48{
     49    if (m_preferredFramesPerSecond == preferredFramesPerSecond)
     50        return;
     51
     52    m_preferredFramesPerSecond = preferredFramesPerSecond;
     53    DisplayRefreshMonitorManager::sharedManager().setPreferredFramesPerSecond(*this, m_preferredFramesPerSecond);
     54}
     55
     56bool RenderingUpdateScheduler::scheduleAnimation(FramesPerSecond preferredFramesPerSecond)
     57{
     58#if !PLATFORM(IOS_FAMILY)
     59    // PreferredFramesPerSecond can only be changed for iOS DisplayRefreshMonitor.
     60    // The caller has to fall back to using the timer.
     61    if (preferredFramesPerSecond != FullSpeedFramesPerSecond)
     62        return false;
     63#endif
     64    setPreferredFramesPerSecond(preferredFramesPerSecond);
     65    return DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
     66}
     67#endif
     68
     69void RenderingUpdateScheduler::adjustRenderingUpdateFrequency()
     70{
     71#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
     72    Seconds interval = m_page.preferredRenderingUpdateInterval();
     73
     74    // PreferredFramesPerSecond is an integer and should be > 0.
     75    if (interval <= 1_s)
     76        setPreferredFramesPerSecond(preferredFramesPerSecond(interval));
     77#endif
     78    if (isScheduled()) {
     79        clearScheduled();
     80        scheduleTimedRenderingUpdate();
     81    }
     82}
     83
    4584void RenderingUpdateScheduler::scheduleTimedRenderingUpdate()
    4685{
     
    5695    tracePoint(ScheduleRenderingUpdate);
    5796
     97    Seconds interval = m_page.preferredRenderingUpdateInterval();
     98
    5899#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    59     if (!DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this))
     100    // PreferredFramesPerSecond is an integer and should be > 0.
     101    if (interval <= 1_s)
     102        m_scheduled = scheduleAnimation(preferredFramesPerSecond(interval));
    60103#endif
    61         startTimer(Seconds(1.0 / 60));
    62104
    63     m_scheduled = true;
     105    if (!isScheduled())
     106        startTimer(interval);
    64107}
    65108
     
    75118    m_refreshTimer = makeUnique<Timer>(*this, &RenderingUpdateScheduler::displayRefreshFired);
    76119    m_refreshTimer->startOneShot(delay);
     120    m_scheduled = true;
    77121}
    78122
  • trunk/Source/WebCore/page/RenderingUpdateScheduler.h

    r256512 r261113  
    11/*
    2  * Copyright (C) 2019 Apple Inc. All rights reserved.
     2 * Copyright (C) 2019-2020 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2626#pragma once
    2727
     28#include "AnimationFrameRate.h"
    2829#include "DisplayRefreshMonitorClient.h"
    2930#include <wtf/Seconds.h>
     
    4748
    4849    RenderingUpdateScheduler(Page&);
     50   
     51    void adjustRenderingUpdateFrequency();
    4952    void scheduleTimedRenderingUpdate();
    5053    void scheduleImmediateRenderingUpdate();
     
    5760private:
    5861#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
     62    void setPreferredFramesPerSecond(FramesPerSecond);
     63    bool scheduleAnimation(FramesPerSecond);
    5964    RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const final;
    6065    void displayRefreshFired() final;
     
    7075    bool m_scheduled { false };
    7176    std::unique_ptr<Timer> m_refreshTimer;
     77#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
     78    FramesPerSecond m_preferredFramesPerSecond { FullSpeedFramesPerSecond };
     79#endif
    7280};
    7381
  • trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h

    r256512 r261113  
    2828#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    2929
     30#include "AnimationFrameRate.h"
    3031#include "PlatformScreen.h"
    3132#include <wtf/HashSet.h>
     
    4546
    4647    virtual void displayLinkFired() { }
     48
     49    virtual void setPreferredFramesPerSecond(FramesPerSecond) { }
    4750
    4851    // Return true if callback request was scheduled, false if it couldn't be
  • trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp

    r256512 r261113  
    4343}
    4444
    45 DisplayRefreshMonitor* DisplayRefreshMonitorManager::createMonitorForClient(DisplayRefreshMonitorClient& client)
     45DisplayRefreshMonitor* DisplayRefreshMonitorManager::monitorForClient(DisplayRefreshMonitorClient& client)
    4646{
     47    if (!client.hasDisplayID())
     48        return nullptr;
     49
    4750    PlatformDisplayID clientDisplayID = client.displayID();
    4851    if (auto* existingMonitor = monitorForDisplayID(clientDisplayID)) {
     
    5558        return nullptr;
    5659
    57     LOG(RequestAnimationFrame, "DisplayRefreshMonitorManager::createMonitorForClient() - created monitor %p", monitor.get());
     60    LOG(RequestAnimationFrame, "DisplayRefreshMonitorManager::monitorForClient() - created monitor %p", monitor.get());
    5861    monitor->addClient(client);
    5962    DisplayRefreshMonitor* result = monitor.get();
    6063    m_monitors.append({ WTFMove(monitor) });
    6164    return result;
    62 }
    63 
    64 void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient& client)
    65 {
    66     if (!client.hasDisplayID())
    67         return;
    68 
    69     createMonitorForClient(client);
    7065}
    7166
     
    8681}
    8782
     83void DisplayRefreshMonitorManager::setPreferredFramesPerSecond(DisplayRefreshMonitorClient& client, FramesPerSecond preferredFramesPerSecond)
     84{
     85    if (auto* monitor = monitorForClient(client))
     86        monitor->setPreferredFramesPerSecond(preferredFramesPerSecond);
     87}
     88
    8889bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient& client)
    8990{
    90     if (!client.hasDisplayID())
    91         return false;
    92 
    93     DisplayRefreshMonitor* monitor = createMonitorForClient(client);
    94     if (!monitor)
    95         return false;
    96 
    97     client.setIsScheduled(true);
    98     return monitor->requestRefreshCallback();
     91    if (auto* monitor = monitorForClient(client)) {
     92        client.setIsScheduled(true);
     93        return monitor->requestRefreshCallback();
     94    }
     95    return false;
    9996}
    10097
     
    117114    unregisterClient(client);
    118115    client.setDisplayID(displayID);
    119     registerClient(client);
    120116    if (client.isScheduled())
    121117        scheduleAnimation(client);
  • trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h

    r256512 r261113  
    2828#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
    2929
     30#include "AnimationFrameRate.h"
    3031#include "DisplayRefreshMonitor.h"
    3132#include "PlatformScreen.h"
     
    3839class DisplayRefreshMonitorManager {
    3940    friend class NeverDestroyed<DisplayRefreshMonitorManager>;
     41    friend class DisplayRefreshMonitor;
    4042public:
    4143    WEBCORE_EXPORT static DisplayRefreshMonitorManager& sharedManager();
    42    
    43     void registerClient(DisplayRefreshMonitorClient&);
     44
    4445    void unregisterClient(DisplayRefreshMonitorClient&);
    4546
     47    void setPreferredFramesPerSecond(DisplayRefreshMonitorClient&, FramesPerSecond);
    4648    bool scheduleAnimation(DisplayRefreshMonitorClient&);
    4749    void windowScreenDidChange(PlatformDisplayID, DisplayRefreshMonitorClient&);
    4850
    4951    WEBCORE_EXPORT void displayWasUpdated(PlatformDisplayID);
    50    
     52
    5153private:
    52     friend class DisplayRefreshMonitor;
     54    DisplayRefreshMonitorManager() = default;
     55    virtual ~DisplayRefreshMonitorManager();
     56
    5357    void displayDidRefresh(DisplayRefreshMonitor&);
    54    
    55     DisplayRefreshMonitorManager() { }
    56     virtual ~DisplayRefreshMonitorManager();
    5758
    5859    size_t findMonitorForDisplayID(PlatformDisplayID) const;
    5960    DisplayRefreshMonitor* monitorForDisplayID(PlatformDisplayID) const;
    60 
    61     DisplayRefreshMonitor* createMonitorForClient(DisplayRefreshMonitorClient&);
     61    DisplayRefreshMonitor* monitorForClient(DisplayRefreshMonitorClient&);
    6262
    6363    struct DisplayRefreshMonitorWrapper {
  • trunk/Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp

    r256512 r261113  
    3636    : m_client(client)
    3737{
    38     DisplayRefreshMonitorManager::sharedManager().registerClient(*this);
    3938    DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
    4039    DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
  • trunk/Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.mm

    r256512 r261113  
    4141
    4242- (id)initWithMonitor:(DisplayRefreshMonitorIOS*)monitor;
     43- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond;
    4344- (void)handleDisplayLink:(CADisplayLink *)sender;
    4445- (void)invalidate;
     
    6465    ASSERT(!m_displayLink); // -invalidate should have been called already.
    6566    [super dealloc];
     67}
     68
     69- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond
     70{
     71    m_displayLink.preferredFramesPerSecond = preferredFramesPerSecond;
    6672}
    6773
  • trunk/Source/WebCore/testing/Internals.cpp

    r261056 r261113  
    13881388}
    13891389
    1390 bool Internals::isRequestAnimationFrameThrottled() const
     1390String Internals::requestAnimationFrameThrottlingReasons() const
    13911391{
    13921392    auto* scriptedAnimationController = contextDocument()->scriptedAnimationController();
    13931393    if (!scriptedAnimationController)
    1394         return false;
    1395     return scriptedAnimationController->isThrottled();
     1394        return String();
     1395       
     1396    TextStream ts;
     1397    ts << scriptedAnimationController->throttlingReasons();
     1398    return ts.release();
    13961399}
    13971400
  • trunk/Source/WebCore/testing/Internals.h

    r261056 r261113  
    206206    // DOMTimers throttling testing.
    207207    ExceptionOr<bool> isTimerThrottled(int timeoutId);
    208     bool isRequestAnimationFrameThrottled() const;
     208    String requestAnimationFrameThrottlingReasons() const;
    209209    double requestAnimationFrameInterval() const;
    210210    bool scriptedAnimationsAreSuspended() const;
  • trunk/Source/WebCore/testing/Internals.idl

    r261056 r261113  
    558558    [MayThrowException] boolean isTimerThrottled(long timerHandle);
    559559
    560     boolean isRequestAnimationFrameThrottled();
     560    DOMString requestAnimationFrameThrottlingReasons();
    561561    boolean areTimersThrottled();
    562562
  • trunk/Source/WebKit/ChangeLog

    r261112 r261113  
     12020-05-04  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
     4        https://bugs.webkit.org/show_bug.cgi?id=204713
     5
     6        Reviewed by Simon Fraser.
     7
     8        Create an IPC message on the DrawingArea to send a message from the
     9        WebProcess to the UIProcess to setPreferredFramesPerSecond of the
     10        DisplayRefreshMonitor.
     11
     12        * UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.h:
     13        * UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.messages.in:
     14        * UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm:
     15        (-[WKOneShotDisplayLinkHandler setPreferredFramesPerSecond:]):
     16        (WebKit::RemoteLayerTreeDrawingAreaProxy::setPreferredFramesPerSecond):
     17        Set the preferredFramesPerSecond of the CADisplayLink.
     18
     19        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h:
     20        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm:
     21        (WebKit::RemoteLayerTreeDisplayRefreshMonitor::setPreferredFramesPerSecond):
     22        Forward the call to RemoteLayerTreeDrawingArea.
     23
     24        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.h:
     25        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm:
     26        (WebKit::RemoteLayerTreeDrawingArea::setPreferredFramesPerSecond):
     27        Send the IPC message from the WebProcess to the UIProcess.
     28
    1292020-05-04  Alex Christensen  <achristensen@webkit.org>
    230
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.h

    r261044 r261113  
    2929#include "RemoteLayerTreeHost.h"
    3030#include "TransactionID.h"
     31#include <WebCore/AnimationFrameRate.h>
    3132#include <WebCore/FloatPoint.h>
    3233#include <WebCore/IntPoint.h>
     
    9899
    99100    // Message handlers
     101    void setPreferredFramesPerSecond(WebCore::FramesPerSecond);
    100102    void willCommitLayerTree(TransactionID);
    101103    void commitLayerTree(const RemoteLayerTreeTransaction&, const RemoteScrollingCoordinatorTransaction&);
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.messages.in

    r256512 r261113  
    2222
    2323messages -> RemoteLayerTreeDrawingAreaProxy : DrawingAreaProxy NotRefCounted {
     24    void SetPreferredFramesPerSecond(unsigned preferredFramesPerSecond)
    2425    void WillCommitLayerTree(WebKit::TransactionID transactionID)
    2526    void CommitLayerTree(WebKit::RemoteLayerTreeTransaction layerTreeTransaction, WebKit::RemoteScrollingCoordinatorTransaction scrollingTreeTransaction)
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm

    r261044 r261113  
    5050
    5151- (id)initWithDrawingAreaProxy:(WebKit::RemoteLayerTreeDrawingAreaProxy*)drawingAreaProxy;
     52- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond;
    5253- (void)displayLinkFired:(CADisplayLink *)sender;
    5354- (void)invalidate;
     
    7778}
    7879
     80- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond
     81{
     82    _displayLink.preferredFramesPerSecond = preferredFramesPerSecond;
     83}
     84
    7985- (void)displayLinkFired:(CADisplayLink *)sender
    8086{
     
    182188    send(Messages::DrawingArea::UpdateGeometry(m_size, false /* flushSynchronously */, MachSendRight()));
    183189    m_isWaitingForDidUpdateGeometry = true;
     190}
     191
     192void RemoteLayerTreeDrawingAreaProxy::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
     193{
     194#if PLATFORM(IOS_FAMILY)
     195    [displayLinkHandler() setPreferredFramesPerSecond:preferredFramesPerSecond];
     196#else
     197    UNUSED_PARAM(preferredFramesPerSecond);
     198#endif
    184199}
    185200
  • trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h

    r256512 r261113  
    2929
    3030#include "RemoteLayerTreeDrawingArea.h"
     31#include <WebCore/AnimationFrameRate.h>
    3132#include <WebCore/DisplayRefreshMonitor.h>
    3233
     
    4243    virtual ~RemoteLayerTreeDisplayRefreshMonitor();
    4344
     45    void setPreferredFramesPerSecond(WebCore::FramesPerSecond) override;
    4446    bool requestRefreshCallback() override;
    4547
  • trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm

    r256834 r261113  
    4444}
    4545
     46void RemoteLayerTreeDisplayRefreshMonitor::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
     47{
     48    if (m_drawingArea)
     49        m_drawingArea->setPreferredFramesPerSecond(preferredFramesPerSecond);
     50}
     51
    4652bool RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback()
    4753{
  • trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.h

    r261044 r261113  
    3030#include "GraphicsLayerCARemote.h"
    3131#include "RemoteLayerTreeTransaction.h"
     32#include <WebCore/AnimationFrameRate.h>
    3233#include <WebCore/GraphicsLayerClient.h>
    3334#include <WebCore/Timer.h>
     
    7374    RefPtr<WebCore::DisplayRefreshMonitor> createDisplayRefreshMonitor(WebCore::PlatformDisplayID) override;
    7475    void willDestroyDisplayRefreshMonitor(WebCore::DisplayRefreshMonitor*);
     76    void setPreferredFramesPerSecond(WebCore::FramesPerSecond);
    7577
    7678    bool shouldUseTiledBackingForFrameView(const WebCore::FrameView&) const override;
  • trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm

    r261044 r261113  
    127127}
    128128
     129void RemoteLayerTreeDrawingArea::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
     130{
     131    send(Messages::RemoteLayerTreeDrawingAreaProxy::SetPreferredFramesPerSecond(preferredFramesPerSecond));
     132}
     133
    129134void RemoteLayerTreeDrawingArea::updateRootLayers()
    130135{
Note: See TracChangeset for help on using the changeset viewer.