Title: [271827] branches/safari-611-branch
Revision
271827
Author
alanc...@apple.com
Date
2021-01-25 14:11:27 -0800 (Mon, 25 Jan 2021)

Log Message

Cherry-pick r271524. rdar://problem/73473371

    Reversed transform animation not applied alongside other transform animations
    https://bugs.webkit.org/show_bug.cgi?id=218655
    <rdar://problem/71116284>

    Reviewed by Simon Fraser.

    Source/WebCore:

    Tests: webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html
           webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html
           webanimations/combining-transform-animations-with-different-acceleration-capabilities.html

    While, in theory, animations for a transform-related CSS property (translate, rotate, scale and transform)
    can be accelerated, there are various reasons why it might not, in fact, run accelerated.

    One example is that the timing function is not something we can translate in terms Core Animation can
    understand, such as the steps() timing function. In this case, the KeyframeEffect itself is aware of
    the limitation and the method KeyframeEffect::canBeAccelerated() returns false.

    Another example is that the playback rate of the animation is not 1, which we currently don't support for
    Core Animation animations (see bug 211839). In this case, GraphicsLayerCA is where the impossibility to
    run an animation accelerated is determined.

    While we support running transform-related animations with or without acceleration, one thing we cannot
    support is, for the same element, running some transform-related animations with acceleration, and some
    without.

    Thus, regardless of where we determine that a transform-related animation cannot be accelerated, we need
    to send this information up to the KeyframeEffectStack in which this animation's effect belongs to make
    sure that any other transform-related animation that may already be running accelerated no longer does
    and continues running without acceleration.

    There are two locations where we determine that a transform-related animation cannot be accelerated:

        1. in DocumentTimeline::applyPendingAcceleratedAnimations() under which we start, update or stop
           accelerated animations that have been invalidated since the last page rendering,
        2. in KeyframeEffect::updateAcceleratedActions() which is called for each page rendering, including
           animations that cannot be accelerated.

    In the first case, we catch situations where an animation that could have been accelerated but failed
    to be started due to the internal logic of GraphicsLayerCA. We use the new KeyframeEffect method
    applyPendingAcceleratedActions() return value to determine this, and for each effect where the result
    indicates that a transform-related animation could not be accelerated, we add the KeyframeEffectStack
    to which it belongs and, once we're done with updating all effects, call the new
    stopAcceleratingTransformRelatedProperties() method on the keyframe effect stack.

    In the second case, we catch situations where an animation is known to not be able to run accelerated
    even without involving GraphicsLayerCA. We check whether the animation targets a transform-related
    property and if it is active, and if so call stopAcceleratingTransformRelatedProperties()
    on the keyframe effect stack there as well.

    When KeyframeEffectStack::stopAcceleratingTransformRelatedProperties() is called, we go
    through all the registered effects and call stopAcceleratingTransformRelatedProperties(). This new
    KeyframeEffect method will either add a pending accelerated action to stop the accelerated animation,
    or if we're currently apply accelerated actions (ie. during DocumentTimeline::applyPendingAcceleratedAnimations()),
    we stop the accelerated animation right away.

    In both cases we know not to try running this animation again with acceleration by setting m_runningAccelerated
    to RunningAccelerated::No.

    * animation/DocumentTimeline.cpp:
    (WebCore::DocumentTimeline::applyPendingAcceleratedAnimations):
    * animation/KeyframeEffect.cpp:
    (WebCore::KeyframeEffect::isTargetingTransformRelatedProperty const):
    (WebCore::KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation const):
    (WebCore::KeyframeEffect::updateAcceleratedActions):
    (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
    (WebCore::KeyframeEffect::stopAcceleratingTransformRelatedProperties):
    * animation/KeyframeEffect.h:
    * animation/KeyframeEffectStack.cpp:
    (WebCore::KeyframeEffectStack::stopAcceleratingTransformRelatedProperties):
    * animation/KeyframeEffectStack.h:
    * animation/WebAnimation.cpp:
    (WebCore::WebAnimation::applyPendingAcceleratedActions): Deleted.
    * animation/WebAnimation.h:
    * animation/WebAnimationTypes.h:

    LayoutTests:

    Add new tests that start a transform-related animation that runs accelerated, then add another
    transform-related animation that either initially or eventually is not accelerated. In all cases,
    we check that once the second animation is no longer accelerated that the first animation is also
    no longer accelerated.

    * platform/win/TestExpectations:
    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt: Added.
    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html: Added.
    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt: Added.
    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html: Added.
    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt: Added.
    * webanimations/combining-transform-animations-with-different-acceleration-capabilities.html: Added.

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

Modified Paths

Added Paths

Diff

Modified: branches/safari-611-branch/LayoutTests/ChangeLog (271826 => 271827)


--- branches/safari-611-branch/LayoutTests/ChangeLog	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/LayoutTests/ChangeLog	2021-01-25 22:11:27 UTC (rev 271827)
@@ -1,5 +1,125 @@
 2021-01-25  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r271524. rdar://problem/73473371
+
+    Reversed transform animation not applied alongside other transform animations
+    https://bugs.webkit.org/show_bug.cgi?id=218655
+    <rdar://problem/71116284>
+    
+    Reviewed by Simon Fraser.
+    
+    Source/WebCore:
+    
+    Tests: webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html
+           webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html
+           webanimations/combining-transform-animations-with-different-acceleration-capabilities.html
+    
+    While, in theory, animations for a transform-related CSS property (translate, rotate, scale and transform)
+    can be accelerated, there are various reasons why it might not, in fact, run accelerated.
+    
+    One example is that the timing function is not something we can translate in terms Core Animation can
+    understand, such as the steps() timing function. In this case, the KeyframeEffect itself is aware of
+    the limitation and the method KeyframeEffect::canBeAccelerated() returns false.
+    
+    Another example is that the playback rate of the animation is not 1, which we currently don't support for
+    Core Animation animations (see bug 211839). In this case, GraphicsLayerCA is where the impossibility to
+    run an animation accelerated is determined.
+    
+    While we support running transform-related animations with or without acceleration, one thing we cannot
+    support is, for the same element, running some transform-related animations with acceleration, and some
+    without.
+    
+    Thus, regardless of where we determine that a transform-related animation cannot be accelerated, we need
+    to send this information up to the KeyframeEffectStack in which this animation's effect belongs to make
+    sure that any other transform-related animation that may already be running accelerated no longer does
+    and continues running without acceleration.
+    
+    There are two locations where we determine that a transform-related animation cannot be accelerated:
+    
+        1. in DocumentTimeline::applyPendingAcceleratedAnimations() under which we start, update or stop
+           accelerated animations that have been invalidated since the last page rendering,
+        2. in KeyframeEffect::updateAcceleratedActions() which is called for each page rendering, including
+           animations that cannot be accelerated.
+    
+    In the first case, we catch situations where an animation that could have been accelerated but failed
+    to be started due to the internal logic of GraphicsLayerCA. We use the new KeyframeEffect method
+    applyPendingAcceleratedActions() return value to determine this, and for each effect where the result
+    indicates that a transform-related animation could not be accelerated, we add the KeyframeEffectStack
+    to which it belongs and, once we're done with updating all effects, call the new
+    stopAcceleratingTransformRelatedProperties() method on the keyframe effect stack.
+    
+    In the second case, we catch situations where an animation is known to not be able to run accelerated
+    even without involving GraphicsLayerCA. We check whether the animation targets a transform-related
+    property and if it is active, and if so call stopAcceleratingTransformRelatedProperties()
+    on the keyframe effect stack there as well.
+    
+    When KeyframeEffectStack::stopAcceleratingTransformRelatedProperties() is called, we go
+    through all the registered effects and call stopAcceleratingTransformRelatedProperties(). This new
+    KeyframeEffect method will either add a pending accelerated action to stop the accelerated animation,
+    or if we're currently apply accelerated actions (ie. during DocumentTimeline::applyPendingAcceleratedAnimations()),
+    we stop the accelerated animation right away.
+    
+    In both cases we know not to try running this animation again with acceleration by setting m_runningAccelerated
+    to RunningAccelerated::No.
+    
+    * animation/DocumentTimeline.cpp:
+    (WebCore::DocumentTimeline::applyPendingAcceleratedAnimations):
+    * animation/KeyframeEffect.cpp:
+    (WebCore::KeyframeEffect::isTargetingTransformRelatedProperty const):
+    (WebCore::KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation const):
+    (WebCore::KeyframeEffect::updateAcceleratedActions):
+    (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
+    (WebCore::KeyframeEffect::stopAcceleratingTransformRelatedProperties):
+    * animation/KeyframeEffect.h:
+    * animation/KeyframeEffectStack.cpp:
+    (WebCore::KeyframeEffectStack::stopAcceleratingTransformRelatedProperties):
+    * animation/KeyframeEffectStack.h:
+    * animation/WebAnimation.cpp:
+    (WebCore::WebAnimation::applyPendingAcceleratedActions): Deleted.
+    * animation/WebAnimation.h:
+    * animation/WebAnimationTypes.h:
+    
+    LayoutTests:
+    
+    Add new tests that start a transform-related animation that runs accelerated, then add another
+    transform-related animation that either initially or eventually is not accelerated. In all cases,
+    we check that once the second animation is no longer accelerated that the first animation is also
+    no longer accelerated.
+    
+    * platform/win/TestExpectations:
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities.html: Added.
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@271524 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-01-15  Antoine Quint  <grao...@webkit.org>
+
+            Reversed transform animation not applied alongside other transform animations
+            https://bugs.webkit.org/show_bug.cgi?id=218655
+            <rdar://problem/71116284>
+
+            Reviewed by Simon Fraser.
+
+            Add new tests that start a transform-related animation that runs accelerated, then add another
+            transform-related animation that either initially or eventually is not accelerated. In all cases,
+            we check that once the second animation is no longer accelerated that the first animation is also
+            no longer accelerated.
+
+            * platform/win/TestExpectations:
+            * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt: Added.
+            * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html: Added.
+            * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt: Added.
+            * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html: Added.
+            * webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt: Added.
+            * webanimations/combining-transform-animations-with-different-acceleration-capabilities.html: Added.
+
+2021-01-25  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r271446. rdar://problem/73469344
 
     REGRESSION (r257839): Broken focus when 'display' changes in an attribute selector

Modified: branches/safari-611-branch/LayoutTests/platform/win/TestExpectations (271826 => 271827)


--- branches/safari-611-branch/LayoutTests/platform/win/TestExpectations	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/LayoutTests/platform/win/TestExpectations	2021-01-25 22:11:27 UTC (rev 271827)
@@ -4612,3 +4612,8 @@
 storage/indexeddb/shared-memory-structured-clone.html [ Skip ]
 
 webkit.org/b/220536 fast/text/cjk-multi-codepoint-cluster-vertical.html [ ImageOnlyFailure ]
+
+# These tests time out on Windows
+webkit.org/b/220653 webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html [ Skip ]
+webkit.org/b/220653 webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html [ Skip ]
+webkit.org/b/220653 webanimations/combining-transform-animations-with-different-acceleration-capabilities.html [ Skip ]

Added: branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt (0 => 271827)


--- branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt	                        (rev 0)
+++ branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt	2021-01-25 22:11:27 UTC (rev 271827)
@@ -0,0 +1,7 @@
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS internals.acceleratedAnimationsForElement(element).length became 1
+PASS internals.acceleratedAnimationsForElement(element).length became 2
+PASS internals.acceleratedAnimationsForElement(element).length became 0
+

Added: branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html (0 => 271827)


--- branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html	                        (rev 0)
+++ branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html	2021-01-25 22:11:27 UTC (rev 271827)
@@ -0,0 +1,35 @@
+<div id="target" style="width: 100px; height: 100px; background-color: black;"></div>
+<script src=""
+<script>
+
+const element = document.getElementById("target");
+const timing = { duration: 1000, iterations: Infinity };
+
+const shouldBecomeEqualAsync = async (actual, expected) => new Promise(resolve => shouldBecomeEqual(actual, expected, resolve));
+
+(async () => {
+    if (!window.testRunner || !window.internals) {
+        debug("This test should be run in a test runner.");
+        return;
+    }
+
+    testRunner.waitUntilDone();
+
+    // First, start a transform-related animation that can be accelerated.
+    element.animate({ scale: [1, 2] }, timing);
+    await shouldBecomeEqualAsync("internals.acceleratedAnimationsForElement(element).length", "1");
+
+    // Now, start another transform-related animation that can also be accelerated.
+    const rotationAnimation = element.animate({ rotate: ["30deg", "60deg"] }, timing);
+    await shouldBecomeEqualAsync("internals.acceleratedAnimationsForElement(element).length", "2");
+
+    // Now, update the last transform-related animation to a state that cannot be accelerated anymore
+    // due to using a steps() timing function. This should not only prevent this animation from
+    // running accelerated, but also make the first animation revert to a non-accelerated state.
+    rotationAnimation.effect.updateTiming({ easing: "steps(60)" });
+    await shouldBecomeEqualAsync("internals.acceleratedAnimationsForElement(element).length", "0");
+
+    testRunner.notifyDone();
+})();
+    
+</script>

Added: branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt (0 => 271827)


--- branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt	                        (rev 0)
+++ branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt	2021-01-25 22:11:27 UTC (rev 271827)
@@ -0,0 +1,6 @@
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS internals.acceleratedAnimationsForElement(element).length became 1
+PASS internals.acceleratedAnimationsForElement(element).length became 0
+

Added: branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html (0 => 271827)


--- branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html	                        (rev 0)
+++ branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html	2021-01-25 22:11:27 UTC (rev 271827)
@@ -0,0 +1,32 @@
+<div id="target" style="width: 100px; height: 100px; background-color: black;"></div>
+<script src=""
+<script>
+
+const element = document.getElementById("target");
+const duration = 1000;
+const iterations = Infinity;
+
+const shouldBecomeEqualAsync = async (actual, expected) => new Promise(resolve => shouldBecomeEqual(actual, expected, resolve));
+
+(async () => {
+    if (!window.testRunner || !window.internals) {
+        debug("This test should be run in a test runner.");
+        return;
+    }
+
+    testRunner.waitUntilDone();
+
+    // First, start a transform-related animation that can be accelerated.
+    element.animate({ scale: [1, 2] }, { duration, iterations });
+    await shouldBecomeEqualAsync("internals.acceleratedAnimationsForElement(element).length", "1");
+
+    // Now, start another transform-related animation that cannot be accelerated due to using
+    // steps() easing. This should not only prevent this animation from running accelerated,
+    // but also make the previous animation revert to a non-accelerated state.
+    element.animate({ rotate: ["30deg", "60deg"] }, { duration, iterations, easing: "steps(60)" });
+    await shouldBecomeEqualAsync("internals.acceleratedAnimationsForElement(element).length", "0");
+
+    testRunner.notifyDone();
+})();
+    
+</script>

Added: branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt (0 => 271827)


--- branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt	                        (rev 0)
+++ branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt	2021-01-25 22:11:27 UTC (rev 271827)
@@ -0,0 +1,6 @@
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS internals.acceleratedAnimationsForElement(element).length became 1
+PASS internals.acceleratedAnimationsForElement(element).length became 0
+

Added: branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities.html (0 => 271827)


--- branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities.html	                        (rev 0)
+++ branches/safari-611-branch/LayoutTests/webanimations/combining-transform-animations-with-different-acceleration-capabilities.html	2021-01-25 22:11:27 UTC (rev 271827)
@@ -0,0 +1,31 @@
+<div id="target" style="width: 100px; height: 100px; background-color: black;"></div>
+<script src=""
+<script>
+
+const element = document.getElementById("target");
+const timing = { duration: 1000, iterations: Infinity };
+
+const shouldBecomeEqualAsync = async (actual, expected) => new Promise(resolve => shouldBecomeEqual(actual, expected, resolve));
+
+(async () => {
+    if (!window.testRunner || !window.internals) {
+        debug("This test should be run in a test runner.");
+        return;
+    }
+
+    testRunner.waitUntilDone();
+
+    // First, start a transform-related animation that can be accelerated.
+    element.animate({ scale: [1, 2] }, timing);
+    await shouldBecomeEqualAsync("internals.acceleratedAnimationsForElement(element).length", "1");
+
+    // Now, start another transform-related animation that cannot be accelerated due to running at
+    // a speed other than 1. This should not only prevent this animation from running accelerated,
+    // but also make the previous animation revert to a non-accelerated state.
+    element.animate({ rotate: ["30deg", "60deg"] }, timing).updatePlaybackRate(0.5);
+    await shouldBecomeEqualAsync("internals.acceleratedAnimationsForElement(element).length", "0");
+
+    testRunner.notifyDone();
+})();
+    
+</script>

Modified: branches/safari-611-branch/Source/WebCore/ChangeLog (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/ChangeLog	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/ChangeLog	2021-01-25 22:11:27 UTC (rev 271827)
@@ -1,5 +1,181 @@
 2021-01-25  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r271524. rdar://problem/73473371
+
+    Reversed transform animation not applied alongside other transform animations
+    https://bugs.webkit.org/show_bug.cgi?id=218655
+    <rdar://problem/71116284>
+    
+    Reviewed by Simon Fraser.
+    
+    Source/WebCore:
+    
+    Tests: webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html
+           webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html
+           webanimations/combining-transform-animations-with-different-acceleration-capabilities.html
+    
+    While, in theory, animations for a transform-related CSS property (translate, rotate, scale and transform)
+    can be accelerated, there are various reasons why it might not, in fact, run accelerated.
+    
+    One example is that the timing function is not something we can translate in terms Core Animation can
+    understand, such as the steps() timing function. In this case, the KeyframeEffect itself is aware of
+    the limitation and the method KeyframeEffect::canBeAccelerated() returns false.
+    
+    Another example is that the playback rate of the animation is not 1, which we currently don't support for
+    Core Animation animations (see bug 211839). In this case, GraphicsLayerCA is where the impossibility to
+    run an animation accelerated is determined.
+    
+    While we support running transform-related animations with or without acceleration, one thing we cannot
+    support is, for the same element, running some transform-related animations with acceleration, and some
+    without.
+    
+    Thus, regardless of where we determine that a transform-related animation cannot be accelerated, we need
+    to send this information up to the KeyframeEffectStack in which this animation's effect belongs to make
+    sure that any other transform-related animation that may already be running accelerated no longer does
+    and continues running without acceleration.
+    
+    There are two locations where we determine that a transform-related animation cannot be accelerated:
+    
+        1. in DocumentTimeline::applyPendingAcceleratedAnimations() under which we start, update or stop
+           accelerated animations that have been invalidated since the last page rendering,
+        2. in KeyframeEffect::updateAcceleratedActions() which is called for each page rendering, including
+           animations that cannot be accelerated.
+    
+    In the first case, we catch situations where an animation that could have been accelerated but failed
+    to be started due to the internal logic of GraphicsLayerCA. We use the new KeyframeEffect method
+    applyPendingAcceleratedActions() return value to determine this, and for each effect where the result
+    indicates that a transform-related animation could not be accelerated, we add the KeyframeEffectStack
+    to which it belongs and, once we're done with updating all effects, call the new
+    stopAcceleratingTransformRelatedProperties() method on the keyframe effect stack.
+    
+    In the second case, we catch situations where an animation is known to not be able to run accelerated
+    even without involving GraphicsLayerCA. We check whether the animation targets a transform-related
+    property and if it is active, and if so call stopAcceleratingTransformRelatedProperties()
+    on the keyframe effect stack there as well.
+    
+    When KeyframeEffectStack::stopAcceleratingTransformRelatedProperties() is called, we go
+    through all the registered effects and call stopAcceleratingTransformRelatedProperties(). This new
+    KeyframeEffect method will either add a pending accelerated action to stop the accelerated animation,
+    or if we're currently apply accelerated actions (ie. during DocumentTimeline::applyPendingAcceleratedAnimations()),
+    we stop the accelerated animation right away.
+    
+    In both cases we know not to try running this animation again with acceleration by setting m_runningAccelerated
+    to RunningAccelerated::No.
+    
+    * animation/DocumentTimeline.cpp:
+    (WebCore::DocumentTimeline::applyPendingAcceleratedAnimations):
+    * animation/KeyframeEffect.cpp:
+    (WebCore::KeyframeEffect::isTargetingTransformRelatedProperty const):
+    (WebCore::KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation const):
+    (WebCore::KeyframeEffect::updateAcceleratedActions):
+    (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
+    (WebCore::KeyframeEffect::stopAcceleratingTransformRelatedProperties):
+    * animation/KeyframeEffect.h:
+    * animation/KeyframeEffectStack.cpp:
+    (WebCore::KeyframeEffectStack::stopAcceleratingTransformRelatedProperties):
+    * animation/KeyframeEffectStack.h:
+    * animation/WebAnimation.cpp:
+    (WebCore::WebAnimation::applyPendingAcceleratedActions): Deleted.
+    * animation/WebAnimation.h:
+    * animation/WebAnimationTypes.h:
+    
+    LayoutTests:
+    
+    Add new tests that start a transform-related animation that runs accelerated, then add another
+    transform-related animation that either initially or eventually is not accelerated. In all cases,
+    we check that once the second animation is no longer accelerated that the first animation is also
+    no longer accelerated.
+    
+    * platform/win/TestExpectations:
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt: Added.
+    * webanimations/combining-transform-animations-with-different-acceleration-capabilities.html: Added.
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@271524 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-01-15  Antoine Quint  <grao...@webkit.org>
+
+            Reversed transform animation not applied alongside other transform animations
+            https://bugs.webkit.org/show_bug.cgi?id=218655
+            <rdar://problem/71116284>
+
+            Reviewed by Simon Fraser.
+
+            Tests: webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html
+                   webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html
+                   webanimations/combining-transform-animations-with-different-acceleration-capabilities.html
+
+            While, in theory, animations for a transform-related CSS property (translate, rotate, scale and transform)
+            can be accelerated, there are various reasons why it might not, in fact, run accelerated.
+
+            One example is that the timing function is not something we can translate in terms Core Animation can
+            understand, such as the steps() timing function. In this case, the KeyframeEffect itself is aware of
+            the limitation and the method KeyframeEffect::canBeAccelerated() returns false.
+
+            Another example is that the playback rate of the animation is not 1, which we currently don't support for
+            Core Animation animations (see bug 211839). In this case, GraphicsLayerCA is where the impossibility to
+            run an animation accelerated is determined.
+
+            While we support running transform-related animations with or without acceleration, one thing we cannot
+            support is, for the same element, running some transform-related animations with acceleration, and some
+            without.
+
+            Thus, regardless of where we determine that a transform-related animation cannot be accelerated, we need
+            to send this information up to the KeyframeEffectStack in which this animation's effect belongs to make
+            sure that any other transform-related animation that may already be running accelerated no longer does
+            and continues running without acceleration.
+
+            There are two locations where we determine that a transform-related animation cannot be accelerated:
+
+                1. in DocumentTimeline::applyPendingAcceleratedAnimations() under which we start, update or stop
+                   accelerated animations that have been invalidated since the last page rendering,
+                2. in KeyframeEffect::updateAcceleratedActions() which is called for each page rendering, including
+                   animations that cannot be accelerated.
+
+            In the first case, we catch situations where an animation that could have been accelerated but failed
+            to be started due to the internal logic of GraphicsLayerCA. We use the new KeyframeEffect method
+            applyPendingAcceleratedActions() return value to determine this, and for each effect where the result
+            indicates that a transform-related animation could not be accelerated, we add the KeyframeEffectStack
+            to which it belongs and, once we're done with updating all effects, call the new
+            stopAcceleratingTransformRelatedProperties() method on the keyframe effect stack.
+
+            In the second case, we catch situations where an animation is known to not be able to run accelerated
+            even without involving GraphicsLayerCA. We check whether the animation targets a transform-related
+            property and if it is active, and if so call stopAcceleratingTransformRelatedProperties()
+            on the keyframe effect stack there as well.
+
+            When KeyframeEffectStack::stopAcceleratingTransformRelatedProperties() is called, we go
+            through all the registered effects and call stopAcceleratingTransformRelatedProperties(). This new
+            KeyframeEffect method will either add a pending accelerated action to stop the accelerated animation,
+            or if we're currently apply accelerated actions (ie. during DocumentTimeline::applyPendingAcceleratedAnimations()),
+            we stop the accelerated animation right away.
+
+            In both cases we know not to try running this animation again with acceleration by setting m_runningAccelerated
+            to RunningAccelerated::No.
+
+            * animation/DocumentTimeline.cpp:
+            (WebCore::DocumentTimeline::applyPendingAcceleratedAnimations):
+            * animation/KeyframeEffect.cpp:
+            (WebCore::KeyframeEffect::isTargetingTransformRelatedProperty const):
+            (WebCore::KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation const):
+            (WebCore::KeyframeEffect::updateAcceleratedActions):
+            (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
+            (WebCore::KeyframeEffect::stopAcceleratingTransformRelatedProperties):
+            * animation/KeyframeEffect.h:
+            * animation/KeyframeEffectStack.cpp:
+            (WebCore::KeyframeEffectStack::stopAcceleratingTransformRelatedProperties):
+            * animation/KeyframeEffectStack.h:
+            * animation/WebAnimation.cpp:
+            (WebCore::WebAnimation::applyPendingAcceleratedActions): Deleted.
+            * animation/WebAnimation.h:
+            * animation/WebAnimationTypes.h:
+
+2021-01-25  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r271446. rdar://problem/73469344
 
     REGRESSION (r257839): Broken focus when 'display' changes in an attribute selector

Modified: branches/safari-611-branch/Source/WebCore/animation/DocumentTimeline.cpp (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/DocumentTimeline.cpp	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/DocumentTimeline.cpp	2021-01-25 22:11:27 UTC (rev 271827)
@@ -41,6 +41,7 @@
 #include "RenderElement.h"
 #include "RenderLayer.h"
 #include "RenderLayerBacking.h"
+#include "WebAnimationTypes.h"
 
 namespace WebCore {
 
@@ -442,15 +443,32 @@
     auto acceleratedAnimationsPendingRunningStateChange = m_acceleratedAnimationsPendingRunningStateChange;
     m_acceleratedAnimationsPendingRunningStateChange.clear();
 
+    // Animations may fail to run accelerated for reasons private to GraphicsLayerCA. If that happens, and the animation
+    // in question targets a transform-related property, we must prevent all other transform-related animations for this
+    // element to run accelerated since we can't run some transform-related animations accelerated, and some not. To do
+    // this, we keep a list of all KeyframeEffectStack objects containing an effect that failed to start a transform-related
+    // animation so that we can return any transform-related accelerated animation to run non-accelerated.
+    HashSet<KeyframeEffectStack*> effectStacksContainingEffectThatFailedToRunAcceleratedTransformRelatedAnimation;
+
     bool hasForcedLayout = false;
     for (auto& animation : acceleratedAnimationsPendingRunningStateChange) {
-        if (!hasForcedLayout) {
-            auto* effect = animation->effect();
-            if (is<KeyframeEffect>(effect))
-                hasForcedLayout |= downcast<KeyframeEffect>(effect)->forceLayoutIfNeeded();
+        auto* effect = animation->effect();
+        if (!is<KeyframeEffect>(effect))
+            continue;
+
+        auto& keyframeEffect = downcast<KeyframeEffect>(*effect);
+        if (!hasForcedLayout)
+            hasForcedLayout |= keyframeEffect.forceLayoutIfNeeded();
+        auto pendingAccelerationActionResult = keyframeEffect.applyPendingAcceleratedActions();
+        if (pendingAccelerationActionResult.contains(AcceleratedActionApplicationResult::TransformRelatedAnimationCannotBeAccelerated)) {
+            ASSERT(keyframeEffect.targetStyleable());
+            ASSERT(keyframeEffect.targetStyleable()->keyframeEffectStack());
+            effectStacksContainingEffectThatFailedToRunAcceleratedTransformRelatedAnimation.add(keyframeEffect.targetStyleable()->keyframeEffectStack());
         }
-        animation->applyPendingAcceleratedActions();
     }
+
+    for (auto& effectStack : effectStacksContainingEffectThatFailedToRunAcceleratedTransformRelatedAnimation)
+        effectStack->stopAcceleratingTransformRelatedProperties(UseAcceleratedAction::No);
 }
 
 bool DocumentTimeline::runningAnimationsForRendererAreAllAccelerated(const RenderBoxModelObject& renderer) const

Modified: branches/safari-611-branch/Source/WebCore/animation/KeyframeEffect.cpp (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/KeyframeEffect.cpp	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/KeyframeEffect.cpp	2021-01-25 22:11:27 UTC (rev 271827)
@@ -1281,11 +1281,8 @@
     return isRunningAccelerated() && CSSPropertyAnimation::animationOfPropertyIsAccelerated(property) && m_blendingKeyframes.properties().contains(property);
 }
 
-bool KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation() const
+bool KeyframeEffect::isTargetingTransformRelatedProperty() const
 {
-    if (!isRunningAccelerated())
-        return false;
-
     return m_blendingKeyframes.properties().contains(CSSPropertyTranslate)
         || m_blendingKeyframes.properties().contains(CSSPropertyScale)
         || m_blendingKeyframes.properties().contains(CSSPropertyRotate)
@@ -1292,6 +1289,11 @@
         || m_blendingKeyframes.properties().contains(CSSPropertyTransform);
 }
 
+bool KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation() const
+{
+    return isRunningAccelerated() && isTargetingTransformRelatedProperty();
+}
+
 void KeyframeEffect::invalidate()
 {
     LOG_WITH_STREAM(Animations, stream << "KeyframeEffect::invalidate on element " << ValueOrNull(targetElementOrPseudoElement()));
@@ -1574,8 +1576,19 @@
 
 void KeyframeEffect::updateAcceleratedActions()
 {
-    if (!canBeAccelerated())
+    if (!canBeAccelerated()) {
+        // In the case where this animation is actively targeting a transform-related property and yet
+        // cannot be accelerated, we must notify the effect stack such that any running accelerated
+        // transform-related animation targeting this element reverts to running non-accelerated.
+        if (isTargetingTransformRelatedProperty()
+            && animation()->playState() == WebAnimation::PlayState::Running
+            && getComputedTiming().phase == AnimationEffectPhase::Active) {
+            ASSERT(targetStyleable());
+            ASSERT(targetStyleable()->keyframeEffectStack());
+            targetStyleable()->keyframeEffectStack()->stopAcceleratingTransformRelatedProperties(UseAcceleratedAction::Yes);
+        }
         return;
+    }
 
     auto computedTiming = getComputedTiming();
 
@@ -1667,8 +1680,10 @@
         addPendingAcceleratedAction(animationIsSuspended ? AcceleratedAction::Pause : AcceleratedAction::Play);
 }
 
-void KeyframeEffect::applyPendingAcceleratedActions()
+OptionSet<AcceleratedActionApplicationResult> KeyframeEffect::applyPendingAcceleratedActions()
 {
+    OptionSet<AcceleratedActionApplicationResult> result;
+
     // Once an accelerated animation has been committed, we no longer want to force a layout.
     // This should have been performed by a call to forceLayoutIfNeeded() prior to applying
     // pending accelerated actions.
@@ -1675,7 +1690,7 @@
     m_needsForcedLayout = false;
 
     if (m_pendingAcceleratedActions.isEmpty())
-        return;
+        return result;
 
     auto* renderer = this->renderer();
     if (!renderer || !renderer->isComposited()) {
@@ -1685,7 +1700,7 @@
             m_pendingAcceleratedActions.clear();
             m_runningAccelerated = RunningAccelerated::NotStarted;
         }
-        return;
+        return result;
     }
 
     auto pendingAcceleratedActions = m_pendingAcceleratedActions;
@@ -1719,7 +1734,9 @@
             LOG_WITH_STREAM(Animations, stream << "KeyframeEffect " << this << " applyPendingAcceleratedActions " << m_blendingKeyframes.animationName() << " Play, started accelerated: " << (m_runningAccelerated == RunningAccelerated::Yes));
             if (m_runningAccelerated == RunningAccelerated::No) {
                 m_lastRecordedAcceleratedAction = AcceleratedAction::Stop;
-                return;
+                if (isTargetingTransformRelatedProperty())
+                    result.add(AcceleratedActionApplicationResult::TransformRelatedAnimationCannotBeAccelerated);
+                return result;
             }
             break;
         case AcceleratedAction::Pause:
@@ -1736,7 +1753,7 @@
             renderer->animationFinished(m_blendingKeyframes.animationName());
             if (!document()->renderTreeBeingDestroyed())
                 m_target->invalidateStyleAndLayerComposition();
-            m_runningAccelerated = RunningAccelerated::NotStarted;
+            m_runningAccelerated = canBeAccelerated() ? RunningAccelerated::NotStarted : RunningAccelerated::No;
             break;
         case AcceleratedAction::TransformChange:
             renderer->transformRelatedPropertyDidChange();
@@ -1743,8 +1760,35 @@
             break;
         }
     }
+
+    if (m_runningAccelerated == RunningAccelerated::No && isTargetingTransformRelatedProperty())
+        result.add(AcceleratedActionApplicationResult::TransformRelatedAnimationCannotBeAccelerated);
+
+    return result;
 }
 
+void KeyframeEffect::stopAcceleratingTransformRelatedProperties(UseAcceleratedAction useAcceleratedAction)
+{
+    if (!isRunningAcceleratedTransformRelatedAnimation())
+        return;
+
+    if (useAcceleratedAction == UseAcceleratedAction::Yes) {
+        addPendingAcceleratedAction(AcceleratedAction::Stop);
+        return;
+    }
+
+    auto* renderer = this->renderer();
+    if (!renderer || !renderer->isComposited())
+        return;
+
+    ASSERT(document());
+    renderer->animationFinished(m_blendingKeyframes.animationName());
+    if (!document()->renderTreeBeingDestroyed())
+        m_target->invalidateStyleAndLayerComposition();
+
+    m_runningAccelerated = RunningAccelerated::No;
+}
+
 Ref<const Animation> KeyframeEffect::backingAnimationForCompositedRenderer() const
 {
     auto effectAnimation = animation();

Modified: branches/safari-611-branch/Source/WebCore/animation/KeyframeEffect.h (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/KeyframeEffect.h	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/KeyframeEffect.h	2021-01-25 22:11:27 UTC (rev 271827)
@@ -133,7 +133,7 @@
     void animationTimelineDidChange(AnimationTimeline*) final;
     void animationTimingDidChange();
     void transformRelatedPropertyDidChange();
-    void applyPendingAcceleratedActions();
+    OptionSet<AcceleratedActionApplicationResult> applyPendingAcceleratedActions();
 
     void willChangeRenderer();
 
@@ -172,6 +172,8 @@
     bool requiresPseudoElement() const;
     bool hasImplicitKeyframes() const;
 
+    void stopAcceleratingTransformRelatedProperties(UseAcceleratedAction);
+
 private:
     KeyframeEffect(Element*, PseudoId);
 
@@ -203,6 +205,7 @@
     void setBlendingKeyframes(KeyframeList&);
     Seconds timeToNextTick() const final;
     Optional<double> progressUntilNextStep(double) const final;
+    bool isTargetingTransformRelatedProperty() const;
     void checkForMatchingTransformFunctionLists();
     void checkForMatchingFilterFunctionLists();
     void checkForMatchingColorFilterFunctionLists();

Modified: branches/safari-611-branch/Source/WebCore/animation/KeyframeEffectStack.cpp (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/KeyframeEffectStack.cpp	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/KeyframeEffectStack.cpp	2021-01-25 22:11:27 UTC (rev 271827)
@@ -148,4 +148,10 @@
     return impact;
 }
 
+void KeyframeEffectStack::stopAcceleratingTransformRelatedProperties(UseAcceleratedAction useAcceleratedAction)
+{
+    for (auto& effect : m_effects)
+        effect->stopAcceleratingTransformRelatedProperties(useAcceleratedAction);
+}
+
 } // namespace WebCore

Modified: branches/safari-611-branch/Source/WebCore/animation/KeyframeEffectStack.h (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/KeyframeEffectStack.h	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/KeyframeEffectStack.h	2021-01-25 22:11:27 UTC (rev 271827)
@@ -53,6 +53,8 @@
     OptionSet<AnimationImpact> applyKeyframeEffects(RenderStyle& targetStyle, const RenderStyle& previousLastStyleChangeEventStyle);
     bool hasEffectWithImplicitKeyframes() const;
 
+    void stopAcceleratingTransformRelatedProperties(UseAcceleratedAction);
+
 private:
     void ensureEffectsAreSorted();
 

Modified: branches/safari-611-branch/Source/WebCore/animation/WebAnimation.cpp (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/WebAnimation.cpp	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/WebAnimation.cpp	2021-01-25 22:11:27 UTC (rev 271827)
@@ -1253,12 +1253,6 @@
         downcast<DocumentTimeline>(*m_timeline).animationAcceleratedRunningStateDidChange(*this);
 }
 
-void WebAnimation::applyPendingAcceleratedActions()
-{
-    if (is<KeyframeEffect>(m_effect))
-        downcast<KeyframeEffect>(*m_effect).applyPendingAcceleratedActions();
-}
-
 WebAnimation& WebAnimation::readyPromiseResolve()
 {
     return *this;

Modified: branches/safari-611-branch/Source/WebCore/animation/WebAnimation.h (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/WebAnimation.h	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/WebAnimation.h	2021-01-25 22:11:27 UTC (rev 271827)
@@ -124,7 +124,6 @@
     virtual void resolve(RenderStyle&, Optional<Seconds> = WTF::nullopt);
     void effectTargetDidChange(const Optional<const Styleable>& previousTarget, const Optional<const Styleable>& newTarget);
     void acceleratedStateDidChange();
-    void applyPendingAcceleratedActions();
     void willChangeRenderer();
 
     bool isRunningAccelerated() const;

Modified: branches/safari-611-branch/Source/WebCore/animation/WebAnimationTypes.h (271826 => 271827)


--- branches/safari-611-branch/Source/WebCore/animation/WebAnimationTypes.h	2021-01-25 22:11:21 UTC (rev 271826)
+++ branches/safari-611-branch/Source/WebCore/animation/WebAnimationTypes.h	2021-01-25 22:11:27 UTC (rev 271827)
@@ -55,6 +55,12 @@
     ForcesStackingContext   = 1 << 1
 };
 
+enum class AcceleratedActionApplicationResult {
+    TransformRelatedAnimationCannotBeAccelerated  = 1 << 0
+};
+
+enum class UseAcceleratedAction : uint8_t { Yes, No };
+
 using MarkableDouble = Markable<double, WebAnimationsMarkableDoubleTraits>;
 
 using AnimationCollection = ListHashSet<RefPtr<WebAnimation>>;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to