[Web Animations] Optimize blending for CSS Transitions
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 Oct 2019 16:14:58 +0000 (16:14 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 Oct 2019 16:14:58 +0000 (16:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=203561

Reviewed by Simon Fraser.

The work performed in KeyframeEffect::setAnimatedPropertiesInStyle() has a level of complexity warranted by the
flexibility of how keyframes can be specified via the Web Animations JS API. However, in the case of CSS Transitions,
we already know that there are only two keyframes, one where offset=0 and one where offset=1, and that only a single
CSS property is specified so we can simplify the process greatly.

To ensure we only perform this quicker blending operation for keyframes computed for a CSS Transition and that no
modification to the keyframes have been applied via the Web Animations JS API after the fact, we now keep track
of whether the blending keyframes (KeyframeList) were generated for a CSS Transition or a CSS Animation and only
use this information to decide whether we're blending for declarative animations.

* animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::processKeyframes):
(WebCore::KeyframeEffect::clearBlendingKeyframes):
(WebCore::KeyframeEffect::computeCSSAnimationBlendingKeyframes):
(WebCore::KeyframeEffect::computeCSSTransitionBlendingKeyframes):
(WebCore::KeyframeEffect::setTarget):
(WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):
* animation/KeyframeEffect.h:

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

Source/WebCore/ChangeLog
Source/WebCore/animation/KeyframeEffect.cpp
Source/WebCore/animation/KeyframeEffect.h

index ee35292..996ed10 100644 (file)
@@ -1,3 +1,29 @@
+2019-10-29  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Optimize blending for CSS Transitions
+        https://bugs.webkit.org/show_bug.cgi?id=203561
+
+        Reviewed by Simon Fraser.
+
+        The work performed in KeyframeEffect::setAnimatedPropertiesInStyle() has a level of complexity warranted by the
+        flexibility of how keyframes can be specified via the Web Animations JS API. However, in the case of CSS Transitions,
+        we already know that there are only two keyframes, one where offset=0 and one where offset=1, and that only a single
+        CSS property is specified so we can simplify the process greatly.
+
+        To ensure we only perform this quicker blending operation for keyframes computed for a CSS Transition and that no
+        modification to the keyframes have been applied via the Web Animations JS API after the fact, we now keep track
+        of whether the blending keyframes (KeyframeList) were generated for a CSS Transition or a CSS Animation and only
+        use this information to decide whether we're blending for declarative animations.
+
+        * animation/KeyframeEffect.cpp:
+        (WebCore::KeyframeEffect::processKeyframes):
+        (WebCore::KeyframeEffect::clearBlendingKeyframes):
+        (WebCore::KeyframeEffect::computeCSSAnimationBlendingKeyframes):
+        (WebCore::KeyframeEffect::computeCSSTransitionBlendingKeyframes):
+        (WebCore::KeyframeEffect::setTarget):
+        (WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):
+        * animation/KeyframeEffect.h:
+
 2019-10-29  Adrian Perez de Castro  <aperez@igalia.com>
 
         [GTK][WPE] Fix non-unified build after r251691
index fadc6d3..4f1d62c 100644 (file)
@@ -726,7 +726,7 @@ ExceptionOr<void> KeyframeEffect::processKeyframes(JSGlobalObject& lexicalGlobal
 
     m_parsedKeyframes = WTFMove(parsedKeyframes);
 
-    m_blendingKeyframes.clear();
+    clearBlendingKeyframes();
 
     return { };
 }
@@ -772,6 +772,13 @@ bool KeyframeEffect::forceLayoutIfNeeded()
     return true;
 }
 
+
+void KeyframeEffect::clearBlendingKeyframes()
+{
+    m_blendingKeyframesSource = BlendingKeyframesSource::WebAnimation;
+    m_blendingKeyframes.clear();
+}
+
 void KeyframeEffect::setBlendingKeyframes(KeyframeList& blendingKeyframes)
 {
     m_blendingKeyframes = WTFMove(blendingKeyframes);
@@ -909,6 +916,7 @@ void KeyframeEffect::computeCSSAnimationBlendingKeyframes()
             Style::loadPendingResources(*style, m_target->document(), m_target.get());
     }
 
+    m_blendingKeyframesSource = BlendingKeyframesSource::CSSAnimation;
     setBlendingKeyframes(keyframeList);
 }
 
@@ -936,6 +944,7 @@ void KeyframeEffect::computeCSSTransitionBlendingKeyframes(const RenderStyle* ol
     toKeyframeValue.addProperty(property);
     keyframeList.insert(WTFMove(toKeyframeValue));
 
+    m_blendingKeyframesSource = BlendingKeyframesSource::CSSTransition;
     setBlendingKeyframes(keyframeList);
 }
 
