Bug 1345017 - Add animation sampling for WR, r?kats draft
authorpeter chang <pchang@mozilla.com>
Wed, 12 Apr 2017 16:40:48 +0800
changeset 564066 85d1de0c7e17cafde693932c8e64c14556a69c25
parent 564065 cf5a0ee96189d6646637f4a93c81c30367b47995
child 564067 a11c4fa227dbe0d7c483720ec29d0dda78c3c081
push id54512
push userbmo:howareyou322@gmail.com
push dateTue, 18 Apr 2017 08:40:13 +0000
reviewerskats
bugs1345017
milestone55.0a1
Bug 1345017 - Add animation sampling for WR, r?kats MozReview-Commit-ID: AR2vajUf2o0
gfx/layers/AnimationHelper.cpp
gfx/layers/AnimationHelper.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/composite/AsyncCompositionManager.cpp
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
gfx/layers/wr/WebRenderContainerLayer.cpp
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/webrender_bindings/WebRenderTypes.h
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -7,33 +7,42 @@
 #include "AnimationHelper.h"
 #include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
 #include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
 #include "mozilla/dom/KeyframeEffectReadOnly.h" // for dom::KeyFrameEffectReadOnly
 #include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
 #include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+#include "nsDisplayList.h"              // for nsDisplayTransform, etc
 
 namespace mozilla {
 namespace layers {
 
 struct StyleAnimationValueCompositePair {
   StyleAnimationValue mValue;
   dom::CompositeOperation mComposite;
 };
 
 void
 CompositorAnimationStorage::Clear()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   mAnimatedValues.Clear();
   mAnimations.Clear();
+}
 
+void
+CompositorAnimationStorage::ClearById(const uint64_t& aId)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  mAnimatedValues.Remove(aId);
+  mAnimations.Remove(aId);
 }
 
 AnimatedValue*
 CompositorAnimationStorage::GetAnimatedValue(const uint64_t& aId) const
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mAnimatedValues.Get(aId);
 }
@@ -126,17 +135,17 @@ SampleValue(float aPortion, const layers
     StyleAnimationValue::Interpolate(aAnimation.property(),
                                      startValue, endValue,
                                      aPortion, interpolatedValue);
   MOZ_ASSERT(uncomputeResult, "could not uncompute value");
   return interpolatedValue;
 }
 
 bool
