From a477fc54897ee184c8cda8132b7c8720644ba18d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 7 Mar 2011 11:58:48 -0500 Subject: [PATCH] Bug 630127. Use a precise timer when we have animation frame callbacks. r=dbaron --- layout/base/nsRefreshDriver.cpp | 48 ++++++++++++++++++++++++-------- layout/base/nsRefreshDriver.h | 10 +++++++ layout/build/nsLayoutStatics.cpp | 2 ++ 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index ee83b41ceb3..e94f5993a58 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -58,6 +58,15 @@ using mozilla::TimeStamp; #define DEFAULT_FRAME_RATE 60 #define DEFAULT_THROTTLED_FRAME_RATE 1 +static PRBool sPrecisePref; + +/* static */ void +nsRefreshDriver::InitializeStatics() +{ + nsContentUtils::AddBoolPrefVarCache("layout.frame_rate.precise", + &sPrecisePref, + PR_FALSE); +} // Compute the interval to use for the refresh driver timer, in // milliseconds PRInt32 @@ -85,16 +94,17 @@ nsRefreshDriver::GetRefreshTimerType() const if (mThrottled) { return nsITimer::TYPE_ONE_SHOT; } - PRBool precise = - nsContentUtils::GetBoolPref("layout.frame_rate.precise", PR_FALSE); - return precise ? (PRInt32)nsITimer::TYPE_REPEATING_PRECISE - : (PRInt32)nsITimer::TYPE_REPEATING_SLACK; + if (HaveAnimationFrameListeners() || sPrecisePref) { + return nsITimer::TYPE_REPEATING_PRECISE; + } + return nsITimer::TYPE_REPEATING_SLACK; } nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext) : mPresContext(aPresContext), mFrozen(false), - mThrottled(false) + mThrottled(false), + mTimerIsPrecise(false) { } @@ -157,9 +167,12 @@ nsRefreshDriver::EnsureTimerStarted() return; } + PRInt32 timerType = GetRefreshTimerType(); + mTimerIsPrecise = (timerType == nsITimer::TYPE_REPEATING_PRECISE); + nsresult rv = mTimer->InitWithCallback(this, GetRefreshTimerInterval(), - GetRefreshTimerType()); + timerType); if (NS_FAILED(rv)) { mTimer = nsnull; } @@ -338,12 +351,19 @@ nsRefreshDriver::Notify(nsITimer * /* unused */) } } - if (mThrottled) { - // Stop the timer now and restart it here. Stopping is ok because either - // it's already one-shot, and it just fired, and all we need to do is null - // it out, or it's repeating and we need to reset it to be one-shot. Note - // that the EnsureTimerStarted() call here is ok because EnsureTimerStarted - // makes sure to not start the timer if it shouldn't be started. + if (mThrottled || + (mTimerIsPrecise != + (GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE))) { + // Stop the timer now and restart it here. Stopping is in the mThrottled + // case ok because either it's already one-shot, and it just fired, and all + // we need to do is null it out, or it's repeating and we need to reset it + // to be one-shot. Stopping and restarting in the case when we need to + // switch from precise to slack timers or vice versa is unfortunately + // required. + + // Note that the EnsureTimerStarted() call here is ok because + // EnsureTimerStarted makes sure to not start the timer if it shouldn't be + // started. StopTimer(); EnsureTimerStarted(); } @@ -421,6 +441,8 @@ nsRefreshDriver::ScheduleAnimationFrameListeners(nsIDocument* aDocument) mAnimationFrameListenerDocs.NoIndex, "Don't schedule the same document multiple times"); mAnimationFrameListenerDocs.AppendElement(aDocument); + // No need to worry about restarting our timer in precise mode if it's + // already running; that will happen automatically when it fires. EnsureTimerStarted(); } @@ -434,4 +456,6 @@ void nsRefreshDriver::RevokeAnimationFrameListeners(nsIDocument* aDocument) { mAnimationFrameListenerDocs.RemoveElement(aDocument); + // 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 0711c64ff81..8deed35c0a0 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -79,6 +79,8 @@ public: nsRefreshDriver(nsPresContext *aPresContext); ~nsRefreshDriver(); + static void InitializeStatics(); + // nsISupports implementation NS_DECL_ISUPPORTS @@ -221,6 +223,10 @@ private: PRInt32 GetRefreshTimerInterval() const; PRInt32 GetRefreshTimerType() const; + bool HaveAnimationFrameListeners() const { + return mAnimationFrameListenerDocs.Length() != 0; + } + nsCOMPtr mTimer; mozilla::TimeStamp mMostRecentRefresh; // only valid when mTimer non-null PRInt64 mMostRecentRefreshEpochTime; // same thing as mMostRecentRefresh, @@ -231,6 +237,10 @@ private: bool mFrozen; bool mThrottled; + /* If mTimer is non-null, this boolean indicates whether the timer is + a precise timer. If mTimer is null, this boolean's value can be + anything. */ + bool mTimerIsPrecise; // separate arrays for each flush type we support ObserverArray mObservers[3]; diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 11a02866119..9a34ae895ee 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -128,6 +128,7 @@ #include "nsJSEnvironment.h" #include "nsContentSink.h" #include "nsFrameMessageManager.h" +#include "nsRefreshDriver.h" extern void NS_ShutdownChainItemPool(); @@ -273,6 +274,7 @@ nsLayoutStatics::Initialize() nsContentSink::InitializeStatics(); nsHtml5Module::InitializeStatics(); nsIPresShell::InitializeStatics(); + nsRefreshDriver::InitializeStatics(); nsCrossSiteListenerProxy::Startup();