Backed out changeset 59f507847beb (bug 541588) to see if it was responsible for minor SVG perf regression.
authorDaniel Holbert <dholbert@cs.stanford.edu>
Thu, 18 Mar 2010 07:58:09 -0700
changeset 39580 33925fdc28073fc9a47ed32da1a20ee2c2e246b5
parent 39520 59f507847bebe3248f436d9c95e9acfd14871efc
child 39581 60088b59cb41c86398c3f61ef8fe37cf14e97ec6
push id12278
push userdholbert@mozilla.com
push dateThu, 18 Mar 2010 14:58:59 +0000
treeherdermozilla-central@60088b59cb41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs541588
milestone1.9.3a4pre
backs out59f507847bebe3248f436d9c95e9acfd14871efc
Backed out changeset 59f507847beb (bug 541588) to see if it was responsible for minor SVG perf regression.
content/base/public/nsIDocument.h
content/smil/nsSMILAnimationController.cpp
content/smil/nsSMILAnimationController.h
layout/base/nsPresShell.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1355,17 +1355,17 @@ protected:
   // The set of all object, embed, applet, video and audio elements for
   // which this is the owner document. (They might not be in the document.)
   // These are non-owning pointers, the elements are responsible for removing
   // themselves when they go away.
   nsAutoPtr<nsTHashtable<nsPtrHashKey<nsIContent> > > mFreezableElements;
 
 #ifdef MOZ_SMIL
   // SMIL Animation Controller, lazily-initialized in GetAnimationController
-  nsRefPtr<nsSMILAnimationController> mAnimationController;
+  nsAutoPtr<nsSMILAnimationController> mAnimationController;
 #endif // MOZ_SMIL
 
   // Table of element properties for this document.
   nsPropertyTable mPropertyTable;
 
   // Compatibility mode
   nsCompatibility mCompatMode;
 
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -46,44 +46,45 @@
 #include "nsIDocument.h"
 #include "nsISMILAnimationElement.h"
 #include "nsIDOMSVGAnimationElement.h"
 #include "nsSMILTimedElement.h"
 
 //----------------------------------------------------------------------
 // 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
 
 nsSMILAnimationController::nsSMILAnimationController()
   : mResampleNeeded(PR_FALSE),
     mDocument(nsnull)
 {
   mAnimationElementTable.Init();
   mChildContainerTable.Init();
 }
 
 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");
 }
 
 nsSMILAnimationController* NS_NewSMILAnimationController(nsIDocument* aDoc)
 {
   nsSMILAnimationController* animationController =
@@ -99,16 +100,19 @@ nsSMILAnimationController* NS_NewSMILAni
   return animationController;
 }
 
 nsresult
 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;
 
   Begin();
 
   return NS_OK;
 }
 
@@ -116,55 +120,40 @@ nsSMILAnimationController::Init(nsIDocum
 // nsSMILTimeContainer methods:
 
 void
 nsSMILAnimationController::Pause(PRUint32 aType)
 {
   nsSMILTimeContainer::Pause(aType);
 
   if (mPauseState) {
-    StopSampling(GetRefreshDriverForDoc(mDocument));
+    StopTimer();
   }
 }
 
 void
 nsSMILAnimationController::Resume(PRUint32 aType)
 {
   PRBool wasPaused = (mPauseState != 0);
 
   nsSMILTimeContainer::Resume(aType);
 
   if (wasPaused && !mPauseState && mChildContainerTable.Count()) {
-    Sample(); // Run the first sample manually
-    StartSampling(GetRefreshDriverForDoc(mDocument));
+    StartTimer();
   }
 }
 
 nsSMILTime
 nsSMILAnimationController::GetParentTime() const
 {
   // Our parent time is wallclock time
   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:
 
 void
 nsSMILAnimationController::RegisterAnimationElement(
                                   nsISMILAnimationElement* aAnimationElement)
 {
   mAnimationElementTable.PutEntry(aAnimationElement);
 }
@@ -220,39 +209,52 @@ void
 nsSMILAnimationController::Unlink()
 {
   mLastCompositorTable = nsnull;
 }
 
 //----------------------------------------------------------------------
 // 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();
 }
 
 //----------------------------------------------------------------------
 // Sample-related methods and callbacks
 
 PR_CALLBACK PLDHashOperator
 TransferCachedBaseValue(nsSMILCompositor* aCompositor,
                         void* aData)
@@ -661,24 +663,23 @@ nsSMILAnimationController::GetTargetIden
 
 nsresult
 nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
 {
   TimeContainerPtrKey* key = mChildContainerTable.PutEntry(&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;
 }
 
 void
 nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild)
 {
   mChildContainerTable.RemoveEntry(&aChild);
 
   if (!mPauseState && mChildContainerTable.Count() == 0) {
-    StopSampling(GetRefreshDriverForDoc(mDocument));
+    StopTimer();
   }
 }
--- a/content/smil/nsSMILAnimationController.h
+++ b/content/smil/nsSMILAnimationController.h
@@ -43,17 +43,16 @@
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsITimer.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsSMILTimeContainer.h"
 #include "nsSMILCompositorTable.h"
 #include "nsSMILMilestone.h"
-#include "nsRefreshDriver.h"
 
 struct nsSMILTargetIdentifier;
 class nsISMILAnimationElement;
 class nsIDocument;
 
 //----------------------------------------------------------------------
 // nsSMILAnimationController
 //
@@ -62,34 +61,27 @@ class nsIDocument;
 // at most one animation controller per nsDocument so that frame-rate tuning can
 // be performed at a document-level.
 //
 // The animation controller can contain many child time containers (timed
 // document root objects) which may correspond to SVG document fragments within
 // 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();
   ~nsSMILAnimationController();
 
   // nsSMILContainer
   virtual void Pause(PRUint32 aType);
   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);
 
   // Methods for resampling all animations
   // (A resample performs the same operations as a sample but doesn't advance
   // the current time and doesn't check if the container is paused)
   void Resample() { DoSample(PR_FALSE); }
