Bug 1754562, make parent process' idle detection be aware of active RefreshDrivers in the other processes, r=mstange

Differential Revision: https://phabricator.services.mozilla.com/D138504
This commit is contained in:
Olli Pettay 2022-02-26 22:37:03 +00:00
parent 2057b98f7b
commit 1edf4c885e
10 changed files with 123 additions and 13 deletions

View File

@ -1172,11 +1172,7 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal,
/* static */ /* static */
bool ChromeUtils::VsyncEnabled(GlobalObject& aGlobal) { bool ChromeUtils::VsyncEnabled(GlobalObject& aGlobal) {
mozilla::gfx::VsyncSource* vsyncSource = return mozilla::gfx::VsyncSource::GetFastestVsyncRate().isSome();
gfxPlatform::GetPlatform()->GetHardwareVsync();
MOZ_ASSERT(vsyncSource != nullptr);
return vsyncSource->GetGlobalDisplay().IsVsyncEnabled();
} }
/* static */ /* static */

View File

@ -8,6 +8,11 @@
#include "nsXULAppAPI.h" #include "nsXULAppAPI.h"
#include "mozilla/VsyncDispatcher.h" #include "mozilla/VsyncDispatcher.h"
#include "MainThreadUtils.h" #include "MainThreadUtils.h"
#include "gfxPlatform.h"
#ifdef MOZ_WAYLAND
# include "WaylandVsyncSource.h"
#endif
namespace mozilla { namespace mozilla {
namespace gfx { namespace gfx {
@ -271,6 +276,33 @@ VsyncSource::Display::GetRefreshTimerVsyncDispatcher() {
return mRefreshTimerVsyncDispatcher; return mRefreshTimerVsyncDispatcher;
} }
// static
Maybe<TimeDuration> VsyncSource::GetFastestVsyncRate() {
Maybe<TimeDuration> retVal;
if (!gfxPlatform::Initialized()) {
return retVal;
}
mozilla::gfx::VsyncSource* vsyncSource =
gfxPlatform::GetPlatform()->GetHardwareVsync();
if (vsyncSource && vsyncSource->GetGlobalDisplay().IsVsyncEnabled()) {
retVal.emplace(vsyncSource->GetGlobalDisplay().GetVsyncRate());
}
#ifdef MOZ_WAYLAND
Maybe<TimeDuration> waylandRate = WaylandVsyncSource::GetFastestVsyncRate();
if (waylandRate) {
if (!retVal) {
retVal.emplace(*waylandRate);
} else if (*waylandRate < *retVal) {
retVal = waylandRate;
}
}
#endif
return retVal;
}
void VsyncSource::Shutdown() { GetGlobalDisplay().Shutdown(); } void VsyncSource::Shutdown() { GetGlobalDisplay().Shutdown(); }
} // namespace gfx } // namespace gfx

View File

@ -8,6 +8,7 @@
#include "nsTArray.h" #include "nsTArray.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "nsISupportsImpl.h" #include "nsISupportsImpl.h"
@ -125,6 +126,9 @@ class VsyncSource {
virtual Display& GetGlobalDisplay() = 0; // Works across all displays virtual Display& GetGlobalDisplay() = 0; // Works across all displays
void Shutdown(); void Shutdown();
// Returns the rate of the fastest enabled VsyncSource::Display or Nothing().
static Maybe<TimeDuration> GetFastestVsyncRate();
protected: protected:
virtual ~VsyncSource() = default; virtual ~VsyncSource() = default;
}; };

View File

@ -260,6 +260,7 @@ LOCAL_INCLUDES += [
"/dom/media/platforms/apple", "/dom/media/platforms/apple",
"/dom/xml", "/dom/xml",
"/gfx/cairo/cairo/src", "/gfx/cairo/cairo/src",
"/widget/gtk",
] ]
if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("android", "gtk"): if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("android", "gtk"):

View File

