Bug 1321428 - Handle scroll timelines in AsyncCompositionManager. r=mattwoodrow draft
authorMantaroh Yoshinaga <mantaroh@gmail.com>
Mon, 20 Mar 2017 16:26:44 -0400
changeset 542202 af5a4d47d9fe6df1c1ef7a465ec9c486e6f9fa4c
parent 542201 8609e1cfc7e52793739986c1fc7540dcdc123964
child 542203 ebc4cc22fc9cfccf6e1ba28e0249793d5b96cd59
push id50961
push userbballo@mozilla.com
push dateFri, 24 Mar 2017 22:03:27 +0000
reviewersmattwoodrow
bugs1321428
milestone55.0a1
Bug 1321428 - Handle scroll timelines in AsyncCompositionManager. r=mattwoodrow MozReview-Commit-ID: 5boXOcgmugW
gfx/layers/AnimationHelper.cpp
gfx/layers/AnimationHelper.h
gfx/layers/composite/AsyncCompositionManager.cpp
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -1,26 +1,41 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "AnimationHelper.h"
+#include "FrameMetrics.h" // for FrameMetrics, ScrollableLayerGuid
+#include "Layers.h" // for Layer, etc
+#include "TreeTraversal.h" // for DepthFirstSearch
+#include "Units.h" // for CSSPoint, etc
+#include "apz/src/AsyncPanZoomController.h"  // for AsyncPanZoomController
+#include "composite/LayerManagerComposite.h" // for LayerManagerComposite
 #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/dom/Nullable.h" // for dom::Nullable
