Bug 1458457 - Use the current frame time stamp instead of the previous frame's time stamp in the case where the animation was play pending state when the animation was sent to the compositor. r?birtles draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 08 May 2018 08:04:29 +0900
changeset 792272 a8618d698222418faeea1ac69f6deccac049a3cc
parent 792271 00dfea3c59220d639d074f6f481f36fd26a9d905
push id109056
push userhikezoe@mozilla.com
push dateMon, 07 May 2018 23:04:50 +0000
reviewersbirtles
bugs1458457
milestone62.0a1
Bug 1458457 - Use the current frame time stamp instead of the previous frame's time stamp in the case where the animation was play pending state when the animation was sent to the compositor. r?birtles When we send play-pending animation to the compositor, we resolve its start time with the time at the very moment we send the animation to the compositor instead of refresh drivers' tick time. That means that it's possible that the start time indicates more future time stamp than the previous frame time stamp on the compositor. Once this situation happens, we did skip composing the initial animation value on the compositor because we consider the animation hasn't started at the previous frame time stamp so that we failed to do the initial paint for the animation. To prevent the failure, we do explicitly use the current frame time stamp for the initial painting. MozReview-Commit-ID: 8TdZ6m0gqMm
gfx/layers/AnimationHelper.cpp
gfx/layers/AnimationHelper.h
gfx/layers/composite/AsyncCompositionManager.cpp
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -147,17 +147,18 @@ CompositorAnimationStorage::SetAnimation
 
 
 AnimationHelper::SampleResult
 AnimationHelper::SampleAnimationForEachNode(
   TimeStamp aPreviousFrameTime,
   TimeStamp aCurrentFrameTime,
   AnimationArray& aAnimations,
   InfallibleTArray<AnimData>& aAnimationData,
-  RefPtr<RawServoAnimationValue>& aAnimationValue)
+  RefPtr<RawServoAnimationValue>& aAnimationValue,
+  const AnimatedValue* aPreviousValue)
 {
   MOZ_ASSERT(!aAnimations.IsEmpty(), "Should be called with animations");
 
   bool hasInEffectAnimations = false;
 #ifdef DEBUG
   // In cases where this function returns a SampleResult::Skipped, we actually
   // do populate aAnimationValue in debug mode, so that we can MOZ_ASSERT at the
   // call site that the value that would have been computed matches the stored
@@ -172,24 +173,43 @@ AnimationHelper::SampleAnimationForEachN
 
     MOZ_ASSERT((!animation.originTime().IsNull() &&
                 animation.startTime().type() ==
                   MaybeTimeDuration::TTimeDuration) ||
                animation.isNotPlaying(),
                "If we are playing, we should have an origin time and a start"
                " time");
 
+    // We consider the animation just started if all following conditions are
+    // true:
+    //  * We have no previous animation value for this animation (it means that
+    //    this is the first frame for the animation since the animation was
+    //    sent to the compositor)
+    //  * The animation has a resolved start time (i.e. not in delay phase)
+    //  * The start time of the animation at the previous timestamp is behind
+    //    of the current time of the animation
+    const bool wasPlayPending =
+      !aPreviousValue &&
+      animation.startTime().type() != MaybeTimeDuration::Tnull_t &&
+      !aPreviousFrameTime.IsNull() &&
+      (aPreviousFrameTime - animation.originTime() -
+         animation.startTime().get_TimeDuration()) < animation.holdTime();
     // Use a previous vsync time to make main thread animations and compositor
     // more in sync with each other.
     // On the initial frame we use the current frame time here so the timestamp
     // on the second frame are the same as the initial frame, but it does not
     // matter.
-    const TimeStamp& timeStamp = !aPreviousFrameTime.IsNull()
-      ? aPreviousFrameTime
-      : aCurrentFrameTime;
+    const TimeStamp& timeStamp =
+      // If the animation was play-pending state when the animation was sent to
+      // the compositor, we should use the current frame time stamp to avoid
+      // using the past timestamp (aPreviousFrameTime) for the animation that
+      // has just started.
+      aPreviousFrameTime.IsNull() || wasPlayPending
+      ? aCurrentFrameTime
+      : aPreviousFrameTime;
 
     // 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.startTime().type() != MaybeTimeDuration::TTimeDuration
       ? animation.holdTime()
       : (timeStamp - animation.originTime() -
@@ -600,22 +620,24 @@ AnimationHelper::SampleAnimations(Compos
       continue;
     }
 
     RefPtr<RawServoAnimationValue> animationValue;
     InfallibleTArray<AnimData> animationData;
     AnimationHelper::SetAnimations(*animations,
                                    animationData,
                                    animationValue);
+    AnimatedValue* previousValue = aStorage->GetAnimatedValue(iter.Key());
     AnimationHelper::SampleResult sampleResult =
       AnimationHelper::SampleAnimationForEachNode(aPreviousFrameTime,
                                                   aCurrentFrameTime,
                                                   *animations,
                                                   animationData,
-                                                  animationValue);
+                                                  animationValue,
+                                                  previousValue);
 
     if (sampleResult != AnimationHelper::SampleResult::Sampled) {
       continue;
     }
 
     // Store the AnimatedValue
     Animation& animation = animations->LastElement();
     switch (animation.property()) {
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -211,30 +211,33 @@ public:
     Sampled
   };
 
   /**
    * Sample animations based on a given time stamp for a element(layer) with
    * its animation data.
    * Generally |aPreviousFrameTimeStamp| is used for the sampling if it's
    * supplied to make the animation more in sync with other animations on the
-   * main-thread.
+   * main-thread.  But in the case where the animation just started at the time
+   * when the animation was sent to the compositor, |aCurrentTime| is used for
+   * the sampling instead to avoid flickering the animation.
    *
    * 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 SampleResult
   SampleAnimationForEachNode(TimeStamp aPreviousFrameTime,
                              TimeStamp aCurrentFrameTime,
                              AnimationArray& aAnimations,
                              InfallibleTArray<AnimData>& aAnimationData,
-                             RefPtr<RawServoAnimationValue>& aAnimationValue);
+                             RefPtr<RawServoAnimationValue>& aAnimationValue,
+                             const AnimatedValue* aPreviousValue);
   /**
    * Populates AnimData stuctures into |aAnimData| and |aBaseAnimationStyle|
    * based on |aAnimations|.
    */
   static void
   SetAnimations(AnimationArray& aAnimations,
                 InfallibleTArray<AnimData>& aAnimData,
                 RefPtr<RawServoAnimationValue>& aBaseAnimationStyle);
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -682,24 +682,27 @@ SampleAnimations(Layer* aLayer,
       aLayer,
       [&] (Layer* layer)
       {
         AnimationArray& animations = layer->GetAnimations();
         if (animations.IsEmpty()) {
           return;
         }
         isAnimating = true;
+        AnimatedValue* previousValue =
+          aStorage->GetAnimatedValue(layer->GetCompositorAnimationsId());
         RefPtr<RawServoAnimationValue> animationValue =
           layer->GetBaseAnimationStyle();
         AnimationHelper::SampleResult sampleResult =
           AnimationHelper::SampleAnimationForEachNode(aPreviousFrameTime,
                                                       aCurrentFrameTime,
                                                       animations,
                                                       layer->GetAnimationData(),
-                                                      animationValue);
+                                                      animationValue,
+                                                      previousValue);
         switch (sampleResult) {
           case AnimationHelper::SampleResult::Sampled: {
             Animation& animation = animations.LastElement();
             ApplyAnimatedValue(layer,
                                aStorage,
                                animation.property(),
                                animation.data(),
                                animationValue);
@@ -717,29 +720,28 @@ SampleAnimations(Layer* aLayer,
                 MOZ_ASSERT(FuzzyEqualsMultiplicative(
                   Servo_AnimationValue_GetOpacity(animationValue),
                   *(aStorage->GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
                 break;
               case eCSSProperty_transform: {
                 MOZ_ASSERT(
                   layer->AsHostLayer()->GetShadowTransformSetByAnimation());
 
-                AnimatedValue* transform =
-                  aStorage->GetAnimatedValue(layer->GetCompositorAnimationsId());
+                MOZ_ASSERT(previousValue);
 
                 const TransformData& transformData =
                   animations[0].data().get_TransformData();
                 Matrix4x4 frameTransform =
                   ServoAnimationValueToMatrix4x4(animationValue, transformData);
                 Matrix4x4 transformInDevice =
                   FrameTransformToTransformInDevice(frameTransform,
                                                     layer,
                                                     transformData);
                 MOZ_ASSERT(
-                  transform->mTransform.mTransformInDevSpace.FuzzyEqualsMultiplicative(
+                  previousValue->mTransform.mTransformInDevSpace.FuzzyEqualsMultiplicative(
                   transformInDevice));
                 break;
               }
               default:
                 MOZ_ASSERT_UNREACHABLE("Unsupported properties");
                 break;
             }
 #endif