Bug 1301305 - Extend PendingAnimationTracker to mark play-pending animations if there are geometric animations starting at the same time; r?hiro draft
authorBrian Birtles <birtles@gmail.com>
Fri, 02 Dec 2016 10:10:44 +0900
changeset 447582 59d2fea0458f833a97a3b32413930f9970c7eddb
parent 447581 1304cdf678095f2eeaa32588b92c0531e8c64fcd
child 447583 64322a0166d97b85ba3e4d7f752c8501774b77ef
push id38084
push userbbirtles@mozilla.com
push dateTue, 06 Dec 2016 07:57:40 +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