Bug 1151694 - Part 3: Manage mIsRunningOnCompositor flags for each properties respectively. r=bbirtles
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Wed, 16 Sep 2015 16:05:00 +0200
changeset 297322 81a35d84fb974c3932071b28bbe9c1c738a1a9f0
parent 297321 372b1c30dc09b14aeb78d1dd61fbcdb48811c767
child 297323 80b4c268049fc680ad6934262155758175941039
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbirtles
bugs1151694
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1151694 - Part 3: Manage mIsRunningOnCompositor flags for each properties respectively. r=bbirtles
dom/animation/Animation.cpp
dom/animation/Animation.h
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/nsDisplayList.cpp
layout/style/AnimationCommon.cpp
layout/style/AnimationCommon.h
layout/style/nsAnimationManager.cpp
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -555,17 +555,17 @@ Animation::CanThrottle() const
   // special handling for newly-idle animations or animations that are newly
   // yet-to-start since any operation that would cause that change (e.g. a call
   // to cancel() on the animation, or seeking its current time) will trigger an
   // unthrottled sample.
   if (!IsInEffect()) {
     return true;
   }
 
-  return mIsRunningOnCompositor;
+  return IsRunningOnCompositor();
 }
 
 void
 Animation::ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
                         nsCSSPropertySet& aSetProperties,
                         bool& aNeedsRefreshes)
 {
   if (!mEffect) {
@@ -758,21 +758,16 @@ Animation::DoPause(ErrorResult& aRv)
   }
 
   bool reuseReadyPromise = false;
   if (mPendingState == PendingState::PlayPending) {
     CancelPendingTasks();
     reuseReadyPromise = true;
   }
 
-  // Mark this as no longer running on the compositor so that next time
-  // we update animations we won't throttle them and will have a chance
-  // to remove the animation from any layer it might be on.
-  mIsRunningOnCompositor = false;
-
   if (!reuseReadyPromise) {
     // Clear ready promise. We'll create a new one lazily.
     mReady = nullptr;
   }
 
   mPendingState = PendingState::PausePending;
 
   nsIDocument* doc = GetRenderedDocument();
@@ -1197,10 +1192,16 @@ Animation::DispatchPlaybackEvent(const n
     AnimationPlaybackEvent::Constructor(this, aName, init);
   event->SetTrusted(true);
 
   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this, event);
   asyncDispatcher->PostDOMEvent();
 }
 
+bool
+Animation::IsRunningOnCompositor() const
+{
+  return mEffect && mEffect->IsRunningOnCompositor();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -53,17 +53,16 @@ protected:
   virtual ~Animation() {}
 
 public:
   explicit Animation(nsIGlobalObject* aGlobal)
     : DOMEventTargetHelper(aGlobal)
     , mPlaybackRate(1.0)
     , mPendingState(PendingState::NotPending)
     , mAnimationIndex(sNextAnimationIndex++)
-    , mIsRunningOnCompositor(false)
     , mFinishedAtLastComposeStyle(false)
     , mIsRelevant(false)
     , mFinishedIsResolved(false)
   {
   }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Animation,
@@ -104,17 +103,17 @@ public:
   AnimationPlayState PlayState() const;
   virtual Promise* GetReady(ErrorResult& aRv);
   virtual Promise* GetFinished(ErrorResult& aRv);
   void Cancel();
   virtual void Finish(ErrorResult& aRv);
   virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior);
   virtual void Pause(ErrorResult& aRv);
   virtual void Reverse(ErrorResult& aRv);
-  bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
+  bool IsRunningOnCompositor() const;
   IMPL_EVENT_HANDLER(finish);
   IMPL_EVENT_HANDLER(cancel);
 
   // Wrapper functions for Animation DOM methods when called
   // from script.
   //
   // We often use the same methods internally and from script but when called
   // from script we (or one of our subclasses) perform extra steps such as
@@ -264,18 +263,16 @@ public:
   bool IsRelevant() const { return mIsRelevant; }
   void UpdateRelevance();
 
   /**
    * Returns true if this Animation has a lower composite order than aOther.
    */
   virtual bool HasLowerCompositeOrderThan(const Animation& aOther) const;
 
