[Web Animations] Precompute an animation effect's active duration and end time
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Oct 2019 18:29:56 +0000 (18:29 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Oct 2019 18:29:56 +0000 (18:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=203611

Reviewed by Dean Jackson.

We would compute an animation effect's active duration and end time in AnimationEffect::getBasicTiming()
but these two properties could be computed ahead of time when the other static timing properties of an
animation effect are changed. This allows several calls sites to WebAnimation::effectEndTime() to no
longer require the dynamic computation of all the other timing properties in AnimationEffect::getBasicTiming(),
(local time, active time and phase) which need to be computed dynamically as they rely on the current
timeline time.

* animation/AnimationEffect.cpp:
(WebCore::AnimationEffect::getBasicTiming const):
(WebCore::AnimationEffect::getComputedTiming const):
(WebCore::AnimationEffect::updateTiming):
(WebCore::AnimationEffect::updateStaticTimingProperties):
* animation/AnimationEffect.h:
(WebCore::AnimationEffect::activeDuration const):
(WebCore::AnimationEffect::endTime const):
* animation/CSSAnimation.cpp:
(WebCore::CSSAnimation::syncPropertiesWithBackingAnimation):
* animation/CSSTransition.cpp:
(WebCore::CSSTransition::setTimingProperties):
* animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::copyPropertiesFromSource):
* animation/WebAnimation.cpp:
(WebCore::WebAnimation::effectEndTime const):
(WebCore::WebAnimation::timeToNextTick const):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@251785 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/animation/AnimationEffect.cpp
Source/WebCore/animation/AnimationEffect.h
Source/WebCore/animation/CSSAnimation.cpp
Source/WebCore/animation/CSSTransition.cpp
Source/WebCore/animation/KeyframeEffect.cpp
Source/WebCore/animation/WebAnimation.cpp

index 170a6d9..4b9c328 100644 (file)
@@ -1,3 +1,35 @@
+2019-10-30  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Precompute an animation effect's active duration and end time
+        https://bugs.webkit.org/show_bug.cgi?id=203611
+
+        Reviewed by Dean Jackson.
+
+        We would compute an animation effect's active duration and end time in AnimationEffect::getBasicTiming()
+        but these two properties could be computed ahead of time when the other static timing properties of an
+        animation effect are changed. This allows several calls sites to WebAnimation::effectEndTime() to no
+        longer require the dynamic computation of all the other timing properties in AnimationEffect::getBasicTiming(),
+        (local time, active time and phase) which need to be computed dynamically as they rely on the current
+        timeline time.
+
+        * animation/AnimationEffect.cpp:
+        (WebCore::AnimationEffect::getBasicTiming const):
+        (WebCore::AnimationEffect::getComputedTiming const):
+        (WebCore::AnimationEffect::updateTiming):
+        (WebCore::AnimationEffect::updateStaticTimingProperties):
+        * animation/AnimationEffect.h:
+        (WebCore::AnimationEffect::activeDuration const):
+        (WebCore::AnimationEffect::endTime const):
+        * animation/CSSAnimation.cpp:
+        (WebCore::CSSAnimation::syncPropertiesWithBackingAnimation):
+        * animation/CSSTransition.cpp:
+        (WebCore::CSSTransition::setTimingProperties):
+        * animation/KeyframeEffect.cpp:
+        (WebCore::KeyframeEffect::copyPropertiesFromSource):
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::effectEndTime const):
+        (WebCore::WebAnimation::timeToNextTick const):
+
 2019-10-30  Antti Koivisto  <antti@apple.com>
 
         ::before/::after elements not filling their grid cell when container has display: contents
index f7af6ef..b184973 100644 (file)
@@ -64,27 +64,6 @@ BasicEffectTiming AnimationEffect::getBasicTiming() const
     // to each other a fair bit, so rather than implementing them as individual methods, it's more efficient
     // to return them all as a single BasicEffectTiming.
 
-    auto activeDuration = [this]() -> Seconds {
-        // 3.8.2. Calculating the active duration
-        // https://drafts.csswg.org/web-animations-1/#calculating-the-active-duration
-
-        // The active duration is calculated as follows:
-        // active duration = iteration duration × iteration count
-        // If either the iteration duration or iteration count are zero, the active duration is zero.
-        if (!m_iterationDuration || !m_iterations)
-            return 0_s;
-        return m_iterationDuration * m_iterations;
-    }();
-
-    auto endTime = [this, activeDuration]() -> Seconds {
-        // 3.5.3 The active interval
-        // https://drafts.csswg.org/web-animations-1/#end-time
-
-        // The end time of an animation effect is the result of evaluating max(start delay + active duration + end delay, 0).
-        auto endTime = m_delay + activeDuration + m_endDelay;
-        return endTime > 0_s ? endTime : 0_s;
-    }();
-
     auto localTime = [this]() -> Optional<Seconds> {
         // 4.5.4. Local time
         // https://drafts.csswg.org/web-animations-1/#local-time-section
@@ -97,13 +76,13 @@ BasicEffectTiming AnimationEffect::getBasicTiming() const
         return WTF::nullopt;
     }();
 
