Bug 1301305 - Extend PendingAnimationTracker to mark play-pending animations if there are geometric animations starting at the same time; r=hiro
authorBrian Birtles <birtles@gmail.com>
Fri, 02 Dec 2016 10:10:44 +0900
changeset 325334 8f7a10d823e2dcc2fd9fa0a976443f4068ce7c1f
parent 325333 dc0424e6e32de0cfca4cd5a218f8b11c129ae388
child 325335 aea2d20ddd3495a0519bf6f6891be66b2fc99e3b
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewershiro
bugs1301305
milestone53.0a1
Bug 1301305 - Extend PendingAnimationTracker to mark play-pending animations if there are geometric animations starting at the same time; r=hiro The approach here is to lazily check if we have such animations. This allows animations to be modified after being added to the pending animation tracker (but not after HasPlayPendingGeometricAnimations is called since we cache the result at that point) and avoids poor performance when calling RemovePlayPending. MozReview-Commit-ID: LRLpCRnzvw
dom/animation/PendingAnimationTracker.cpp
dom/animation/PendingAnimationTracker.h
--- a/dom/animation/PendingAnimationTracker.cpp
+++ b/dom/animation/PendingAnimationTracker.cpp
@@ -80,30 +80,85 @@ PendingAnimationTracker::TriggerPendingA
       animation->TriggerOnNextTick(readyTime);
 
       iter.Remove();
     }
   };
 
   triggerAnimationsAtReadyTime(mPlayPendingSet);
   triggerAnimationsAtReadyTime(mPausePendingSet);
+
+  mHasPlayPendingGeometricAnimations = mPlayPendingSet.Count()
+                                       ? CheckState::Indeterminate
+                                       : CheckState::Absent;
 }
 
 void
 PendingAnimationTracker::TriggerPendingAnimationsNow()
 {
   auto triggerAndClearAnimations = [](AnimationSet& aAnimationSet) {
     for (auto iter = aAnimationSet.Iter(); !iter.Done(); iter.Next()) {
       iter.Get()->GetKey()->TriggerNow();
     }
     aAnimationSet.Clear();
   };
 
   triggerAndClearAnimations(mPlayPendingSet);
   triggerAndClearAnimations(mPausePendingSet);
+
+  mHasPlayPendingGeometricAnimations = CheckState::Absent;
+}
+
+void
+PendingAnimationTracker::MarkAnimationsThatMightNeedSynchronization()
+{
+  // We only ever set mHasPlayPendingGeometricAnimations to 'present' in
+  // HasPlayPendingGeometricAnimations(). So, if it is 'present' already,
+  // (i.e. before calling HasPlayPendingGeometricAnimations()) we can assume
+  // that this method has already been called for the current set of
+  // play-pending animations and it is not necessary to run again.
+  //
+  // We can't make the same assumption about 'absent', but if this method
+  // was already called and the result was 'absent', then this method is
+  // a no-op anyway so it's ok to run again.
+  //
+  // Note that *without* this optimization, starting animations would become
+  // O(n^2) in that case where each animation is on a different element and
+  // contains a compositor-animatable property since we would end up iterating
+  // over all animations in the play-pending set for each target element.
+  if (mHasPlayPendingGeometricAnimations == CheckState::Present) {
+    return;
+  }
+
+  if (!HasPlayPendingGeometricAnimations()) {
+    return;
+  }
+
+  for (auto iter = mPlayPendingSet.Iter(); !iter.Done(); iter.Next()) {
+    iter.Get()->GetKey()->NotifyGeometricAnimationsStartingThisFrame();
+  }
+}
+
+bool
+PendingAnimationTracker::HasPlayPendingGeometricAnimations()
+{
+  if (mHasPlayPendingGeometricAnimations != CheckState::Indeterminate) {
+    return mHasPlayPendingGeometricAnimations == CheckState::Present;
+  }
+
+  mHasPlayPendingGeometricAnimations = CheckState::Absent;
+  for (auto iter = mPlayPendingSet.ConstIter(); !iter.Done(); iter.Next()) {
+    auto animation = iter.Get()->GetKey();
+    if (animation->GetEffect() && animation->GetEffect()->AffectsGeometry()) {
+      mHasPlayPendingGeometricAnimations = CheckState::Present;
+      break;
+    }
+  }
+
+  return mHasPlayPendingGeometricAnimations == CheckState::Present;
 }
 
 void
 PendingAnimationTracker::EnsurePaintIsScheduled()
 {
   if (!mDocument) {
     return;
   }
--- a/dom/animation/PendingAnimationTracker.h
+++ b/dom/animation/PendingAnimationTracker.h
@@ -26,20 +26,22 @@ public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingAnimationTracker)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingAnimationTracker)
 
   void AddPlayPending(dom::Animation& aAnimation)
   {
     MOZ_ASSERT(!IsWaitingToPause(aAnimation),
                "Animation is already waiting to pause");
     AddPending(aAnimation, mPlayPendingSet);
+    mHasPlayPendingGeometricAnimations = CheckState::Indeterminate;
   }
   void RemovePlayPending(dom::Animation& aAnimation)
   {
     RemovePending(aAnimation, mPlayPendingSet);
+    mHasPlayPendingGeometricAnimations = CheckState::Indeterminate;
   }
   bool IsWaitingToPlay(const dom::Animation& aAnimation) const
   {
     return IsWaiting(aAnimation, mPlayPendingSet);
   }
 
   void AddPausePending(dom::Animation& aAnimation)
   {
@@ -57,28 +59,43 @@ public:
   }
 
   void TriggerPendingAnimationsOnNextTick(const TimeStamp& aReadyTime);
   void TriggerPendingAnimationsNow();
   bool HasPendingAnimations() const {
     return mPlayPendingSet.Count() > 0 || mPausePendingSet.Count() > 0;
   }
 
+  /**
+   * Looks amongst the set of play-pending animations, and, if there are
+   * animations that affect geometric properties, notifies all play-pending
+   * animations so that they can be synchronized, if needed.
+   */
+  void MarkAnimationsThatMightNeedSynchronization();
+
 private:
   ~PendingAnimationTracker() { }
 
+  bool HasPlayPendingGeometricAnimations();
   void EnsurePaintIsScheduled();
 
   typedef nsTHashtable<nsRefPtrHashKey<dom::Animation>> AnimationSet;
 
   void AddPending(dom::Animation& aAnimation, AnimationSet& aSet);
   void RemovePending(dom::Animation& aAnimation, AnimationSet& aSet);
   bool IsWaiting(const dom::Animation& aAnimation,
                  const AnimationSet& aSet) const;
 
   AnimationSet mPlayPendingSet;
   AnimationSet mPausePendingSet;
   nsCOMPtr<nsIDocument> mDocument;
+
+  enum class CheckState {
+    Indeterminate,
+    Absent,
+    Present
+  };
+  CheckState mHasPlayPendingGeometricAnimations = CheckState::Indeterminate;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_dom_PendingAnimationTracker_h