+#include "mozilla/dom/ScrollTimelineUtils.h" // for dom::ScrollTimelineUtils
+#include "mozilla/layers/Compositor.h"  // for Compositor
+#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
 #include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
 #include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/Maybe.h" // for Maybe
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
 
 namespace mozilla {
 namespace layers {
 
+using dom::Nullable;
+using dom::ScrollTimelineUtils;
+
 struct StyleAnimationValueCompositePair {
   StyleAnimationValue mValue;
   dom::CompositeOperation mComposite;
 };
 
 void
 CompositorAnimationStorage::Clear()
 {
@@ -125,44 +140,143 @@ SampleValue(float aPortion, const layers
   DebugOnly<bool> uncomputeResult =
     StyleAnimationValue::Interpolate(aAnimation.property(),
                                      startValue, endValue,
                                      aPortion, interpolatedValue);
   MOZ_ASSERT(uncomputeResult, "could not uncompute value");
   return interpolatedValue;
 }
 
+static uint64_t
+LayersIdFor(Layer* aLayer)
+{
+  Layer* layer = aLayer;
+  while (layer) {
+    if (layer->AsRefLayer()) {
+      return layer->AsRefLayer()->GetReferentId();
+    }
+    layer = layer->GetParent();
+  }
+
+  // Return the root layer tree ID.
+  if (LayerManager* manager = aLayer->Manager()) {
+    if (Compositor* compositor = manager->AsLayerManagerComposite()->GetCompositor()) {
+      if (CompositorBridgeParent* parent = compositor->GetCompositorBridgeParent()) {
+        return parent->RootLayerTreeId();
+      }
+    }
+  }
+  return 0;
+}
+
+static AsyncPanZoomController*
+FindAPZC(LayerMetricsWrapper aRoot, uint64_t aLayersId, FrameMetrics::ViewID aViewId)
+{
+  LayerMetricsWrapper result = DepthFirstSearch<ForwardIterator>(
+      aRoot,
+      [aLayersId, aViewId] (LayerMetricsWrapper aLayer)
+      {
+        if (AsyncPanZoomController* apzc = aLayer.GetApzc()) {
+          ScrollableLayerGuid guid = apzc->GetGuid();
+          if (guid.mLayersId == aLayersId && guid.mScrollId == aViewId) {
+            return true;
+          }
+        }
+        return false;
+      });
+  return result.IsValid() ? result.GetApzc() : nullptr;
+}
+
+/**
+ * Helper function to get the current time of a scroll timeline for a
+ * scroll-driven animation animation.
+ *
+ * @param aRoot The root of the layer tree.
+ * @param aLayer The layer which has the scroll-driven animation.
+ * @param animation The scroll-driven animation. Its scrollTimelineInfo()
+ *                  must be populated.
+ * @return The current time of the animation's scroll timeline, or Nothing()
+ *         if the scrollable layer referenced by the scroll timeline can't
+ *         be found or if the scroll timeline has an unresolved current time.
+ */
+static Maybe<TimeStamp>
+GetCurrentTimeOfScrollTimeline(Layer* aRoot,
+                               Layer* aLayer,
+                               const Animation& animation)
+{
+  const ScrollTimelineInfo& info = animation.scrollTimelineInfo();
+  if (AsyncPanZoomController* apzc = FindAPZC(LayerMetricsWrapper(aRoot),
+                                              LayersIdFor(aLayer),
+                                              info.scrollFrame())) {
+    CSSPoint scrollOffset = apzc->GetCurrentAsyncScrollOffsetInCssPixels(
+        AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+    nscoord minScroll = info.minScroll();
+    nscoord maxScroll = info.maxScroll();
+    nscoord currentScroll;
+    if (info.direction() == dom::ScrollDirection::Horizontal) {
+      currentScroll = CSSPixel::ToAppUnits(scrollOffset.x);
+    } else {
+      currentScroll = CSSPixel::ToAppUnits(scrollOffset.y);
+    }
+    Nullable<TimeDuration> currentTime =
+        ScrollTimelineUtils::CalculateCurrentTime(currentScroll,
+            minScroll, maxScroll, info.timeRange(), info.fillMode());
+    if (!currentTime.IsNull()) {
+      return Some(animation.startTime() + currentTime.Value());
+    }
+  }
+  return Nothing();
+
+}
+
 bool
-AnimationHelper::SampleAnimationForEachNode(TimeStamp aPoint,
+AnimationHelper::SampleAnimationForEachNode(Layer* aRoot,
+                           Layer* aLayer,
+                           TimeStamp aPoint,
                            AnimationArray& aAnimations,
                            InfallibleTArray<AnimData>& aAnimationData,
                            StyleAnimationValue& aAnimationValue,
                            bool& aHasInEffectAnimations)
 {
   bool activeAnimations = false;
 
   if (aAnimations.IsEmpty()) {
     return activeAnimations;
   }
 
   // Process in order, since later aAnimations override earlier ones.
   for (size_t i = 0, iEnd = aAnimations.Length(); i < iEnd; ++i) {
     Animation& animation = aAnimations[i];
     AnimData& animData = aAnimationData[i];
 
-    activeAnimations = true;
+    TimeStamp timelineTime = aPoint;
+    if (animation.scrollTimelineInfo().type() == MaybeScrollTimelineInfo::TScrollTimelineInfo) {
+      if (Maybe<TimeStamp> currentTime = GetCurrentTimeOfScrollTimeline(
+              aRoot, aLayer, animation)) {
+        timelineTime = currentTime.ref();
+      } else {
+        // Skip animation.
+        continue;
+      }
+    } else {
+      // Scroll-driven animations do not trigger recomposites (if
+      // something is triggering scrolling, that will trigger a
+      // recomposite itself, and if we're not scrolling, there is no
+      // need to sample scroll-driven animations).
+      activeAnimations = true;
+    }
 
     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())
+      : (timelineTime - 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 =
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -11,16 +11,17 @@
 #include "mozilla/layers/LayersMessages.h" // for TransformData, etc
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 
 
 namespace mozilla {
 class StyleAnimationValue;
 namespace layers {
 class Animation;
+class Layer;
 
 typedef InfallibleTArray<layers::Animation> AnimationArray;
 
 struct AnimData {
   InfallibleTArray<mozilla::StyleAnimationValue> mStartValues;
   InfallibleTArray<mozilla::StyleAnimationValue> mEndValues;
   InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
 };
@@ -123,17 +124,19 @@ private:
   AnimatedValueTable mAnimatedValues;
   AnimationsTable mAnimations;
 };
 
 class AnimationHelper
 {
 public:
   static bool
-  SampleAnimationForEachNode(TimeStamp aPoint,
+  SampleAnimationForEachNode(Layer* aRoot,
+                             Layer* aLayer,
+                             TimeStamp aPoint,
                              AnimationArray& aAnimations,
                              InfallibleTArray<AnimData>& aAnimationData,
                              StyleAnimationValue& aAnimationValue,
                              bool& aHasInEffectAnimations);
 
   static void
   SetAnimations(AnimationArray& aAnimations,
                 InfallibleTArray<AnimData>& aAnimData,
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -640,84 +640,49 @@ ApplyAnimatedValue(Layer* aLayer,
                                  transformData);
       break;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
   }
 }
 
-static uint64_t
-LayersIdFor(Layer* aLayer)
-{
-  Layer* layer = aLayer;
-  while (layer) {
-    if (layer->AsRefLayer()) {
-      return layer->AsRefLayer()->GetReferentId();
-    }
-    layer = layer->GetParent();
-  }
-
-  // Return the root layer tree ID.
-  if (LayerManager* manager = aLayer->Manager()) {
-    if (Compositor* compositor = manager->AsLayerManagerComposite()->GetCompositor()) {
-      if (CompositorBridgeParent* parent = compositor->GetCompositorBridgeParent()) {
-        return parent->RootLayerTreeId();
-      }
-    }
-  }
-  return 0;
-}
-
-static AsyncPanZoomController*
-FindAPZC(LayerMetricsWrapper aRoot, uint64_t aLayersId, FrameMetrics::ViewID aViewId)
-{
-  LayerMetricsWrapper result = DepthFirstSearch<ForwardIterator>(
-      aRoot,
-      [aLayersId, aViewId] (LayerMetricsWrapper aLayer)
-      {
-        if (AsyncPanZoomController* apzc = aLayer.GetApzc()) {
-          ScrollableLayerGuid guid = apzc->GetGuid();
-          if (guid.mLayersId == aLayersId && guid.mScrollId == aViewId) {
-            return true;
-          }
-        }
-        return false;
-      });
-  return result.IsValid() ? result.GetApzc() : nullptr;
-}
-
 static bool
-SampleAnimations(Layer* aLayer,
+SampleAnimations(Layer* aRoot,
                  CompositorAnimationStorage* aStorage,
                  TimeStamp aPoint,
                  uint64_t* aLayerAreaAnimated)
 {
   bool activeAnimations = false;
 
   ForEachNode<ForwardIterator>(
-      aLayer,
-      [aStorage, &activeAnimations, &aPoint, &aLayerAreaAnimated] (Layer* layer)
+      aRoot,
+      [aStorage, &activeAnimations, &aPoint, &aLayerAreaAnimated, &aRoot] (Layer* layer)
       {
         bool hasInEffectAnimations = false;
         StyleAnimationValue animationValue = layer->GetBaseAnimationStyle();
         activeAnimations |=
-          AnimationHelper::SampleAnimationForEachNode(aPoint,
+          AnimationHelper::SampleAnimationForEachNode(aRoot, layer, aPoint,
                                                       layer->GetAnimations(),
                                                       layer->GetAnimationData(),
                                                       animationValue,
                                                       hasInEffectAnimations);
         if (hasInEffectAnimations) {
           Animation& animation = layer->GetAnimations().LastElement();
           ApplyAnimatedValue(layer,
                              aStorage,
                              animation.property(),
                              animation.data(),
                              animationValue);
-          if (aLayerAreaAnimated) {
+          if (aLayerAreaAnimated && activeAnimations) {
+            // Only track animated layer area if we have have active
+            // (time-driven) animations that the compositor will continue
+            // sampling. Scroll-driven animations can be in effect but not
+            // "active" in the sense that only further scrolling will cause
+            // them to be sampled again.
             *aLayerAreaAnimated += (layer->GetVisibleRegion().Area());
           }
         }
       });
 
   return activeAnimations;
 }