1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "CompositorAnimationStorage.h"
8 
9 #include "AnimationHelper.h"
10 #include "mozilla/gfx/MatrixFwd.h"
11 #include "mozilla/layers/APZSampler.h"              // for APZSampler
12 #include "mozilla/layers/CompositorBridgeParent.h"  // for CompositorBridgeParent
13 #include "mozilla/layers/CompositorThread.h"       // for CompositorThreadHolder
14 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite, etc
15 #include "mozilla/layers/LayerMetricsWrapper.h"    // for LayerMetricsWrapper
16 #include "mozilla/layers/OMTAController.h"         // for OMTAController
17 #include "mozilla/ScopeExit.h"
18 #include "mozilla/ServoStyleConsts.h"
19 #include "mozilla/webrender/WebRenderTypes.h"  // for ToWrTransformProperty, etc
20 #include "nsDeviceContext.h"                   // for AppUnitsPerCSSPixel
21 #include "nsDisplayList.h"                     // for nsDisplayTransform, etc
22 #include "nsLayoutUtils.h"
23 #include "TreeTraversal.h"  // for ForEachNode, BreadthFirstSearch
24 
25 namespace mozilla {
26 namespace layers {
27 
28 using gfx::Matrix4x4;
29 
Clear()30 void CompositorAnimationStorage::Clear() {
31   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
32   // This function should only be called via the non Webrender version of
33   // SampleAnimations.
34   mLock.AssertCurrentThreadOwns();
35 
36   mAnimatedValues.Clear();
37   mAnimations.clear();
38 }
39 
ClearById(const uint64_t & aId)40 void CompositorAnimationStorage::ClearById(const uint64_t& aId) {
41   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
42   MutexAutoLock lock(mLock);
43 
44   mAnimatedValues.Remove(aId);
45   mAnimations.erase(aId);
46 }
47 
HasAnimations() const48 bool CompositorAnimationStorage::HasAnimations() const {
49   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
50   MutexAutoLock lock(mLock);
51 
52   return !mAnimations.empty();
53 }
54 
GetAnimatedValue(const uint64_t & aId) const55 AnimatedValue* CompositorAnimationStorage::GetAnimatedValue(
56     const uint64_t& aId) const {
57   mLock.AssertCurrentThreadOwns();
58 
59   return mAnimatedValues.Get(aId);
60 }
61 
GetOMTAValue(const uint64_t & aId) const62 OMTAValue CompositorAnimationStorage::GetOMTAValue(const uint64_t& aId) const {
63   MutexAutoLock lock(mLock);
64 
65   OMTAValue omtaValue = mozilla::null_t();
66   auto animatedValue = GetAnimatedValue(aId);
67   if (!animatedValue) {
68     return omtaValue;
69   }
70 
71   animatedValue->Value().match(
72       [&](const AnimationTransform& aTransform) {
73         gfx::Matrix4x4 transform = aTransform.mFrameTransform;
74         const TransformData& data = aTransform.mData;
75         float scale = data.appUnitsPerDevPixel();
76         gfx::Point3D transformOrigin = data.transformOrigin();
77 
78         // Undo the rebasing applied by
79         // nsDisplayTransform::GetResultingTransformMatrixInternal
80         transform.ChangeBasis(-transformOrigin);
81 
82         // Convert to CSS pixels (this undoes the operations performed by
83         // nsStyleTransformMatrix::ProcessTranslatePart which is called from
84         // nsDisplayTransform::GetResultingTransformMatrix)
85         double devPerCss = double(scale) / double(AppUnitsPerCSSPixel());
86         transform._41 *= devPerCss;
87         transform._42 *= devPerCss;
88         transform._43 *= devPerCss;
89         omtaValue = transform;
90       },
91       [&](const float& aOpacity) { omtaValue = aOpacity; },
92       [&](const nscolor& aColor) { omtaValue = aColor; });
93   return omtaValue;
94 }
95 
SetAnimatedValueForWebRender(uint64_t aId,AnimatedValue * aPreviousValue,const gfx::Matrix4x4 & aFrameTransform,const TransformData & aData)96 void CompositorAnimationStorage::SetAnimatedValueForWebRender(
97     uint64_t aId, AnimatedValue* aPreviousValue,
98     const gfx::Matrix4x4& aFrameTransform, const TransformData& aData) {
99   mLock.AssertCurrentThreadOwns();
100 
101   if (!aPreviousValue) {
102     MOZ_ASSERT(!mAnimatedValues.Contains(aId));
103     mAnimatedValues.InsertOrUpdate(
104         aId,
105         MakeUnique<AnimatedValue>(gfx::Matrix4x4(), aFrameTransform, aData));
106     return;
107   }
108   MOZ_ASSERT(aPreviousValue->Is<AnimationTransform>());
109   MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId));
110 
111   aPreviousValue->SetTransformForWebRender(aFrameTransform, aData);
112 }
113 
SetAnimatedValue(uint64_t aId,AnimatedValue * aPreviousValue,const gfx::Matrix4x4 & aTransformInDevSpace,const gfx::Matrix4x4 & aFrameTransform,const TransformData & aData)114 void CompositorAnimationStorage::SetAnimatedValue(
115     uint64_t aId, AnimatedValue* aPreviousValue,
116     const gfx::Matrix4x4& aTransformInDevSpace,
117     const gfx::Matrix4x4& aFrameTransform, const TransformData& aData) {
118   mLock.AssertCurrentThreadOwns();
119 
120   if (!aPreviousValue) {
121     MOZ_ASSERT(!mAnimatedValues.Contains(aId));
122     mAnimatedValues.InsertOrUpdate(
123         aId, MakeUnique<AnimatedValue>(aTransformInDevSpace, aFrameTransform,
124                                        aData));
125     return;
126   }
127   MOZ_ASSERT(aPreviousValue->Is<AnimationTransform>());
128   MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId));
129 
130   aPreviousValue->SetTransform(aTransformInDevSpace, aFrameTransform, aData);
131 }
132 
SetAnimatedValue(uint64_t aId,AnimatedValue * aPreviousValue,nscolor aColor)133 void CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
134                                                   AnimatedValue* aPreviousValue,
135                                                   nscolor aColor) {
136   mLock.AssertCurrentThreadOwns();
137 
138   if (!aPreviousValue) {
139     MOZ_ASSERT(!mAnimatedValues.Contains(aId));
140     mAnimatedValues.InsertOrUpdate(aId, MakeUnique<AnimatedValue>(aColor));
141     return;
142   }
143 
144   MOZ_ASSERT(aPreviousValue->Is<nscolor>());
145   MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId));
146   aPreviousValue->SetColor(aColor);
147 }
148 
SetAnimatedValue(uint64_t aId,AnimatedValue * aPreviousValue,float aOpacity)149 void CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
150                                                   AnimatedValue* aPreviousValue,
151                                                   float aOpacity) {
152   mLock.AssertCurrentThreadOwns();
153 
154   if (!aPreviousValue) {
155     MOZ_ASSERT(!mAnimatedValues.Contains(aId));
156     mAnimatedValues.InsertOrUpdate(aId, MakeUnique<AnimatedValue>(aOpacity));
157     return;
158   }
159 
160   MOZ_ASSERT(aPreviousValue->Is<float>());
161   MOZ_ASSERT(aPreviousValue == GetAnimatedValue(aId));
162   aPreviousValue->SetOpacity(aOpacity);
163 }
164 
SetAnimations(uint64_t aId,const LayersId & aLayersId,const AnimationArray & aValue)165 void CompositorAnimationStorage::SetAnimations(uint64_t aId,
166                                                const LayersId& aLayersId,
167                                                const AnimationArray& aValue) {
168   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
169   MutexAutoLock lock(mLock);
170 
171   mAnimations[aId] = std::make_unique<AnimationStorageData>(
172       AnimationHelper::ExtractAnimations(aLayersId, aValue));
173 
174   // If there is the last animated value, then we need to store the id to remove
175   // the value if the new animation doesn't produce any animated data (i.e. in
176   // the delay phase) when we sample this new animation.
177   if (mAnimatedValues.Contains(aId)) {
178     mNewAnimations.insert(aId);
179   }
180 }
181 
182 // Returns clip rect in the scroll frame's coordinate space.
GetClipRectForPartialPrerender(const LayersId aLayersId,const PartialPrerenderData & aPartialPrerenderData,const RefPtr<APZSampler> & aSampler)183 static ParentLayerRect GetClipRectForPartialPrerender(
184     const LayersId aLayersId, const PartialPrerenderData& aPartialPrerenderData,
185     const RefPtr<APZSampler>& aSampler) {
186   if (aSampler &&
187       aPartialPrerenderData.scrollId() != ScrollableLayerGuid::NULL_SCROLL_ID) {
188     return aSampler->GetCompositionBounds(aLayersId,
189                                           aPartialPrerenderData.scrollId());
190   }
191 
192   return aPartialPrerenderData.clipRect();
193 }
194 
SampleAnimations(const OMTAController * aOMTAController,TimeStamp aPreviousFrameTime,TimeStamp aCurrentFrameTime)195 bool CompositorAnimationStorage::SampleAnimations(
196     const OMTAController* aOMTAController, TimeStamp aPreviousFrameTime,
197     TimeStamp aCurrentFrameTime) {
198   MutexAutoLock lock(mLock);
199 
200   bool isAnimating = false;
201   auto cleanup = MakeScopeExit([&] { mNewAnimations.clear(); });
202 
203   // Do nothing if there are no compositor animations
204   if (mAnimations.empty()) {
205     return isAnimating;
206   }
207 
208   std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn> janked;
209 
210   RefPtr<APZSampler> apzSampler = mCompositorBridge->GetAPZSampler();
211 
212   for (const auto& iter : mAnimations) {
213     const auto& animationStorageData = iter.second;
214     if (animationStorageData->mAnimation.IsEmpty()) {
215       continue;
216     }
217 
218     isAnimating = true;
219     AutoTArray<RefPtr<RawServoAnimationValue>, 1> animationValues;
220     AnimatedValue* previousValue = GetAnimatedValue(iter.first);
221     AnimationHelper::SampleResult sampleResult =
222         AnimationHelper::SampleAnimationForEachNode(
223             aPreviousFrameTime, aCurrentFrameTime, previousValue,
224             animationStorageData->mAnimation, animationValues);
225 
226     if (sampleResult != AnimationHelper::SampleResult::Sampled) {
227       if (mNewAnimations.find(iter.first) != mNewAnimations.end()) {
228         mAnimatedValues.Remove(iter.first);
229       }
230       continue;
231     }
232 
233     const PropertyAnimationGroup& lastPropertyAnimationGroup =
234         animationStorageData->mAnimation.LastElement();
235 
236     // Store the AnimatedValue
237     switch (lastPropertyAnimationGroup.mProperty) {
238       case eCSSProperty_background_color: {
239         SetAnimatedValue(iter.first, previousValue,
240                          Servo_AnimationValue_GetColor(animationValues[0],
241                                                        NS_RGBA(0, 0, 0, 0)));
242         break;
243       }
244       case eCSSProperty_opacity: {
245         MOZ_ASSERT(animationValues.Length() == 1);
246         SetAnimatedValue(iter.first, previousValue,
247                          Servo_AnimationValue_GetOpacity(animationValues[0]));
248         break;
249       }
250       case eCSSProperty_rotate:
251       case eCSSProperty_scale:
252       case eCSSProperty_translate:
253       case eCSSProperty_transform:
254       case eCSSProperty_offset_path:
255       case eCSSProperty_offset_distance:
256       case eCSSProperty_offset_rotate:
257       case eCSSProperty_offset_anchor: {
258         MOZ_ASSERT(animationStorageData->mTransformData);
259 
260         const TransformData& transformData =
261             *animationStorageData->mTransformData;
262         MOZ_ASSERT(transformData.origin() == nsPoint());
263 
264         gfx::Matrix4x4 frameTransform =
265             AnimationHelper::ServoAnimationValueToMatrix4x4(
266                 animationValues, transformData,
267                 animationStorageData->mCachedMotionPath);
268 
269         if (const Maybe<PartialPrerenderData>& partialPrerenderData =
270                 transformData.partialPrerenderData()) {
271           gfx::Matrix4x4 transform = frameTransform;
272           transform.PostTranslate(
273               partialPrerenderData->position().ToUnknownPoint());
274 
275           gfx::Matrix4x4 transformInClip =
276               partialPrerenderData->transformInClip();
277           if (apzSampler && partialPrerenderData->scrollId() !=
278                                 ScrollableLayerGuid::NULL_SCROLL_ID) {
279             AsyncTransform asyncTransform =
280                 apzSampler->GetCurrentAsyncTransform(
281                     animationStorageData->mLayersId,
282                     partialPrerenderData->scrollId(), LayoutAndVisual);
283             transformInClip.PostTranslate(
284                 asyncTransform.mTranslation.ToUnknownPoint());
285           }
286           transformInClip = transform * transformInClip;
287 
288           ParentLayerRect clipRect =
289               GetClipRectForPartialPrerender(animationStorageData->mLayersId,
290                                              *partialPrerenderData, apzSampler);
291           if (AnimationHelper::ShouldBeJank(
292                   partialPrerenderData->rect(),
293                   partialPrerenderData->overflowedSides(), transformInClip,
294                   clipRect)) {
295             if (previousValue) {
296               frameTransform = previousValue->Transform().mFrameTransform;
297             }
298             janked[animationStorageData->mLayersId].AppendElement(iter.first);
299           }
300         }
301 
302         SetAnimatedValueForWebRender(iter.first, previousValue, frameTransform,
303                                      transformData);
304         break;
305       }
306       default:
307         MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
308     }
309   }
310 
311   if (!janked.empty() && aOMTAController) {
312     aOMTAController->NotifyJankedAnimations(std::move(janked));
313   }
314 
315   return isAnimating;
316 }
317 
CollectWebRenderAnimations() const318 WrAnimations CompositorAnimationStorage::CollectWebRenderAnimations() const {
319   MutexAutoLock lock(mLock);
320 
321   WrAnimations animations;
322 
323   for (const auto& animatedValueEntry : mAnimatedValues) {
324     AnimatedValue* value = animatedValueEntry.GetWeak();
325     value->Value().match(
326         [&](const AnimationTransform& aTransform) {
327           animations.mTransformArrays.AppendElement(wr::ToWrTransformProperty(
328               animatedValueEntry.GetKey(), aTransform.mFrameTransform));
329         },
330         [&](const float& aOpacity) {
331           animations.mOpacityArrays.AppendElement(
332               wr::ToWrOpacityProperty(animatedValueEntry.GetKey(), aOpacity));
333         },
334         [&](const nscolor& aColor) {
335           animations.mColorArrays.AppendElement(wr::ToWrColorProperty(
336               animatedValueEntry.GetKey(),
337               ToDeviceColor(gfx::sRGBColor::FromABGR(aColor))));
338         });
339   }
340 
341   return animations;
342 }
343 
FrameTransformToTransformInDevice(const gfx::Matrix4x4 & aFrameTransform,Layer * aLayer,const TransformData & aTransformData)344 static gfx::Matrix4x4 FrameTransformToTransformInDevice(
345     const gfx::Matrix4x4& aFrameTransform, Layer* aLayer,
346     const TransformData& aTransformData) {
347   gfx::Matrix4x4 transformInDevice = aFrameTransform;
348   // If our parent layer is a perspective layer, then the offset into reference
349   // frame coordinates is already on that layer. If not, then we need to ask
350   // for it to be added here.
351   if (!aLayer->GetParent() ||
352       !aLayer->GetParent()->GetTransformIsPerspective()) {
353     nsLayoutUtils::PostTranslate(
354         transformInDevice, aTransformData.origin(),
355         aTransformData.appUnitsPerDevPixel(),
356         aLayer->GetContentFlags() & Layer::CONTENT_SNAP_TO_GRID);
357   }
358 
359   if (ContainerLayer* c = aLayer->AsContainerLayer()) {
360     transformInDevice.PostScale(c->GetInheritedXScale(),
361                                 c->GetInheritedYScale(), 1);
362   }
363 
364   return transformInDevice;
365 }
366 
GetTransformForPartialPrerender(Layer * aLayer,const LayersId aLayersId,const ScrollableLayerGuid::ViewID & aScrollId,const RefPtr<APZSampler> & aSampler)367 static Matrix4x4 GetTransformForPartialPrerender(
368     Layer* aLayer, const LayersId aLayersId,
369     const ScrollableLayerGuid::ViewID& aScrollId,
370     const RefPtr<APZSampler>& aSampler) {
371   MOZ_ASSERT(aLayer);
372 
373   ParentLayerPoint translationByApz;
374   Matrix4x4 transform;
375 
376   for (Layer* layer = aLayer; layer; layer = layer->GetParent()) {
377     if (layer->AsRefLayer()) {
378       MOZ_ASSERT(layer->AsRefLayer()->GetReferentId() == aLayersId);
379       break;
380     }
381 
382     // Accumulate static transforms.
383     if (layer != aLayer) {
384       Matrix4x4 baseTransform = layer->GetBaseTransform();
385       if (ContainerLayer* container = layer->AsContainerLayer()) {
386         baseTransform.PostScale(container->GetPreXScale(),
387                                 container->GetPreYScale(), 1);
388       }
389       transform *= baseTransform;
390     }
391 
392     if (!layer->GetIsStickyPosition() && !layer->GetIsFixedPosition()) {
393       bool hasSameScrollId = false;
394       for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
395         // Factor APZ translation if there exists.
396         if (aSampler) {
397           LayerMetricsWrapper wrapper = LayerMetricsWrapper(layer, i);
398           AsyncTransform asyncTransform =
399               aSampler->GetCurrentAsyncTransform(wrapper, LayoutAndVisual);
400           translationByApz += asyncTransform.mTranslation;
401         }
402         if (layer->GetFrameMetrics(i).GetScrollId() == aScrollId) {
403           hasSameScrollId = true;
404         }
405       }
406 
407       if (hasSameScrollId) {
408         break;
409       }
410     } else {
411       // Bug 1642547: Fix for position:sticky layers.
412     }
413   }
414 
415   transform.PostTranslate(translationByApz.ToUnknownPoint());
416 
417   return transform;
418 }
419 
ApplyAnimatedValue(CompositorBridgeParent * aCompositorBridge,Layer * aLayer,nsCSSPropertyID aProperty,AnimatedValue * aPreviousValue,const nsTArray<RefPtr<RawServoAnimationValue>> & aValues)420 bool CompositorAnimationStorage::ApplyAnimatedValue(
421     CompositorBridgeParent* aCompositorBridge, Layer* aLayer,
422     nsCSSPropertyID aProperty, AnimatedValue* aPreviousValue,
423     const nsTArray<RefPtr<RawServoAnimationValue>>& aValues) {
424   mLock.AssertCurrentThreadOwns();
425 
426   MOZ_ASSERT(!aValues.IsEmpty());
427 
428   bool janked = false;
429   HostLayer* layerCompositor = aLayer->AsHostLayer();
430   switch (aProperty) {
431     case eCSSProperty_background_color: {
432       MOZ_ASSERT(aValues.Length() == 1);
433       // We don't support 'color' animations on the compositor yet so we never
434       // meet currentColor on the compositor.
435       nscolor color =
436           Servo_AnimationValue_GetColor(aValues[0], NS_RGBA(0, 0, 0, 0));
437       aLayer->AsColorLayer()->SetColor(gfx::ToDeviceColor(color));
438       SetAnimatedValue(aLayer->GetCompositorAnimationsId(), aPreviousValue,
439                        color);
440 
441       layerCompositor->SetShadowOpacity(aLayer->GetOpacity());
442       layerCompositor->SetShadowOpacitySetByAnimation(false);
443       layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform());
444       layerCompositor->SetShadowTransformSetByAnimation(false);
445       break;
446     }
447     case eCSSProperty_opacity: {
448       MOZ_ASSERT(aValues.Length() == 1);
449       float opacity = Servo_AnimationValue_GetOpacity(aValues[0]);
450       layerCompositor->SetShadowOpacity(opacity);
451       layerCompositor->SetShadowOpacitySetByAnimation(true);
452       SetAnimatedValue(aLayer->GetCompositorAnimationsId(), aPreviousValue,
453                        opacity);
454 
455       layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform());
456       layerCompositor->SetShadowTransformSetByAnimation(false);
457       break;
458     }
459     case eCSSProperty_rotate:
460     case eCSSProperty_scale:
461     case eCSSProperty_translate:
462     case eCSSProperty_transform:
463     case eCSSProperty_offset_path:
464     case eCSSProperty_offset_distance:
465     case eCSSProperty_offset_rotate:
466     case eCSSProperty_offset_anchor: {
467       MOZ_ASSERT(aLayer->GetTransformData());
468       const TransformData& transformData = *aLayer->GetTransformData();
469       gfx::Matrix4x4 frameTransform =
470           AnimationHelper::ServoAnimationValueToMatrix4x4(
471               aValues, transformData, aLayer->CachedMotionPath());
472 
473       gfx::Matrix4x4 transform = FrameTransformToTransformInDevice(
474           frameTransform, aLayer, transformData);
475       if (const Maybe<PartialPrerenderData>& partialPrerenderData =
476               transformData.partialPrerenderData()) {
477         Matrix4x4 transformInClip = GetTransformForPartialPrerender(
478             aLayer, aLayer->GetAnimationLayersId(),
479             partialPrerenderData->scrollId(),
480             aCompositorBridge->GetAPZSampler());
481         transformInClip = transform * transformInClip;
482         ParentLayerRect clipRect = GetClipRectForPartialPrerender(
483             aLayer->GetAnimationLayersId(), *partialPrerenderData,
484             aCompositorBridge->GetAPZSampler());
485         if (AnimationHelper::ShouldBeJank(
486                 partialPrerenderData->rect(),
487                 partialPrerenderData->overflowedSides(), transformInClip,
488                 clipRect)) {
489           // It's possible that we don't have the previous value and we don't
490           // either have enough area to composite in the first composition,
491           // e.g. a translate animation with a step timing function.  In such
492           // cases we use the base transform value which was calculated on the
493           // main-thread as a fallback value.
494           transform = aPreviousValue
495                           ? aPreviousValue->Transform().mTransformInDevSpace
496                           : aLayer->GetBaseTransform();
497           janked = true;
498         }
499       }
500 
501       layerCompositor->SetShadowBaseTransform(transform);
502       layerCompositor->SetShadowTransformSetByAnimation(true);
503       SetAnimatedValue(aLayer->GetCompositorAnimationsId(), aPreviousValue,
504                        transform, frameTransform, transformData);
505 
506       layerCompositor->SetShadowOpacity(aLayer->GetOpacity());
507       layerCompositor->SetShadowOpacitySetByAnimation(false);
508       break;
509     }
510     default:
511       MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
512   }
513   return !janked;
514 }
515 
SampleAnimations(Layer * aRoot,CompositorBridgeParent * aCompositorBridge,TimeStamp aPreviousFrameTime,TimeStamp aCurrentFrameTime)516 bool CompositorAnimationStorage::SampleAnimations(
517     Layer* aRoot, CompositorBridgeParent* aCompositorBridge,
518     TimeStamp aPreviousFrameTime, TimeStamp aCurrentFrameTime) {
519   MutexAutoLock lock(mLock);
520 
521   bool isAnimating = false;
522 
523   auto autoClearAnimationStorage = MakeScopeExit([&] {
524     if (!isAnimating) {
525       // Clean up the CompositorAnimationStorage because
526       // there are no active animations running
527       Clear();
528     }
529   });
530 
531   std::unordered_map<LayersId, nsTArray<uint64_t>, LayersId::HashFn> janked;
532 
533   ForEachNode<ForwardIterator>(aRoot, [&](Layer* layer) {
534     auto& propertyAnimationGroups = layer->GetPropertyAnimationGroups();
535     if (propertyAnimationGroups.IsEmpty()) {
536       return;
537     }
538 
539     isAnimating = true;
540     AnimatedValue* previousValue =
541         GetAnimatedValue(layer->GetCompositorAnimationsId());
542 
543     AutoTArray<RefPtr<RawServoAnimationValue>, 1> animationValues;
544     AnimationHelper::SampleResult sampleResult =
545         AnimationHelper::SampleAnimationForEachNode(
546             aPreviousFrameTime, aCurrentFrameTime, previousValue,
547             propertyAnimationGroups, animationValues);
548 
549     const PropertyAnimationGroup& lastPropertyAnimationGroup =
550         propertyAnimationGroups.LastElement();
551 
552     switch (sampleResult) {
553       case AnimationHelper::SampleResult::Sampled:
554         // We assume all transform like properties (on the same frame) live in
555         // a single same layer, so using the transform data of the last element
556         // should be fine.
557         if (!ApplyAnimatedValue(aCompositorBridge, layer,
558                                 lastPropertyAnimationGroup.mProperty,
559                                 previousValue, animationValues)) {
560           // Reset the last composition values in cases of jank so that we will
561           // never mis-compare in a sanity check in the case of
562           // SampleResult::Skipped below in this function.
563           //
564           // An example;
565           // a translateX(0px) -> translateX(100px) animation with step(2,start)
566           // and if the animation janked at translateX(50px) and in a later
567           // frame if the calculated transform value is going to be still
568           // translateX(50px) (i.e. at the same timing portion calculated by the
569           // step timing function), we skip sampling. That's correct ideally.
570           // But we have an assertion to do a sanity check for the skip sampling
571           // case that the check compares the calculated value translateX(50px)
572           // with the previous composited value. In this case the previous
573           // composited value is translateX(0px).
574           //
575           // NOTE: Ideally we shouldn't update the last composition values when
576           // we met janks, but it's quite hard to tell whether the jank will
577           // happen or not when we calculate each transform like properties'
578           // value (i.e. when we set the last composition value) since janks are
579           // caused by a result of the combinations of all transform like
580           // properties (e.g. `transform: translateX(50px)` and
581           // `translate: -50px` results `translateX(0px)`.
582           for (PropertyAnimationGroup& group : propertyAnimationGroups) {
583             group.ResetLastCompositionValues();
584           }
585           janked[layer->GetAnimationLayersId()].AppendElement(
586               layer->GetCompositorAnimationsId());
587         }
588         break;
589       case AnimationHelper::SampleResult::Skipped:
590         switch (lastPropertyAnimationGroup.mProperty) {
591           case eCSSProperty_background_color:
592           case eCSSProperty_opacity: {
593             if (lastPropertyAnimationGroup.mProperty == eCSSProperty_opacity) {
594               MOZ_ASSERT(
595                   layer->AsHostLayer()->GetShadowOpacitySetByAnimation());
596 #ifdef DEBUG
597               // Disable this assertion until the root cause is fixed in bug
598               // 1459775.
599               // MOZ_ASSERT(FuzzyEqualsMultiplicative(
600               //   Servo_AnimationValue_GetOpacity(animationValue),
601               //   *(GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
602 #endif
603             }
604             // Even if opacity or background-color  animation value has
605             // unchanged, we have to set the shadow base transform value
606             // here since the value might have been changed by APZC.
607             HostLayer* layerCompositor = layer->AsHostLayer();
608             layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform());
609             layerCompositor->SetShadowTransformSetByAnimation(false);
610             break;
611           }
612           case eCSSProperty_rotate:
613           case eCSSProperty_scale:
614           case eCSSProperty_translate:
615           case eCSSProperty_transform:
616           case eCSSProperty_offset_path:
617           case eCSSProperty_offset_distance:
618           case eCSSProperty_offset_rotate:
619           case eCSSProperty_offset_anchor: {
620             MOZ_ASSERT(
621                 layer->AsHostLayer()->GetShadowTransformSetByAnimation());
622             MOZ_ASSERT(previousValue);
623             MOZ_ASSERT(layer->GetTransformData());
624 #ifdef DEBUG
625             gfx::Matrix4x4 frameTransform =
626                 AnimationHelper::ServoAnimationValueToMatrix4x4(
627                     animationValues, *layer->GetTransformData(),
628                     layer->CachedMotionPath());
629             gfx::Matrix4x4 transformInDevice =
630                 FrameTransformToTransformInDevice(frameTransform, layer,
631                                                   *layer->GetTransformData());
632             MOZ_ASSERT(previousValue->Transform()
633                            .mTransformInDevSpace.FuzzyEqualsMultiplicative(
634                                transformInDevice));
635 #endif
636             // In the case of transform we have to set the unchanged
637             // transform value again because APZC might have modified the
638             // previous shadow base transform value.
639             HostLayer* layerCompositor = layer->AsHostLayer();
640             layerCompositor->SetShadowBaseTransform(
641                 // FIXME: Bug 1459775: It seems possible that we somehow try
642                 // to sample animations and skip it even if the previous value
643                 // has been discarded from the animation storage when we enable
644                 // layer tree cache. So for the safety, in the case where we
645                 // have no previous animation value, we set non-animating value
646                 // instead.
647                 previousValue ? previousValue->Transform().mTransformInDevSpace
648                               : layer->GetBaseTransform());
649             break;
650           }
651           default:
652             MOZ_ASSERT_UNREACHABLE("Unsupported properties");
653             break;
654         }
655         break;
656       case AnimationHelper::SampleResult::None: {
657         HostLayer* layerCompositor = layer->AsHostLayer();
658         layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform());
659         layerCompositor->SetShadowTransformSetByAnimation(false);
660         layerCompositor->SetShadowOpacity(layer->GetOpacity());
661         layerCompositor->SetShadowOpacitySetByAnimation(false);
662         break;
663       }
664       default:
665         break;
666     }
667   });
668 
669   if (!janked.empty()) {
670     aCompositorBridge->NotifyJankedAnimations(janked);
671   }
672 
673   return isAnimating;
674 }
675 
676 }  // namespace layers
677 }  // namespace mozilla
678