From aaa64b9647ac443917213038fb4bd6881b77a1b5 Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Sat, 16 May 2020 06:52:54 +0800 Subject: [PATCH] Reverted "Issue #1507 - Remove nsRefreshDriver's use of high res system timers. (7630b1c56)" This helps resolving constant stutter when scrolling web pages on XP. --- layout/base/nsRefreshDriver.cpp | 98 +++++++++++++++++++++++++++++++++ layout/base/nsRefreshDriver.h | 5 ++ 2 files changed, 103 insertions(+) diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index c0d09d2a0b..5ea7a01881 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -851,6 +851,12 @@ NS_IMPL_ISUPPORTS(VsyncChildCreateCallback, nsIIPCBackgroundChildCreateCallback) static StaticRefPtr sRegularRateTimer; static StaticRefPtr sThrottledRateTimer; +#ifdef XP_WIN +static int32_t sHighPrecisionTimerRequests = 0; +// a bare pointer to avoid introducing a static constructor +static nsITimer *sDisableHighPrecisionTimersTimer = nullptr; +#endif + static void CreateContentVsyncRefreshTimer(void*) { @@ -928,6 +934,16 @@ nsRefreshDriver::Shutdown() sRegularRateTimer = nullptr; sThrottledRateTimer = nullptr; + +#ifdef XP_WIN + if (sDisableHighPrecisionTimersTimer) { + sDisableHighPrecisionTimersTimer->Cancel(); + NS_RELEASE(sDisableHighPrecisionTimersTimer); + timeEndPeriod(1); + } else if (sHighPrecisionTimerRequests) { + timeEndPeriod(1); + } +#endif } /* static */ int32_t @@ -1046,6 +1062,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext) mNeedToRecomputeVisibility(false), mTestControllingRefreshes(false), mViewManagerFlushIsPending(false), + mRequestedHighPrecision(false), mInRefresh(false), mWaitingForTransaction(false), mSkippedPaints(false), @@ -1283,6 +1300,83 @@ nsRefreshDriver::StopTimer() mActiveTimer->RemoveRefreshDriver(this); mActiveTimer = nullptr; + + if (mRequestedHighPrecision) { + SetHighPrecisionTimersEnabled(false); + } +} + +#ifdef XP_WIN +static void +DisableHighPrecisionTimersCallback(nsITimer *aTimer, void *aClosure) +{ + timeEndPeriod(1); + NS_RELEASE(sDisableHighPrecisionTimersTimer); +} +#endif + +void +nsRefreshDriver::ConfigureHighPrecision() +{ + bool haveUnthrottledFrameRequestCallbacks = + mFrameRequestCallbackDocs.Length() > 0; + + // if the only change that's needed is that we need high precision, + // then just set that + if (!mThrottled && !mRequestedHighPrecision && + haveUnthrottledFrameRequestCallbacks) { + SetHighPrecisionTimersEnabled(true); + } else if (mRequestedHighPrecision && !haveUnthrottledFrameRequestCallbacks) { + SetHighPrecisionTimersEnabled(false); + } +} + +void +nsRefreshDriver::SetHighPrecisionTimersEnabled(bool aEnable) +{ + LOG("[%p] SetHighPrecisionTimersEnabled (%s)", this, aEnable ? "true" : "false"); + + if (aEnable) { + NS_ASSERTION(!mRequestedHighPrecision, "SetHighPrecisionTimersEnabled(true) called when already requested!"); +#ifdef XP_WIN + if (++sHighPrecisionTimerRequests == 1) { + // If we had a timer scheduled to disable it, that means that it's already + // enabled; just cancel the timer. Otherwise, really enable it. + if (sDisableHighPrecisionTimersTimer) { + sDisableHighPrecisionTimersTimer->Cancel(); + NS_RELEASE(sDisableHighPrecisionTimersTimer); + } else { + timeBeginPeriod(1); + } + } +#endif + mRequestedHighPrecision = true; + } else { + NS_ASSERTION(mRequestedHighPrecision, "SetHighPrecisionTimersEnabled(false) called when not requested!"); +#ifdef XP_WIN + if (--sHighPrecisionTimerRequests == 0) { + // Don't jerk us around between high precision and low precision + // timers; instead, only allow leaving high precision timers + // after 90 seconds. This is arbitrary, but hopefully good + // enough. + NS_ASSERTION(!sDisableHighPrecisionTimersTimer, "We shouldn't have an outstanding disable-high-precision timer !"); + + nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID); + if (timer) { + timer.forget(&sDisableHighPrecisionTimersTimer); + sDisableHighPrecisionTimersTimer->InitWithFuncCallback(DisableHighPrecisionTimersCallback, + nullptr, + 90 * 1000, + nsITimer::TYPE_ONE_SHOT); + } else { + // might happen if we're shutting down XPCOM; just drop the time period down + // immediately + timeEndPeriod(1); + } + } +#endif + mRequestedHighPrecision = false; + } } uint32_t @@ -1899,6 +1993,8 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) observer->DidRefresh(); } + ConfigureHighPrecision(); + NS_ASSERTION(mInRefresh, "Still in refresh"); if (mPresContext->IsRoot() && XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) { @@ -2151,6 +2247,7 @@ nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument) } // make sure that the timer is running + ConfigureHighPrecision(); EnsureTimerStarted(); } @@ -2159,6 +2256,7 @@ nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument) { mFrameRequestCallbackDocs.RemoveElement(aDocument); mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument); + ConfigureHighPrecision(); // No need to worry about restarting our timer in slack mode if it's already // running; that will happen automatically when it fires. } diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index 4e839e7641..b2dd9be4be 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -450,6 +450,7 @@ private: bool mNeedToRecomputeVisibility; bool mTestControllingRefreshes; bool mViewManagerFlushIsPending; + bool mRequestedHighPrecision; bool mInRefresh; // True if the refresh driver is suspended waiting for transaction @@ -501,6 +502,10 @@ private: friend class mozilla::RefreshDriverTimer; + // turn on or turn off high precision based on various factors + void ConfigureHighPrecision(); + void SetHighPrecisionTimersEnabled(bool aEnable); + static void Shutdown(); // `true` if we are currently in jank-critical mode.