Bug 1305325 - Part 11: Cache non-animated base values. r=birtles
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Sun, 04 Dec 2016 08:07:40 +0900
changeset 325233 b02732a3873018fd8fe09efd6bb4ffb8bf0ab5da
parent 325232 6cf6c430f5a02058ec4acb28e690129306aab6be
child 325234 a037e71a1150b10d2f7dae927b57ccf2929c6443
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersbirtles
bugs1305325
milestone53.0a1
Bug 1305325 - Part 11: Cache non-animated base values. r=birtles This patch adds a hashtable to store the non-animated base style of each property in EffectSet class if the following conditions are met. 1) the effect is the lowest priority of the effect 2) the effect is additive or accumulative The styles are stored as StyleAnimationValue objects since it's handy class to store a CSS value for different properties. MozReview-Commit-ID: 1MZV7MnqzfI
dom/animation/EffectCompositor.cpp
dom/animation/EffectCompositor.h
dom/animation/EffectSet.h
dom/animation/KeyframeEffectReadOnly.cpp
dom/animation/KeyframeEffectReadOnly.h
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -325,16 +325,18 @@ EffectCompositor::UpdateEffectProperties
   if (!effectSet) {
     return;
   }
 
   // Style context change might cause CSS cascade level,
   // e.g removing !important, so we should update the cascading result.
   effectSet->MarkCascadeNeedsUpdate();
 
+  ClearBaseStyles(*aElement, aPseudoType);
+
   for (KeyframeEffectReadOnly* effect : *effectSet) {
     effect->UpdateProperties(aStyleContext);
   }
 }
 
 void
 EffectCompositor::MaybeUpdateAnimationRule(dom::Element* aElement,
                                            CSSPseudoElementType aPseudoType,
@@ -867,35 +869,65 @@ EffectCompositor::SetPerformanceWarning(
   for (KeyframeEffectReadOnly* effect : *effects) {
     effect->SetPerformanceWarning(aProperty, aWarning);
   }
 }
 
 /* static */ StyleAnimationValue
 EffectCompositor::GetBaseStyle(nsCSSPropertyID aProperty,
                                nsStyleContext* aStyleContext,
-                               dom::Element& aElement)
+                               dom::Element& aElement,
+                               CSSPseudoElementType aPseudoType)
 {
   MOZ_ASSERT(aStyleContext, "Need style context to resolve the base value");
   MOZ_ASSERT(!aStyleContext->StyleSource().IsServoComputedValues(),
              "Bug 1311257: Servo backend does not support the base value yet");
 
+  StyleAnimationValue result;
+
+  EffectSet* effectSet =
+    EffectSet::GetEffectSet(&aElement, aPseudoType);
+  if (!effectSet) {
+    return result;
+  }
+
+  // Check whether there is a cached style.
+  result = effectSet->GetBaseStyle(aProperty);
+  if (!result.IsNull()) {
+    return result;
+  }
+
   RefPtr<nsStyleContext> styleContextWithoutAnimation =
     aStyleContext->PresContext()->StyleSet()->AsGecko()->
       ResolveStyleWithoutAnimation(&aElement, aStyleContext,
                                    eRestyle_AllHintsWithAnimations);
 
-  StyleAnimationValue baseStyle;
-  DebugOnly<bool> result =
+  DebugOnly<bool> success =
     StyleAnimationValue::ExtractComputedValue(aProperty,
                                               styleContextWithoutAnimation,
-                                              baseStyle);
-  MOZ_ASSERT(result, "could not extract computed value");
+                                              result);
+  MOZ_ASSERT(success, "Should be able to extract computed animation value");
+  MOZ_ASSERT(!result.IsNull(), "Should have a valid StyleAnimationValue");
+
+  effectSet->PutBaseStyle(aProperty, result);
+
+  return result;
+}
 
-  return baseStyle;
+/* static */ void
+EffectCompositor::ClearBaseStyles(dom::Element& aElement,
+                                  CSSPseudoElementType aPseudoType)
+{
+  EffectSet* effectSet =
+    EffectSet::GetEffectSet(&aElement, aPseudoType);
+  if (!effectSet) {
+    return;
+  }
+
+  effectSet->ClearBaseStyles();
 }
 
 // ---------------------------------------------------------
 //
 // Nested class: AnimationStyleRuleProcessor
 //
 // ---------------------------------------------------------
 
