From 6252fa89ede6e01971295cf150f63af5f030e6e9 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Thu, 18 Mar 2010 07:58:09 -0700 Subject: [PATCH] Backed out changeset 59f507847beb (bug 541588) to see if it was responsible for minor SVG perf regression. --- content/base/public/nsIDocument.h | 2 +- content/smil/nsSMILAnimationController.cpp | 107 +++++++++++---------- content/smil/nsSMILAnimationController.h | 21 +--- layout/base/nsPresShell.cpp | 16 +-- 4 files changed, 60 insertions(+), 86 deletions(-) diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 6f597b17207e..bbbc01537262 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -1360,7 +1360,7 @@ protected: #ifdef MOZ_SMIL // SMIL Animation Controller, lazily-initialized in GetAnimationController - nsRefPtr mAnimationController; + nsAutoPtr mAnimationController; #endif // MOZ_SMIL // Table of element properties for this document. diff --git a/content/smil/nsSMILAnimationController.cpp b/content/smil/nsSMILAnimationController.cpp index 7c2ce56a2fea..9fda1a9b34a3 100644 --- a/content/smil/nsSMILAnimationController.cpp +++ b/content/smil/nsSMILAnimationController.cpp @@ -51,19 +51,16 @@ //---------------------------------------------------------------------- // nsSMILAnimationController implementation -// Helper method -static nsRefreshDriver* -GetRefreshDriverForDoc(nsIDocument* aDoc) -{ - nsIPresShell* shell = aDoc->GetPrimaryShell(); - if (!shell) { - return nsnull; - } - - nsPresContext* context = shell->GetPresContext(); - return context ? context->RefreshDriver() : nsnull; -} - +// In my testing the minimum needed for smooth animation is 36 frames per +// second which seems like a lot (Flash traditionally uses 14fps). +// +// Redrawing is synchronous. This is deliberate so that later we can tune the +// timer based on how long the callback takes. To achieve 36fps we'd need 28ms +// between frames. For now we set the timer interval to be a little less than +// this (to allow for the render itself) and then let performance decay as the +// image gets more complicated and render times increase. +// +const PRUint32 nsSMILAnimationController::kTimerInterval = 22; //---------------------------------------------------------------------- // ctors, dtors, factory methods @@ -78,7 +75,11 @@ nsSMILAnimationController::nsSMILAnimationController() nsSMILAnimationController::~nsSMILAnimationController() { - StopSampling(GetRefreshDriverForDoc(mDocument)); + if (mTimer) { + mTimer->Cancel(); + mTimer = nsnull; + } + NS_ASSERTION(mAnimationElementTable.Count() == 0, "Animation controller shouldn't be tracking any animation" " elements when it dies"); @@ -104,6 +105,9 @@ nsSMILAnimationController::Init(nsIDocument* aDoc) { NS_ENSURE_ARG_POINTER(aDoc); + mTimer = do_CreateInstance("@mozilla.org/timer;1"); + NS_ENSURE_TRUE(mTimer, NS_ERROR_OUT_OF_MEMORY); + // Keep track of document, so we can traverse its set of animation elements mDocument = aDoc; @@ -121,7 +125,7 @@ nsSMILAnimationController::Pause(PRUint32 aType) nsSMILTimeContainer::Pause(aType); if (mPauseState) { - StopSampling(GetRefreshDriverForDoc(mDocument)); + StopTimer(); } } @@ -133,8 +137,7 @@ nsSMILAnimationController::Resume(PRUint32 aType) nsSMILTimeContainer::Resume(aType); if (wasPaused && !mPauseState && mChildContainerTable.Count()) { - Sample(); // Run the first sample manually - StartSampling(GetRefreshDriverForDoc(mDocument)); + StartTimer(); } } @@ -145,20 +148,6 @@ nsSMILAnimationController::GetParentTime() const return PR_Now() / PR_USEC_PER_MSEC; } -//---------------------------------------------------------------------- -// nsARefreshObserver methods: -NS_IMPL_ADDREF(nsSMILAnimationController) -NS_IMPL_RELEASE(nsSMILAnimationController) - -void -nsSMILAnimationController::WillRefresh(mozilla::TimeStamp aTime) -{ - // XXXdholbert Eventually we should be sampling based on aTime. For now, - // though, we keep track of the time on our own, and we just use - // nsRefreshDriver for scheduling samples. - Sample(); -} - //---------------------------------------------------------------------- // Animation element registration methods: @@ -225,29 +214,42 @@ nsSMILAnimationController::Unlink() //---------------------------------------------------------------------- // Timer-related implementation helpers -void -nsSMILAnimationController::StartSampling(nsRefreshDriver* aRefreshDriver) +/*static*/ void +nsSMILAnimationController::Notify(nsITimer* timer, void* aClosure) { - NS_ASSERTION(mPauseState == 0, "Starting sampling but controller is paused"); - if (aRefreshDriver) { - NS_ABORT_IF_FALSE(!GetRefreshDriverForDoc(mDocument) || - aRefreshDriver == GetRefreshDriverForDoc(mDocument), - "Starting sampling with wrong refresh driver"); - aRefreshDriver->AddRefreshObserver(this, Flush_Style); - } + nsSMILAnimationController* controller = (nsSMILAnimationController*)aClosure; + + NS_ASSERTION(controller->mTimer == timer, + "nsSMILAnimationController::Notify called with incorrect timer"); + + controller->Sample(); } -void -nsSMILAnimationController::StopSampling(nsRefreshDriver* aRefreshDriver) +nsresult +nsSMILAnimationController::StartTimer() { - if (aRefreshDriver) { - // NOTE: The document might already have been detached from its PresContext - // (and RefreshDriver), which would make GetRefreshDriverForDoc return null. - NS_ABORT_IF_FALSE(!GetRefreshDriverForDoc(mDocument) || - aRefreshDriver == GetRefreshDriverForDoc(mDocument), - "Stopping sampling with wrong refresh driver"); - aRefreshDriver->RemoveRefreshObserver(this, Flush_Style); - } + NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE); + NS_ASSERTION(mPauseState == 0, "Starting timer but controller is paused"); + + // Run the first sample manually + Sample(); + + // + // XXX Make this self-tuning. Sounds like control theory to me and not + // something I'm familiar with. + // + return mTimer->InitWithFuncCallback(nsSMILAnimationController::Notify, + this, + kTimerInterval, + nsITimer::TYPE_REPEATING_SLACK); +} + +nsresult +nsSMILAnimationController::StopTimer() +{ + NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE); + + return mTimer->Cancel(); } //---------------------------------------------------------------------- @@ -666,8 +668,7 @@ nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild) NS_ENSURE_TRUE(key,NS_ERROR_OUT_OF_MEMORY); if (!mPauseState && mChildContainerTable.Count() == 1) { - Sample(); // Run the first sample manually - StartSampling(GetRefreshDriverForDoc(mDocument)); + StartTimer(); } return NS_OK; @@ -679,6 +680,6 @@ nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild) mChildContainerTable.RemoveEntry(&aChild); if (!mPauseState && mChildContainerTable.Count() == 0) { - StopSampling(GetRefreshDriverForDoc(mDocument)); + StopTimer(); } } diff --git a/content/smil/nsSMILAnimationController.h b/content/smil/nsSMILAnimationController.h index ae96e22ff488..7365ddf6b811 100644 --- a/content/smil/nsSMILAnimationController.h +++ b/content/smil/nsSMILAnimationController.h @@ -48,7 +48,6 @@ #include "nsSMILTimeContainer.h" #include "nsSMILCompositorTable.h" #include "nsSMILMilestone.h" -#include "nsRefreshDriver.h" struct nsSMILTargetIdentifier; class nsISMILAnimationElement; @@ -67,8 +66,7 @@ class nsIDocument; // a compound document. These time containers can be paused individually or // here, at the document level. // -class nsSMILAnimationController : public nsSMILTimeContainer, - public nsARefreshObserver +class nsSMILAnimationController : public nsSMILTimeContainer { public: nsSMILAnimationController(); @@ -79,12 +77,6 @@ public: virtual void Resume(PRUint32 aType); virtual nsSMILTime GetParentTime() const; - // nsARefreshObserver - NS_IMETHOD_(nsrefcnt) AddRef(); - NS_IMETHOD_(nsrefcnt) Release(); - - virtual void WillRefresh(mozilla::TimeStamp aTime); - // Methods for registering and enumerating animation elements void RegisterAnimationElement(nsISMILAnimationElement* aAnimationElement); void UnregisterAnimationElement(nsISMILAnimationElement* aAnimationElement); @@ -110,11 +102,6 @@ public: void Traverse(nsCycleCollectionTraversalCallback* aCallback); void Unlink(); - // Methods for controlling whether we're sampling - // (Use to register/unregister us with the given nsRefreshDriver) - void StartSampling(nsRefreshDriver* aRefreshDriver); - void StopSampling(nsRefreshDriver* aRefreshDriver); - protected: // Typedefs typedef nsPtrHashKey TimeContainerPtrKey; @@ -151,6 +138,8 @@ protected: // Timer-related implementation helpers static void Notify(nsITimer* aTimer, void* aClosure); + nsresult StartTimer(); + nsresult StopTimer(); // Sample-related callbacks and implementation helpers virtual void DoSample(); @@ -176,10 +165,8 @@ protected: virtual void RemoveChild(nsSMILTimeContainer& aChild); // Members - nsAutoRefCnt mRefCnt; - NS_DECL_OWNINGTHREAD - static const PRUint32 kTimerInterval; + nsCOMPtr mTimer; AnimationElementHashtable mAnimationElementTable; TimeContainerHashtable mChildContainerTable; PRPackedBool mResampleNeeded; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 6f3d5f3cb30f..391d5a9eb237 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1708,15 +1708,6 @@ PresShell::Init(nsIDocument* aDocument, } #endif -#ifdef MOZ_SMIL - if (mDocument->HasAnimationController()) { - nsSMILAnimationController* animCtrl = mDocument->GetAnimationController(); - if (!animCtrl->IsPaused()) { - animCtrl->StartSampling(GetPresContext()->RefreshDriver()); - } - } -#endif // MOZ_SMIL - return NS_OK; } @@ -1826,15 +1817,10 @@ PresShell::Destroy() mDocument->DeleteShell(); } - nsRefreshDriver* rd = GetPresContext()->RefreshDriver(); - if (mDocument->HasAnimationController()) { - mDocument->GetAnimationController()->StopSampling(rd); - } - // Revoke any pending events. We need to do this and cancel pending reflows // before we destroy the frame manager, since apparently frame destruction // sometimes spins the event queue when plug-ins are involved(!). - rd->RemoveRefreshObserver(this, Flush_Layout); + GetPresContext()->RefreshDriver()->RemoveRefreshObserver(this, Flush_Layout); mResizeEvent.Revoke(); if (mAsyncResizeTimerIsActive) { mAsyncResizeEventTimer->Cancel();