зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1454324 - Skip calculating animation value if animation's progress value hasn't been changed since the last composition and if there are no other animations on the same element. r=birtles,kats
Note that we have to calculate animation values if there is another animation since the other animation might be 'accumulate' or 'add', or might have a missing keyframe (i.e. the calculated animation values will be used in the missing keyframe). MozReview-Commit-ID: rQyS9nuVJi --HG-- extra : rebase_source : 6ddc70308e223a709eba9c4c2f05e42bbc0f3160
This commit is contained in:
Родитель
1695970073
Коммит
df02c10b40
|
@ -1586,19 +1586,30 @@ KeyframeEffectReadOnly::MarkCascadeNeedsUpdate()
|
|||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::HasComputedTimingChanged() const
|
||||
/* static */ bool
|
||||
KeyframeEffectReadOnly::HasComputedTimingChanged(
|
||||
const ComputedTiming& aComputedTiming,
|
||||
IterationCompositeOperation aIterationComposite,
|
||||
const Nullable<double>& aProgressOnLastCompose,
|
||||
uint64_t aCurrentIterationOnLastCompose)
|
||||
{
|
||||
// Typically we don't need to request a restyle if the progress hasn't
|
||||
// changed since the last call to ComposeStyle. The one exception is if the
|
||||
// iteration composite mode is 'accumulate' and the current iteration has
|
||||
// changed, since that will often produce a different result.
|
||||
return aComputedTiming.mProgress != aProgressOnLastCompose ||
|
||||
(aIterationComposite == IterationCompositeOperation::Accumulate &&
|
||||
aComputedTiming.mCurrentIteration != aCurrentIterationOnLastCompose);
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::HasComputedTimingChanged() const
|
||||
{
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
return computedTiming.mProgress != mProgressOnLastCompose ||
|
||||
(mEffectOptions.mIterationComposite ==
|
||||
IterationCompositeOperation::Accumulate &&
|
||||
computedTiming.mCurrentIteration !=
|
||||
mCurrentIterationOnLastCompose);
|
||||
return HasComputedTimingChanged(computedTiming,
|
||||
mEffectOptions.mIterationComposite,
|
||||
mProgressOnLastCompose,
|
||||
mCurrentIterationOnLastCompose);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -266,6 +266,12 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool HasComputedTimingChanged(
|
||||
const ComputedTiming& aComputedTiming,
|
||||
IterationCompositeOperation aIterationComposite,
|
||||
const Nullable<double>& aProgressOnLastCompose,
|
||||
uint64_t aCurrentIterationOnLastCompose);
|
||||
|
||||
protected:
|
||||
KeyframeEffectReadOnly(nsIDocument* aDocument,
|
||||
const Maybe<OwningAnimationTarget>& aTarget,
|
||||
|
|
|
@ -135,16 +135,16 @@ CompositorAnimationStorage::SetAnimations(uint64_t aId, const AnimationArray& aV
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
AnimationHelper::SampleResult
|
||||
AnimationHelper::SampleAnimationForEachNode(
|
||||
TimeStamp aTime,
|
||||
AnimationArray& aAnimations,
|
||||
InfallibleTArray<AnimData>& aAnimationData,
|
||||
RefPtr<RawServoAnimationValue>& aAnimationValue,
|
||||
bool& aHasInEffectAnimations)
|
||||
RefPtr<RawServoAnimationValue>& aAnimationValue)
|
||||
{
|
||||
MOZ_ASSERT(!aAnimations.IsEmpty(), "Should be called with animations");
|
||||
|
||||
bool hasInEffectAnimations = false;
|
||||
// Process in order, since later aAnimations override earlier ones.
|
||||
for (size_t i = 0, iEnd = aAnimations.Length(); i < iEnd; ++i) {
|
||||
Animation& animation = aAnimations[i];
|
||||
|
@ -175,6 +175,27 @@ AnimationHelper::SampleAnimationForEachNode(
|
|||
continue;
|
||||
}
|
||||
|
||||
dom::IterationCompositeOperation iterCompositeOperation =
|
||||
static_cast<dom::IterationCompositeOperation>(
|
||||
animation.iterationComposite());
|
||||
|
||||
// Skip caluculation if the progress hasn't changed since the last
|
||||
// calculation.
|
||||
// Note that we don't skip calculate this animation if there is another
|
||||
// animation since the other animation might be 'accumulate' or 'add', or
|
||||
// might have a missing keyframe (i.e. this animation value will be used in
|
||||
// the missing keyframe).
|
||||
// FIXME Bug 1455476: We should do this optimizations for the case where
|
||||
// the layer has multiple animations.
|
||||
if (iEnd == 1 &&
|
||||
!dom::KeyframeEffectReadOnly::HasComputedTimingChanged(
|
||||
computedTiming,
|
||||
iterCompositeOperation,
|
||||
animData.mProgressOnLastCompose,
|
||||
animData.mCurrentIterationOnLastCompose)) {
|
||||
return SampleResult::Skipped;
|
||||
}
|
||||
|
||||
uint32_t segmentIndex = 0;
|
||||
size_t segmentSize = animation.segments().Length();
|
||||
AnimationSegment* segment = animation.segments().Elements();
|
||||
|
@ -193,6 +214,20 @@ AnimationHelper::SampleAnimationForEachNode(
|
|||
positionInSegment,
|
||||
computedTiming.mBeforeFlag);
|
||||
|
||||
// Like above optimization, skip caluculation if the target segment isn't
|
||||
// changed and if the portion in the segment isn't changed.
|
||||
// This optimization is needed for CSS animations/transitions with step
|
||||
// timing functions (e.g. the throbber animation on tab or frame based
|
||||
// animations).
|
||||
// FIXME Bug 1455476: Like the above optimization, we should apply this
|
||||
// optimizations for multiple animation cases as well.
|
||||
if (iEnd == 1 &&
|
||||
animData.mSegmentIndexOnLastCompose == segmentIndex &&
|
||||
!animData.mPortionInSegmentOnLastCompose.IsNull() &&
|
||||
animData.mPortionInSegmentOnLastCompose.Value() == portion) {
|
||||
return SampleResult::Skipped;
|
||||
}
|
||||
|
||||
AnimationPropertySegment animSegment;
|
||||
animSegment.mFromKey = 0.0;
|
||||
animSegment.mToKey = 1.0;
|
||||
|
@ -206,10 +241,6 @@ AnimationHelper::SampleAnimationForEachNode(
|
|||
static_cast<dom::CompositeOperation>(segment->endComposite());
|
||||
|
||||
// interpolate the property
|
||||
dom::IterationCompositeOperation iterCompositeOperation =
|
||||
static_cast<dom::IterationCompositeOperation>(
|
||||
animation.iterationComposite());
|
||||
|
||||
aAnimationValue =
|
||||
Servo_ComposeAnimationSegment(
|
||||
&animSegment,
|
||||
|
@ -218,7 +249,11 @@ AnimationHelper::SampleAnimationForEachNode(
|
|||
iterCompositeOperation,
|
||||
portion,
|
||||
computedTiming.mCurrentIteration).Consume();
|
||||
aHasInEffectAnimations = true;
|
||||
hasInEffectAnimations = true;
|
||||
animData.mProgressOnLastCompose = computedTiming.mProgress;
|
||||
animData.mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
|
||||
animData.mSegmentIndexOnLastCompose = segmentIndex;
|
||||
animData.mPortionInSegmentOnLastCompose.SetValue(portion);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -244,6 +279,8 @@ AnimationHelper::SampleAnimationForEachNode(
|
|||
"All of members of TransformData should be the same");
|
||||
}
|
||||
#endif
|
||||
|
||||
return hasInEffectAnimations ? SampleResult::Sampled : SampleResult::None;
|
||||
}
|
||||
|
||||
struct BogusAnimation {};
|
||||
|
@ -512,7 +549,6 @@ AnimationHelper::SampleAnimations(CompositorAnimationStorage* aStorage,
|
|||
//Sample the animations in CompositorAnimationStorage
|
||||
for (auto iter = aStorage->ConstAnimationsTableIter();
|
||||
!iter.Done(); iter.Next()) {
|
||||
bool hasInEffectAnimations = false;
|
||||
AnimationArray* animations = iter.UserData();
|
||||
if (animations->IsEmpty()) {
|
||||
continue;
|
||||
|
@ -523,13 +559,13 @@ AnimationHelper::SampleAnimations(CompositorAnimationStorage* aStorage,
|
|||
AnimationHelper::SetAnimations(*animations,
|
||||
animationData,
|
||||
animationValue);
|
||||
AnimationHelper::SampleAnimationForEachNode(aTime,
|
||||
*animations,
|
||||
animationData,
|
||||
animationValue,
|
||||
hasInEffectAnimations);
|
||||
AnimationHelper::SampleResult sampleResult =
|
||||
AnimationHelper::SampleAnimationForEachNode(aTime,
|
||||
*animations,
|
||||
animationData,
|
||||
animationValue);
|
||||
|
||||
if (!hasInEffectAnimations) {
|
||||
if (sampleResult != AnimationHelper::SampleResult::Sampled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
#ifndef mozilla_layers_AnimationHelper_h
|
||||
#define mozilla_layers_AnimationHelper_h
|
||||
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
|
||||
#include "mozilla/layers/LayersMessages.h" // for TransformData, etc
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp
|
||||
#include "mozilla/TimingParams.h"
|
||||
|
||||
#include "X11UndefineNone.h"
|
||||
|
||||
namespace mozilla {
|
||||
struct AnimationValue;
|
||||
|
@ -25,6 +26,15 @@ struct AnimData {
|
|||
InfallibleTArray<RefPtr<RawServoAnimationValue>> mEndValues;
|
||||
InfallibleTArray<Maybe<mozilla::ComputedTimingFunction>> mFunctions;
|
||||
TimingParams mTiming;
|
||||
// These two variables correspond to the variables of the same name in
|
||||
// KeyframeEffectReadOnly and are used for the same purpose: to skip composing
|
||||
// animations whose progress has not changed.
|
||||
dom::Nullable<double> mProgressOnLastCompose;
|
||||
uint64_t mCurrentIterationOnLastCompose = 0;
|
||||
// These two variables are used for a similar optimization above but are
|
||||
// applied to the timing function in each keyframe.
|
||||
uint32_t mSegmentIndexOnLastCompose = 0;
|
||||
dom::Nullable<double> mPortionInSegmentOnLastCompose;
|
||||
};
|
||||
|
||||
struct AnimationTransform {
|
||||
|
@ -195,16 +205,27 @@ class AnimationHelper
|
|||
{
|
||||
public:
|
||||
|
||||
enum class SampleResult {
|
||||
None,
|
||||
Skipped,
|
||||
Sampled
|
||||
};
|
||||
|
||||
/**
|
||||
* Sample animations based on a given time stamp for a element(layer) with
|
||||
* its animation data.
|
||||
*
|
||||
* Returns SampleResult::None if none of the animations are producing a result
|
||||
* (e.g. they are in the delay phase with no backwards fill),
|
||||
* SampleResult::Skipped if the animation output did not change since the last
|
||||
* call of this function,
|
||||
* SampleResult::Sampled if the animation output was updated.
|
||||
*/
|
||||
static void
|
||||
static SampleResult
|
||||
SampleAnimationForEachNode(TimeStamp aTime,
|
||||
AnimationArray& aAnimations,
|
||||
InfallibleTArray<AnimData>& aAnimationData,
|
||||
RefPtr<RawServoAnimationValue>& aAnimationValue,
|
||||
bool& aHasInEffectAnimations);
|
||||
RefPtr<RawServoAnimationValue>& aAnimationValue);
|
||||
/**
|
||||
* Populates AnimData stuctures into |aAnimData| and |aBaseAnimationStyle|
|
||||
* based on |aAnimations|.
|
||||
|
|
|
@ -660,30 +660,58 @@ SampleAnimations(Layer* aLayer,
|
|||
return;
|
||||
}
|
||||
isAnimating = true;
|
||||
bool hasInEffectAnimations = false;
|
||||
RefPtr<RawServoAnimationValue> animationValue =
|
||||
layer->GetBaseAnimationStyle();
|
||||
AnimationHelper::SampleAnimationForEachNode(aTime,
|
||||
animations,
|
||||
layer->GetAnimationData(),
|
||||
animationValue,
|
||||
hasInEffectAnimations);
|
||||
if (hasInEffectAnimations) {
|
||||
Animation& animation = animations.LastElement();
|
||||
ApplyAnimatedValue(layer,
|
||||
aStorage,
|
||||
animation.property(),
|
||||
animation.data(),
|
||||
animationValue);
|
||||
} else {
|
||||
// Set non-animation values in the case there are no in-effect
|
||||
// animations (i.e. all animations are in delay state or already
|
||||
// finished with non-forward fill modes).
|
||||
HostLayer* layerCompositor = layer->AsHostLayer();
|
||||
layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform());
|
||||
layerCompositor->SetShadowTransformSetByAnimation(false);
|
||||
layerCompositor->SetShadowOpacity(layer->GetOpacity());
|
||||
layerCompositor->SetShadowOpacitySetByAnimation(false);
|
||||
AnimationHelper::SampleResult sampleResult =
|
||||
AnimationHelper::SampleAnimationForEachNode(aTime,
|
||||
animations,
|
||||
layer->GetAnimationData(),
|
||||
animationValue);
|
||||
switch (sampleResult) {
|
||||
case AnimationHelper::SampleResult::Sampled: {
|
||||
Animation& animation = animations.LastElement();
|
||||
ApplyAnimatedValue(layer,
|
||||
aStorage,
|
||||
animation.property(),
|
||||
animation.data(),
|
||||
animationValue);
|
||||
break;
|
||||
}
|
||||
case AnimationHelper::SampleResult::Skipped:
|
||||
// We don't need to update animation values for this layer since
|
||||
// the values haven't changed.
|
||||
#ifdef DEBUG
|
||||
// Sanity check that the animation value is surely unchanged.
|
||||
switch (animations[0].property()) {
|
||||
case eCSSProperty_opacity:
|
||||
MOZ_ASSERT(FuzzyEqualsMultiplicative(
|
||||
layer->AsHostLayer()->GetShadowOpacity(),
|
||||
*(aStorage->GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
|
||||
break;
|
||||
case eCSSProperty_transform: {
|
||||
AnimatedValue* transform =
|
||||
aStorage->GetAnimatedValue(layer->GetCompositorAnimationsId());
|
||||
MOZ_ASSERT(
|
||||
transform->mTransform.mTransformInDevSpace.FuzzyEqualsMultiplicative(
|
||||
(layer->AsHostLayer()->GetShadowBaseTransform())));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unsupported properties");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case AnimationHelper::SampleResult::None: {
|
||||
HostLayer* layerCompositor = layer->AsHostLayer();
|
||||
layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform());
|
||||
layerCompositor->SetShadowTransformSetByAnimation(false);
|
||||
layerCompositor->SetShadowOpacity(layer->GetOpacity());
|
||||
layerCompositor->SetShadowOpacitySetByAnimation(false);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче