Bug 1345017 - Add animation sampling for WR, r?kats
MozReview-Commit-ID: AR2vajUf2o0
--- 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;