diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/CompositorAnimationStorage.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/CompositorAnimationStorage.cpp')
-rw-r--r-- | gfx/layers/CompositorAnimationStorage.cpp | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/gfx/layers/CompositorAnimationStorage.cpp b/gfx/layers/CompositorAnimationStorage.cpp new file mode 100644 index 0000000000..0a004cfa9e --- /dev/null +++ b/gfx/layers/CompositorAnimationStorage.cpp @@ -0,0 +1,674 @@ +/* -*- 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 "CompositorAnimationStorage.h" + +#include "AnimationHelper.h" +#include "mozilla/gfx/MatrixFwd.h" +#include "mozilla/layers/APZSampler.h" // for APZSampler +#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent +#include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder +#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc +#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper +#include "mozilla/layers/OMTAController.h" // for OMTAController +#include "mozilla/ScopeExit.h" +#include "mozilla/ServoStyleConsts.h" +#include "mozilla/webrender/WebRenderTypes.h" // for ToWrTransformProperty, etc +#include "nsDeviceContext.h" // for AppUnitsPerCSSPixel +#include "nsDisplayList.h" // for nsDisplayTransform, etc +#include "nsLayoutUtils.h" +#include "TreeTraversal.h" // for ForEachNode, BreadthFirstSearch + +namespace mozilla { +namespace layers { + +using gfx::Matrix4x4; + +void CompositorAnimationStorage::Clear() { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + // This function should only be called via the non Webrender version of + // SampleAnimations. + mLock.AssertCurrentThreadOwns(); + + mAnimatedValues.Clear(); + mAnimations.clear(); +} + +void CompositorAnimationStorage::ClearById(const uint64_t& aId) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mLock); + + mAnimatedValues.Remove(aId); + mAnimations.erase(aId); +} + +bool CompositorAnimationStorage::HasAnimations() const { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mLock); + + return !mAnimations.empty(); +} + +AnimatedValue* CompositorAnimationStorage::GetAnimatedValue( + const uint64_t& aId) const { + mLock.AssertCurrentThreadOwns(); + + return mAnimatedValues.Get(aId); +} + +OMTAValue CompositorAnimationStorage::GetOMTAValue(const uint64_t& aId) const { + MutexAutoLock lock(mLock); + + OMTAValue omtaValue = mozilla::null_t(); + auto animatedValue = GetAnimatedValue(aId); + if (!animatedValue) { + return omtaValue; + } + + animatedValue->Value().match( + [&](const AnimationTransform& aTransform) { + gfx::Matrix4x4 transform = aTransform.mFrameTransform; + const TransformData& data = aTransform.mData; + float scale = data.appUnitsPerDevPixel(); + gfx::Point3D transformOrigin = data.transformOrigin(); + + // Undo the rebasing applied by + // nsDisplayTransform::GetResultingTransformMatrixInternal + transform.ChangeBasis(-transformOrigin); + + // Convert to CSS pixels (this undoes the operations performed by + // nsStyleTransformMatrix::ProcessTranslatePart which is called from + // nsDisplayTransform::GetResultingTransformMatrix) + double devPerCss = double(scale) / double(AppUnitsPerCSSPixel()); + transform._41 *= devPerCss; + transform._42 *= devPerCss; + transform._43 *= devPerCss; + omtaValue = transform; + }, + [&](const float& aOpacity) { omtaValue = aOpacity; }, + [&](const nscolor& aColor) { omtaValue = aColor; }); + return omtaValue; +} + +void CompositorAnimationStorage::SetAnimatedValueForWebRender( + uint64_t aId, AnimatedValue* aPreviousValue, + const gfx::Matrix4x4& aFrameTransform, const TransformData& aData) { + mLock.AssertCurrentThreadOwns(); + + if (!aPreviousValue) { + MOZ_ASSERT(!mAnimatedValues.Contains(aId)); + mAnimatedValues.Put(aId, MakeUnique<AnimatedValue>(gfx::Matrix4x4(), + aFrameTransform, aData)); + return; + } + MOZ_ASSERT(aPreviousValue->Is<AnimationTransform>()); + MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId)); + + aPreviousValue->SetTransformForWebRender(aFrameTransform, aData); +} + +void CompositorAnimationStorage::SetAnimatedValue( + uint64_t aId, AnimatedValue* aPreviousValue, + const gfx::Matrix4x4& aTransformInDevSpace, + const gfx::Matrix4x4& aFrameTransform, const TransformData& aData) { + mLock.AssertCurrentThreadOwns(); + + if (!aPreviousValue) { + MOZ_ASSERT(!mAnimatedValues.Contains(aId)); + mAnimatedValues.Put(aId, MakeUnique<AnimatedValue>(aTransformInDevSpace, + aFrameTransform, aData)); + return; + } + MOZ_ASSERT(aPreviousValue->Is<AnimationTransform>()); + MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId)); + + aPreviousValue->SetTransform(aTransformInDevSpace, aFrameTransform, aData); +} + +void CompositorAnimationStorage::SetAnimatedValue(uint64_t aId, + AnimatedValue* aPreviousValue, + nscolor aColor) { + mLock.AssertCurrentThreadOwns(); + + if (!aPreviousValue) { + MOZ_ASSERT(!mAnimatedValues.Contains(aId)); + mAnimatedValues.Put(aId, MakeUnique<AnimatedValue>(aColor)); + return; + } + + MOZ_ASSERT(aPreviousValue->Is<nscolor>()); + MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId)); + aPreviousValue->SetColor(aColor); +} + +void CompositorAnimationStorage::SetAnimatedValue(uint64_t aId, + AnimatedValue* aPreviousValue, + float aOpacity) { + mLock.AssertCurrentThreadOwns(); + + if (!aPreviousValue) { + MOZ_ASSERT(!mAnimatedValues.Contains(aId)); + mAnimatedValues.Put(aId, MakeUnique<AnimatedValue>(aOpacity)); + return; + } + + MOZ_ASSERT(aPreviousValue->Is<float>()); + MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId)); + aPreviousValue->SetOpacity(aOpacity); +} + +void CompositorAnimationStorage::SetAnimations(uint64_t aId, + const LayersId& aLayersId, + const AnimationArray& aValue) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MutexAutoLock lock(mLock); + + mAnimations[aId] = std::make_unique<AnimationStorageData>( + AnimationHelper::ExtractAnimations(aLayersId, aValue)); + + // If there is the last animated value, then we need to store the id to remove + // the value if the new animation doesn't produce any animated data (i.e. in + // the delay phase) when we sample this new animation. + if (mAnimatedValues.Contains(aId)) { + mNewAnimations.insert(aId); + } +} + +// Returns clip rect in the scroll frame's coordinate space. +static ParentLayerRect GetClipRectForPartialPrerender( + const LayersId aLayersId, const PartialPrerenderData& aPartialPrerenderData, + const RefPtr<APZSampler>& aSampler) { + if (aSampler && + aPartialPrerenderData.scrollId() != ScrollableLayerGuid::NULL_SCROLL_ID) { + return aSampler->GetCompositionBounds(aLayersId, + aPartialPrerenderData.scrollId()); + } + + return aPartialPrerenderData.clipRect(); +} + +bool CompositorAnimationStorage::SampleAnimations( + const OMTAController* aOMTAController, TimeStamp aPreviousFrameTime, + TimeStamp aCurrentFrameTime) { + MutexAutoLock lock(mLock); + + bool isAnimating = false; + auto cleanup = MakeScopeExit([&] { mNewAnimations.clear(); }); + + // Do nothing if there are no compositor animations + if (mAnimations.empty()) { + return isAnimating; + } + + std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn> janked; + + RefPtr<APZSampler> apzSampler = mCompositorBridge->GetAPZSampler(); + + for (const auto& iter : mAnimations) { + const auto& animationStorageData = iter.second; + if (animationStorageData->mAnimation.IsEmpty()) { + continue; + } + + isAnimating = true; + AutoTArray<RefPtr<RawServoAnimationValue>, 1> animationValues; + AnimatedValue* previousValue = GetAnimatedValue(iter.first); + AnimationHelper::SampleResult sampleResult = + AnimationHelper::SampleAnimationForEachNode( + aPreviousFrameTime, aCurrentFrameTime, previousValue, + animationStorageData->mAnimation, animationValues); + + if (sampleResult != AnimationHelper::SampleResult::Sampled) { + if (mNewAnimations.find(iter.first) != mNewAnimations.end()) { + mAnimatedValues.Remove(iter.first); + } + continue; + } + + const PropertyAnimationGroup& lastPropertyAnimationGroup = + animationStorageData->mAnimation.LastElement(); + + // Store the AnimatedValue + switch (lastPropertyAnimationGroup.mProperty) { + case eCSSProperty_background_color: { + SetAnimatedValue(iter.first, previousValue, + Servo_AnimationValue_GetColor(animationValues[0], + NS_RGBA(0, 0, 0, 0))); + break; + } + case eCSSProperty_opacity: { + MOZ_ASSERT(animationValues.Length() == 1); + SetAnimatedValue(iter.first, previousValue, + Servo_AnimationValue_GetOpacity(animationValues[0])); + break; + } + case eCSSProperty_rotate: + case eCSSProperty_scale: + case eCSSProperty_translate: + case eCSSProperty_transform: + case eCSSProperty_offset_path: + case eCSSProperty_offset_distance: + case eCSSProperty_offset_rotate: + case eCSSProperty_offset_anchor: { + MOZ_ASSERT(animationStorageData->mTransformData); + + const TransformData& transformData = + *animationStorageData->mTransformData; + MOZ_ASSERT(transformData.origin() == nsPoint()); + + gfx::Matrix4x4 frameTransform = + AnimationHelper::ServoAnimationValueToMatrix4x4( + animationValues, transformData, + animationStorageData->mCachedMotionPath); + + if (const Maybe<PartialPrerenderData>& partialPrerenderData = + transformData.partialPrerenderData()) { + gfx::Matrix4x4 transform = frameTransform; + transform.PostTranslate( + partialPrerenderData->position().ToUnknownPoint()); + + gfx::Matrix4x4 transformInClip = + partialPrerenderData->transformInClip(); + if (apzSampler && partialPrerenderData->scrollId() != + ScrollableLayerGuid::NULL_SCROLL_ID) { + AsyncTransform asyncTransform = + apzSampler->GetCurrentAsyncTransform( + animationStorageData->mLayersId, + partialPrerenderData->scrollId(), LayoutAndVisual); + transformInClip.PostTranslate( + asyncTransform.mTranslation.ToUnknownPoint()); + } + transformInClip = transform * transformInClip; + + ParentLayerRect clipRect = + GetClipRectForPartialPrerender(animationStorageData->mLayersId, + *partialPrerenderData, apzSampler); + if (AnimationHelper::ShouldBeJank( + partialPrerenderData->rect(), + partialPrerenderData->overflowedSides(), transformInClip, + clipRect)) { + if (previousValue) { + frameTransform = previousValue->Transform().mFrameTransform; + } + janked[animationStorageData->mLayersId].AppendElement(iter.first); + } + } + + SetAnimatedValueForWebRender(iter.first, previousValue, frameTransform, + transformData); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("Unhandled animated property"); + } + } + + if (!janked.empty() && aOMTAController) { + aOMTAController->NotifyJankedAnimations(std::move(janked)); + } + + return isAnimating; +} + +WrAnimations CompositorAnimationStorage::CollectWebRenderAnimations() const { + MutexAutoLock lock(mLock); + + WrAnimations animations; + + for (auto iter = mAnimatedValues.ConstIter(); !iter.Done(); iter.Next()) { + AnimatedValue* value = iter.UserData(); + value->Value().match( + [&](const AnimationTransform& aTransform) { + animations.mTransformArrays.AppendElement(wr::ToWrTransformProperty( + iter.Key(), aTransform.mFrameTransform)); + }, + [&](const float& aOpacity) { + animations.mOpacityArrays.AppendElement( + wr::ToWrOpacityProperty(iter.Key(), aOpacity)); + }, + [&](const nscolor& aColor) { + animations.mColorArrays.AppendElement(wr::ToWrColorProperty( + iter.Key(), ToDeviceColor(gfx::sRGBColor::FromABGR(aColor)))); + }); + } + + return animations; +} + +static gfx::Matrix4x4 FrameTransformToTransformInDevice( + const gfx::Matrix4x4& aFrameTransform, Layer* aLayer, + const TransformData& aTransformData) { + gfx::Matrix4x4 transformInDevice = aFrameTransform; + // 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( + transformInDevice, aTransformData.origin(), + aTransformData.appUnitsPerDevPixel(), + aLayer->GetContentFlags() & Layer::CONTENT_SNAP_TO_GRID); + } + + if (ContainerLayer* c = aLayer->AsContainerLayer()) { + transformInDevice.PostScale(c->GetInheritedXScale(), + c->GetInheritedYScale(), 1); + } + + return transformInDevice; +} + +static Matrix4x4 GetTransformForPartialPrerender( + Layer* aLayer, const LayersId aLayersId, + const ScrollableLayerGuid::ViewID& aScrollId, + const RefPtr<APZSampler>& aSampler) { + MOZ_ASSERT(aLayer); + + ParentLayerPoint translationByApz; + Matrix4x4 transform; + + for (Layer* layer = aLayer; layer; layer = layer->GetParent()) { + if (layer->AsRefLayer()) { + MOZ_ASSERT(layer->AsRefLayer()->GetReferentId() == aLayersId); + break; + } + + // Accumulate static transforms. + if (layer != aLayer) { + Matrix4x4 baseTransform = layer->GetBaseTransform(); + if (ContainerLayer* container = layer->AsContainerLayer()) { + baseTransform.PostScale(container->GetPreXScale(), + container->GetPreYScale(), 1); + } + transform *= baseTransform; + } + + if (!layer->GetIsStickyPosition() && !layer->GetIsFixedPosition()) { + bool hasSameScrollId = false; + for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) { + // Factor APZ translation if there exists. + if (aSampler) { + LayerMetricsWrapper wrapper = LayerMetricsWrapper(layer, i); + AsyncTransform asyncTransform = + aSampler->GetCurrentAsyncTransform(wrapper, LayoutAndVisual); + translationByApz += asyncTransform.mTranslation; + } + if (layer->GetFrameMetrics(i).GetScrollId() == aScrollId) { + hasSameScrollId = true; + } + } + + if (hasSameScrollId) { + break; + } + } else { + // Bug 1642547: Fix for position:sticky layers. + } + } + + transform.PostTranslate(translationByApz.ToUnknownPoint()); + + return transform; +} + +bool CompositorAnimationStorage::ApplyAnimatedValue( + CompositorBridgeParent* aCompositorBridge, Layer* aLayer, + nsCSSPropertyID aProperty, AnimatedValue* aPreviousValue, + const nsTArray<RefPtr<RawServoAnimationValue>>& aValues) { + mLock.AssertCurrentThreadOwns(); + + MOZ_ASSERT(!aValues.IsEmpty()); + + bool janked = false; + HostLayer* layerCompositor = aLayer->AsHostLayer(); + switch (aProperty) { + case eCSSProperty_background_color: { + MOZ_ASSERT(aValues.Length() == 1); + // We don't support 'color' animations on the compositor yet so we never + // meet currentColor on the compositor. + nscolor color = + Servo_AnimationValue_GetColor(aValues[0], NS_RGBA(0, 0, 0, 0)); + aLayer->AsColorLayer()->SetColor(gfx::ToDeviceColor(color)); + SetAnimatedValue(aLayer->GetCompositorAnimationsId(), aPreviousValue, + color); + + layerCompositor->SetShadowOpacity(aLayer->GetOpacity()); + layerCompositor->SetShadowOpacitySetByAnimation(false); + layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform()); + layerCompositor->SetShadowTransformSetByAnimation(false); + break; + } + case eCSSProperty_opacity: { + MOZ_ASSERT(aValues.Length() == 1); + float opacity = Servo_AnimationValue_GetOpacity(aValues[0]); + layerCompositor->SetShadowOpacity(opacity); + layerCompositor->SetShadowOpacitySetByAnimation(true); + SetAnimatedValue(aLayer->GetCompositorAnimationsId(), aPreviousValue, + opacity); + + layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform()); + layerCompositor->SetShadowTransformSetByAnimation(false); + break; + } + case eCSSProperty_rotate: + case eCSSProperty_scale: + case eCSSProperty_translate: + case eCSSProperty_transform: + case eCSSProperty_offset_path: + case eCSSProperty_offset_distance: + case eCSSProperty_offset_rotate: + case eCSSProperty_offset_anchor: { + MOZ_ASSERT(aLayer->GetTransformData()); + const TransformData& transformData = *aLayer->GetTransformData(); + gfx::Matrix4x4 frameTransform = + AnimationHelper::ServoAnimationValueToMatrix4x4( + aValues, transformData, aLayer->CachedMotionPath()); + + gfx::Matrix4x4 transform = FrameTransformToTransformInDevice( + frameTransform, aLayer, transformData); + if (const Maybe<PartialPrerenderData>& partialPrerenderData = + transformData.partialPrerenderData()) { + Matrix4x4 transformInClip = GetTransformForPartialPrerender( + aLayer, aLayer->GetAnimationLayersId(), + partialPrerenderData->scrollId(), + aCompositorBridge->GetAPZSampler()); + transformInClip = transform * transformInClip; + ParentLayerRect clipRect = GetClipRectForPartialPrerender( + aLayer->GetAnimationLayersId(), *partialPrerenderData, + aCompositorBridge->GetAPZSampler()); + if (AnimationHelper::ShouldBeJank( + partialPrerenderData->rect(), + partialPrerenderData->overflowedSides(), transformInClip, + clipRect)) { + // It's possible that we don't have the previous value and we don't + // either have enough area to composite in the first composition, + // e.g. a translate animation with a step timing function. In such + // cases we use the base transform value which was calculated on the + // main-thread as a fallback value. + transform = aPreviousValue + ? aPreviousValue->Transform().mTransformInDevSpace + : aLayer->GetBaseTransform(); + janked = true; + } + } + + layerCompositor->SetShadowBaseTransform(transform); + layerCompositor->SetShadowTransformSetByAnimation(true); + SetAnimatedValue(aLayer->GetCompositorAnimationsId(), aPreviousValue, + transform, frameTransform, transformData); + + layerCompositor->SetShadowOpacity(aLayer->GetOpacity()); + layerCompositor->SetShadowOpacitySetByAnimation(false); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("Unhandled animated property"); + } + return !janked; +} + +bool CompositorAnimationStorage::SampleAnimations( + Layer* aRoot, CompositorBridgeParent* aCompositorBridge, + TimeStamp aPreviousFrameTime, TimeStamp aCurrentFrameTime) { + MutexAutoLock lock(mLock); + + bool isAnimating = false; + + auto autoClearAnimationStorage = MakeScopeExit([&] { + if (!isAnimating) { + // Clean up the CompositorAnimationStorage because + // there are no active animations running + Clear(); + } + }); + + std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn> janked; + + ForEachNode<ForwardIterator>(aRoot, [&](Layer* layer) { + auto& propertyAnimationGroups = layer->GetPropertyAnimationGroups(); + if (propertyAnimationGroups.IsEmpty()) { + return; + } + + isAnimating = true; + AnimatedValue* previousValue = + GetAnimatedValue(layer->GetCompositorAnimationsId()); + + AutoTArray<RefPtr<RawServoAnimationValue>, 1> animationValues; + AnimationHelper::SampleResult sampleResult = + AnimationHelper::SampleAnimationForEachNode( + aPreviousFrameTime, aCurrentFrameTime, previousValue, + propertyAnimationGroups, animationValues); + + const PropertyAnimationGroup& lastPropertyAnimationGroup = + propertyAnimationGroups.LastElement(); + + switch (sampleResult) { + case AnimationHelper::SampleResult::Sampled: + // We assume all transform like properties (on the same frame) live in + // a single same layer, so using the transform data of the last element + // should be fine. + if (!ApplyAnimatedValue(aCompositorBridge, layer, + lastPropertyAnimationGroup.mProperty, + previousValue, animationValues)) { + // Reset the last composition values in cases of jank so that we will + // never mis-compare in a sanity check in the case of + // SampleResult::Skipped below in this function. + // + // An example; + // a translateX(0px) -> translateX(100px) animation with step(2,start) + // and if the animation janked at translateX(50px) and in a later + // frame if the calculated transform value is going to be still + // translateX(50px) (i.e. at the same timing portion calculated by the + // step timing function), we skip sampling. That's correct ideally. + // But we have an assertion to do a sanity check for the skip sampling + // case that the check compares the calculated value translateX(50px) + // with the previous composited value. In this case the previous + // composited value is translateX(0px). + // + // NOTE: Ideally we shouldn't update the last composition values when + // we met janks, but it's quite hard to tell whether the jank will + // happen or not when we calculate each transform like properties' + // value (i.e. when we set the last composition value) since janks are + // caused by a result of the combinations of all transform like + // properties (e.g. `transform: translateX(50px)` and + // `translate: -50px` results `translateX(0px)`. + for (PropertyAnimationGroup& group : propertyAnimationGroups) { + group.ResetLastCompositionValues(); + } + janked[layer->GetAnimationLayersId()].AppendElement( + layer->GetCompositorAnimationsId()); + } + break; + case AnimationHelper::SampleResult::Skipped: + switch (lastPropertyAnimationGroup.mProperty) { + case eCSSProperty_background_color: + case eCSSProperty_opacity: { + if (lastPropertyAnimationGroup.mProperty == eCSSProperty_opacity) { + MOZ_ASSERT( + layer->AsHostLayer()->GetShadowOpacitySetByAnimation()); +#ifdef DEBUG + // Disable this assertion until the root cause is fixed in bug + // 1459775. + // MOZ_ASSERT(FuzzyEqualsMultiplicative( + // Servo_AnimationValue_GetOpacity(animationValue), + // *(GetAnimationOpacity(layer->GetCompositorAnimationsId())))); +#endif + } + // Even if opacity or background-color animation value has + // unchanged, we have to set the shadow base transform value + // here since the value might have been changed by APZC. + HostLayer* layerCompositor = layer->AsHostLayer(); + layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform()); + layerCompositor->SetShadowTransformSetByAnimation(false); + break; + } + case eCSSProperty_rotate: + case eCSSProperty_scale: + case eCSSProperty_translate: + case eCSSProperty_transform: + case eCSSProperty_offset_path: + case eCSSProperty_offset_distance: + case eCSSProperty_offset_rotate: + case eCSSProperty_offset_anchor: { + MOZ_ASSERT( + layer->AsHostLayer()->GetShadowTransformSetByAnimation()); + MOZ_ASSERT(previousValue); + MOZ_ASSERT(layer->GetTransformData()); +#ifdef DEBUG + gfx::Matrix4x4 frameTransform = + AnimationHelper::ServoAnimationValueToMatrix4x4( + animationValues, *layer->GetTransformData(), + layer->CachedMotionPath()); + gfx::Matrix4x4 transformInDevice = + FrameTransformToTransformInDevice(frameTransform, layer, + *layer->GetTransformData()); + MOZ_ASSERT(previousValue->Transform() + .mTransformInDevSpace.FuzzyEqualsMultiplicative( + transformInDevice)); +#endif + // In the case of transform we have to set the unchanged + // transform value again because APZC might have modified the + // previous shadow base transform value. + HostLayer* layerCompositor = layer->AsHostLayer(); + layerCompositor->SetShadowBaseTransform( + // FIXME: Bug 1459775: It seems possible that we somehow try + // to sample animations and skip it even if the previous value + // has been discarded from the animation storage when we enable + // layer tree cache. So for the safety, in the case where we + // have no previous animation value, we set non-animating value + // instead. + previousValue ? previousValue->Transform().mTransformInDevSpace + : layer->GetBaseTransform()); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("Unsupported properties"); + break; + } + 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; + } + }); + + if (!janked.empty()) { + aCompositorBridge->NotifyJankedAnimations(janked); + } + + return isAnimating; +} + +} // namespace layers +} // namespace mozilla |