Bug 1321428 - Handle scroll timelines in AsyncCompositionManager. r=mattwoodrow
MozReview-Commit-ID: 5boXOcgmugW
--- 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;
}