-AnimationHelper::SampleAnimationForEachNode(TimeStamp aPoint,
+AnimationHelper::SampleAnimationForEachNode(TimeStamp aTime,
                            AnimationArray& aAnimations,
                            InfallibleTArray<AnimData>& aAnimationData,
                            StyleAnimationValue& aAnimationValue,
                            bool& aHasInEffectAnimations)
 {
   bool activeAnimations = false;
 
   if (aAnimations.IsEmpty()) {
@@ -152,17 +161,17 @@ AnimationHelper::SampleAnimationForEachN
 
     MOZ_ASSERT(!animation.startTime().IsNull() ||
                animation.isNotPlaying(),
                "Failed to resolve start time of play-pending animations");
     // If the animation is not currently playing , e.g. paused or
     // finished, then use the hold time to stay at the same position.
     TimeDuration elapsedDuration = animation.isNotPlaying()
       ? animation.holdTime()
-      : (aPoint - animation.startTime())
+      : (aTime - animation.startTime())
           .MultDouble(animation.playbackRate());
     TimingParams timing;
     timing.mDuration.emplace(animation.duration());
     timing.mDelay = animation.delay();
     timing.mEndDelay = animation.endDelay();
     timing.mIterations = animation.iterations();
     timing.mIterationStart = animation.iterationStart();
     timing.mDirection =
@@ -473,10 +482,90 @@ AnimationHelper::GetNextCompositorAnimat
   ++sNextId;
 
   uint32_t procId = static_cast<uint32_t>(base::GetCurrentProcId());
   uint64_t nextId = procId;
   nextId = nextId << 32 | sNextId;
   return nextId;
 }
 
+void
+AnimationHelper::SampleAnimations(CompositorAnimationStorage* aStorage,
+                                  TimeStamp aTime)
+{
+  MOZ_ASSERT(aStorage);
+
+  // Do nothing if there are no compositor animations
+  if (!aStorage->AnimationsCount()) {
+    return;
+  }
+
+  //Sample the animations in CompositorAnimationStorage
+  for (auto iter = aStorage->ConstAnimationsTableIter();
+       !iter.Done(); iter.Next()) {
+    bool hasInEffectAnimations = false;
+    AnimationArray* animations = iter.UserData();
+    StyleAnimationValue animationValue;
+    InfallibleTArray<AnimData> animationData;
+    AnimationHelper::SetAnimations(*animations,
+                                   animationData,
+                                   animationValue);
+    AnimationHelper::SampleAnimationForEachNode(aTime,
+                                                *animations,
+                                                animationData,
+                                                animationValue,
+                                                hasInEffectAnimations);
+
+    if (!hasInEffectAnimations) {
+      continue;
+    }
+
+    // Store the AnimatedValue
+    Animation& animation = animations->LastElement();
+    switch (animation.property()) {
+      case eCSSProperty_opacity: {
+        aStorage->SetAnimatedValue(iter.Key(),
+                                   animationValue.GetFloatValue());
+        break;
+      }
+      case eCSSProperty_transform: {
+        nsCSSValueSharedList* list = animationValue.GetCSSValueSharedListValue();
+        const TransformData& transformData = animation.data().get_TransformData();
+        nsPoint origin = transformData.origin();
+        // we expect all our transform data to arrive in device pixels
+        gfx::Point3D transformOrigin = transformData.transformOrigin();
+        nsDisplayTransform::FrameTransformProperties props(list,
+                                                           transformOrigin);
+
+        gfx::Matrix4x4 transform =
+          nsDisplayTransform::GetResultingTransformMatrix(props, origin,
+                                                          transformData.appUnitsPerDevPixel(),
+                                                          0, &transformData.bounds());
+        gfx::Matrix4x4 frameTransform = transform;
+
+        //TODO how do we support this without layer information
+        // If our parent layer is a perspective layer, then the offset into reference
+        // frame coordinates is already on that layer. If not, then we need to ask
+        // for it to be added here.
+        // if (!aLayer->GetParent() ||
+        //     !aLayer->GetParent()->GetTransformIsPerspective()) {
+        //   nsLayoutUtils::PostTranslate(transform, origin,
+        //                                transformData.appUnitsPerDevPixel(),
+        //                                true);
+        // }
+
+        // if (ContainerLayer* c = aLayer->AsContainerLayer()) {
+        //   transform.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(), 1);
+        // }
+
+        aStorage->SetAnimatedValue(iter.Key(),
+                                   Move(transform), Move(frameTransform),
+                                   transformData);
+        break;
+      }
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
+    }
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -97,51 +97,113 @@ public:
   void SetAnimatedValue(uint64_t aId, const float& aOpacity);
 
   /**
    * Return the animated value if a given id can map to its animated value
    */
   AnimatedValue* GetAnimatedValue(const uint64_t& aId) const;
 
   /**
+   * Return the iterator of animated value table
+   */
+  AnimatedValueTable::Iterator ConstAnimatedValueTableIter() const
+  {
+    return mAnimatedValues.ConstIter();
+  }
+
+  uint32_t AnimatedValueCount() const
+  {
+    return mAnimatedValues.Count();
+  }
+
+  /**
    * Set the animations based on the unique id
    */
   void SetAnimations(uint64_t aId, const AnimationArray& aAnimations);
 
   /**
    * Return the animations if a given id can map to its animations
    */
   AnimationArray* GetAnimations(const uint64_t& aId) const;
 
   /**
+   * Return the iterator of animations table
+   */
+  AnimationsTable::Iterator ConstAnimationsTableIter() const
+  {
+    return mAnimations.ConstIter();
+  }
+
+  uint32_t AnimationsCount() const
+  {
+    return mAnimations.Count();
+  }
+
+  /**
    * Clear AnimatedValues and Animations data
    */
   void Clear();
 
+  void ClearById(const uint64_t& aId);
 private:
   ~CompositorAnimationStorage() { Clear(); };
 
 private:
   AnimatedValueTable mAnimatedValues;
   AnimationsTable mAnimations;
 };
 
 class AnimationHelper
 {
 public:
+
+
+  /*
+   * TODO Bug 1356509 Once we decouple the compositor animations and layers
+   * in parent side, the API will be called inside SampleAnimations.
+   * Before this, we expose this API for AsyncCompositionManager.
+   *
+   * Sample animations based on a given time stamp for a element(layer) with
+   * its animation data.
+   * Returns true if there exists compositor animation, and stores corresponding
+   * animated value in |aAnimationValue|.
+   */
   static bool
-  SampleAnimationForEachNode(TimeStamp aPoint,
+  SampleAnimationForEachNode(TimeStamp aTime,
                              AnimationArray& aAnimations,
                              InfallibleTArray<AnimData>& aAnimationData,
                              StyleAnimationValue& aAnimationValue,
                              bool& aHasInEffectAnimations);
-
+  /*
+   * TODO Bug 1356509 Once we decouple the compositor animations and layers
+   * in parent side, the API will be called inside SampleAnimations.
+   * Before this, we expose this API for AsyncCompositionManager.
+   *
+   * Populates AnimData stuctures into |aAnimData| based on |aAnimations|
+   */
   static void
   SetAnimations(AnimationArray& aAnimations,
                 InfallibleTArray<AnimData>& aAnimData,
                 StyleAnimationValue& aBaseAnimationStyle);
+
+  /*
+   * Get a unique id to represent the compositor animation between child
+   * and parent side. This id will be used as a key to store animation
+   * data in the CompositorAnimationStorage per compositor.
+   * Each layer on the content side calls this when it gets new animation
+   * data.
+   */
   static uint64_t GetNextCompositorAnimationsId();
+
+  /*
+   * Sample animation based a given time stamp |aTime| and the animation
+   * data inside CompositorAnimationStorage |aStorage|. The animated values
+   * after sampling will be stored in CompositorAnimationStorage as well.
+   */
+  static void
+  SampleAnimations(CompositorAnimationStorage* aStorage,
+                   TimeStamp aTime);
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_AnimationHelper_h
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -691,16 +691,27 @@ Layer::GetLocalTransform()
 
 const LayerToParentLayerMatrix4x4
 Layer::GetLocalTransformTyped()
 {
   return ViewAs<LayerToParentLayerMatrix4x4>(GetLocalTransform());
 }
 
 bool
+Layer::HasOpacityAnimation() const
+{
+  for (uint32_t i = 0; i < mAnimations.Length(); i++) {
+    if (mAnimations[i].property() == eCSSProperty_opacity) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
 Layer::HasTransformAnimation() const
 {
   for (uint32_t i = 0; i < mAnimations.Length(); i++) {
     if (mAnimations[i].property() == eCSSProperty_transform) {
       return true;
     }
   }
   return false;
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1435,16 +1435,17 @@ public:
   AnimationArray& GetAnimations() { return mAnimations; }
   uint64_t GetCompositorAnimationsId() { return mCompositorAnimationsId; }
   InfallibleTArray<AnimData>& GetAnimationData() { return mAnimationData; }
 
   uint64_t GetAnimationGeneration() { return mAnimationGeneration; }
   void SetAnimationGeneration(uint64_t aCount) { mAnimationGeneration = aCount; }
 
   bool HasTransformAnimation() const;
+  bool HasOpacityAnimation() const;
 
   StyleAnimationValue GetBaseAnimationStyle() const
   {
     return mBaseAnimationStyle;
   }
 
   /**
    * Returns the local transform for this layer: either mTransform or,
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -643,17 +643,17 @@ ApplyAnimatedValue(Layer* aLayer,
     default:
       MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
   }
 }
 
 static AnimationProcessTypes
 SampleAnimations(Layer* aLayer,
                  CompositorAnimationStorage* aStorage,
-                 TimeStamp aPoint,
+                 TimeStamp aTime,
                  uint64_t* aLayerAreaAnimated)
 {
   // This tracks the first-encountered RefLayer in the layer tree. Since we are
   // doing a depth-first traversal, it is set to a non-null value if and only if
   // the currently-being-traversed node has a RefLayer ancestor. In the case of
   // nested RefLayers it points to the rootmost RefLayer.
   RefLayer* ancestorRefLayer = nullptr;
 
@@ -667,17 +667,17 @@ SampleAnimations(Layer* aLayer,
       [&] (Layer* layer)
       {
         if (!ancestorRefLayer) {
           ancestorRefLayer = layer->AsRefLayer();
         }
 
         bool hasInEffectAnimations = false;
         StyleAnimationValue animationValue = layer->GetBaseAnimationStyle();
-        if (AnimationHelper::SampleAnimationForEachNode(aPoint,
+        if (AnimationHelper::SampleAnimationForEachNode(aTime,
                                                         layer->GetAnimations(),
                                                         layer->GetAnimationData(),
                                                         animationValue,
                                                         hasInEffectAnimations)) {
           animProcess |= (ancestorRefLayer ? AnimationProcessTypes::eContent
                                            : AnimationProcessTypes::eChrome);
         }
         if (hasInEffectAnimations) {
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -590,22 +590,60 @@ WebRenderBridgeParent::RecvForceComposit
 
 void
 WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   Destroy();
 }
 
 void
+WebRenderBridgeParent::SampleAnimations(nsTArray<WrOpacityProperty>& aOpacityArray,
+                                        nsTArray<WrTransformProperty>& aTransformArray)
+{
+  uint64_t id = mWidget ? 0 : mPipelineId.mHandle;
+  CompositorAnimationStorage* storage =
+    mCompositorBridge->GetAnimationStorage(id);
+
+  AnimationHelper::SampleAnimations(storage,
+                                    mCompositorScheduler->
+                                      GetLastComposeTime());
+
+  // return the animated data if has
+  if (storage->AnimatedValueCount()) {
+    for(auto iter = storage->ConstAnimatedValueTableIter();
+        !iter.Done(); iter.Next()) {
+      AnimatedValue * value = iter.UserData();
+      if (value->mType == AnimatedValue::TRANSFORM) {
+        aTransformArray.AppendElement(
+          wr::ToWrTransformProperty(iter.Key(), value->mTransform.mTransformInDevSpace));
+      } else if (value->mType == AnimatedValue::OPACITY) {
+        aOpacityArray.AppendElement(
+          wr::ToWrOpacityProperty(iter.Key(), value->mOpacity));
+      }
+    }
+  }
+}
+
+void
 WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect)
 {
   if (mPaused) {
     return;
   }
-  mApi->GenerateFrame();
+
+  nsTArray<WrOpacityProperty> opacityArray;
+  nsTArray<WrTransformProperty> transformArray;
+  SampleAnimations(opacityArray, transformArray);
+
+  if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
+    mApi->GenerateFrame(opacityArray, transformArray);
+    ScheduleComposition();
+  } else {
+    mApi->GenerateFrame();
+  }
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(uint32_t aWrEpoch, uint64_t aTransactionId)
 {
   // The transaction ID might get reset to 1 if the page gets reloaded, see
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
   // Otherwise, it should be continually increasing.
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -176,16 +176,19 @@ private:
                    InfallibleTArray<OpDestroy>&& aToDestroy,
                    const uint64_t& aFwdTransactionId,
                    const uint64_t& aTransactionId,
                    const ByteBuffer& dl,
                    const WrBuiltDisplayListDescriptor& dlDesc,
                    const ByteBuffer& aux,
                    const WrAuxiliaryListsDescriptor& auxDesc);
 
+  void SampleAnimations(nsTArray<WrOpacityProperty>& aOpacityArray,
+                        nsTArray<WrTransformProperty>& aTransformArray);
+
 private:
   struct PendingTransactionId {
     PendingTransactionId(wr::Epoch aEpoch, uint64_t aId)
       : mEpoch(aEpoch)
       , mId(aId)
     {}
     wr::Epoch mEpoch;
     uint64_t mId;
--- a/gfx/layers/wr/WebRenderContainerLayer.cpp
+++ b/gfx/layers/wr/WebRenderContainerLayer.cpp
@@ -15,46 +15,57 @@ namespace mozilla {
 namespace layers {
 
 void
 WebRenderContainerLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   nsTArray<LayerPolygon> children = SortChildrenBy3DZOrder(SortMode::WITHOUT_GEOMETRY);
 
   gfx::Matrix4x4 transform = GetTransform();
+  float opacity = GetLocalOpacity();
   gfx::Rect relBounds = GetWrRelBounds();
   gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
 
   Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
 
   wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
   if (gfxPrefs::LayersDump()) {
     printf_stderr("ContainerLayer %p using bounds=%s, overflow=%s, transform=%s, mix-blend-mode=%s\n",
                   this->GetLayer(),
                   Stringify(relBounds).c_str(),
                   Stringify(overflow).c_str(),
                   Stringify(transform).c_str(),
                   Stringify(mixBlendMode).c_str());
   }
-  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
-                               GetLocalOpacity(),
-                               transform,
-                               mixBlendMode);
-  aBuilder.PushScrollLayer(wr::ToWrRect(overflow),
-                           wr::ToWrRect(overflow),
-                           mask.ptrOr(nullptr));
 
   if (GetCompositorAnimationsId()) {
     CompositorAnimations anim;
     anim.animations() = GetAnimations();
     anim.id() = GetCompositorAnimationsId();
     WrBridge()->AddWebRenderParentCommand(OpAddCompositorAnimations(anim));
+
+    float* maybeOpacity = HasOpacityAnimation() ? nullptr : &opacity;
+    gfx::Matrix4x4* maybeTransform = HasTransformAnimation() ? nullptr : &transform;
+    aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
+                                 GetCompositorAnimationsId(),
+                                 maybeOpacity,
+                                 maybeTransform,
+                                 mixBlendMode);
+  } else {
+    aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
+                                 opacity,
+                                 transform,
+                                 mixBlendMode);
   }
 
+  aBuilder.PushScrollLayer(wr::ToWrRect(overflow),
+                           wr::ToWrRect(overflow),
+                           mask.ptrOr(nullptr));
+
   for (LayerPolygon& child : children) {
     if (child.layer->IsBackfaceHidden()) {
       continue;
     }
     ToWebRenderLayer(child.layer)->RenderLayer(aBuilder);
   }
   aBuilder.PopScrollLayer();
   aBuilder.PopStackingContext();
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -307,16 +307,17 @@ WebRenderLayerManager::EndTransaction(Dr
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
   if (!WrBridge()->DPBegin(size.ToUnknownSize())) {
     return;
   }
+  mRoot->StartPendingAnimations(mAnimationReadyTime);
 
   wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
   WebRenderLayer::ToWebRenderLayer(mRoot)->RenderLayer(builder);
 
   bool sync = mTarget != nullptr;
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
 
   WrBridge()->DPEnd(builder, size.ToUnknownSize(), sync, mLatestTransactionId);
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -348,16 +348,32 @@ static inline WrRepeatMode ToWrRepeatMod
     return WrRepeatMode::Space;
   default:
     MOZ_ASSERT(false);
   }
 
   return WrRepeatMode::Stretch;
 }
 
+static inline WrTransformProperty ToWrTransformProperty(uint64_t id, gfx::Matrix4x4& transform)
+{
+  WrTransformProperty prop;
+  prop.id = id;
+  prop.transform = ToWrMatrix(transform);
+  return prop;
+}
+
+static inline WrOpacityProperty ToWrOpacityProperty(uint64_t id, const float opacity)
+{
+  WrOpacityProperty prop;
+  prop.id = id;
+  prop.opacity = opacity;
+  return prop;
+}
+
 template<class T>
 static inline WrComplexClipRegion ToWrComplexClipRegion(const gfx::RectTyped<T>& rect,
                                                         const LayerSize& size)
 {
   WrComplexClipRegion complex_clip;
   complex_clip.rect = wr::ToWrRect(rect);
   complex_clip.radii = wr::ToWrUniformBorderRadius(size);
   return complex_clip;