-  void SetIsRunningOnCompositor() { mIsRunningOnCompositor = true; }
-  void ClearIsRunningOnCompositor() { mIsRunningOnCompositor = false; }
   /**
    * Returns true if this animation does not currently need to update
    * style on the main thread (e.g. because it is empty, or is
    * running on the compositor).
    */
   bool CanThrottle() const;
   /**
    * Updates |aStyleRule| with the animation values of this animation's effect,
@@ -404,17 +401,16 @@ protected:
   // This is kNoIndex while the animation is in the idle state and is updated
   // each time the animation transitions out of the idle state.
   //
   // Note that subclasses such as CSSTransition and CSSAnimation may repurpose
   // this member to implement their own brand of sorting. As a result, it is
   // possible for two different objects to have the same index.
   uint64_t mAnimationIndex;
 
-  bool mIsRunningOnCompositor;
   bool mFinishedAtLastComposeStyle;
   // Indicates that the animation should be exposed in an element's
   // getAnimations() list.
   bool mIsRelevant;
 
   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
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/KeyframeEffect.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 #include "mozilla/FloatingPoint.h"
 #include "AnimationCommon.h"
 #include "nsCSSPropertySet.h"
+#include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
 
 namespace mozilla {
 
 void
 ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
 {
   mType = aFunction.mType;
   if (mType == nsTimingFunction::Function) {
@@ -69,16 +70,30 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INH
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly)
 NS_INTERFACE_MAP_END_INHERITING(AnimationEffectReadOnly)
 
 NS_IMPL_ADDREF_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly)
 NS_IMPL_RELEASE_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly)
 
+KeyframeEffectReadOnly::KeyframeEffectReadOnly(
+  nsIDocument* aDocument,
+  Element* aTarget,
+  nsCSSPseudoElements::Type aPseudoType,
+  const AnimationTiming& aTiming)
+  : AnimationEffectReadOnly(aDocument)
+  , mTarget(aTarget)
+  , mTiming(aTiming)
+  , mPseudoType(aPseudoType)
+{
+  MOZ_ASSERT(aTarget, "null animation target is not yet supported");
+  ResetIsRunningOnCompositor();
+}
+
 JSObject*
 KeyframeEffectReadOnly::WrapObject(JSContext* aCx,
                                    JS::Handle<JSObject*> aGivenProto)
 {
   return KeyframeEffectReadOnlyBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
@@ -391,10 +406,55 @@ KeyframeEffectReadOnly::ComposeStyle(nsR
       StyleAnimationValue::Interpolate(prop.mProperty,
                                        segment->mFromValue,
                                        segment->mToValue,
                                        valuePosition, *val);
     MOZ_ASSERT(result, "interpolate must succeed now");
   }
 }
 
+bool
+KeyframeEffectReadOnly::IsRunningOnCompositor() const
+{
+  // We consider animation is running on compositor if there is at least
+  // one property running on compositor.
+  // Animation.IsRunningOnCompotitor will return more fine grained
+  // information in bug 1196114.
+  for (bool isPropertyRunningOnCompositor : mIsPropertyRunningOnCompositor) {
+    if (isPropertyRunningOnCompositor) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void
+KeyframeEffectReadOnly::SetIsRunningOnCompositor(nsCSSProperty aProperty,
+                                                 bool aIsRunning)
+{
+  static_assert(
+    MOZ_ARRAY_LENGTH(LayerAnimationInfo::sRecords) ==
+      MOZ_ARRAY_LENGTH(mIsPropertyRunningOnCompositor),
+    "The length of mIsPropertyRunningOnCompositor should equal to"
+    "the length of LayserAnimationInfo::sRecords");
+  MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
+                                      CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
+             "Property being animated on compositor is a recognized "
+             "compositor-animatable property");
+  const auto& info = LayerAnimationInfo::sRecords;
+  for (size_t i = 0; i < ArrayLength(mIsPropertyRunningOnCompositor); i++) {
+    if (info[i].mProperty == aProperty) {
+      mIsPropertyRunningOnCompositor[i] = aIsRunning;
+      return;
+    }
+  }
+}
+
+void
+KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
+{
+  for (bool& isPropertyRunningOnCompositor : mIsPropertyRunningOnCompositor) {
+    isPropertyRunningOnCompositor = false;
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_KeyframeEffect_h
 
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCSSPseudoElements.h"
 #include "nsIDocument.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
 #include "mozilla/StickyTimeDuration.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/AnimationEffectReadOnly.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsSMILKeySpline.h"
 #include "nsStyleStruct.h" // for nsTimingFunction
@@ -194,24 +195,17 @@ struct ElementPropertyTransition;
 namespace dom {
 
 class KeyframeEffectReadOnly : public AnimationEffectReadOnly
 {
 public:
   KeyframeEffectReadOnly(nsIDocument* aDocument,
                          Element* aTarget,
                          nsCSSPseudoElements::Type aPseudoType,
-                         const AnimationTiming &aTiming)
-    : AnimationEffectReadOnly(aDocument)
-    , mTarget(aTarget)
-    , mTiming(aTiming)
-    , mPseudoType(aPseudoType)
-  {
-    MOZ_ASSERT(aTarget, "null animation target is not yet supported");
-  }
+                         const AnimationTiming& aTiming);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(KeyframeEffectReadOnly,
                                                         AnimationEffectReadOnly)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
@@ -303,25 +297,39 @@ public:
   }
 
   // Updates |aStyleRule| with the animation values produced by this
   // Animation for the current time except any properties already contained
   // in |aSetProperties|.
   // Any updated properties are added to |aSetProperties|.
   void ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
                     nsCSSPropertySet& aSetProperties);
+  bool IsRunningOnCompositor() const;
+  void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning);
 
 protected:
   virtual ~KeyframeEffectReadOnly() { }
+  void ResetIsRunningOnCompositor();
 
   nsCOMPtr<Element> mTarget;
   Nullable<TimeDuration> mParentTime;
 
   AnimationTiming mTiming;
   nsCSSPseudoElements::Type mPseudoType;
 
   InfallibleTArray<AnimationProperty> mProperties;
+
+  // Parallel array corresponding to CommonAnimationManager::sLayerAnimationInfo
+  // such that mIsPropertyRunningOnCompositor[x] is true only if this effect has
+  // an animation of CommonAnimationManager::sLayerAnimationInfo[x].mProperty
+  // that is currently running on the compositor.
+  //
+  // Note that when the owning Animation requests a non-throttled restyle, in
+  // between calling RequestRestyle on its AnimationCollection and when the
+  // restyle is performed, this member may temporarily become false even if
+  // the animation remains on the layer after the restyle.
+  bool mIsPropertyRunningOnCompositor[LayerAnimationInfo::kRecords];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyframeEffect_h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -226,16 +226,34 @@ FrameLayerBuilder::DisplayItemData::~Dis
   MOZ_RELEASE_ASSERT(sAliveDisplayItemDatas && sAliveDisplayItemDatas->Contains(this));
   sAliveDisplayItemDatas->RemoveEntry(this);
   if (sAliveDisplayItemDatas->Count() == 0) {
     delete sAliveDisplayItemDatas;
     sAliveDisplayItemDatas = nullptr;
   }
 }
 
+void
+FrameLayerBuilder::DisplayItemData::ClearAnimationCompositorState()
+{
+  if (mDisplayItemKey != nsDisplayItem::TYPE_TRANSFORM &&
+      mDisplayItemKey != nsDisplayItem::TYPE_OPACITY) {
+    return;
+  }
+
+  for (nsIFrame* frame : mFrameList) {
+    nsCSSProperty prop = mDisplayItemKey == nsDisplayItem::TYPE_TRANSFORM ?
+      eCSSProperty_transform : eCSSProperty_opacity;
+    frame->PresContext()->AnimationManager()->
+      ClearIsRunningOnCompositor(frame, prop);
+    frame->PresContext()->TransitionManager()->
+      ClearIsRunningOnCompositor(frame, prop);
+  }
+}
+
 const nsTArray<nsIFrame*>&
 FrameLayerBuilder::DisplayItemData::GetFrameListChanges()
 {
   return mFrameListChanges;
 }
 
 /**
  * This is the userdata we associate with a layer manager.
@@ -1777,16 +1795,17 @@ FrameLayerBuilder::WillEndTransaction()
         }
 #endif
         InvalidatePostTransformRegion(t,
                                       data->mGeometry->ComputeInvalidationRegion(),
                                       data->mClip,
                                       GetLastPaintOffset(t));
       }
 
+      data->ClearAnimationCompositorState();
       iter.Remove();
     } else {
       ComputeGeometryChangeForItem(data);
     }
   }
 
   data->mInvalidateAllLayers = false;
 }
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -438,16 +438,17 @@ public:
   class DisplayItemData final {
   public:
     friend class FrameLayerBuilder;
 
     uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
     Layer* GetLayer() { return mLayer; }
     nsDisplayItemGeometry* GetGeometry() const { return mGeometry.get(); }
     void Invalidate() { mIsInvalid = true; }
+    void ClearAnimationCompositorState();
 
   private:
     DisplayItemData(LayerManagerData* aParent,
                     uint32_t aKey,
                     Layer* aLayer,
                     nsIFrame* aFrame = nullptr);
 
     /**
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -468,17 +468,17 @@ AddAnimationsForProperty(nsIFrame* aFram
     // driver is advanced it will trigger any pending animations.
     if (anim->PlayState() == AnimationPlayState::Pending &&
         (!anim->GetTimeline() ||
          !anim->GetTimeline()->TracksWallclockTime())) {
       continue;
     }
 
     AddAnimationForProperty(aFrame, *property, anim, aLayer, aData, aPending);
-    anim->SetIsRunningOnCompositor();
+    effect->SetIsRunningOnCompositor(aProperty, true);
   }
 }
 
 /* static */ void
 nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,
                                                          nsDisplayListBuilder* aBuilder,
                                                          nsDisplayItem* aItem,
                                                          nsIFrame* aFrame,