@@ -988,7 +997,7 @@ void KeyframeEffect::setTarget(RefPtr<Element>&& newTarget)
     if (auto* effectAnimation = animation())
         effectAnimation->effectTargetDidChange(previousTarget.get(), m_target.get());
 
-    m_blendingKeyframes.clear();
+    clearBlendingKeyframes();
 
     // We need to invalidate the effect now that the target has changed
     // to ensure the effect's styles are applied to the new target right away.
@@ -1054,6 +1063,15 @@ void KeyframeEffect::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyl
 
 void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, double iterationProgress)
 {
+    // In the case of CSS Transitions we already know that there are only two keyframes, one where offset=0 and one where offset=1,
+    // and only a single CSS property so we can simply blend based on the style available on those keyframes with the provided iteration
+    // progress which already accounts for the transition's timing function.
+    if (m_blendingKeyframesSource == BlendingKeyframesSource::CSSTransition) {
+        ASSERT(is<CSSTransition>(animation()));
+        CSSPropertyAnimation::blendProperties(this, downcast<CSSTransition>(animation())->property(), &targetStyle, m_blendingKeyframes[0].style(), m_blendingKeyframes[1].style(), iterationProgress);
+        return;
+    }
+
     // 4.4.3. The effect value of a keyframe effect
     // https://drafts.csswg.org/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect
     //
@@ -1064,9 +1082,6 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
     if (m_blendingKeyframes.isEmpty())
         return;
 
-    bool isCSSAnimation = is<CSSAnimation>(animation());
-    bool isCSSTransition = is<CSSTransition>(animation());
-
     for (auto cssPropertyId : m_blendingKeyframes.properties()) {
         // 1. If iteration progress is unresolved abort this procedure.
         // 2. Let target property be the longhand property for which the effect value is to be calculated.
@@ -1085,7 +1100,7 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
             if (!keyframe.containsProperty(cssPropertyId)) {
                 // If we're dealing with a CSS animation, we consider the first and last keyframes to always have the property listed
                 // since the underlying style was provided and should be captured.
-                if (!isCSSAnimation || (offset && offset < 1))
+                if (m_blendingKeyframesSource == BlendingKeyframesSource::WebAnimation || (offset && offset < 1))
                     continue;
             }
             if (!offset)
@@ -1184,10 +1199,8 @@ void KeyframeEffect::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, doub
 
         // 17. Let transformed distance be the result of evaluating the timing function associated with the first keyframe in interval endpoints
         //     passing interval distance as the input progress.
-        // We do not need to do this for CSS Transitions since the timing function is applied to the AnimationEffect as a whole and thus
-        // iterationProgress is already transformed.
         auto transformedDistance = intervalDistance;
-        if (!isCSSTransition && startKeyframeIndex) {
+        if (startKeyframeIndex) {
             if (auto duration = iterationDuration()) {
                 auto rangeDuration = (endOffset - startOffset) * duration.seconds();
                 if (auto* timingFunction = timingFunctionForKeyframeAtIndex(startKeyframeIndex.value()))
index 4b8f4ed..17758aa 100644 (file)
@@ -141,6 +141,7 @@ private:
     KeyframeEffect(Element*);
 
     enum class AcceleratedAction : uint8_t { Play, Pause, Seek, Stop };
+    enum class BlendingKeyframesSource : uint8_t { CSSAnimation, CSSTransition, WebAnimation };
 
     void copyPropertiesFromSource(Ref<KeyframeEffect>&&);
     ExceptionOr<void> processKeyframes(JSC::JSGlobalObject&, JSC::Strong<JSC::JSObject>&&);
@@ -151,6 +152,7 @@ private:
     Ref<const Animation> backingAnimationForCompositedRenderer() const;
     void computedNeedsForcedLayout();
     void computeStackingContextImpact();
+    void clearBlendingKeyframes();
     void updateBlendingKeyframes(RenderStyle&);
     void computeCSSAnimationBlendingKeyframes();
     void computeCSSTransitionBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle);
@@ -170,6 +172,7 @@ private:
     RefPtr<Element> m_target;
 
     AcceleratedAction m_lastRecordedAcceleratedAction { AcceleratedAction::Stop };
+    BlendingKeyframesSource m_blendingKeyframesSource { BlendingKeyframesSource::WebAnimation };
     IterationCompositeOperation m_iterationCompositeOperation { IterationCompositeOperation::Replace };
     CompositeOperation m_compositeOperation { CompositeOperation::Replace };
     bool m_shouldRunAccelerated { false };