--- a/dom/animation/EffectCompositor.h
+++ b/dom/animation/EffectCompositor.h
@@ -25,16 +25,17 @@ class nsPresContext;
 class nsStyleContext;
 
 namespace mozilla {
 
 class EffectSet;
 class RestyleTracker;
 class StyleAnimationValue;
 struct AnimationPerformanceWarning;
+struct AnimationProperty;
 struct NonOwningAnimationTarget;
 
 namespace dom {
 class Animation;
 class Element;
 }
 
 class EffectCompositor
@@ -211,20 +212,29 @@ public:
 
   // Associates a performance warning with effects on |aFrame| that animates
   // |aProperty|.
   static void SetPerformanceWarning(
     const nsIFrame* aFrame,
     nsCSSPropertyID aProperty,
     const AnimationPerformanceWarning& aWarning);
 
-  // Get resolved base style on |aElement| for |aProperty| with |aStyleContext|.
+  // Returns the base style of (pseudo-)element for |aProperty|.
+  // If there is no cached base style for the property, a new base style value
+  // is resolved with |aStyleContext|. The new resolved base style is cached
+  // until ClearBaseStyles is called.
   static StyleAnimationValue GetBaseStyle(nsCSSPropertyID aProperty,
                                           nsStyleContext* aStyleContext,
-                                          dom::Element& aElement);
+                                          dom::Element& aElement,
+                                          CSSPseudoElementType aPseudoType);
+
+  // Clear cached base styles of (pseudo-)element.
+  static void ClearBaseStyles(dom::Element& aElement,
+                              CSSPseudoElementType aPseudoType);
+
 private:
   ~EffectCompositor() = default;
 
   // Rebuilds the animation rule corresponding to |aCascadeLevel| on the
   // EffectSet associated with the specified (pseudo-)element.
   static void ComposeAnimationRule(dom::Element* aElement,
                                    CSSPseudoElementType aPseudoType,
                                    CascadeLevel aCascadeLevel,
--- a/dom/animation/EffectSet.h
+++ b/dom/animation/EffectSet.h
@@ -194,16 +194,34 @@ public:
   {
     return mPropertiesWithImportantRules;
   }
   nsCSSPropertyIDSet& PropertiesForAnimationsLevel()
   {
     return mPropertiesForAnimationsLevel;
   }
 
+  StyleAnimationValue GetBaseStyle(nsCSSPropertyID aProperty) const
+  {
+    StyleAnimationValue result;
+    DebugOnly<bool> hasProperty = mBaseStyleValues.Get(aProperty, &result);
+    MOZ_ASSERT(hasProperty || result.IsNull());
+    return result;
+  }
+
+  void PutBaseStyle(nsCSSPropertyID aProperty,
+                    const StyleAnimationValue& aValue)
+  {
+    return mBaseStyleValues.Put(aProperty, aValue);
+  }
+  void ClearBaseStyles()
+  {
+    return mBaseStyleValues.Clear();
+  }
+
 private:
   static nsIAtom* GetEffectSetPropertyAtom(CSSPseudoElementType aPseudoType);
 
   OwningEffectSet mEffects;
 
   // These style rules contain the style data for currently animating
   // values.  They only match when styling with animation.  When we
   // style without animation, we need to not use them so that we can
@@ -242,16 +260,21 @@ private:
   // Specifies the compositor-animatable properties that are overridden by
   // !important rules.
   nsCSSPropertyIDSet mPropertiesWithImportantRules;
   // Specifies the properties for which the result will be added to the
   // animations level of the cascade and hence should be skipped when we are
   // composing the animation style for the transitions level of the cascede.
   nsCSSPropertyIDSet mPropertiesForAnimationsLevel;
 
+  // The non-animated values for properties animated by effects in this set that
+  // contain at least one animation value that is composited with the underlying
+  // value (i.e. it uses the additive or accumulate composite mode).
+  nsDataHashtable<nsUint32HashKey, StyleAnimationValue> mBaseStyleValues;
+
 #ifdef DEBUG
   // Track how many iterators are referencing this effect set when we are
   // destroyed, we can assert that nothing is still pointing to us.
   uint64_t mActiveIterators;
 
   bool mCalledPropertyDtor;
 #endif
 };
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -339,17 +339,20 @@ KeyframeEffectReadOnly::CompositeValue(
     MOZ_ASSERT(success, "AnimValuesStyleRule::GetValue should not fail");
   } else {
     // If we are composing with composite operation that is not 'replace'
     // and we have not composed style for the property yet, we have to get
     // the base style for the property.
     RefPtr<nsStyleContext> styleContext = GetTargetStyleContext();
     result = EffectCompositor::GetBaseStyle(aProperty,
                                             styleContext,
-                                            *mTarget->mElement);
+                                            *mTarget->mElement,
+                                            mTarget->mPseudoType);
+    MOZ_ASSERT(!result.IsNull(), "The base style should be set");
+    SetNeedsBaseStyle(aProperty);
   }
 
   switch (aCompositeOperation) {
     case dom::CompositeOperation::Add:
       // So far nothing to do since we come to here only in case of missing
       // keyframe, that means we just use the base value as the composited
       // value.
       // FIXME: Bug 1311620: Once we implement additive composition, we need to
@@ -379,16 +382,18 @@ KeyframeEffectReadOnly::ComposeStyle(
   mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
 
   // If the progress is null, we don't have fill data for the current
   // time so we shouldn't animate.
   if (computedTiming.mProgress.IsNull()) {
     return;
   }
 
+  mNeedsBaseStyleSet.Empty();
+
   for (size_t propIdx = 0, propEnd = mProperties.Length();
        propIdx != propEnd; ++propIdx)
   {
     const AnimationProperty& prop = mProperties[propIdx];
 
     MOZ_ASSERT(prop.mSegments[0].mFromKey == 0.0, "incorrect first from key");
     MOZ_ASSERT(prop.mSegments[prop.mSegments.Length() - 1].mToKey == 1.0,
                "incorrect last to key");
@@ -1500,10 +1505,35 @@ KeyframeEffectReadOnly::HasComputedTimin
   ComputedTiming computedTiming = GetComputedTiming();
   return computedTiming.mProgress != mProgressOnLastCompose ||
          (mEffectOptions.mIterationComposite ==
             IterationCompositeOperation::Accumulate &&
          computedTiming.mCurrentIteration !=
           mCurrentIterationOnLastCompose);
 }
 
+void
+KeyframeEffectReadOnly::SetNeedsBaseStyle(nsCSSPropertyID aProperty)
+{
+  for (size_t i = 0; i < LayerAnimationInfo::kRecords; i++) {
+    if (LayerAnimationInfo::sRecords[i].mProperty == aProperty) {
+      mNeedsBaseStyleSet.AddProperty(aProperty);
+      break;
+    }
+  }
+}
+
+bool
+KeyframeEffectReadOnly::NeedsBaseStyle(nsCSSPropertyID aProperty) const
+{
+  for (size_t i = 0; i < LayerAnimationInfo::kRecords; i++) {
+    if (LayerAnimationInfo::sRecords[i].mProperty == aProperty) {
+      return mNeedsBaseStyleSet.HasProperty(aProperty);
+    }
+  }
+  MOZ_ASSERT_UNREACHABLE(
+    "Expected a property that can be run on the compositor");
+
+  return false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -4,36 +4,35 @@
  * 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/. */
 
 #ifndef mozilla_dom_KeyframeEffectReadOnly_h
 #define mozilla_dom_KeyframeEffectReadOnly_h
 
 #include "nsChangeHint.h"
 #include "nsCSSPropertyID.h"
+#include "nsCSSPropertyIDSet.h"
 #include "nsCSSValue.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/AnimationTarget.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedTimingFunction.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/KeyframeEffectParams.h"
-#include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
 #include "mozilla/ServoBindingTypes.h" // RawServoDeclarationBlock and
                                        // associated RefPtrTraits
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/AnimationEffectReadOnly.h"
 #include "mozilla/dom/Element.h"
 
 struct JSContext;
 class JSObject;
-class nsCSSPropertyIDSet;
 class nsIContent;
 class nsIDocument;
 class nsIFrame;
 class nsIPresShell;
 class nsPresContext;
 
 namespace mozilla {
 
@@ -325,16 +324,20 @@ public:
   void CalculateCumulativeChangeHint(nsStyleContext* aStyleContext);
 
   // Returns true if all of animation properties' change hints
   // can ignore painting if the animation is not visible.
   // See nsChangeHint_Hints_CanIgnoreIfNotVisible in nsChangeHint.h
   // in detail which change hint can be ignored.
   bool CanIgnoreIfNotVisible() const;
 
+  // Returns true if the effect is run on the compositor for |aProperty| and
+  // needs a base style to composite with.
+  bool NeedsBaseStyle(nsCSSPropertyID aProperty) const;
+
 protected:
   KeyframeEffectReadOnly(nsIDocument* aDocument,
                          const Maybe<OwningAnimationTarget>& aTarget,
                          AnimationEffectTimingReadOnly* aTiming,
                          const KeyframeEffectParams& aOptions);
 
   ~KeyframeEffectReadOnly() override = default;
 
@@ -399,16 +402,20 @@ protected:
   // |aAnimationRule|, uses the base value for the property recorded on the
   // target element's EffectSet.
   StyleAnimationValue CompositeValue(
     nsCSSPropertyID aProperty,
     const RefPtr<AnimValuesStyleRule>& aAnimationRule,
     const StyleAnimationValue& aValueToComposite,
     CompositeOperation aCompositeOperation);
 
+  // Set a bit in mNeedsBaseStyleSet if |aProperty| can be run on the
+  // compositor.
+  void SetNeedsBaseStyle(nsCSSPropertyID aProperty);
+
   Maybe<OwningAnimationTarget> mTarget;
 
   KeyframeEffectParams mEffectOptions;
 
   // The specified keyframes.
   nsTArray<Keyframe>          mKeyframes;
 
   // A set of per-property value arrays, derived from |mKeyframes|.
@@ -423,16 +430,21 @@ protected:
   // this is used to detect when the current iteration is not changing
   // in the case when iterationComposite is accumulate.
   uint64_t mCurrentIterationOnLastCompose = 0;
 
   // We need to track when we go to or from being "in effect" since
   // we need to re-evaluate the cascade of animations when that changes.
   bool mInEffectOnLastAnimationTimingUpdate;
 
+  // Represents whether or not the corresponding property requires a base style
+  // to composite with. This is only set when the property is run on the
+  // compositor.
+  nsCSSPropertyIDSet mNeedsBaseStyleSet;
+
 private:
   nsChangeHint mCumulativeChangeHint;
 
   nsIFrame* GetAnimationFrame() const;
 
   bool CanThrottle() const;
   bool CanThrottleTransformChanges(nsIFrame& aFrame) const;