@@ -517,21 +517,26 @@ nsDisplayListBuilder::AddAnimationsAndTr
   // layer, we still need to mark it as up-to-date with regards to animations.
   // Otherwise, in RestyleManager we'll notice the discrepancy between the
   // animation generation numbers and update the layer indefinitely.
   uint64_t animationGeneration =
     RestyleManager::GetMaxAnimationGenerationForFrame(aFrame);
   aLayer->SetAnimationGeneration(animationGeneration);
 
   nsPresContext* presContext = aFrame->PresContext();
+  presContext->TransitionManager()->ClearIsRunningOnCompositor(aFrame,
+                                                               aProperty);
+  presContext->AnimationManager()->ClearIsRunningOnCompositor(aFrame,
+                                                              aProperty);
   AnimationCollection* transitions =
-    presContext->TransitionManager()->GetAnimationsForCompositor(aFrame, aProperty);
+    presContext->TransitionManager()->GetAnimationsForCompositor(aFrame,
+                                                                 aProperty);
   AnimationCollection* animations =
-    presContext->AnimationManager()->GetAnimationsForCompositor(aFrame, aProperty);
-
+    presContext->AnimationManager()->GetAnimationsForCompositor(aFrame,
+                                                                aProperty);
   if (!animations && !transitions) {
     return;
   }
 
   // If the frame is not prerendered, bail out.
   // Do this check only during layer construction; during updating the
   // caller is required to check it appropriately.
   if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) {
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -432,16 +432,26 @@ CommonAnimationManager::WillRefresh(Time
   for (AnimationCollection* collection = mElementCollections.getFirst();
        collection; collection = collection->getNext()) {
     collection->Tick();
   }
 
   MaybeStartOrStopObservingRefreshDriver();
 }
 
+void
+CommonAnimationManager::ClearIsRunningOnCompositor(const nsIFrame* aFrame,
+                                                   nsCSSProperty aProperty)
+{
+  AnimationCollection* collection = GetAnimationCollection(aFrame);
+  if (collection) {
+    collection->ClearIsRunningOnCompositor(aProperty);
+  }
+}
+
 NS_IMPL_ISUPPORTS(AnimValuesStyleRule, nsIStyleRule)
 
 /* virtual */ void
 AnimValuesStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
 {
   nsStyleContext *contextParent = aRuleData->mStyleContext->GetParent();
   if (contextParent && contextParent->HasPseudoElementData()) {
     // Don't apply transitions or animations to things inside of
@@ -872,16 +882,27 @@ AnimationCollection::CanThrottleAnimatio
       return false;
     }
   }
 
   return true;
 }
 
 void