-    auto phase = [this, endTime, localTime, activeDuration]() -> AnimationEffectPhase {
+    auto phase = [this, localTime]() -> AnimationEffectPhase {
         // 3.5.5. Animation effect phases and states
         // https://drafts.csswg.org/web-animations-1/#animation-effect-phases-and-states
 
         bool animationIsBackwards = m_animation && m_animation->playbackRate() < 0;
-        auto beforeActiveBoundaryTime = std::max(std::min(m_delay, endTime), 0_s);
-        auto activeAfterBoundaryTime = std::max(std::min(m_delay + activeDuration, endTime), 0_s);
+        auto beforeActiveBoundaryTime = std::max(std::min(m_delay, m_endTime), 0_s);
+        auto activeAfterBoundaryTime = std::max(std::min(m_delay + m_activeDuration, m_endTime), 0_s);
 
         // (This should be the last statement, but it's more efficient to cache the local time and return right away if it's not resolved.)
         // Furthermore, it is often convenient to refer to the case when an animation effect is in none of the above phases
@@ -131,7 +110,7 @@ BasicEffectTiming AnimationEffect::getBasicTiming() const
         return AnimationEffectPhase::Active;
     }();
 
-    auto activeTime = [this, localTime, phase, activeDuration]() -> Optional<Seconds> {
+    auto activeTime = [this, localTime, phase]() -> Optional<Seconds> {
         // 3.8.3.1. Calculating the active time
         // https://drafts.csswg.org/web-animations-1/#calculating-the-active-time
 
@@ -160,7 +139,7 @@ BasicEffectTiming AnimationEffect::getBasicTiming() const
             // If the fill mode is forwards or both, return the result of evaluating
             // max(min(local time - start delay, active duration), 0).
             if (m_fill == FillMode::Forwards || m_fill == FillMode::Both)
-                return std::max(std::min(*localTime - m_delay, activeDuration), 0_s);
+                return std::max(std::min(*localTime - m_delay, m_activeDuration), 0_s);
             // Otherwise, return an unresolved time value.
             return WTF::nullopt;
         }
@@ -169,7 +148,7 @@ BasicEffectTiming AnimationEffect::getBasicTiming() const
         return WTF::nullopt;
     }();
 
-    return { localTime, activeTime, endTime, activeDuration, phase };
+    return { localTime, activeTime, m_endTime, m_activeDuration, phase };
 }
 
 ComputedEffectTiming AnimationEffect::getComputedTiming() const
@@ -180,7 +159,6 @@ ComputedEffectTiming AnimationEffect::getComputedTiming() const
 
     auto basicEffectTiming = getBasicTiming();
     auto activeTime = basicEffectTiming.activeTime;
-    auto activeDuration = basicEffectTiming.activeDuration;
     auto phase = basicEffectTiming.phase;
 
     auto overallProgress = [this, phase, activeTime]() -> Optional<double> {
@@ -210,7 +188,7 @@ ComputedEffectTiming AnimationEffect::getComputedTiming() const
         return std::abs(overallProgress);
     }();
 
