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 */
bool ChromeUtils::VsyncEnabled(GlobalObject& aGlobal) {
mozilla::gfx::VsyncSource* vsyncSource =
gfxPlatform::GetPlatform()->GetHardwareVsync();
MOZ_ASSERT(vsyncSource != nullptr);
return vsyncSource->GetGlobalDisplay().IsVsyncEnabled();
return mozilla::gfx::VsyncSource::GetFastestVsyncRate().isSome();
}
/* static */

View File

@ -8,6 +8,11 @@
#include "nsXULAppAPI.h"
#include "mozilla/VsyncDispatcher.h"
#include "MainThreadUtils.h"
#include "gfxPlatform.h"
#ifdef MOZ_WAYLAND
# include "WaylandVsyncSource.h"
#endif
namespace mozilla {
namespace gfx {
@ -271,6 +276,33 @@ VsyncSource::Display::GetRefreshTimerVsyncDispatcher() {
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(); }
} // namespace gfx

View File

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

View File

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

View File

@ -2927,7 +2927,8 @@ void nsRefreshDriver::CancelPendingAnimationEvents(
}
/* static */
TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault,
IdleCheck aCheckType) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aDefault.IsNull());
@ -2938,7 +2939,10 @@ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
// busy but tasks resulting from a tick on sThrottledRateTimer
// counts as being idle.
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
@ -2950,6 +2954,8 @@ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
// For now we use a somewhat simplistic approach that in many situations
// gives us similar behaviour to what we would get using sRegularRateTimer:
// 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();
if (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 */

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
* 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

View File

@ -7,6 +7,7 @@
#ifdef MOZ_WAYLAND
# include "WaylandVsyncSource.h"
# include "mozilla/UniquePtr.h"
# include "nsThreadUtils.h"
# include "nsISupportsImpl.h"
# include "MainThreadUtils.h"
@ -50,6 +51,27 @@ static float GetFPS(TimeDuration aVsyncRate) {
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()
: mMutex("WaylandVsyncSource"),
mIsShutdown(false),
@ -60,6 +82,19 @@ WaylandVsyncSource::WaylandDisplay::WaylandDisplay()
mVsyncRate(TimeDuration::FromMilliseconds(1000.0 / 60.0)),
mLastVsyncTimeStamp(TimeStamp::Now()) {
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(

View File

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

View File

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

View File

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