+AnimationCollection::ClearIsRunningOnCompositor(nsCSSProperty aProperty)
+{
+  for (Animation* anim : mAnimations) {
+    dom::KeyframeEffectReadOnly* effect = anim->GetEffect();
+    if (effect) {
+      effect->SetIsRunningOnCompositor(aProperty, false);
+    }
+  }
+}
+
+void
 AnimationCollection::RequestRestyle(RestyleType aRestyleType)
 {
   MOZ_ASSERT(IsForElement() || IsForBeforePseudo() || IsForAfterPseudo(),
              "Unexpected mElementProperty; might restyle too much");
 
   nsPresContext* presContext = mManager->PresContext();
   if (!presContext) {
     // Pres context will be null after the manager is disconnected.
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -157,16 +157,18 @@ public:
                              nsCSSProperty aProperty);
 
   // Given the frame aFrame with possibly animated content, finds its
   // associated collection of animations. If it is a generated content
   // frame, it may examine the parent frame to search for such animations.
   AnimationCollection*
   GetAnimationCollection(const nsIFrame* aFrame);
 
+  void ClearIsRunningOnCompositor(const nsIFrame *aFrame,
+                                  nsCSSProperty aProperty);
 protected:
   LinkedList<AnimationCollection> mElementCollections;
   nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
   bool mIsObservingRefreshDriver;
 };
 
 /**
  * A style rule that maps property-StyleAnimationValue pairs.
@@ -277,16 +279,17 @@ struct AnimationCollection : public Link
     // This is needed in cases such as when an animation becomes paused or has
     // its playback rate changed. In such a case, although the computed style
     // and refresh driver time might not change, we still need to ensure the
     // corresponding animations on layers are updated to reflect the new
     // configuration of the animation.
     Layer
   };
   void RequestRestyle(RestyleType aRestyleType);
+  void ClearIsRunningOnCompositor(nsCSSProperty aProperty);
 
 private:
   static bool
   CanAnimatePropertyOnCompositor(const dom::Element *aElement,
                                  nsCSSProperty aProperty,
                                  CanAnimateFlags aFlags);
 
   bool CanThrottleAnimation(TimeStamp aTime);
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -504,19 +504,16 @@ nsAnimationManager::CheckAnimationRule(n
           KeyframeEffectReadOnly* newEffect = newAnim->GetEffect();
           animationChanged =
             oldEffect->Timing() != newEffect->Timing() ||
             oldEffect->Properties() != newEffect->Properties();
           oldEffect->SetTiming(newEffect->Timing(), *oldAnim);
           oldEffect->Properties() = newEffect->Properties();
         }
 
-        // Reset compositor state so animation will be re-synchronized.
-        oldAnim->ClearIsRunningOnCompositor();
-
         // Handle changes in play state. If the animation is idle, however,
         // changes to animation-play-state should *not* restart it.
         if (oldAnim->PlayState() != AnimationPlayState::Idle) {
           // CSSAnimation takes care of override behavior so that,
           // for example, if the author has called pause(), that will
           // override the animation-play-state.
           // (We should check newAnim->IsStylePaused() but that requires
           //  downcasting to CSSAnimation and we happen to know that