-    auto simpleIterationProgress = [this, overallProgress, phase, activeTime, activeDuration]() -> Optional<double> {
+    auto simpleIterationProgress = [this, overallProgress, phase, activeTime]() -> Optional<double> {
         // 3.8.3.3. Calculating the simple iteration progress
         // https://drafts.csswg.org/web-animations-1/#calculating-the-simple-iteration-progress
 
@@ -233,7 +211,7 @@ ComputedEffectTiming AnimationEffect::getComputedTiming() const
         // the active time is equal to the active duration, and
         // the iteration count is not equal to zero.
         // let the simple iteration progress be 1.0.
-        if (!simpleIterationProgress && (phase == AnimationEffectPhase::Active || phase == AnimationEffectPhase::After) && std::abs(activeTime->microseconds() - activeDuration.microseconds()) < timeEpsilon.microseconds() && m_iterations)
+        if (!simpleIterationProgress && (phase == AnimationEffectPhase::Active || phase == AnimationEffectPhase::After) && std::abs(activeTime->microseconds() - m_activeDuration.microseconds()) < timeEpsilon.microseconds() && m_iterations)
             return 1;
 
         return simpleIterationProgress;
@@ -347,8 +325,8 @@ ComputedEffectTiming AnimationEffect::getComputedTiming() const
     computedTiming.duration = secondsToWebAnimationsAPITime(m_iterationDuration);
     computedTiming.direction = m_direction;
     computedTiming.easing = m_timingFunction->cssText();
-    computedTiming.endTime = secondsToWebAnimationsAPITime(basicEffectTiming.endTime);
-    computedTiming.activeDuration = secondsToWebAnimationsAPITime(activeDuration);
+    computedTiming.endTime = secondsToWebAnimationsAPITime(m_endTime);
+    computedTiming.activeDuration = secondsToWebAnimationsAPITime(m_activeDuration);
     if (basicEffectTiming.localTime)
         computedTiming.localTime = secondsToWebAnimationsAPITime(*basicEffectTiming.localTime);
     computedTiming.simpleIterationProgress = simpleIterationProgress;
@@ -432,12 +410,36 @@ ExceptionOr<void> AnimationEffect::updateTiming(Optional<OptionalEffectTiming> t
     if (timing->direction)
         m_direction = timing->direction.value();
 
+    updateStaticTimingProperties();
+
     if (m_animation)
         m_animation->effectTimingDidChange();
 
     return { };
 }
 
+void AnimationEffect::updateStaticTimingProperties()
+{
+    // 3.8.2. Calculating the active duration
+    // https://drafts.csswg.org/web-animations-1/#calculating-the-active-duration
+
+    // The active duration is calculated as follows:
+    // active duration = iteration duration × iteration count
+    // If either the iteration duration or iteration count are zero, the active duration is zero.
+    if (!m_iterationDuration || !m_iterations)
+        m_activeDuration = 0_s;
+    else
+        m_activeDuration = m_iterationDuration * m_iterations;
+
+    // 3.5.3 The active interval
+    // https://drafts.csswg.org/web-animations-1/#end-time
+
+    // The end time of an animation effect is the result of evaluating max(start delay + active duration + end delay, 0).
+    m_endTime = m_delay + m_activeDuration + m_endDelay;
+    if (m_endTime < 0_s)
+        m_endTime = 0_s;
+}
+
 ExceptionOr<void> AnimationEffect::setIterationStart(double iterationStart)
 {
     // https://drafts.csswg.org/web-animations-1/#dom-animationeffecttiming-iterationstart
index 82de742..f3ff3e9 100644 (file)
@@ -90,6 +90,11 @@ public:
     TimingFunction* timingFunction() const { return m_timingFunction.get(); }
     void setTimingFunction(const RefPtr<TimingFunction>&);
 
+    Seconds activeDuration() const { return m_activeDuration; }
+    Seconds endTime() const { return m_endTime; }
+
+    void updateStaticTimingProperties();
+
 protected:
     explicit AnimationEffect();
 
@@ -108,6 +113,8 @@ private:
     Seconds m_delay { 0_s };
     Seconds m_endDelay { 0_s };
     Seconds m_iterationDuration { 0_s };
+    Seconds m_activeDuration { 0_s };
+    Seconds m_endTime { 0_s };
 };
 
 } // namespace WebCore
index 97e94b5..4ecbf03 100644 (file)
@@ -96,6 +96,7 @@ void CSSAnimation::syncPropertiesWithBackingAnimation()
 
     animationEffect->setDelay(Seconds(animation.delay()));
     animationEffect->setIterationDuration(Seconds(animation.duration()));
+    animationEffect->updateStaticTimingProperties();
 
     // Synchronize the play state
     if (animation.playState() == AnimationPlayState::Playing && playState() == WebAnimation::PlayState::Paused) {
index a46c61b..9c7f690 100644 (file)
@@ -73,6 +73,7 @@ void CSSTransition::setTimingProperties(Seconds delay, Seconds duration)
     animationEffect->setDelay(delay);
     animationEffect->setIterationDuration(duration);
     animationEffect->setTimingFunction(backingAnimation().timingFunction());
+    animationEffect->updateStaticTimingProperties();
 
     unsuspendEffectInvalidation();
 }
index 4f1d62c..2398cb3 100644 (file)
@@ -542,6 +542,7 @@ void KeyframeEffect::copyPropertiesFromSource(Ref<KeyframeEffect>&& source)
     setTimingFunction(source->timingFunction());
     setIterationStart(source->iterationStart());
     setIterationDuration(source->iterationDuration());
+    updateStaticTimingProperties();
 
     KeyframeList keyframeList("keyframe-effect-" + createCanonicalUUIDString());
     for (auto& keyframe : source->m_blendingKeyframes.keyframes()) {
index 884ff4c..0fd2cae 100644 (file)
@@ -545,7 +545,7 @@ Seconds WebAnimation::effectEndTime() const
 {
     // The target effect end of an animation is equal to the end time of the animation's target effect.
     // If the animation has no target effect, the target effect end is zero.
-    return m_effect ? m_effect->getBasicTiming().endTime : 0_s;
+    return m_effect ? m_effect->endTime() : 0_s;
 }
 
 void WebAnimation::cancel()
@@ -1291,7 +1291,7 @@ Seconds WebAnimation::timeToNextTick() const
                 return animationEffect->delay() - localTime;
         }
     } else if (auto animationCurrentTime = currentTime())
-        return effect()->getBasicTiming().endTime - *animationCurrentTime;
+        return effect()->endTime() - *animationCurrentTime;
 
     ASSERT_NOT_REACHED();
     return Seconds::infinity();