@@ -105,21 +97,16 @@ public:
   // Methods for handling page transitions
   void OnPageShow();
   void OnPageHide();
 
   // Methods for supporting cycle-collection
   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<nsSMILTimeContainer> TimeContainerPtrKey;
   typedef nsTHashtable<TimeContainerPtrKey> TimeContainerHashtable;
   typedef nsPtrHashKey<nsISMILAnimationElement> AnimationElementPtrKey;
   typedef nsTHashtable<AnimationElementPtrKey> AnimationElementHashtable;
 
   struct SampleTimeContainerParams
@@ -146,16 +133,18 @@ protected:
   nsresult    Init(nsIDocument* aDoc);
 
   // Cycle-collection implementation helpers
   PR_STATIC_CALLBACK(PLDHashOperator) CompositorTableEntryTraverse(
       nsSMILCompositor* aCompositor, void* aArg);
 
   // Timer-related implementation helpers
   static void Notify(nsITimer* aTimer, void* aClosure);
+  nsresult    StartTimer();
+  nsresult    StopTimer();
 
   // Sample-related callbacks and implementation helpers
   virtual void DoSample();
   void DoSample(PRBool aSkipUnchangedContainers);
   void DoMilestoneSamples();
   PR_STATIC_CALLBACK(PLDHashOperator) GetNextMilestone(
       TimeContainerPtrKey* aKey, void* aData);
   PR_STATIC_CALLBACK(PLDHashOperator) GetMilestoneElements(
@@ -171,20 +160,18 @@ protected:
   static PRBool GetTargetIdentifierForAnimation(
       nsISMILAnimationElement* aAnimElem, nsSMILTargetIdentifier& aResult);
 
   // Methods for adding/removing time containers
   virtual nsresult AddChild(nsSMILTimeContainer& aChild);
   virtual void     RemoveChild(nsSMILTimeContainer& aChild);
 
   // Members
-  nsAutoRefCnt mRefCnt;
-  NS_DECL_OWNINGTHREAD
-
   static const PRUint32      kTimerInterval;
+  nsCOMPtr<nsITimer>         mTimer;
   AnimationElementHashtable  mAnimationElementTable;
   TimeContainerHashtable     mChildContainerTable;
   PRPackedBool               mResampleNeeded;
 
   // Store raw ptr to mDocument.  It owns the controller, so controller
   // shouldn't outlive it
   nsIDocument* mDocument;
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1703,25 +1703,16 @@ PresShell::Init(nsIDocument* aDocument,
         nsContentUtils::GetBoolPref("layout.reflow.dumpframebyframecounts");
 
       mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
       mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
       mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
     }
 #endif
 
-#ifdef MOZ_SMIL
-  if (mDocument->HasAnimationController()) {
-    nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
-    if (!animCtrl->IsPaused()) {
-      animCtrl->StartSampling(GetPresContext()->RefreshDriver());
-    }
-  }
-#endif // MOZ_SMIL
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PresShell::Destroy()
 {
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
     "destroy called on presshell while scripts not blocked");
@@ -1821,25 +1812,20 @@ PresShell::Destroy()
   // This shell must be removed from the document before the frame
   // hierarchy is torn down to avoid finding deleted frames through
   // this presshell while the frames are being torn down
   if (mDocument) {
     NS_ASSERTION(mDocument->GetPrimaryShell() == this, "Wrong shell?");
     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();
     mAsyncResizeTimerIsActive = PR_FALSE;
   }
 
   CancelAllPendingReflows();
   CancelPostedReflowCallbacks();