@ -2927,7 +2927,8 @@ void nsRefreshDriver::CancelPendingAnimationEvents(
} }
/* static */ /* static */
TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) { TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault,
IdleCheck aCheckType) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aDefault.IsNull()); MOZ_ASSERT(!aDefault.IsNull());
@ -2938,7 +2939,10 @@ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
// busy but tasks resulting from a tick on sThrottledRateTimer // busy but tasks resulting from a tick on sThrottledRateTimer
// counts as being idle. // counts as being idle.
if (sRegularRateTimer) { if (sRegularRateTimer) {
return sRegularRateTimer->GetIdleDeadlineHint(aDefault); TimeStamp retVal = sRegularRateTimer->GetIdleDeadlineHint(aDefault);
if (retVal != aDefault) {
return retVal;
}
} }
// The following calculation is only used on platform using per-BrowserChild // The following calculation is only used on platform using per-BrowserChild
@ -2950,6 +2954,8 @@ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
// For now we use a somewhat simplistic approach that in many situations // For now we use a somewhat simplistic approach that in many situations
// gives us similar behaviour to what we would get using sRegularRateTimer: // gives us similar behaviour to what we would get using sRegularRateTimer:
// use the highest result that is still lower than the aDefault fallback. // use the highest result that is still lower than the aDefault fallback.
// XXXsmaug None of this makes much sense. We should always return the
// lowest result, not highest.
TimeStamp hint = TimeStamp(); TimeStamp hint = TimeStamp();
if (sRegularRateTimerList) { if (sRegularRateTimerList) {
for (RefreshDriverTimer* timer : *sRegularRateTimerList) { for (RefreshDriverTimer* timer : *sRegularRateTimerList) {
@ -2960,7 +2966,23 @@ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
} }
} }
return hint.IsNull() ? aDefault : hint; if (!hint.IsNull()) {
return hint;
}
if (aCheckType == IdleCheck::AllVsyncListeners && XRE_IsParentProcess()) {
Maybe<TimeDuration> rate = mozilla::gfx::VsyncSource::GetFastestVsyncRate();
if (rate.isSome()) {
TimeStamp newHint = TimeStamp::Now() + *rate -
TimeDuration::FromMilliseconds(
StaticPrefs::layout_idle_period_time_limit());
if (newHint < aDefault) {
return newHint;
}
}
}
return aDefault;
} }
/* static */ /* static */

View File

