Bug 1301305 - Add a member to track if an animation needs to be synchronized with geometric animations or not; r?hiro draft
authorBrian Birtles <birtles@gmail.com>
Fri, 02 Dec 2016 10:19:10 +0900
changeset 447581 1304cdf678095f2eeaa32588b92c0531e8c64fcd
parent 447580 60a706d51897a0522794cd140734ad7158f4ccd6
child 447582 59d2fea0458f833a97a3b32413930f9970c7eddb
push id38084
push userbbirtles@mozilla.com
push dateTue, 06 Dec 2016 07:57:40 +0000
reviewershiro
bugs1301305
milestone53.0a1
Bug 1301305 - Add a member to track if an animation needs to be synchronized with geometric animations or not; r?hiro Note that in this patch, the mSyncWithGeometricAnimations member is never set to true since no one calls NotifyGeometricAnimationsStartingThisFrame yet. MozReview-Commit-ID: GSTQmfkSdoy
dom/animation/Animation.cpp
dom/animation/Animation.h
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -791,25 +791,34 @@ Animation::ShouldBeSynchronizedWithMainT
     return false;
   }
 
   // Currently only transform animations need to be synchronized
   if (aProperty != eCSSProperty_transform) {
     return false;
   }
 
-  // Is there anything about our effect itself that means it should be
-  // main-thread bound?
   KeyframeEffectReadOnly* keyframeEffect = mEffect
                                            ? mEffect->AsKeyframeEffect()
                                            : nullptr;
   if (!keyframeEffect) {
     return false;
   }
 
+  // Are we starting at the same time as other geometric animations?
+  // We check this before calling ShouldBlockAsyncTransformAnimations, partly
+  // because it's cheaper, but also because it's often the most useful thing
+  // to know when you're debugging performance.
+  if (mSyncWithGeometricAnimations &&
+      keyframeEffect->HasAnimationOfProperty(eCSSProperty_transform)) {
+    aPerformanceWarning = AnimationPerformanceWarning::Type::
+                          TransformWithSyncGeometricAnimations;
+    return true;
+  }
+
   return keyframeEffect->
            ShouldBlockAsyncTransformAnimations(aFrame, aPerformanceWarning);
 }
 
 void
 Animation::UpdateRelevance()
 {
   bool wasRelevant = mIsRelevant;
@@ -962,16 +971,26 @@ Animation::NotifyEffectTimingUpdated()
 {
   MOZ_ASSERT(mEffect,
              "We should only update timing effect when we have a target "
              "effect");
   UpdateTiming(Animation::SeekFlag::NoSeek,
                Animation::SyncNotifyFlag::Async);
 }
 
+void
+Animation::NotifyGeometricAnimationsStartingThisFrame()
+{
+  if (!IsNewlyStarted() || !mEffect) {
+    return;
+  }
+
+  mSyncWithGeometricAnimations = true;
+}
+
 // https://w3c.github.io/web-animations/#play-an-animation
 void
 Animation::PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior)
 {
   AutoMutationBatchForAnimation mb(*this);
 
   bool abortedPause = mPendingState == PendingState::PausePending;
 
@@ -1027,16 +1046,21 @@ Animation::PlayNoUpdate(ErrorResult& aRv
 
   if (!reuseReadyPromise) {
     // Clear ready promise. We'll create a new one lazily.
     mReady = nullptr;
   }
 
   mPendingState = PendingState::PlayPending;
 
+  // Clear flag that causes us to sync transform animations with the main
+  // thread for now. We'll set this when we go to set up compositor
+  // animations if it applies.
+  mSyncWithGeometricAnimations = false;
+
   nsIDocument* doc = GetRenderedDocument();
   if (doc) {
     PendingAnimationTracker* tracker =
       doc->GetOrCreatePendingAnimationTracker();
     tracker->AddPlayPending(*this);
   } else {
     TriggerOnNextTick(Nullable<TimeDuration>());
   }
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -58,16 +58,17 @@ public:
   explicit Animation(nsIGlobalObject* aGlobal)
     : DOMEventTargetHelper(aGlobal)
     , mPlaybackRate(1.0)
     , mPendingState(PendingState::NotPending)
     , mAnimationIndex(sNextAnimationIndex++)
     , mFinishedAtLastComposeStyle(false)
     , mIsRelevant(false)
     , mFinishedIsResolved(false)
+    , mSyncWithGeometricAnimations(false)
   {
   }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Animation,
                                            DOMEventTargetHelper)
 
   nsIGlobalObject* GetParentObject() const { return GetOwnerGlobal(); }
@@ -315,16 +316,17 @@ public:
    * if any.
    * Any properties contained in |aPropertiesToSkip| will not be added or
    * updated in |aStyleRule|.
    */
   void ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
                     const nsCSSPropertyIDSet& aPropertiesToSkip);
 
   void NotifyEffectTimingUpdated();
+  void NotifyGeometricAnimationsStartingThisFrame();
 
 protected:
   void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
   void SilentlySetPlaybackRate(double aPlaybackRate);
   void CancelNoUpdate();
   void PlayNoUpdate(ErrorResult& aRv, LimitBehavior aLimitBehavior);
   void PauseNoUpdate(ErrorResult& aRv);
   void ResumeAt(const TimeDuration& aReadyTime);
@@ -375,16 +377,28 @@ protected:
   void CancelPendingTasks();
 
   /**
    * Performs the same steps as CancelPendingTasks and also rejects and
    * recreates the ready promise if the animation was pending.
    */
   void ResetPendingTasks();
 
+  /**
+   * Returns true if this animation is not only play-pending, but has
+   * yet to be given a pending ready time. This roughly corresponds to
+   * animations that are waiting to be painted (since we set the pending
+   * ready time at the end of painting). Identifying such animations is
+   * useful because in some cases animations that are painted together
+   * may need to be synchronized.
+   */
+  bool IsNewlyStarted() const {
+    return mPendingState == PendingState::PlayPending &&
+           mPendingReadyTime.IsNull();
+  }
   bool IsPossiblyOrphanedPendingAnimation() const;
   StickyTimeDuration EffectEnd() const;
 
   nsIDocument* GetRenderedDocument() const;
 
   RefPtr<AnimationTimeline> mTimeline;
   RefPtr<AnimationEffectReadOnly> mEffect;
   // The beginning of the delay period.
@@ -434,15 +448,20 @@ protected:
 
   nsRevocableEventPtr<nsRunnableMethod<Animation>> mFinishNotificationTask;
   // True if mFinished is resolved or would be resolved if mFinished has
   // yet to be created. This is not set when mFinished is rejected since
   // in that case mFinished is immediately reset to represent a new current
   // finished promise.
   bool mFinishedIsResolved;
 
+  // True if this animation was triggered at the same time as one or more
+  // geometric animations and hence we should run any transform animations on
+  // the main thread.
+  bool mSyncWithGeometricAnimations;
+
   nsString mId;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Animation_h