From 1edf4c885e9b0a6e34cb217050ceed3be2e0545a Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Sat, 26 Feb 2022 22:37:03 +0000 Subject: [PATCH] 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 --- dom/base/ChromeUtils.cpp | 6 +---- gfx/thebes/VsyncSource.cpp | 32 +++++++++++++++++++++++ gfx/thebes/VsyncSource.h | 4 +++ gfx/thebes/moz.build | 1 + layout/base/nsRefreshDriver.cpp | 28 ++++++++++++++++++--- layout/base/nsRefreshDriver.h | 13 +++++++++- widget/gtk/WaylandVsyncSource.cpp | 35 ++++++++++++++++++++++++++ widget/gtk/WaylandVsyncSource.h | 9 +++++-- xpcom/threads/IdleTaskRunner.cpp | 5 +++- xpcom/threads/MainThreadIdlePeriod.cpp | 3 ++- 10 files changed, 123 insertions(+), 13 deletions(-) diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index e8584a3e9143..e65a0dc8e249 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -1172,11 +1172,7 @@ already_AddRefed 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 */ diff --git a/gfx/thebes/VsyncSource.cpp b/gfx/thebes/VsyncSource.cpp index ba678254e053..243fa82a8b62 100644 --- a/gfx/thebes/VsyncSource.cpp +++ b/gfx/thebes/VsyncSource.cpp @@ -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 VsyncSource::GetFastestVsyncRate() { + Maybe 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 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 diff --git a/gfx/thebes/VsyncSource.h b/gfx/thebes/VsyncSource.h index 95a0cf34d3ef..37db77a95f83 100644 --- a/gfx/thebes/VsyncSource.h +++ b/gfx/thebes/VsyncSource.h @@ -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 GetFastestVsyncRate(); + protected: virtual ~VsyncSource() = default; }; diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index 17368d8ffec4..2ccb40db322f 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -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"): diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 84a569597e88..1d0ab9808a14 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -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 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 */ diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index 59a7dc48e955..e41bea586b4a 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -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 diff --git a/widget/gtk/WaylandVsyncSource.cpp b/widget/gtk/WaylandVsyncSource.cpp index 68027885198a..9b0b681cdee4 100644 --- a/widget/gtk/WaylandVsyncSource.cpp +++ b/widget/gtk/WaylandVsyncSource.cpp @@ -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> + gWaylandDisplays; + +Maybe WaylandVsyncSource::GetFastestVsyncRate() { + Maybe 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>(); + } + gWaylandDisplays->insertBack(this); +} + +WaylandVsyncSource::WaylandDisplay::~WaylandDisplay() { + remove(); // Remove from the linked list. + if (gWaylandDisplays->isEmpty()) { + gWaylandDisplays = nullptr; + } } void WaylandVsyncSource::WaylandDisplay::MaybeUpdateSource( diff --git a/widget/gtk/WaylandVsyncSource.h b/widget/gtk/WaylandVsyncSource.h index ff416bfddc16..5375064e5342 100644 --- a/widget/gtk/WaylandVsyncSource.h +++ b/widget/gtk/WaylandVsyncSource.h @@ -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 GetFastestVsyncRate(); + + class WaylandDisplay final : public mozilla::gfx::VsyncSource::Display, + public LinkedListElement { 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, diff --git a/xpcom/threads/IdleTaskRunner.cpp b/xpcom/threads/IdleTaskRunner.cpp index fbc2780cd77c..5609599a7902 100644 --- a/xpcom/threads/IdleTaskRunner.cpp +++ b/xpcom/threads/IdleTaskRunner.cpp @@ -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"); diff --git a/xpcom/threads/MainThreadIdlePeriod.cpp b/xpcom/threads/MainThreadIdlePeriod.cpp index 428cc11f3716..aafd31746d5d 100644 --- a/xpcom/threads/MainThreadIdlePeriod.cpp +++ b/xpcom/threads/MainThreadIdlePeriod.cpp @@ -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); }