Bug 1305325 - Part 12: Pass base value for opacity or transform to the compositor. r?birtles,mstange draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Sun, 04 Dec 2016 08:07:40 +0900
changeset 447347 ee8855c3a1772bc06dfaa893f56aff763b0663de
parent 447346 e49f288df499edbd25f25e1417e1cd3725329372
child 447348 a07714983b759d9c00e660bf145108c453f7bb09
push id38039
push userhiikezoe@mozilla-japan.org
push dateSat, 03 Dec 2016 23:26:35 +0000
reviewersbirtles, mstange
bugs1305325
milestone53.0a1
Bug 1305325 - Part 12: Pass base value for opacity or transform to the compositor. r?birtles,mstange MozReview-Commit-ID: 3mxatMvsNKk
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/ipc/LayersMessages.ipdlh
layout/painting/nsDisplayList.cpp
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -418,16 +418,38 @@ CreateCSSValueList(const InfallibleTArra
   }
   if (aFunctions.Length() == 0) {
     result = new nsCSSValueList();
     result->mValue.SetNoneValue();
   }
   return new nsCSSValueSharedList(result.forget());
 }
 
+static StyleAnimationValue
+ToStyleAnimationValue(const Animatable& aAnimatable)
+{
+  StyleAnimationValue result;
+
+  switch (aAnimatable.type()) {
+    case Animatable::TArrayOfTransformFunction: {
+      const InfallibleTArray<TransformFunction>& transforms =
+        aAnimatable.get_ArrayOfTransformFunction();
+      result.SetTransformValue(CreateCSSValueList(transforms));
+      break;
+    }
+    case Animatable::Tfloat:
+      result.SetFloatValue(aAnimatable.get_float());
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unsupported type");
+  }
+
+  return result;
+}
+
 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,50 +463,40 @@ Layer::SetAnimations(const AnimationArra
         break;
       case dom::FillMode::Backwards:
         animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
         break;
       default:
         break;
     }
 
+    if (animation.baseStyle().type() != BaseAnimationStyle::Tnull_t) {
+      mBaseAnimationStyle =
+        ToStyleAnimationValue(animation.baseStyle().get_Animatable());
+    }
+
     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 =
         AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
       functions.AppendElement(ctf);
     }
 
     // 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());
-      }
+    for (const AnimationSegment& segment : segments) {
+      startValues.AppendElement(ToStyleAnimationValue(segment.startState()));
+      endValues.AppendElement(ToStyleAnimationValue(segment.endState()));
     }
   }
 
   Mutated();
 }
 
 void
 Layer::StartPendingAnimations(const TimeStamp& aReadyTime)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1933,16 +1933,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 mBaseAnimationStyle;
 };
 
 /**
  * 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
@@ -155,16 +155,21 @@ union TransformFunction {
   TransformMatrix;
 };
 
 union Animatable {
   float;
   TransformFunction[];
 };
 
+union BaseAnimationStyle {
+  null_t;
+  Animatable;
+};
+
 struct AnimationSegment {
   Animatable startState;
   Animatable endState;
   float startPortion;
   float endPortion;
   TimingFunction sampleFn;
 };
 
@@ -216,16 +221,21 @@ struct Animation {
   AnimationData data;
   float playbackRate;
   // This is used in the transformed progress calculation.
   TimingFunction easingFunction;
   uint8_t iterationComposite;
   // True if the animation has a fixed current time (e.g. paused and
   // forward-filling animations).
   bool isNotPlaying;
+  // The base style that animations should composite with. This is only set for
+  // animations with a composite mode of additive or accumulate, and only for
+  // the first animation in the set (i.e. the animation that is lowest in the
+  // stack). In all other cases the value is null_t.
+  BaseAnimationStyle baseStyle;
 };
 
 // Change a layer's attributes
 struct CommonLayerAttributes {
   IntRect layerBounds;
   LayerIntRegion visibleRegion;
   EventRegions eventRegions;
   TransformMatrix transform;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -381,34 +381,84 @@ 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
+SetAnimatable(nsCSSPropertyID aProperty,
+              const StyleAnimationValue& aAnimationValue,
+              nsIFrame* aFrame,
+              const TransformReferenceBox& aRefBox,
+              layers::Animatable& aAnimatable)
+{
+  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
+SetBaseAnimationStyle(nsCSSPropertyID aProperty,
+                      nsIFrame* aFrame,
+                      const TransformReferenceBox& aRefBox,
+                      layers::BaseAnimationStyle& aBaseStyle)
+{
+  MOZ_ASSERT(aFrame);
+
+  EffectSet* effects = EffectSet::GetEffectSet(aFrame);
+  StyleAnimationValue baseValue = effects->GetBaseStyle(aProperty);
+  MOZ_ASSERT(!baseValue.IsNull(),
+             "The base value should be already there");
+
+  layers::Animatable animatable;
+  SetAnimatable(aProperty, baseValue, aFrame, aRefBox, animatable);
+  aBaseStyle = 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->IsPlaying() ||
              (aAnimation->GetTimeline() &&
               aAnimation->GetTimeline()->TracksWallclockTime()),
              "If the animation has an unresolved start time it should either"
              " be static (so we don't need a start time) or else have a"
              " timeline capable of converting TimeStamps (so we can calculate"
              " one later");
-  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();
 
@@ -454,36 +504,41 @@ AddAnimationForProperty(nsIFrame* aFrame
   animation->playbackRate() = aAnimation->PlaybackRate();
   animation->data() = aData;
   animation->easingFunction() = ToTimingFunction(timing.mFunction);
   animation->iterationComposite() =
     static_cast<uint8_t>(aAnimation->GetEffect()->
                          AsKeyframeEffect()->IterationComposite());
   animation->isNotPlaying() = !aAnimation->IsPlaying();
 
+  TransformReferenceBox refBox(aFrame);
+
+  // If the animation is additive or accumulates, we need to pass its base value
+  // to the compositor.
+  if (aAnimation->GetEffect()->AsKeyframeEffect()->
+        NeedsBaseStyle(aProperty.mProperty)) {
+    SetBaseAnimationStyle(aProperty.mProperty,
+                          aFrame, refBox,
+                          animation->baseStyle());
+  } else {
+    animation->baseStyle() = 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();
-    }
+    SetAnimatable(aProperty.mProperty,
+                  segment.mFromValue,
+                  aFrame, refBox,
+                  animSegment->startState());
+    SetAnimatable(aProperty.mProperty,
+                  segment.mToValue,
+                  aFrame, refBox,
+                  animSegment->endState());
 
     animSegment->startPortion() = segment.mFromKey;
     animSegment->endPortion() = segment.mToKey;
     animSegment->sampleFn() = ToTimingFunction(segment.mTimingFunction);
   }
 }
 
 static void