зеркало из https://github.com/mozilla/gecko-dev.git
370 строки
13 KiB
C++
370 строки
13 KiB
C++
/* -*- 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/. */
|
|
|
|
#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/webrender/WebRenderTypes.h" // for RenderRoot
|
|
#include "mozilla/TimeStamp.h" // for TimeStamp
|
|
#include "mozilla/TimingParams.h"
|
|
#include "mozilla/Variant.h"
|
|
#include "X11UndefineNone.h"
|
|
|
|
namespace mozilla {
|
|
struct AnimationValue;
|
|
|
|
namespace dom {
|
|
enum class CompositeOperation : uint8_t;
|
|
enum class IterationCompositeOperation : uint8_t;
|
|
}; // namespace dom
|
|
|
|
namespace layers {
|
|
class Animation;
|
|
|
|
typedef nsTArray<layers::Animation> AnimationArray;
|
|
|
|
struct PropertyAnimation {
|
|
struct SegmentData {
|
|
RefPtr<RawServoAnimationValue> mStartValue;
|
|
RefPtr<RawServoAnimationValue> mEndValue;
|
|
Maybe<mozilla::ComputedTimingFunction> mFunction;
|
|
float mStartPortion;
|
|
float mEndPortion;
|
|
dom::CompositeOperation mStartComposite;
|
|
dom::CompositeOperation mEndComposite;
|
|
};
|
|
nsTArray<SegmentData> mSegments;
|
|
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;
|
|
|
|
TimeStamp mOriginTime;
|
|
Maybe<TimeDuration> mStartTime;
|
|
TimeDuration mHoldTime;
|
|
float mPlaybackRate;
|
|
dom::IterationCompositeOperation mIterationComposite;
|
|
bool mIsNotPlaying;
|
|
};
|
|
|
|
struct PropertyAnimationGroup {
|
|
nsCSSPropertyID mProperty;
|
|
|
|
nsTArray<PropertyAnimation> mAnimations;
|
|
RefPtr<RawServoAnimationValue> mBaseStyle;
|
|
|
|
bool IsEmpty() const { return mAnimations.IsEmpty(); }
|
|
void Clear() {
|
|
mAnimations.Clear();
|
|
mBaseStyle = nullptr;
|
|
}
|
|
};
|
|
|
|
struct AnimationTransform {
|
|
/*
|
|
* This transform is calculated from sampleanimation in device pixel
|
|
* and used by compositor.
|
|
*/
|
|
gfx::Matrix4x4 mTransformInDevSpace;
|
|
/*
|
|
* This transform is calculated from frame and used by getOMTAStyle()
|
|
* for OMTA testing.
|
|
*/
|
|
gfx::Matrix4x4 mFrameTransform;
|
|
TransformData mData;
|
|
};
|
|
|
|
struct AnimatedValue final {
|
|
typedef Variant<AnimationTransform, float, nscolor> AnimatedValueType;
|
|
|
|
const AnimatedValueType& Value() const { return mValue; }
|
|
const AnimationTransform& Transform() const {
|
|
return mValue.as<AnimationTransform>();
|
|
}
|
|
const float& Opacity() const { return mValue.as<float>(); }
|
|
const nscolor& Color() const { return mValue.as<nscolor>(); }
|
|
template <typename T>
|
|
bool Is() const {
|
|
return mValue.is<T>();
|
|
}
|
|
|
|
AnimatedValue(gfx::Matrix4x4&& aTransformInDevSpace,
|
|
gfx::Matrix4x4&& aFrameTransform, const TransformData& aData)
|
|
: mValue(
|
|
AsVariant(AnimationTransform{std::move(aTransformInDevSpace),
|
|
std::move(aFrameTransform), aData})) {}
|
|
|
|
explicit AnimatedValue(const float& aValue) : mValue(AsVariant(aValue)) {}
|
|
|
|
explicit AnimatedValue(nscolor aValue) : mValue(AsVariant(aValue)) {}
|
|
|
|
private:
|
|
AnimatedValueType mValue;
|
|
};
|
|
|
|
struct CompositorAnimationData {
|
|
Maybe<TransformData> mTransform;
|
|
Maybe<MotionPathData> mMotionPath;
|
|
|
|
bool HasData() const { return mTransform || mMotionPath; }
|
|
void Clear() {
|
|
mTransform.reset();
|
|
mMotionPath.reset();
|
|
}
|
|
};
|
|
|
|
struct AnimationStorageData {
|
|
nsTArray<PropertyAnimationGroup> mAnimation;
|
|
CompositorAnimationData mTransformLikeMetaData;
|
|
RefPtr<gfx::Path> mCachedMotionPath;
|
|
|
|
AnimationStorageData() = default;
|
|
AnimationStorageData(AnimationStorageData&& aOther) = default;
|
|
AnimationStorageData& operator=(AnimationStorageData&& aOther) = default;
|
|
|
|
// Avoid any copy because mAnimation could be a large array.
|
|
AnimationStorageData(const AnimationStorageData& aOther) = delete;
|
|
AnimationStorageData& operator=(const AnimationStorageData& aOther) = delete;
|
|
};
|
|
|
|
// CompositorAnimationStorage stores the animations and animated values
|
|
// keyed by a CompositorAnimationsId. The "animations" are a representation of
|
|
// an entire animation over time, while the "animated values" are values sampled
|
|
// from the animations at a particular point in time.
|
|
//
|
|
// There is one CompositorAnimationStorage per CompositorBridgeParent (i.e.
|
|
// one per browser window), and the CompositorAnimationsId key is unique within
|
|
// a particular CompositorAnimationStorage instance.
|
|
//
|
|
// Each layer which has animations gets a CompositorAnimationsId key, and reuses
|
|
// that key during its lifetime. Likewise, in layers-free webrender, a display
|
|
// item that is animated (e.g. nsDisplayTransform) gets a CompositorAnimationsId
|
|
// key and reuses that key (it persists the key via the frame user-data
|
|
// mechanism).
|
|
class CompositorAnimationStorage final {
|
|
typedef nsClassHashtable<nsUint64HashKey, AnimatedValue> AnimatedValueTable;
|
|
typedef nsDataHashtable<nsUint64HashKey, AnimationStorageData>
|
|
AnimationsTable;
|
|
typedef nsDataHashtable<nsUint64HashKey, wr::RenderRoot>
|
|
AnimationsRenderRootsTable;
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorAnimationStorage)
|
|
public:
|
|
/**
|
|
* Set the animation transform based on the unique id and also
|
|
* set up |aFrameTransform| and |aData| for OMTA testing
|
|
*/
|
|
void SetAnimatedValue(uint64_t aId, gfx::Matrix4x4&& aTransformInDevSpace,
|
|
gfx::Matrix4x4&& aFrameTransform,
|
|
const TransformData& aData);
|
|
|
|
/**
|
|
* Set the animation transform in device pixel based on the unique id
|
|
*/
|
|
void SetAnimatedValue(uint64_t aId, gfx::Matrix4x4&& aTransformInDevSpace);
|
|
|
|
/**
|
|
* Set the animation opacity based on the unique id
|
|
*/
|
|
void SetAnimatedValue(uint64_t aId, const float& aOpacity);
|
|
|
|
/**
|
|
* Set the animation color based on the unique id
|
|
*/
|
|
void SetAnimatedValue(uint64_t aId, nscolor aColor);
|
|
|
|
/**
|
|
* Return the animated value if a given id can map to its animated value
|
|
*/
|
|
AnimatedValue* GetAnimatedValue(const uint64_t& aId) const;
|
|
|
|
OMTAValue GetOMTAValue(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,
|
|
wr::RenderRoot aRenderRoot);
|
|
|
|
/**
|
|
* Return the iterator of animations table
|
|
*/
|
|
AnimationsTable::Iterator ConstAnimationsTableIter() const {
|
|
return mAnimations.ConstIter();
|
|
}
|
|
|
|
uint32_t AnimationsCount() const { return mAnimations.Count(); }
|
|
|
|
wr::RenderRoot AnimationRenderRoot(const uint64_t& aId) const {
|
|
return mAnimationRenderRoots.Get(aId);
|
|
}
|
|
|
|
/**
|
|
* Clear AnimatedValues and Animations data
|
|
*/
|
|
void Clear();
|
|
void ClearById(const uint64_t& aId);
|
|
|
|
private:
|
|
~CompositorAnimationStorage(){};
|
|
|
|
private:
|
|
AnimatedValueTable mAnimatedValues;
|
|
AnimationsTable mAnimations;
|
|
AnimationsRenderRootsTable mAnimationRenderRoots;
|
|
};
|
|
|
|
/**
|
|
* This utility class allows reusing code between the webrender and
|
|
* non-webrender compositor-side implementations. It provides
|
|
* utility functions for sampling animations at particular timestamps.
|
|
*/
|
|
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.
|
|
* Generally |aPreviousFrameTime| is used for the sampling if it's
|
|
* supplied to make the animation more in sync with other animations on the
|
|
* main-thread. But in the case where the animation just started at the time
|
|
* when the animation was sent to the compositor, |aCurrentFrameTime| is used
|
|
* for sampling instead to avoid flicker.
|
|
*
|
|
* 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.
|
|
*
|
|
* Using the same example from ExtractAnimations (below):
|
|
*
|
|
* Input |aPropertyAnimationGroups| (ignoring the base animation style):
|
|
*
|
|
* [
|
|
* Group A: [ { rotate, Animation A }, { rotate, Animation B } ],
|
|
* Group B: [ { scale, Animation B } ],
|
|
* Group C: [ { transform, Animation A }, { transform, Animation B } ],
|
|
* ]
|
|
*
|
|
* For each property group, this function interpolates each animation in turn,
|
|
* using the result of interpolating one animation as input for the next such
|
|
* that it reduces each property group to a single output value:
|
|
*
|
|
* [
|
|
* { rotate, RawServoAnimationValue },
|
|
* { scale, RawServoAnimationValue },
|
|
* { transform, RawServoAnimationValue },
|
|
* ]
|
|
*
|
|
* For transform animations, the caller (SampleAnimations) will combine the
|
|
* result of the various transform properties into a final matrix.
|
|
*/
|
|
static SampleResult SampleAnimationForEachNode(
|
|
TimeStamp aPreviousFrameTime, TimeStamp aCurrentFrameTime,
|
|
const AnimatedValue* aPreviousValue,
|
|
nsTArray<PropertyAnimationGroup>& aPropertyAnimationGroups,
|
|
nsTArray<RefPtr<RawServoAnimationValue>>& aAnimationValues);
|
|
|
|
/**
|
|
* Extract organized animation data by property into an array of
|
|
* PropertyAnimationGroup objects.
|
|
*
|
|
* For example, suppose we have the following animations:
|
|
*
|
|
* Animation A: [ transform, rotate ]
|
|
* Animation B: [ rotate, scale ]
|
|
* Animation C: [ transform ]
|
|
* Animation D: [ opacity ]
|
|
*
|
|
* When we go to send transform-like properties to the compositor, we
|
|
* sort them as follows:
|
|
*
|
|
* [
|
|
* { rotate: Animation A (rotate segments only) },
|
|
* { rotate: Animation B ( " " ) },
|
|
* { scale: Animation B (scale segments only) },
|
|
* { transform: Animation A (transform segments only) },
|
|
* { transform: Animation C ( " " ) },
|
|
* ]
|
|
*
|
|
* In this function, we group these animations together by property producing
|
|
* output such as the following:
|
|
*
|
|
* [
|
|
* [ { rotate, Animation A }, { rotate, Animation B } ],
|
|
* [ { scale, Animation B } ],
|
|
* [ { transform, Animation A }, { transform, Animation B } ],
|
|
* ]
|
|
*
|
|
* In the process of grouping these animations, we also convert their values
|
|
* from the rather compact representation we use for transferring across the
|
|
* IPC boundary into something we can readily use for sampling.
|
|
*/
|
|
static AnimationStorageData ExtractAnimations(
|
|
const AnimationArray& aAnimations);
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Returns true if there is any animation.
|
|
* Note that even if there are only in-delay phase animations (i.e. not
|
|
* visually effective), this function returns true to ensure we composite
|
|
* again on the next tick.
|
|
*
|
|
* Note: This is called only by WebRender.
|
|
*/
|
|
static bool SampleAnimations(CompositorAnimationStorage* aStorage,
|
|
TimeStamp aPreviousFrameTime,
|
|
TimeStamp aCurrentFrameTime);
|
|
|
|
/**
|
|
* Convert an array of animation values into a matrix given the corresponding
|
|
* transform parameters. |aValue| must be a transform-like value
|
|
* (e.g. transform, translate etc.).
|
|
*/
|
|
static gfx::Matrix4x4 ServoAnimationValueToMatrix4x4(
|
|
const nsTArray<RefPtr<RawServoAnimationValue>>& aValue,
|
|
const CompositorAnimationData& aAnimationData,
|
|
gfx::Path* aCachedMotionPath);
|
|
};
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_layers_AnimationHelper_h
|