Bug 1305325 - Part 8: Pass base value for opacity or transform to the compositor. r?birtles,mstange draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Wed, 16 Nov 2016 06:30:57 +0900
changeset 439349 ffc31fd11ea54de56a00ce2385a58c819769fe25
parent 439348 292a8eb611a81baafba5820de46ad46b2c8d4e09
child 439350 5099483fe909584853c05a66df21ecbd07cb24a6
push id35977
push userhiikezoe@mozilla-japan.org
push dateTue, 15 Nov 2016 21:34:46 +0000
reviewersbirtles, mstange
bugs1305325
milestone53.0a1
Bug 1305325 - Part 8: Pass base value for opacity or transform to the compositor. r?birtles,mstange MozReview-Commit-ID: 3mxatMvsNKk
dom/animation/KeyframeEffectReadOnly.cpp
dom/animation/KeyframeEffectReadOnly.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/ipc/LayersMessages.ipdlh
layout/base/nsDisplayList.cpp
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -1423,10 +1423,22 @@ KeyframeEffectReadOnly::HasComputedTimin
   ComputedTiming computedTiming = GetComputedTiming();
   return computedTiming.mProgress != mProgressOnLastCompose ||
          (mEffectOptions.mIterationComposite ==
             IterationCompositeOperation::Accumulate &&
          computedTiming.mCurrentIteration !=
           mCurrentIterationOnLastCompose);
 }
 
