Log Message
Allow non-60fps display updates to be driven by DisplayRefreshMonitor https://bugs.webkit.org/show_bug.cgi?id=223912
Reviewed by Sam Weinig. Source/WebCore: Previously, RenderingUpdateScheduler::scheduleAnimation() would return false for any preferredFramesPerSecond which is not 60fps, causing RenderingUpdateScheduler to fall back to its timer mechanism. This meant that throttled (e.g. by low power) pages and display updates on non-60fps display would all use timers. This is undesirable because we want alignment with display refresh, and to avoid timer drift between multiple throttled documents. The fix has two parts. First, we need to fix RenderingUpdateScheduler and ScriptedAnimationController to compute their frame rates in terms of FramesPerSecond, rather than Seconds, because using the latter requires conversion to Seconds and back via m_page.preferredRenderingUpdateInterval() which was awkward. So have Page expose preferredRenderingUpdateFramesPerSecond(). Also add preferredFramesPerSecond(), and have both it and preferredFrameInterval() take the "near 60fps" flag as an argument, so doing the common math in AnimationFrameRate. The second part of the fix is to move "preferredFramesPerSecond" from RenderingUpdateScheduler to DisplayRefreshMonitorClient, since clients should be able to request different frame rates. Then we pass the DisplayUpdate to DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(), and finally use it to do the math of whether to skip an update for this client. Tested by API tests in AnimationFrameRate.cpp, and by fast/animation/request-animation-frame-throttling-lowPowerMode.html * dom/ScriptedAnimationController.cpp: (WebCore::ScriptedAnimationController::interval const): This function (used for testing) is just a synonym for preferredScriptedAnimationInterval(), but left here because it's exported and maybe the compiler can inline preferredScriptedAnimationInterval(). (WebCore::ScriptedAnimationController::preferredScriptedAnimationInterval const): Just call preferredFrameInterval() with throttlingReasons(), which is the union of the reasons for the page and for this ScriptedAnimationController. (WebCore::ScriptedAnimationController::throttlingReasons const): No reason not to return m_throttlingReasons if we don't have a page. Should never happen. (WebCore::ScriptedAnimationController::shouldRescheduleRequestAnimationFrame const): Logging. (WebCore::ScriptedAnimationController::serviceRequestAnimationFrameCallbacks): Logging. * page/Page.cpp: (WebCore::Page::windowScreenDidChange): The display nominal FPS may have changed so recompute the update frequency. (WebCore::Page::preferredRenderingUpdateFramesPerSecond const): Return the preferred update in FramesPerSecond. Will return null if throttling results in a < 1fps update. (WebCore::Page::preferredRenderingUpdateInterval const): Push the "near 60fps" logic into the helper function. (WebCore::Page::setIsVisuallyIdleInternal): Clearer to use OptionSet<>::set(). (WebCore::Page::handleLowModePowerChange): Ditto. * page/Page.h: (WebCore::Page::displayNominalFramesPerSecond const): (WebCore::Page::throttlingReasons const): * page/RenderingUpdateScheduler.cpp: (WebCore::RenderingUpdateScheduler::scheduleAnimation): Remove code that fell back to a timer for any non-60fps rate. (WebCore::RenderingUpdateScheduler::adjustRenderingUpdateFrequency): Compute if we have to use a timer. (WebCore::RenderingUpdateScheduler::scheduleRenderingUpdate): (WebCore::RenderingUpdateScheduler::setPreferredFramesPerSecond): Deleted. * page/RenderingUpdateScheduler.h: * platform/graphics/AnimationFrameRate.cpp: Simplify some code with halfSpeedThrottlingReasons. (WebCore::preferredFramesPerSecond): Similar logic to preferredFrameInterval, but for FramesPerSecond values. (WebCore::preferredFrameInterval): * platform/graphics/AnimationFrameRate.h: * platform/graphics/DisplayRefreshMonitor.cpp: (WebCore::DisplayRefreshMonitor::displayDidRefresh): * platform/graphics/DisplayRefreshMonitorClient.cpp: (WebCore::DisplayRefreshMonitorClient::setPreferredFramesPerSecond): (WebCore::DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded): * platform/graphics/DisplayRefreshMonitorClient.h: (WebCore::DisplayRefreshMonitorClient::preferredFramesPerSecond const): * platform/graphics/DisplayUpdate.cpp: (WebCore::DisplayUpdate::relevantForUpdateFrequency const): Compute whether this update is relevant for a client who wants updates at 'preferredFramesPerSecond'. * platform/graphics/DisplayUpdate.h: Source/WebKit: Improve the logging. * UIProcess/mac/DisplayLink.cpp: (WebKit::DisplayLink::DisplayLink): (WebKit::DisplayLink::addObserver): Tools: New API tests for preferredFramesPerSecond() and DisplayUpdate::relevantForUpdateFrequency(), and duplicate existing tests for the "use display nominal FPS" vs "use near-60 FPS" settings. * TestWebKitAPI/Tests/WebCore/AnimationFrameRate.cpp: (TestWebKitAPI::TEST):
Modified Paths
- trunk/Source/WebCore/ChangeLog
- trunk/Source/WebCore/dom/ScriptedAnimationController.cpp
- trunk/Source/WebCore/page/Page.cpp
- trunk/Source/WebCore/page/Page.h
- trunk/Source/WebCore/page/RenderingUpdateScheduler.cpp
- trunk/Source/WebCore/page/RenderingUpdateScheduler.h
- trunk/Source/WebCore/platform/graphics/AnimationFrameRate.cpp
- trunk/Source/WebCore/platform/graphics/AnimationFrameRate.h
- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp
- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp
- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h
- trunk/Source/WebCore/platform/graphics/DisplayUpdate.cpp
- trunk/Source/WebCore/platform/graphics/DisplayUpdate.h
- trunk/Source/WebKit/ChangeLog
- trunk/Source/WebKit/UIProcess/mac/DisplayLink.cpp
- trunk/Tools/ChangeLog
- trunk/Tools/TestWebKitAPI/Tests/WebCore/AnimationFrameRate.cpp
Diff
Modified: trunk/Source/WebCore/ChangeLog (275214 => 275215)
--- trunk/Source/WebCore/ChangeLog 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/ChangeLog 2021-03-30 18:14:26 UTC (rev 275215)
@@ -1,3 +1,78 @@
+2021-03-29 Simon Fraser <[email protected]>
+
+ Allow non-60fps display updates to be driven by DisplayRefreshMonitor
+ https://bugs.webkit.org/show_bug.cgi?id=223912
+
+ Reviewed by Sam Weinig.
+
+ Previously, RenderingUpdateScheduler::scheduleAnimation() would return false for any
+ preferredFramesPerSecond which is not 60fps, causing RenderingUpdateScheduler to fall back
+ to its timer mechanism. This meant that throttled (e.g. by low power) pages and display
+ updates on non-60fps display would all use timers. This is undesirable because we want
+ alignment with display refresh, and to avoid timer drift between multiple throttled
+ documents.
+
+ The fix has two parts. First, we need to fix RenderingUpdateScheduler and
+ ScriptedAnimationController to compute their frame rates in terms of FramesPerSecond, rather
+ than Seconds, because using the latter requires conversion to Seconds and back via
+ m_page.preferredRenderingUpdateInterval() which was awkward. So have Page expose
+ preferredRenderingUpdateFramesPerSecond().
+
+ Also add preferredFramesPerSecond(), and have both it and preferredFrameInterval() take the
+ "near 60fps" flag as an argument, so doing the common math in AnimationFrameRate.
+
+ The second part of the fix is to move "preferredFramesPerSecond" from
+ RenderingUpdateScheduler to DisplayRefreshMonitorClient, since clients should be able to
+ request different frame rates. Then we pass the DisplayUpdate to
+ DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(), and finally use it to do the math
+ of whether to skip an update for this client.
+
+ Tested by API tests in AnimationFrameRate.cpp, and by
+ fast/animation/request-animation-frame-throttling-lowPowerMode.html
+
+ * dom/ScriptedAnimationController.cpp:
+ (WebCore::ScriptedAnimationController::interval const): This function (used for testing)
+ is just a synonym for preferredScriptedAnimationInterval(), but left here because it's exported
+ and maybe the compiler can inline preferredScriptedAnimationInterval().
+ (WebCore::ScriptedAnimationController::preferredScriptedAnimationInterval const): Just call
+ preferredFrameInterval() with throttlingReasons(), which is the union of the reasons for
+ the page and for this ScriptedAnimationController.
+ (WebCore::ScriptedAnimationController::throttlingReasons const): No reason not to return m_throttlingReasons
+ if we don't have a page. Should never happen.
+ (WebCore::ScriptedAnimationController::shouldRescheduleRequestAnimationFrame const): Logging.
+ (WebCore::ScriptedAnimationController::serviceRequestAnimationFrameCallbacks): Logging.
+ * page/Page.cpp:
+ (WebCore::Page::windowScreenDidChange): The display nominal FPS may have changed so recompute the update frequency.
+ (WebCore::Page::preferredRenderingUpdateFramesPerSecond const): Return the preferred update in FramesPerSecond. Will return
+ null if throttling results in a < 1fps update.
+ (WebCore::Page::preferredRenderingUpdateInterval const): Push the "near 60fps" logic into the helper function.
+ (WebCore::Page::setIsVisuallyIdleInternal): Clearer to use OptionSet<>::set().
+ (WebCore::Page::handleLowModePowerChange): Ditto.
+ * page/Page.h:
+ (WebCore::Page::displayNominalFramesPerSecond const):
+ (WebCore::Page::throttlingReasons const):
+ * page/RenderingUpdateScheduler.cpp:
+ (WebCore::RenderingUpdateScheduler::scheduleAnimation): Remove code that fell back to a timer for any non-60fps rate.
+ (WebCore::RenderingUpdateScheduler::adjustRenderingUpdateFrequency): Compute if we have to use a timer.
+ (WebCore::RenderingUpdateScheduler::scheduleRenderingUpdate):
+ (WebCore::RenderingUpdateScheduler::setPreferredFramesPerSecond): Deleted.
+ * page/RenderingUpdateScheduler.h:
+ * platform/graphics/AnimationFrameRate.cpp: Simplify some code with halfSpeedThrottlingReasons.
+ (WebCore::preferredFramesPerSecond): Similar logic to preferredFrameInterval, but for FramesPerSecond values.
+ (WebCore::preferredFrameInterval):
+ * platform/graphics/AnimationFrameRate.h:
+ * platform/graphics/DisplayRefreshMonitor.cpp:
+ (WebCore::DisplayRefreshMonitor::displayDidRefresh):
+ * platform/graphics/DisplayRefreshMonitorClient.cpp:
+ (WebCore::DisplayRefreshMonitorClient::setPreferredFramesPerSecond):
+ (WebCore::DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded):
+ * platform/graphics/DisplayRefreshMonitorClient.h:
+ (WebCore::DisplayRefreshMonitorClient::preferredFramesPerSecond const):
+ * platform/graphics/DisplayUpdate.cpp:
+ (WebCore::DisplayUpdate::relevantForUpdateFrequency const): Compute whether this update is relevant
+ for a client who wants updates at 'preferredFramesPerSecond'.
+ * platform/graphics/DisplayUpdate.h:
+
2021-03-30 Antti Koivisto <[email protected]>
[LFC][Integration] Elements that overflow inline-blocks are not hit tested correctly
Modified: trunk/Source/WebCore/dom/ScriptedAnimationController.cpp (275214 => 275215)
--- trunk/Source/WebCore/dom/ScriptedAnimationController.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/dom/ScriptedAnimationController.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -28,6 +28,7 @@
#include "ScriptedAnimationController.h"
#include "InspectorInstrumentation.h"
+#include "Logging.h"
#include "Page.h"
#include "Quirks.h"
#include "RequestAnimationFrameCallback.h"
@@ -73,19 +74,16 @@
Seconds ScriptedAnimationController::interval() const
{
- if (auto* page = this->page())
- return std::max(preferredScriptedAnimationInterval(), page->preferredRenderingUpdateInterval());
- return FullSpeedAnimationInterval;
+ return preferredScriptedAnimationInterval();
}
Seconds ScriptedAnimationController::preferredScriptedAnimationInterval() const
{
- Optional<FramesPerSecond> preferredFPS;
- if (auto* page = this->page()) {
- if (page->settings().preferPageRenderingUpdatesNear60FPSEnabled())
- preferredFPS = page->displayNominalFramesPerSecond();
- }
- return preferredFrameInterval(m_throttlingReasons, preferredFPS);
+ auto* page = this->page();
+ if (!page)
+ return FullSpeedAnimationInterval;
+
+ return preferredFrameInterval(throttlingReasons(), page->displayNominalFramesPerSecond(), page->settings().preferPageRenderingUpdatesNear60FPSEnabled());
}
OptionSet<ThrottlingReason> ScriptedAnimationController::throttlingReasons() const
@@ -92,7 +90,8 @@
{
if (auto* page = this->page())
return page->throttlingReasons() | m_throttlingReasons;
- return { };
+
+ return m_throttlingReasons;
}
bool ScriptedAnimationController::isThrottledRelativeToPage() const
@@ -104,6 +103,9 @@
bool ScriptedAnimationController::shouldRescheduleRequestAnimationFrame(ReducedResolutionSeconds timestamp) const
{
+ LOG_WITH_STREAM(RequestAnimationFrame, stream << "ScriptedAnimationController::shouldRescheduleRequestAnimationFrame - throttled relative to page " << isThrottledRelativeToPage()
+ << ", last delta " << (timestamp - m_lastAnimationFrameTimestamp).milliseconds() << "ms, preferred interval " << preferredScriptedAnimationInterval().milliseconds() << ")");
+
return timestamp <= m_lastAnimationFrameTimestamp || (isThrottledRelativeToPage() && (timestamp - m_lastAnimationFrameTimestamp < preferredScriptedAnimationInterval()));
}
@@ -141,6 +143,7 @@
return;
if (shouldRescheduleRequestAnimationFrame(timestamp)) {
+ LOG_WITH_STREAM(RequestAnimationFrame, stream << "ScriptedAnimationController::serviceRequestAnimationFrameCallbacks - rescheduling (page update interval " << (page() ? page()->preferredRenderingUpdateInterval() : 0_s) << ", raf interval " << preferredScriptedAnimationInterval() << ")");
scheduleAnimation();
return;
}
@@ -151,6 +154,8 @@
if (m_document && m_document->quirks().needsMillisecondResolutionForHighResTimeStamp())
highResNowMs += 0.1;
+ LOG_WITH_STREAM(RequestAnimationFrame, stream << "ScriptedAnimationController::serviceRequestAnimationFrameCallbacks at " << highResNowMs << " (throttling reasons " << throttlingReasons() << ", preferred interval " << preferredScriptedAnimationInterval().milliseconds() << "ms)");
+
// First, generate a list of callbacks to consider. Callbacks registered from this point
// on are considered only for the "next" frame, not this one.
Vector<CallbackData> callbackDataList(m_callbackDataList);
Modified: trunk/Source/WebCore/page/Page.cpp (275214 => 275215)
--- trunk/Source/WebCore/page/Page.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/page/Page.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -1198,6 +1198,7 @@
m_scrollingCoordinator->windowScreenDidChange(displayID, nominalFramesPerSecond);
renderingUpdateScheduler().windowScreenDidChange(displayID);
+ renderingUpdateScheduler().adjustRenderingUpdateFrequency();
setNeedsRecalcStyleInAllFrames();
}
@@ -1775,11 +1776,14 @@
});
}
+Optional<FramesPerSecond> Page::preferredRenderingUpdateFramesPerSecond() const
+{
+ return preferredFramesPerSecond(m_throttlingReasons, m_displayNominalFramesPerSecond, settings().preferPageRenderingUpdatesNear60FPSEnabled());
+}
+
Seconds Page::preferredRenderingUpdateInterval() const
{
- if (!settings().preferPageRenderingUpdatesNear60FPSEnabled())
- return preferredFrameInterval(m_throttlingReasons, m_displayNominalFramesPerSecond);
- return preferredFrameInterval(m_throttlingReasons, WTF::nullopt);
+ return preferredFrameInterval(m_throttlingReasons, m_displayNominalFramesPerSecond, settings().preferPageRenderingUpdatesNear60FPSEnabled());
}
void Page::setIsVisuallyIdleInternal(bool isVisuallyIdle)
@@ -1787,7 +1791,7 @@
if (isVisuallyIdle == m_throttlingReasons.contains(ThrottlingReason::VisuallyIdle))
return;
- m_throttlingReasons = m_throttlingReasons ^ ThrottlingReason::VisuallyIdle;
+ m_throttlingReasons.set(ThrottlingReason::VisuallyIdle, isVisuallyIdle);
renderingUpdateScheduler().adjustRenderingUpdateFrequency();
}
@@ -1799,7 +1803,7 @@
if (isLowPowerModeEnabled == m_throttlingReasons.contains(ThrottlingReason::LowPowerMode))
return;
- m_throttlingReasons = m_throttlingReasons ^ ThrottlingReason::LowPowerMode;
+ m_throttlingReasons.set(ThrottlingReason::LowPowerMode, isLowPowerModeEnabled);
renderingUpdateScheduler().adjustRenderingUpdateFrequency();
updateDOMTimerAlignmentInterval();
Modified: trunk/Source/WebCore/page/Page.h (275214 => 275215)
--- trunk/Source/WebCore/page/Page.h 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/page/Page.h 2021-03-30 18:14:26 UTC (rev 275215)
@@ -422,8 +422,14 @@
WEBCORE_EXPORT void screenPropertiesDidChange();
void windowScreenDidChange(PlatformDisplayID, Optional<FramesPerSecond> nominalFramesPerSecond);
PlatformDisplayID displayID() const { return m_displayID; }
- Optional<unsigned> displayNominalFramesPerSecond() const { return m_displayNominalFramesPerSecond; }
+ Optional<FramesPerSecond> displayNominalFramesPerSecond() const { return m_displayNominalFramesPerSecond; }
+ // This can return nullopt if throttling reasons result in a frequency less than one, in which case
+ // preferredRenderingUpdateInterval provides the frequency.
+ // FIXME: Have a single function that returns a Variant<>.
+ Optional<FramesPerSecond> preferredRenderingUpdateFramesPerSecond() const;
+ Seconds preferredRenderingUpdateInterval() const;
+
float topContentInset() const { return m_topContentInset; }
WEBCORE_EXPORT void setTopContentInset(float);
@@ -794,7 +800,6 @@
WEBCORE_EXPORT void setOutsideViewportThrottlingEnabledForTesting(bool);
OptionSet<ThrottlingReason> throttlingReasons() const { return m_throttlingReasons; }
- Seconds preferredRenderingUpdateInterval() const;
WEBCORE_EXPORT void applicationWillResignActive();
WEBCORE_EXPORT void applicationDidEnterBackground();
@@ -929,7 +934,7 @@
RTCController m_rtcController;
PlatformDisplayID m_displayID { 0 };
- Optional<unsigned> m_displayNominalFramesPerSecond;
+ Optional<FramesPerSecond> m_displayNominalFramesPerSecond;
int m_nestedRunLoopCount { 0 };
WTF::Function<void()> m_unnestCallback;
Modified: trunk/Source/WebCore/page/RenderingUpdateScheduler.cpp (275214 => 275215)
--- trunk/Source/WebCore/page/RenderingUpdateScheduler.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/page/RenderingUpdateScheduler.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -42,39 +42,23 @@
windowScreenDidChange(page.chrome().displayID());
}
-void RenderingUpdateScheduler::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
+bool RenderingUpdateScheduler::scheduleAnimation()
{
- if (m_preferredFramesPerSecond == preferredFramesPerSecond)
- return;
-
- m_preferredFramesPerSecond = preferredFramesPerSecond;
- DisplayRefreshMonitorManager::sharedManager().setPreferredFramesPerSecond(*this, m_preferredFramesPerSecond);
-}
-
-bool RenderingUpdateScheduler::scheduleAnimation(FramesPerSecond preferredFramesPerSecond)
-{
-#if !PLATFORM(IOS_FAMILY)
- // PreferredFramesPerSecond can only be changed for iOS DisplayRefreshMonitor.
- // The caller has to fall back to using the timer.
- if (preferredFramesPerSecond != FullSpeedFramesPerSecond)
+ if (m_useTimer)
return false;
-#endif
- setPreferredFramesPerSecond(preferredFramesPerSecond);
- auto result = DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
- LOG_WITH_STREAM(EventLoop, stream << "RenderingUpdateScheduler for page " << &m_page << " scheduleAnimation(" << preferredFramesPerSecond << "fps) - scheduled " << result);
-
- return result;
+ return DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
}
void RenderingUpdateScheduler::adjustRenderingUpdateFrequency()
{
- Seconds interval = m_page.preferredRenderingUpdateInterval();
+ auto renderingUpdateFramesPerSecond = m_page.preferredRenderingUpdateFramesPerSecond();
+ if (renderingUpdateFramesPerSecond) {
+ setPreferredFramesPerSecond(renderingUpdateFramesPerSecond.value());
+ m_useTimer = false;
+ } else
+ m_useTimer = true;
- // PreferredFramesPerSecond is an integer and should be > 0.
- if (interval <= 1_s)
- setPreferredFramesPerSecond(preferredFramesPerSecondFromInterval(interval));
-
if (isScheduled()) {
clearScheduled();
scheduleRenderingUpdate();
@@ -96,14 +80,10 @@
tracePoint(ScheduleRenderingUpdate);
- Seconds interval = m_page.preferredRenderingUpdateInterval();
-
- // PreferredFramesPerSecond is an integer and should be > 0.
- if (interval <= 1_s)
- m_scheduled = scheduleAnimation(preferredFramesPerSecondFromInterval(interval));
-
- if (!isScheduled())
- startTimer(interval);
+ if (!scheduleAnimation()) {
+ LOG_WITH_STREAM(DisplayLink, stream << "RenderingUpdateScheduler::scheduleRenderingUpdate for interval " << m_page.preferredRenderingUpdateInterval() << " falling back to timer");
+ startTimer(m_page.preferredRenderingUpdateInterval());
+ }
}
bool RenderingUpdateScheduler::isScheduled() const
Modified: trunk/Source/WebCore/page/RenderingUpdateScheduler.h (275214 => 275215)
--- trunk/Source/WebCore/page/RenderingUpdateScheduler.h 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/page/RenderingUpdateScheduler.h 2021-03-30 18:14:26 UTC (rev 275215)
@@ -52,8 +52,7 @@
void windowScreenDidChange(PlatformDisplayID);
private:
- void setPreferredFramesPerSecond(FramesPerSecond);
- bool scheduleAnimation(FramesPerSecond);
+ bool scheduleAnimation();
void displayRefreshFired() final;
DisplayRefreshMonitorFactory* displayRefreshMonitorFactory() const final;
@@ -66,7 +65,7 @@
Page& m_page;
std::unique_ptr<Timer> m_refreshTimer;
- FramesPerSecond m_preferredFramesPerSecond { FullSpeedFramesPerSecond };
+ bool m_useTimer { false };
bool m_scheduled { false };
};
Modified: trunk/Source/WebCore/platform/graphics/AnimationFrameRate.cpp (275214 => 275215)
--- trunk/Source/WebCore/platform/graphics/AnimationFrameRate.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/platform/graphics/AnimationFrameRate.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -30,6 +30,8 @@
namespace WebCore {
+static constexpr OptionSet<ThrottlingReason> halfSpeedThrottlingReasons { ThrottlingReason::LowPowerMode, ThrottlingReason::NonInteractedCrossOriginFrame, ThrottlingReason::VisuallyIdle };
+
FramesPerSecond framesPerSecondNearestFullSpeed(FramesPerSecond nominalFramesPerSecond)
{
if (nominalFramesPerSecond <= FullSpeedFramesPerSecond)
@@ -42,22 +44,42 @@
return fullSpeedRatio - std::floor(fullSpeedRatio) <= 0.5 ? floorSpeed : ceilSpeed;
}
-Seconds preferredFrameInterval(const OptionSet<ThrottlingReason>& reasons, Optional<FramesPerSecond> nominalFramesPerSecond)
+Optional<FramesPerSecond> preferredFramesPerSecond(OptionSet<ThrottlingReason> reasons, Optional<FramesPerSecond> nominalFramesPerSecond, bool preferFrameRatesNear60FPS)
{
if (reasons.contains(ThrottlingReason::OutsideViewport))
+ return WTF::nullopt;
+
+ if (!nominalFramesPerSecond || *nominalFramesPerSecond == FullSpeedFramesPerSecond) {
+ // FIXME: handle ThrottlingReason::VisuallyIdle
+ if (reasons.containsAny(halfSpeedThrottlingReasons))
+ return HalfSpeedThrottlingFramesPerSecond;
+
+ return FullSpeedFramesPerSecond;
+ }
+
+ auto framesPerSecond = preferFrameRatesNear60FPS ? framesPerSecondNearestFullSpeed(*nominalFramesPerSecond) : *nominalFramesPerSecond;
+ if (reasons.containsAny(halfSpeedThrottlingReasons))
+ framesPerSecond /= IntervalThrottlingFactor;
+
+ return framesPerSecond;
+}
+
+Seconds preferredFrameInterval(OptionSet<ThrottlingReason> reasons, Optional<FramesPerSecond> nominalFramesPerSecond, bool preferFrameRatesNear60FPS)
+{
+ if (reasons.contains(ThrottlingReason::OutsideViewport))
return AggressiveThrottlingAnimationInterval;
if (!nominalFramesPerSecond || *nominalFramesPerSecond == FullSpeedFramesPerSecond) {
// FIXME: handle ThrottlingReason::VisuallyIdle
- if (reasons.containsAny({ ThrottlingReason::LowPowerMode, ThrottlingReason::NonInteractedCrossOriginFrame }))
+ if (reasons.containsAny(halfSpeedThrottlingReasons))
return HalfSpeedThrottlingAnimationInterval;
return FullSpeedAnimationInterval;
}
- auto framesPerSecond = framesPerSecondNearestFullSpeed(*nominalFramesPerSecond);
+ auto framesPerSecond = preferFrameRatesNear60FPS ? framesPerSecondNearestFullSpeed(*nominalFramesPerSecond) : *nominalFramesPerSecond;
auto interval = Seconds(1.0 / framesPerSecond);
- if (reasons.containsAny({ ThrottlingReason::LowPowerMode, ThrottlingReason::NonInteractedCrossOriginFrame, ThrottlingReason::VisuallyIdle }))
+ if (reasons.containsAny(halfSpeedThrottlingReasons))
interval *= IntervalThrottlingFactor;
return interval;
Modified: trunk/Source/WebCore/platform/graphics/AnimationFrameRate.h (275214 => 275215)
--- trunk/Source/WebCore/platform/graphics/AnimationFrameRate.h 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/platform/graphics/AnimationFrameRate.h 2021-03-30 18:14:26 UTC (rev 275215)
@@ -37,7 +37,7 @@
using FramesPerSecond = unsigned;
-enum class ThrottlingReason {
+enum class ThrottlingReason : uint8_t {
VisuallyIdle = 1 << 0,
OutsideViewport = 1 << 1,
LowPowerMode = 1 << 2,
@@ -55,7 +55,12 @@
constexpr const FramesPerSecond HalfSpeedThrottlingFramesPerSecond = 30;
WEBCORE_EXPORT FramesPerSecond framesPerSecondNearestFullSpeed(FramesPerSecond);
-WEBCORE_EXPORT Seconds preferredFrameInterval(const OptionSet<ThrottlingReason>&, Optional<FramesPerSecond> nominalFramesPerSecond);
+
+// This will return WTF::nullopt if throttling results in a frequency < 1fps.
+WEBCORE_EXPORT Optional<FramesPerSecond> preferredFramesPerSecond(OptionSet<ThrottlingReason>, Optional<FramesPerSecond> nominalFramesPerSecond, bool preferFrameRatesNear60FPS);
+
+WEBCORE_EXPORT Seconds preferredFrameInterval(OptionSet<ThrottlingReason>, Optional<FramesPerSecond> nominalFramesPerSecond, bool preferFrameRatesNear60FPS);
+
WEBCORE_EXPORT FramesPerSecond preferredFramesPerSecondFromInterval(Seconds);
WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, const OptionSet<ThrottlingReason>&);
Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp (275214 => 275215)
--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -172,7 +172,7 @@
m_clientsToBeNotified = &clientsToBeNotified;
while (!clientsToBeNotified.isEmpty()) {
DisplayRefreshMonitorClient* client = clientsToBeNotified.takeAny();
- client->fireDisplayRefreshIfNeeded();
+ client->fireDisplayRefreshIfNeeded(displayUpdate);
// This checks if this function was reentered. In that case, stop iterating
// since it's not safe to use the set any more.
Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp (275214 => 275215)
--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -38,11 +38,19 @@
DisplayRefreshMonitorManager::sharedManager().unregisterClient(*this);
}
-void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded()
+void DisplayRefreshMonitorClient::setPreferredFramesPerSecond(FramesPerSecond preferredFrameRate)
{
+ m_preferredFramesPerSecond = preferredFrameRate;
+}
+
+void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(const DisplayUpdate& displayUpdate)
+{
if (!m_scheduled)
return;
+ if (!displayUpdate.relevantForUpdateFrequency(m_preferredFramesPerSecond))
+ return;
+
m_scheduled = false;
displayRefreshFired();
}
Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h (275214 => 275215)
--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorClient.h 2021-03-30 18:14:26 UTC (rev 275215)
@@ -25,6 +25,7 @@
#pragma once
+#include "AnimationFrameRate.h"
#include "PlatformScreen.h"
#include <wtf/Forward.h>
#include <wtf/Optional.h>
@@ -33,6 +34,7 @@
class DisplayRefreshMonitor;
class DisplayRefreshMonitorFactory;
+struct DisplayUpdate;
class DisplayRefreshMonitorClient {
public:
@@ -48,14 +50,18 @@
bool hasDisplayID() const { return !!m_displayID; }
void setDisplayID(PlatformDisplayID displayID) { m_displayID = displayID; }
+ void setPreferredFramesPerSecond(FramesPerSecond);
+ FramesPerSecond preferredFramesPerSecond() const { return m_preferredFramesPerSecond; }
+
void setIsScheduled(bool isScheduled) { m_scheduled = isScheduled; }
bool isScheduled() const { return m_scheduled; }
- void fireDisplayRefreshIfNeeded();
+ void fireDisplayRefreshIfNeeded(const DisplayUpdate&);
private:
+ Optional<PlatformDisplayID> m_displayID;
+ FramesPerSecond m_preferredFramesPerSecond { FullSpeedFramesPerSecond };
bool m_scheduled { false };
- Optional<PlatformDisplayID> m_displayID;
};
}
Modified: trunk/Source/WebCore/platform/graphics/DisplayUpdate.cpp (275214 => 275215)
--- trunk/Source/WebCore/platform/graphics/DisplayUpdate.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/platform/graphics/DisplayUpdate.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -27,10 +27,18 @@
#include "config.h"
#include "DisplayUpdate.h"
+#include <wtf/MathExtras.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
+bool DisplayUpdate::relevantForUpdateFrequency(FramesPerSecond preferredFramesPerSecond) const
+{
+ ASSERT(WTF::isIntegral(static_cast<float>(updatesPerSecond) / preferredFramesPerSecond));
+ unsigned rateFactor = updatesPerSecond / preferredFramesPerSecond;
+ return !(updateIndex % rateFactor);
+}
+
TextStream& operator<<(TextStream& ts, const DisplayUpdate& update)
{
ts << update.updateIndex << "/" << update.updatesPerSecond;
Modified: trunk/Source/WebCore/platform/graphics/DisplayUpdate.h (275214 => 275215)
--- trunk/Source/WebCore/platform/graphics/DisplayUpdate.h 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebCore/platform/graphics/DisplayUpdate.h 2021-03-30 18:14:26 UTC (rev 275215)
@@ -44,6 +44,8 @@
{
return { (updateIndex + 1) % updatesPerSecond, updatesPerSecond };
}
+
+ WEBCORE_EXPORT bool relevantForUpdateFrequency(FramesPerSecond) const;
template<class Encoder> void encode(Encoder&) const;
template<class Decoder> static Optional<DisplayUpdate> decode(Decoder&);
Modified: trunk/Source/WebKit/ChangeLog (275214 => 275215)
--- trunk/Source/WebKit/ChangeLog 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebKit/ChangeLog 2021-03-30 18:14:26 UTC (rev 275215)
@@ -1,3 +1,16 @@
+2021-03-29 Simon Fraser <[email protected]>
+
+ Allow non-60fps display updates to be driven by DisplayRefreshMonitor
+ https://bugs.webkit.org/show_bug.cgi?id=223912
+
+ Reviewed by Sam Weinig.
+
+ Improve the logging.
+
+ * UIProcess/mac/DisplayLink.cpp:
+ (WebKit::DisplayLink::DisplayLink):
+ (WebKit::DisplayLink::addObserver):
+
2021-03-30 Jer Noble <[email protected]>
MediaSessionCoordinatorPrivateProxy should have a Client
Modified: trunk/Source/WebKit/UIProcess/mac/DisplayLink.cpp (275214 => 275215)
--- trunk/Source/WebKit/UIProcess/mac/DisplayLink.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Source/WebKit/UIProcess/mac/DisplayLink.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -42,8 +42,6 @@
DisplayLink::DisplayLink(WebCore::PlatformDisplayID displayID)
: m_displayID(displayID)
{
- LOG_WITH_STREAM(DisplayLink, stream << "[UI ] Creating DisplayLink for display " << displayID);
-
// FIXME: We can get here with displayID == 0 (webkit.org/b/212120), in which case CVDisplayLinkCreateWithCGDisplay()
// probably defaults to the main screen.
ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
@@ -60,6 +58,8 @@
}
m_displayNominalFramesPerSecond = nominalFramesPerSecondFromDisplayLink(m_displayLink);
+
+ LOG_WITH_STREAM(DisplayLink, stream << "[UI ] Creating DisplayLink for display " << displayID << " with nominal fps " << m_displayNominalFramesPerSecond);
}
DisplayLink::~DisplayLink()
@@ -93,7 +93,7 @@
}
if (!CVDisplayLinkIsRunning(m_displayLink)) {
- LOG_WITH_STREAM(DisplayLink, stream << "[UI ] DisplayLink for display " << m_displayID << " starting CVDisplayLink");
+ LOG_WITH_STREAM(DisplayLink, stream << "[UI ] DisplayLink for display " << m_displayID << " starting CVDisplayLink with fps " << m_displayNominalFramesPerSecond);
CVReturn error = CVDisplayLinkStart(m_displayLink);
if (error)
WTFLogAlways("Could not start the display link: %d", error);
Modified: trunk/Tools/ChangeLog (275214 => 275215)
--- trunk/Tools/ChangeLog 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Tools/ChangeLog 2021-03-30 18:14:26 UTC (rev 275215)
@@ -1,3 +1,17 @@
+2021-03-29 Simon Fraser <[email protected]>
+
+ Allow non-60fps display updates to be driven by DisplayRefreshMonitor
+ https://bugs.webkit.org/show_bug.cgi?id=223912
+
+ Reviewed by Sam Weinig.
+
+ New API tests for preferredFramesPerSecond() and
+ DisplayUpdate::relevantForUpdateFrequency(), and duplicate existing tests for the "use
+ display nominal FPS" vs "use near-60 FPS" settings.
+
+ * TestWebKitAPI/Tests/WebCore/AnimationFrameRate.cpp:
+ (TestWebKitAPI::TEST):
+
2021-03-30 Aakash Jain <[email protected]>
[ews] Add build step to find list of layout tests modified by a patch
Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/AnimationFrameRate.cpp (275214 => 275215)
--- trunk/Tools/TestWebKitAPI/Tests/WebCore/AnimationFrameRate.cpp 2021-03-30 17:34:53 UTC (rev 275214)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/AnimationFrameRate.cpp 2021-03-30 18:14:26 UTC (rev 275215)
@@ -25,11 +25,15 @@
#include "config.h"
#include <WebCore/AnimationFrameRate.h>
+#include <WebCore/DisplayUpdate.h>
using namespace WebCore;
namespace TestWebKitAPI {
+constexpr bool preferredFramesPerSecondTarget60FPSSetting = true;
+constexpr bool preferredFramesPerSecondMatchNominalFrameRateSetting = false;
+
TEST(AnimationFrameRate, framesPerSecondNearestFullSpeed)
{
ASSERT_EQ(framesPerSecondNearestFullSpeed(240), FramesPerSecond(60));
@@ -45,19 +49,35 @@
{
OptionSet<ThrottlingReason> noThrottling;
Optional<FramesPerSecond> unspecifiedNominalFramesPerSecond;
- ASSERT_EQ(preferredFrameInterval(noThrottling, unspecifiedNominalFramesPerSecond), FullSpeedAnimationInterval);
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, unspecifiedNominalFramesPerSecond), HalfSpeedThrottlingAnimationInterval);
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, unspecifiedNominalFramesPerSecond), HalfSpeedThrottlingAnimationInterval);
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, unspecifiedNominalFramesPerSecond), AggressiveThrottlingAnimationInterval);
+
+ bool preferredFramesPerSecondNear60FPS = preferredFramesPerSecondMatchNominalFrameRateSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), FullSpeedAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
+
+ preferredFramesPerSecondNear60FPS = preferredFramesPerSecondTarget60FPSSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), FullSpeedAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, unspecifiedNominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
}
TEST(AnimationFrameRate, preferredFrameIntervalWithFullSpeedNominalFramesPerSecond)
{
OptionSet<ThrottlingReason> noThrottling;
- ASSERT_EQ(preferredFrameInterval(noThrottling, FullSpeedFramesPerSecond), FullSpeedAnimationInterval);
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, FullSpeedFramesPerSecond), HalfSpeedThrottlingAnimationInterval);
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, FullSpeedFramesPerSecond), HalfSpeedThrottlingAnimationInterval);
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, FullSpeedFramesPerSecond), AggressiveThrottlingAnimationInterval);
+
+ bool preferredFramesPerSecondNear60FPS = preferredFramesPerSecondMatchNominalFrameRateSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), FullSpeedAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
+
+ preferredFramesPerSecondNear60FPS = preferredFramesPerSecondTarget60FPSSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), FullSpeedAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), HalfSpeedThrottlingAnimationInterval);
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, FullSpeedFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
}
TEST(AnimationFrameRate, preferredFrameIntervalWith144FPSNominalFramesPerSecond)
@@ -64,11 +84,20 @@
{
OptionSet<ThrottlingReason> noThrottling;
FramesPerSecond nominalFramesPerSecond = 144;
- ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond), Seconds(1.0 / 72));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond), Seconds(2.0 / 72));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond), Seconds(2.0 / 72));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond), Seconds(2.0 / 72));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond), AggressiveThrottlingAnimationInterval);
+
+ bool preferredFramesPerSecondNear60FPS = preferredFramesPerSecondMatchNominalFrameRateSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 144));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 144));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 144));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 144));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
+
+ preferredFramesPerSecondNear60FPS = preferredFramesPerSecondTarget60FPSSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 72));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 72));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 72));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 72));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
}
TEST(AnimationFrameRate, preferredFrameIntervalWith120FPSNominalFramesPerSecond)
@@ -75,11 +104,20 @@
{
OptionSet<ThrottlingReason> noThrottling;
FramesPerSecond nominalFramesPerSecond = 120;
- ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond), Seconds(1.0 / 60));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond), Seconds(2.0 / 60));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond), Seconds(2.0 / 60));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond), Seconds(2.0 / 60));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond), AggressiveThrottlingAnimationInterval);
+
+ bool preferredFramesPerSecondNear60FPS = preferredFramesPerSecondMatchNominalFrameRateSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 120));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 120));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 120));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 120));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
+
+ preferredFramesPerSecondNear60FPS = preferredFramesPerSecondTarget60FPSSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 60));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 60));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 60));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 60));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
}
TEST(AnimationFrameRate, preferredFrameIntervalWith90FPSNominalFramesPerSecond)
@@ -86,11 +124,20 @@
{
OptionSet<ThrottlingReason> noThrottling;
FramesPerSecond nominalFramesPerSecond = 90;
- ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond), Seconds(1.0 / 90));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond), Seconds(2.0 / 90));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond), Seconds(2.0 / 90));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond), Seconds(2.0 / 90));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond), AggressiveThrottlingAnimationInterval);
+
+ bool preferredFramesPerSecondNear60FPS = preferredFramesPerSecondMatchNominalFrameRateSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
+
+ preferredFramesPerSecondNear60FPS = preferredFramesPerSecondTarget60FPSSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 90));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
}
TEST(AnimationFrameRate, preferredFrameIntervalWith48FPSNominalFramesPerSecond)
@@ -97,11 +144,20 @@
{
OptionSet<ThrottlingReason> noThrottling;
FramesPerSecond nominalFramesPerSecond = 48;
- ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond), Seconds(1.0 / 48));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond), Seconds(2.0 / 48));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond), Seconds(2.0 / 48));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond), Seconds(2.0 / 48));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond), AggressiveThrottlingAnimationInterval);
+
+ bool preferredFramesPerSecondNear60FPS = preferredFramesPerSecondMatchNominalFrameRateSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
+
+ preferredFramesPerSecondNear60FPS = preferredFramesPerSecondTarget60FPSSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 48));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
}
TEST(AnimationFrameRate, preferredFrameIntervalWith30FPSNominalFramesPerSecond)
@@ -108,18 +164,88 @@
{
OptionSet<ThrottlingReason> noThrottling;
FramesPerSecond nominalFramesPerSecond = 30;
- ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond), Seconds(1.0 / 30));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond), Seconds(2.0 / 30));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond), Seconds(2.0 / 30));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond), Seconds(2.0 / 30));
- ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond), AggressiveThrottlingAnimationInterval);
+
+ bool preferredFramesPerSecondNear60FPS = preferredFramesPerSecondMatchNominalFrameRateSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
+
+ preferredFramesPerSecondNear60FPS = preferredFramesPerSecondTarget60FPSSetting;
+ ASSERT_EQ(preferredFrameInterval(noThrottling, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(1.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::LowPowerMode }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::VisuallyIdle }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::NonInteractedCrossOriginFrame }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), Seconds(2.0 / 30));
+ ASSERT_EQ(preferredFrameInterval({ ThrottlingReason::OutsideViewport }, nominalFramesPerSecond, preferredFramesPerSecondNear60FPS), AggressiveThrottlingAnimationInterval);
}
-TEST(AnimationFrameRate, preferredFramesPerSecond)
+TEST(AnimationFrameRate, preferredFramesPerSecondMatchNominalFrameRate)
{
+ ASSERT_EQ(preferredFramesPerSecond({ }, FullSpeedFramesPerSecond, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), FullSpeedFramesPerSecond);
+ ASSERT_EQ(preferredFramesPerSecond({ }, 120, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), FramesPerSecond(120));
+ ASSERT_EQ(preferredFramesPerSecond({ }, 90, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), FramesPerSecond(90));
+ ASSERT_EQ(preferredFramesPerSecond({ }, 50, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), FramesPerSecond(50));
+
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, FullSpeedFramesPerSecond, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), HalfSpeedThrottlingFramesPerSecond);
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, 120, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), FramesPerSecond(60));
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, 90, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), FramesPerSecond(45));
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, 50, preferredFramesPerSecondMatchNominalFrameRateSetting).value(), FramesPerSecond(25));
+
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::OutsideViewport }, FullSpeedFramesPerSecond, preferredFramesPerSecondMatchNominalFrameRateSetting), WTF::nullopt);
+}
+
+TEST(AnimationFrameRate, preferredFramesPerSecondTarget60FPS)
+{
+ ASSERT_EQ(preferredFramesPerSecond({ }, FullSpeedFramesPerSecond, preferredFramesPerSecondTarget60FPSSetting).value(), FullSpeedFramesPerSecond);
+ ASSERT_EQ(preferredFramesPerSecond({ }, 120, preferredFramesPerSecondTarget60FPSSetting).value(), FramesPerSecond(60));
+ ASSERT_EQ(preferredFramesPerSecond({ }, 90, preferredFramesPerSecondTarget60FPSSetting).value(), FramesPerSecond(90));
+ ASSERT_EQ(preferredFramesPerSecond({ }, 50, preferredFramesPerSecondTarget60FPSSetting).value(), FramesPerSecond(50));
+
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, FullSpeedFramesPerSecond, preferredFramesPerSecondTarget60FPSSetting).value(), HalfSpeedThrottlingFramesPerSecond);
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, 120, preferredFramesPerSecondTarget60FPSSetting).value(), FramesPerSecond(30));
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, 90, preferredFramesPerSecondTarget60FPSSetting).value(), FramesPerSecond(45));
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::LowPowerMode }, 50, preferredFramesPerSecondTarget60FPSSetting).value(), FramesPerSecond(25));
+
+ ASSERT_EQ(preferredFramesPerSecond({ ThrottlingReason::OutsideViewport }, FullSpeedFramesPerSecond, preferredFramesPerSecondTarget60FPSSetting), WTF::nullopt);
+}
+
+TEST(AnimationFrameRate, preferredFramesPerSecondFromInterval)
+{
ASSERT_EQ(preferredFramesPerSecondFromInterval(FullSpeedAnimationInterval), FullSpeedFramesPerSecond);
ASSERT_EQ(preferredFramesPerSecondFromInterval(HalfSpeedThrottlingAnimationInterval), HalfSpeedThrottlingFramesPerSecond);
ASSERT_EQ(preferredFramesPerSecondFromInterval(Seconds(1.0 / 60)), FramesPerSecond(60));
}
+TEST(AnimationFrameRate, displayUpdateRelevancy)
+{
+ auto frame0 = DisplayUpdate { 0, FullSpeedFramesPerSecond };
+ auto frame1 = DisplayUpdate { 1, FullSpeedFramesPerSecond };
+ auto frame2 = DisplayUpdate { 2, FullSpeedFramesPerSecond };
+ auto frame3 = DisplayUpdate { 3, FullSpeedFramesPerSecond };
+ auto frame4 = DisplayUpdate { 4, FullSpeedFramesPerSecond };
+
+ auto quarterSpeedFrameRate = HalfSpeedThrottlingFramesPerSecond / 2;
+
+ ASSERT_TRUE(frame0.relevantForUpdateFrequency(FullSpeedFramesPerSecond));
+ ASSERT_TRUE(frame0.relevantForUpdateFrequency(HalfSpeedThrottlingFramesPerSecond));
+ ASSERT_TRUE(frame0.relevantForUpdateFrequency(quarterSpeedFrameRate));
+
+ ASSERT_TRUE(frame1.relevantForUpdateFrequency(FullSpeedFramesPerSecond));
+ ASSERT_FALSE(frame1.relevantForUpdateFrequency(HalfSpeedThrottlingFramesPerSecond));
+ ASSERT_FALSE(frame1.relevantForUpdateFrequency(quarterSpeedFrameRate));
+
+ ASSERT_TRUE(frame2.relevantForUpdateFrequency(FullSpeedFramesPerSecond));
+ ASSERT_TRUE(frame2.relevantForUpdateFrequency(HalfSpeedThrottlingFramesPerSecond));
+ ASSERT_FALSE(frame2.relevantForUpdateFrequency(quarterSpeedFrameRate));
+
+ ASSERT_TRUE(frame3.relevantForUpdateFrequency(FullSpeedFramesPerSecond));
+ ASSERT_FALSE(frame3.relevantForUpdateFrequency(HalfSpeedThrottlingFramesPerSecond));
+ ASSERT_FALSE(frame3.relevantForUpdateFrequency(quarterSpeedFrameRate));
+
+ ASSERT_TRUE(frame4.relevantForUpdateFrequency(FullSpeedFramesPerSecond));
+ ASSERT_TRUE(frame4.relevantForUpdateFrequency(HalfSpeedThrottlingFramesPerSecond));
+ ASSERT_TRUE(frame4.relevantForUpdateFrequency(quarterSpeedFrameRate));
}
+
+} // namespace TestWebKitAPI
_______________________________________________ webkit-changes mailing list [email protected] https://lists.webkit.org/mailman/listinfo/webkit-changes