@ -359,8 +359,19 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
* *
* If we're animating and we have skipped paints a time in the past * If we're animating and we have skipped paints a time in the past
* is returned. * is returned.
*
* If aCheckType is AllVsyncListeners and we're in the parent process,
* which doesn't have a RefreshDriver ticking, but some other process does
* have, the return value is
* (now + refreshrate - layout.idle_period.time_limit) as an approximation
* when something will happen.
* This can be useful check when parent process tries to avoid having too
* long idle periods for example when it is sending input events to an
* active child process.
*/ */
static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault); enum IdleCheck { OnlyThisProcessRefreshDriver, AllVsyncListeners };
static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault,
IdleCheck aCheckType);
/** /**
* It returns the expected timestamp of the next tick or nothing if the next * It returns the expected timestamp of the next tick or nothing if the next

View File

@ -7,6 +7,7 @@
#ifdef MOZ_WAYLAND #ifdef MOZ_WAYLAND
# include "WaylandVsyncSource.h" # include "WaylandVsyncSource.h"
# include "mozilla/UniquePtr.h"
# include "nsThreadUtils.h" # include "nsThreadUtils.h"
# include "nsISupportsImpl.h" # include "nsISupportsImpl.h"
# include "MainThreadUtils.h" # include "MainThreadUtils.h"
@ -50,6 +51,27 @@ static float GetFPS(TimeDuration aVsyncRate) {
return 1000.0 / aVsyncRate.ToMilliseconds(); return 1000.0 / aVsyncRate.ToMilliseconds();
} }
static UniquePtr<LinkedList<WaylandVsyncSource::WaylandDisplay>>
gWaylandDisplays;
Maybe<TimeDuration> WaylandVsyncSource::GetFastestVsyncRate() {
Maybe<TimeDuration> retVal;
if (gWaylandDisplays) {
for (auto* display : *gWaylandDisplays) {
if (display->IsVsyncEnabled()) {
TimeDuration rate = display->GetVsyncRate();
if (!retVal.isSome()) {
retVal.emplace(rate);
} else if (rate < *retVal) {
retVal.ref() = rate;
}
}
}
}
return retVal;
}
WaylandVsyncSource::WaylandDisplay::WaylandDisplay() WaylandVsyncSource::WaylandDisplay::WaylandDisplay()
: mMutex("WaylandVsyncSource"), : mMutex("WaylandVsyncSource"),
mIsShutdown(false), mIsShutdown(false),
@ -60,6 +82,19 @@ WaylandVsyncSource::WaylandDisplay::WaylandDisplay()
mVsyncRate(TimeDuration::FromMilliseconds(1000.0 / 60.0)), mVsyncRate(TimeDuration::FromMilliseconds(1000.0 / 60.0)),
mLastVsyncTimeStamp(TimeStamp::Now()) { mLastVsyncTimeStamp(TimeStamp::Now()) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (!gWaylandDisplays) {
gWaylandDisplays =
MakeUnique<LinkedList<WaylandVsyncSource::WaylandDisplay>>();
}
gWaylandDisplays->insertBack(this);
}
WaylandVsyncSource::WaylandDisplay::~WaylandDisplay() {
remove(); // Remove from the linked list.
if (gWaylandDisplays->isEmpty()) {
gWaylandDisplays = nullptr;
}
} }
void WaylandVsyncSource::WaylandDisplay::MaybeUpdateSource( void WaylandVsyncSource::WaylandDisplay::MaybeUpdateSource(

View File

@ -8,6 +8,8 @@
#include "base/thread.h" #include "base/thread.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "mozilla/Monitor.h" #include "mozilla/Monitor.h"
#include "mozilla/layers/NativeLayerWayland.h" #include "mozilla/layers/NativeLayerWayland.h"
@ -48,7 +50,10 @@ class WaylandVsyncSource final : public gfx::VsyncSource {
virtual Display& GetGlobalDisplay() override { return *mGlobalDisplay; } virtual Display& GetGlobalDisplay() override { return *mGlobalDisplay; }
class WaylandDisplay final : public mozilla::gfx::VsyncSource::Display { static Maybe<TimeDuration> GetFastestVsyncRate();
class WaylandDisplay final : public mozilla::gfx::VsyncSource::Display,
public LinkedListElement<WaylandDisplay> {
public: public:
WaylandDisplay(); WaylandDisplay();
@ -72,7 +77,7 @@ class WaylandVsyncSource final : public gfx::VsyncSource {
virtual void Shutdown() override; virtual void Shutdown() override;
private: private:
virtual ~WaylandDisplay() = default; ~WaylandDisplay() override;
void Refresh(const MutexAutoLock& aProofOfLock); void Refresh(const MutexAutoLock& aProofOfLock);
void SetupFrameCallback(const MutexAutoLock& aProofOfLock); void SetupFrameCallback(const MutexAutoLock& aProofOfLock);
void CalculateVsyncRate(const MutexAutoLock& aProofOfLock, void CalculateVsyncRate(const MutexAutoLock& aProofOfLock,

View File

@ -188,7 +188,10 @@ void IdleTaskRunner::Schedule(bool aAllowIdleDispatch) {
if (now >= mStartTime) { if (now >= mStartTime) {
// Detect whether the refresh driver is ticking by checking if // Detect whether the refresh driver is ticking by checking if
// GetIdleDeadlineHint returns its input parameter. // GetIdleDeadlineHint returns its input parameter.
useRefreshDriver = (nsRefreshDriver::GetIdleDeadlineHint(now) != now); useRefreshDriver =
(nsRefreshDriver::GetIdleDeadlineHint(
now, nsRefreshDriver::IdleCheck::OnlyThisProcessRefreshDriver) !=
now);
} else { } else {
NS_WARNING_ASSERTION(!aAllowIdleDispatch, NS_WARNING_ASSERTION(!aAllowIdleDispatch,
"early callback, or time went backwards"); "early callback, or time went backwards");

View File

@ -38,7 +38,8 @@ MainThreadIdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline) {
TimeStamp currentGuess = TimeStamp currentGuess =
now + TimeDuration::FromMilliseconds(kLongIdlePeriodMS); now + TimeDuration::FromMilliseconds(kLongIdlePeriodMS);
currentGuess = nsRefreshDriver::GetIdleDeadlineHint(currentGuess); currentGuess = nsRefreshDriver::GetIdleDeadlineHint(
currentGuess, nsRefreshDriver::IdleCheck::AllVsyncListeners);
if (XRE_IsContentProcess()) { if (XRE_IsContentProcess()) {
currentGuess = gfx::VRManagerChild::GetIdleDeadlineHint(currentGuess); currentGuess = gfx::VRManagerChild::GetIdleDeadlineHint(currentGuess);
} }