+bool
+KeyframeEffectReadOnly::NeedsBaseValue(nsCSSPropertyID aProperty) const
+{
+  for (const AnimationProperty& property : mProperties) {
+    if (property.mProperty == aProperty &&
+        property.HasNonReplaceComposite()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -332,16 +332,19 @@ 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 needs base values for |aProperty|.
+  bool NeedsBaseValue(nsCSSPropertyID aProperty) const;
+
 protected:
   KeyframeEffectReadOnly(nsIDocument* aDocument,
                          const Maybe<OwningAnimationTarget>& aTarget,
                          AnimationEffectTimingReadOnly* aTiming,
                          const KeyframeEffectParams& aOptions);
 
   ~KeyframeEffectReadOnly() override = default;
 
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -418,16 +418,35 @@ CreateCSSValueList(const InfallibleTArra
   }
   if (aFunctions.Length() == 0) {
     result = new nsCSSValueList();
     result->mValue.SetNoneValue();
   }
   return new nsCSSValueSharedList(result.forget());
 }
 
+static void
+SetBaseAnimationValue(const Animatable& aAnimatable,
+                      StyleAnimationValue& aAnimationValue)
+{
+  switch (aAnimatable.type()) {
+    case Animatable::TArrayOfTransformFunction: {
+      const InfallibleTArray<TransformFunction>& transforms =
+        aAnimatable.get_ArrayOfTransformFunction();
+      aAnimationValue.SetTransformValue(CreateCSSValueList(transforms));
+      break;
+    }
+    case Animatable::Tfloat:
+      aAnimationValue.SetFloatValue(aAnimatable.get_float());
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unsupported type");
+  }
+}
+
 void
 Layer::SetAnimations(const AnimationArray& aAnimations)
 {
   MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) SetAnimations", this));
 
   mAnimations = aAnimations;
   mAnimationData.Clear();
   for (uint32_t i = 0; i < mAnimations.Length(); i++) {
@@ -441,16 +460,21 @@ Layer::SetAnimations(const AnimationArra
         break;
       case dom::FillMode::Backwards:
         animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
         break;
       default:
         break;
     }
 
+    if (animation.baseValue().type() != AnimationBaseValue::Tnull_t) {
+      SetBaseAnimationValue(animation.baseValue().get_Animatable(),
+                            mBaseAnimationValue);
+    }
+
     AnimData* data = mAnimationData.AppendElement();
     InfallibleTArray<Maybe<ComputedTimingFunction>>& functions =
       data->mFunctions;
     const InfallibleTArray<AnimationSegment>& segments = animation.segments();
     for (uint32_t j = 0; j < segments.Length(); j++) {
       TimingFunction tf = segments.ElementAt(j).sampleFn();
 
       Maybe<ComputedTimingFunction> ctf =
@@ -461,30 +485,18 @@ Layer::SetAnimations(const AnimationArra
     // Precompute the StyleAnimationValues that we need if this is a transform
     // animation.
     InfallibleTArray<StyleAnimationValue>& startValues = data->mStartValues;
     InfallibleTArray<StyleAnimationValue>& endValues = data->mEndValues;
     for (uint32_t j = 0; j < segments.Length(); j++) {
       const AnimationSegment& segment = segments[j];
       StyleAnimationValue* startValue = startValues.AppendElement();
       StyleAnimationValue* endValue = endValues.AppendElement();
-      if (segment.endState().type() == Animatable::TArrayOfTransformFunction) {
-        const InfallibleTArray<TransformFunction>& startFunctions =
-          segment.startState().get_ArrayOfTransformFunction();
-        startValue->SetTransformValue(CreateCSSValueList(startFunctions));
-
-        const InfallibleTArray<TransformFunction>& endFunctions =
-          segment.endState().get_ArrayOfTransformFunction();
-        endValue->SetTransformValue(CreateCSSValueList(endFunctions));
-      } else {
-        NS_ASSERTION(segment.endState().type() == Animatable::Tfloat,
-                     "Unknown Animatable type");
-        startValue->SetFloatValue(segment.startState().get_float());
-        endValue->SetFloatValue(segment.endState().get_float());
-      }
+      SetBaseAnimationValue(segment.startState(), *startValue);
+      SetBaseAnimationValue(segment.endState(), *endValue);
     }
   }
 
   Mutated();
 }
 
 void
 Layer::StartPendingAnimations(const TimeStamp& aReadyTime)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1905,16 +1905,18 @@ protected:
   // If this layer is used for OMTA, then this counter is used to ensure we
   // stay in sync with the animation manager
   uint64_t mAnimationGeneration;
 #ifdef MOZ_DUMP_PAINTING
   nsTArray<nsCString> mExtraDumpInfo;
 #endif
   // Store display list log.
   nsCString mDisplayListLog;
+
+  StyleAnimationValue mBaseAnimationValue;
 };
 
 /**
  * A Layer which we can paint into. It is a conceptually
  * infinite surface, but each PaintedLayer has an associated "valid region"
  * of contents that it is currently storing, which is finite. PaintedLayer
  * implementations can store content between paints.
  *
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -147,16 +147,21 @@ union TransformFunction {
   TransformMatrix;
 };
 
 union Animatable {
   float;
   TransformFunction[];
 };
 
+union AnimationBaseValue {
+  null_t;
+  Animatable;
+};
+
 struct AnimationSegment {
   Animatable startState;
   Animatable endState;
   float startPortion;
   float endPortion;
   TimingFunction sampleFn;
 };
 
@@ -206,16 +211,20 @@ struct Animation {
   // This uses dom::FillMode.
   uint8_t fillMode;
   nsCSSPropertyID property;
   AnimationData data;
   float playbackRate;
   // This is used in the transformed progress calculation.
   TimingFunction easingFunction;
   uint8_t iterationComposite;
+  // This value stores base CSS value of the target element of the animation.
+  // The base value is necessary for additive or accumulative animations,
+  // If the animations is neither additive nor accumlative, the value is null_t.
+  AnimationBaseValue baseValue;
 };
 
 // Change a layer's attributes
 struct CommonLayerAttributes {
   IntRect layerBounds;
   LayerIntRegion visibleRegion;
   EventRegions eventRegions;
   TransformMatrix transform;
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -367,33 +367,88 @@ ToTimingFunction(const Maybe<ComputedTim
                                               spline->X2(), spline->Y2()));
   }
 
   uint32_t type = aCTF->GetType() == nsTimingFunction::Type::StepStart ? 1 : 2;
   return TimingFunction(StepFunction(aCTF->GetSteps(), type));
 }
 
 static void
+SetAnimatableValue(layers::Animatable& aAnimatable,
+                   nsCSSPropertyID aProperty,
+                   const StyleAnimationValue& aAnimationValue,
+                   nsIFrame* aFrame,
+                   const TransformReferenceBox& aRefBox)
+{
+  MOZ_ASSERT(aFrame);
+
+  switch (aProperty) {
+    case eCSSProperty_opacity:
+      if (!aAnimationValue.IsNull()) {
+        aAnimatable = aAnimationValue.GetFloatValue();
+      } else {
+        aAnimatable = 0.0;
+      }
+      break;
+    case eCSSProperty_transform:
+      aAnimatable = InfallibleTArray<TransformFunction>();
+      if (!aAnimationValue.IsNull()) {
+        nsCSSValueSharedList* list =
+          aAnimationValue.GetCSSValueSharedListValue();
+        TransformReferenceBox refBox(aFrame);
+        AddTransformFunctions(list->mHead,
+                              aFrame->StyleContext(),
+                              aFrame->PresContext(),
+                              refBox,
+                              aAnimatable.get_ArrayOfTransformFunction());
+      }
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unsupported property");
+  }
+}
+
+static void
+SetAnimationBaseValue(layers::AnimationBaseValue& aBaseValue,
+                      nsCSSPropertyID aProperty,
+                      nsIFrame* aFrame,
+                      const TransformReferenceBox& aRefBox)
+{
+  MOZ_ASSERT(aFrame);
+
+  EffectSet* effects = EffectSet::GetEffectSet(aFrame);
+  StyleAnimationValue baseValue;
+  effects->GetBaseStyleAnimationValue(aProperty, baseValue);
+  MOZ_ASSERT(!baseValue.IsNull(),
+             "The base value should be already there");
+
+  layers::Animatable animatable;
+  SetAnimatableValue(animatable,
+                     aProperty,
+                     baseValue,
+                     aFrame,
+                     aRefBox);
+  aBaseValue = animatable;
+}
+
+static void
 AddAnimationForProperty(nsIFrame* aFrame, const AnimationProperty& aProperty,
                         dom::Animation* aAnimation, Layer* aLayer,
                         AnimationData& aData, bool aPending)
 {
   MOZ_ASSERT(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
   MOZ_ASSERT(aAnimation->GetEffect(),
              "Should not be adding an animation without an effect");
   MOZ_ASSERT((!aAnimation->GetCurrentOrPendingStartTime().IsNull() ||
               (aAnimation->GetTimeline() &&
                aAnimation->GetTimeline()->TracksWallclockTime())) ||
              !aAnimation->HoldTime().IsNull(),
              "If the animation has a resolved start time it should have a "
              "timeline capable of converting it to a TimeStamp, or the "
              "animation should have a hold time");
-  nsStyleContext* styleContext = aFrame->StyleContext();
-  nsPresContext* presContext = aFrame->PresContext();
-  TransformReferenceBox refBox(aFrame);
 
   layers::Animation* animation =
     aPending ?
     aLayer->AddAnimationForNextTransaction() :
     aLayer->AddAnimation();
 
   const TimingParams& timing = aAnimation->GetEffect()->SpecifiedTiming();
 
@@ -448,36 +503,41 @@ AddAnimationForProperty(nsIFrame* aFrame
   animation->property() = aProperty.mProperty;
   animation->playbackRate() = aAnimation->PlaybackRate();
   animation->data() = aData;
   animation->easingFunction() = ToTimingFunction(timing.mFunction);
   animation->iterationComposite() =
     static_cast<uint8_t>(aAnimation->GetEffect()->
                          AsKeyframeEffect()->IterationComposite());
 
+  TransformReferenceBox refBox(aFrame);
+
+  // If the animation is additive or accumulate, we need to pass its base
+  // value to the compositor.
+  if (aAnimation->GetEffect()->AsKeyframeEffect()->
+        NeedsBaseValue(aProperty.mProperty)) {
+    SetAnimationBaseValue(animation->baseValue(),
+                          aProperty.mProperty,
+                          aFrame, refBox);
+  } else {
+    animation->baseValue() = null_t();
+  }
+
   for (uint32_t segIdx = 0; segIdx < aProperty.mSegments.Length(); segIdx++) {
     const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
 
     AnimationSegment* animSegment = animation->segments().AppendElement();
-    if (aProperty.mProperty == eCSSProperty_transform) {
-      animSegment->startState() = InfallibleTArray<TransformFunction>();
-      animSegment->endState() = InfallibleTArray<TransformFunction>();
-
-      nsCSSValueSharedList* list =
-        segment.mFromValue.GetCSSValueSharedListValue();
-      AddTransformFunctions(list->mHead, styleContext, presContext, refBox,
-                            animSegment->startState().get_ArrayOfTransformFunction());
-
-      list = segment.mToValue.GetCSSValueSharedListValue();
-      AddTransformFunctions(list->mHead, styleContext, presContext, refBox,
-                            animSegment->endState().get_ArrayOfTransformFunction());
-    } else if (aProperty.mProperty == eCSSProperty_opacity) {
-      animSegment->startState() = segment.mFromValue.GetFloatValue();
-      animSegment->endState() = segment.mToValue.GetFloatValue();
-    }
+    SetAnimatableValue(animSegment->startState(),
+                       aProperty.mProperty,
+                       segment.mFromValue,
+                       aFrame, refBox);
+    SetAnimatableValue(animSegment->endState(),
+                       aProperty.mProperty,
+                       segment.mToValue,
+                       aFrame, refBox);
 
     animSegment->startPortion() = segment.mFromKey;
     animSegment->endPortion() = segment.mToKey;
     animSegment->sampleFn() = ToTimingFunction(segment.mTimingFunction);
   }
 }
 
 static void