Skip to content
Permalink
Browse files
Support accelerated animation of individual transform CSS properties
https://bugs.webkit.org/show_bug.cgi?id=217842
<rdar://problem/70391914>

Reviewed by Dean Jackson.

LayoutTests/imported/w3c:

* web-platform-tests/css/css-transforms/animation/rotate-interpolation-expected.txt:
* web-platform-tests/css/css-transforms/animation/scale-interpolation-expected.txt:
* web-platform-tests/css/css-transforms/animation/translate-interpolation-expected.txt:

Source/WebCore:

In order to support accelerated animation of individual transform CSS properties, we make a list of the
various animations targeting animation-related properties in GraphicsLayerCA::updateAnimations() and apply
an animation for each of those properties in their expected application order – translate, scale, rotate and
then transform – using Core Animation's additive animation mode.

When one of those properties does not have an animation, we create a non-interpolating base transform value
animation set with both from and to values matching the value set in CSS for this particular property. We use
a new transformMatrixForProperty() method on the GraphicsLayerClient to obtain this value.

We also add a non-additive, non-interpolating identity transform first to make sure all the additive animations
build on top of a neutral transform and not the underlying value applied to the layer.

Finally, when GraphicsLayerCA::setTransform() is updated, we update the animations to ensure that any base
transform value animation is updated to match the current value of the CSS properties contributing to the
compound layer transform.

Tests: webanimations/accelerated-transform-related-animation-property-order.html
       webanimations/accelerated-translate-animation-additional-animation-added-in-flight.html
       webanimations/accelerated-translate-animation-underlying-transform-changed-in-flight.html
       webanimations/accelerated-translate-animation-with-transform.html
       webanimations/accelerated-translate-animation.html

* animation/CSSPropertyAnimation.cpp: Add the necessary animationIsAccelerated() overrides for each of the
individual CSS transform property animation wrappers to indicate that accelerated animations are supported.
* platform/graphics/GraphicsLayer.cpp:
(WebCore::GraphicsLayer::validateTransformOperations): Update the ASSERT to check that the animated property
is any of the transform-related properties.
* platform/graphics/GraphicsLayer.h:
(WebCore::TransformAnimationValue::TransformAnimationValue): Add a new constructor that takes a single TransformOperation*
value as input instead of a TransformOperations& so that the values coming from the individual CSS transform properties
can be used to create a TransformAnimationValue in RenderLayerBacking::startAnimation().
* platform/graphics/GraphicsLayerClient.h: Add the new animated property identifiers for the individual CSS transform properties.
(WebCore::animatedPropertyIsTransformOrRelated): Add a new utility to identify any of the transform-related animated property
identifiers.
(WebCore::GraphicsLayerClient::transformMatrixForProperty const): New method called from GraphicsLayerCA::updateAnimations()
to query the client, such as RenderLayerBacking, for the TransformationMatrix representing a given CSS property value to be
used for base transform value non-interpolating animations.
* platform/graphics/ca/GraphicsLayerCA.cpp:
(WebCore::propertyIdToString): Account for the new animated property identifiers.
(WebCore::GraphicsLayerCA::setTransform): If we are currently running a transform-related animation, a change in underlying
transform value means we must re-evaluate all transform-related animations to ensure that the base value transform animations
are current.
(WebCore::GraphicsLayerCA::moveOrCopyAnimations):
(WebCore::GraphicsLayerCA::addAnimation):
(WebCore::GraphicsLayerCA::updateAnimations): When updating animations, keep track of the most recent animation applicable to each
individual CSS transform property and the list of animations targeting the transform CSS property. Then, ensure those animations
are applied in the specified order – translate, scale, rotate and transform – by adding those animations as additive CA animations
with non-interpolating base transform value animations for each property that does not have a specified interpolating animation.
(WebCore::GraphicsLayerCA::isRunningTransformAnimation const): Ensure we also consider paused animations as "running" animations.
(WebCore::GraphicsLayerCA::appendToUncommittedAnimations): Whether a transform animation needs to be run using the additive mode
is now determined in updateAnimations() so we remove all code related to working out whether animations ought to be additive.
(WebCore::GraphicsLayerCA::createTransformAnimationsFromKeyframes): We force the creation of CATransform3D values for individual
CSS transform properties since only this mode guarantees that additivity of such animations will yield the expected result where
the transforms are multiplied rather than simply adding float values of individual components.
* platform/graphics/ca/GraphicsLayerCA.h:
* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::startAnimation): Ensure we start animations for the individual CSS transform properties.
(WebCore::RenderLayerBacking::graphicsLayerToCSSProperty):
(WebCore::RenderLayerBacking::cssToGraphicsLayerProperty):
(WebCore::RenderLayerBacking::transformMatrixForProperty const): Implement the new GraphicsLayerClient method to provide a
matrix that can be used as a value for non-interpolating base value transform animations in GraphicsLayerCA::updateAnimations()
for any of the transform-related CSS properties.
* rendering/RenderLayerBacking.h:
* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::requiresCompositingForAnimation const): Ensure that an animation for any of the transform-related
CSS properties yields composition of the target layer.
(WebCore::RenderLayerCompositor::isRunningTransformAnimation const):
* rendering/style/WillChangeData.cpp:
(WebCore::propertyTriggersCompositingOnBoxesOnly): Ensure that setting the will-change CSS property to any of the transform-related
CSS properties yields composition of the target.

LayoutTests:

Add some new tests that check animations of individual CSS transform properties can be performed,
checking on their relative order with the transform property, updating the transform property while
an animation is in flight and adding animations while in flight.

* TestExpectations:
* webanimations/accelerated-transform-related-animation-property-order-expected.html: Added.
* webanimations/accelerated-transform-related-animation-property-order.html: Added.
* webanimations/accelerated-translate-animation-additional-animation-added-in-flight-expected.html: Added.
* webanimations/accelerated-translate-animation-additional-animation-added-in-flight.html: Added.
* webanimations/accelerated-translate-animation-expected.html: Added.
* webanimations/accelerated-translate-animation-underlying-transform-changed-in-flight-expected.html: Added.
* webanimations/accelerated-translate-animation-underlying-transform-changed-in-flight.html: Added.
* webanimations/accelerated-translate-animation-with-transform-expected.html: Added.
* webanimations/accelerated-translate-animation-with-transform.html: Added.
* webanimations/accelerated-translate-animation.html: Added.

Canonical link: https://commits.webkit.org/230577@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@268615 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
graouts committed Oct 16, 2020
1 parent 170108f commit 50cf5fe4a35d91e8f15fb7accbe322fda899802a
Showing 27 changed files with 660 additions and 50 deletions.
@@ -1,3 +1,27 @@
2020-10-16 Antoine Quint <graouts@webkit.org>

Support accelerated animation of individual transform CSS properties
https://bugs.webkit.org/show_bug.cgi?id=217842
<rdar://problem/70391914>

Reviewed by Dean Jackson.

Add some new tests that check animations of individual CSS transform properties can be performed,
checking on their relative order with the transform property, updating the transform property while
an animation is in flight and adding animations while in flight.

* TestExpectations:
* webanimations/accelerated-transform-related-animation-property-order-expected.html: Added.
* webanimations/accelerated-transform-related-animation-property-order.html: Added.
* webanimations/accelerated-translate-animation-additional-animation-added-in-flight-expected.html: Added.
* webanimations/accelerated-translate-animation-additional-animation-added-in-flight.html: Added.
* webanimations/accelerated-translate-animation-expected.html: Added.
* webanimations/accelerated-translate-animation-underlying-transform-changed-in-flight-expected.html: Added.
* webanimations/accelerated-translate-animation-underlying-transform-changed-in-flight.html: Added.
* webanimations/accelerated-translate-animation-with-transform-expected.html: Added.
* webanimations/accelerated-translate-animation-with-transform.html: Added.
* webanimations/accelerated-translate-animation.html: Added.

2020-10-16 Karl Rackler <rackler@apple.com>

REGRESSION(268444?): [ Win10 wk1 ews ] transforms/2d/rotate-composited.html is a flaky failure
@@ -4519,3 +4519,9 @@ editing/selection/expando.html [ Failure ]
fast/layoutformattingcontext/ [ ImageOnlyFailure ]
webkit.org/b/217054 fast/layoutformattingcontext/horizontal-sizing-with-trailing-letter-spacing.html [ Skip ]

webkit.org/b/217851 transitions/interrupted-transition-hardware.html [ Pass Failure ]
webkit.org/b/217851 webanimations/accelerated-transform-related-animation-property-order.html [ Pass Failure ]
webkit.org/b/217851 webanimations/accelerated-translate-animation-additional-animation-added-in-flight.html [ Pass Failure ]
webkit.org/b/217851 webanimations/accelerated-translate-animation-underlying-transform-changed-in-flight.html [ Pass Failure ]
webkit.org/b/217851 webanimations/accelerated-translate-animation-with-transform.html [ Pass Failure ]
webkit.org/b/217851 webanimations/accelerated-translate-animation.html [ Pass Failure ]
@@ -1,3 +1,15 @@
2020-10-16 Antoine Quint <graouts@webkit.org>

Support accelerated animation of individual transform CSS properties
https://bugs.webkit.org/show_bug.cgi?id=217842
<rdar://problem/70391914>

Reviewed by Dean Jackson.

* web-platform-tests/css/css-transforms/animation/rotate-interpolation-expected.txt:
* web-platform-tests/css/css-transforms/animation/scale-interpolation-expected.txt:
* web-platform-tests/css/css-transforms/animation/translate-interpolation-expected.txt:

2020-10-15 Sam Weinig <weinig@apple.com>

CSSStyleDeclaration breaks JS spec (properties not showing up in Object.getOwnPropertyNames)
@@ -137,10 +137,10 @@ PASS CSS Animations: property <rotate> from neutral to [30deg] at (0.25) should
PASS CSS Animations: property <rotate> from neutral to [30deg] at (0.75) should be [25deg]
PASS CSS Animations: property <rotate> from neutral to [30deg] at (1) should be [30deg]
PASS CSS Animations: property <rotate> from neutral to [30deg] at (2) should be [50deg]
PASS Web Animations: property <rotate> from neutral to [30deg] at (-1) should be [-10deg]
FAIL Web Animations: property <rotate> from neutral to [30deg] at (-1) should be [-10deg] assert_equals: expected "- 10deg " but got "- 50deg "
FAIL Web Animations: property <rotate> from neutral to [30deg] at (0) should be [10deg] assert_equals: expected "10deg " but got "- 10deg "
FAIL Web Animations: property <rotate> from neutral to [30deg] at (0.25) should be [15deg] assert_equals: expected "15deg " but got "none "
FAIL Web Animations: property <rotate> from neutral to [30deg] at (0.75) should be [25deg] assert_equals: expected "25deg " but got "22.5deg "
FAIL Web Animations: property <rotate> from neutral to [30deg] at (0.25) should be [15deg] assert_equals: expected "15deg " but got "7.5deg "
FAIL Web Animations: property <rotate> from neutral to [30deg] at (0.75) should be [25deg] assert_equals: expected "25deg " but got "28.13deg "
PASS Web Animations: property <rotate> from neutral to [30deg] at (1) should be [30deg]
FAIL Web Animations: property <rotate> from neutral to [30deg] at (2) should be [50deg] assert_equals: expected "50deg " but got "30deg "
PASS CSS Transitions: property <rotate> from [inherit] to [270deg] at (-1) should be [-90deg]
@@ -185,10 +185,10 @@ PASS CSS Animations: property <scale> from neutral to [1.5 1] at (0.25) should b
PASS CSS Animations: property <scale> from neutral to [1.5 1] at (0.75) should be [1.4 1]
PASS CSS Animations: property <scale> from neutral to [1.5 1] at (1) should be [1.5 1]
PASS CSS Animations: property <scale> from neutral to [1.5 1] at (2) should be [1.9 1]
PASS Web Animations: property <scale> from neutral to [1.5 1] at (-1) should be [0.7 1]
FAIL Web Animations: property <scale> from neutral to [1.5 1] at (-1) should be [0.7 1] assert_equals: expected "0.7 1 " but got "- 0.1 1 "
FAIL Web Animations: property <scale> from neutral to [1.5 1] at (0) should be [1.1 1] assert_equals: expected "1.1 1 " but got "0.7 1 "
FAIL Web Animations: property <scale> from neutral to [1.5 1] at (0.25) should be [1.2 1] assert_equals: expected "1.2 1 " but got "0.9 1 "
FAIL Web Animations: property <scale> from neutral to [1.5 1] at (0.75) should be [1.4 1] assert_equals: expected "1.4 1 " but got "1.35 1 "
FAIL Web Animations: property <scale> from neutral to [1.5 1] at (0.25) should be [1.2 1] assert_equals: expected "1.2 1 " but got "1.05 1 "
FAIL Web Animations: property <scale> from neutral to [1.5 1] at (0.75) should be [1.4 1] assert_equals: expected "1.4 1 " but got "1.46 1 "
PASS Web Animations: property <scale> from neutral to [1.5 1] at (1) should be [1.5 1]
FAIL Web Animations: property <scale> from neutral to [1.5 1] at (2) should be [1.9 1] assert_equals: expected "1.9 1 " but got "1.5 1 "
PASS CSS Transitions: property <scale> from [initial] to [2 0.5 1] at (-1) should be [0 1.5]
@@ -233,10 +233,10 @@ PASS CSS Animations: property <translate> from neutral to [20px] at (0.25) shoul
PASS CSS Animations: property <translate> from neutral to [20px] at (0.75) should be [17.5px]
PASS CSS Animations: property <translate> from neutral to [20px] at (1) should be [20px]
PASS CSS Animations: property <translate> from neutral to [20px] at (2) should be [30px]
PASS Web Animations: property <translate> from neutral to [20px] at (-1) should be [0px]
FAIL Web Animations: property <translate> from neutral to [20px] at (-1) should be [0px] assert_equals: expected "none " but got "- 20px "
FAIL Web Animations: property <translate> from neutral to [20px] at (0) should be [10px] assert_equals: expected "10px " but got "none "
FAIL Web Animations: property <translate> from neutral to [20px] at (0.25) should be [12.5px] assert_equals: expected "12.5px " but got "5px "
FAIL Web Animations: property <translate> from neutral to [20px] at (0.75) should be [17.5px] assert_equals: expected "17.5px " but got "16.25px "
FAIL Web Animations: property <translate> from neutral to [20px] at (0.25) should be [12.5px] assert_equals: expected "12.5px " but got "8.75px "
FAIL Web Animations: property <translate> from neutral to [20px] at (0.75) should be [17.5px] assert_equals: expected "17.5px " but got "19.06px "
PASS Web Animations: property <translate> from neutral to [20px] at (1) should be [20px]
FAIL Web Animations: property <translate> from neutral to [20px] at (2) should be [30px] assert_equals: expected "30px " but got "20px "
PASS CSS Transitions: property <translate> from [initial] to [200px 100px 200px] at (-1) should be [-200px -100px -200px]
@@ -0,0 +1,14 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
transform: translateX(250px) scale(1.5) translateY(100px);
}

</style>
<div id="target"></div>
@@ -0,0 +1,35 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
transform: translateY(100px);
}

</style>
<div id="target"></div>
<script src="../resources/ui-helper.js"></script>
<script>

(async () => {
if (window.testRunner)
testRunner.waitUntilDone();

// Start an animation that lasts a day.
const duration = 24 * 60 * 60 * 1000;
const animation = document.getElementById("target").animate({ translate: "500px", scale: "2" }, duration);
animation.currentTime = duration / 2;

// Wait until the animation has been applied.
await animation.ready;
await UIHelper.ensureStablePresentationUpdate();

if (window.testRunner)
testRunner.notifyDone();
})();

</script>
@@ -0,0 +1,14 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
transform: translateX(100px) scale(0.5);
}

</style>
<div id="target"></div>
@@ -0,0 +1,42 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
}

</style>
<div id="target"></div>
<script src="../resources/ui-helper.js"></script>
<script>

(async () => {
if (window.testRunner)
testRunner.waitUntilDone();

// Start an animation that lasts a day.
const duration = 24 * 60 * 60 * 1000;
const translateAnimation = document.getElementById("target").animate({ translate: "200px" }, duration);
translateAnimation.currentTime = duration / 2;

// Wait until the animation has been applied.
await translateAnimation.ready;
await UIHelper.ensureStablePresentationUpdate();

// Add an extra animation.
const scaleAnimation = document.getElementById("target").animate({ scale: "0" }, duration);
scaleAnimation.currentTime = duration / 2;

// Wait until the new animation has been applied.
await scaleAnimation.ready;
await UIHelper.ensureStablePresentationUpdate();

if (window.testRunner)
testRunner.notifyDone();
})();

</script>
@@ -0,0 +1,14 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
transform: translateX(100px);
}

</style>
<div id="target"></div>
@@ -0,0 +1,14 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
transform: translate(100px, 100px);
}

</style>
<div id="target"></div>
@@ -0,0 +1,41 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
}

</style>
<div id="target"></div>
<script src="../resources/ui-helper.js"></script>
<script>

(async () => {
if (window.testRunner)
testRunner.waitUntilDone();

// Start an animation that lasts a day.
const duration = 24 * 60 * 60 * 1000;
const target = document.getElementById("target");
const animation = target.animate({ translate: "200px" }, duration);
animation.currentTime = duration / 2;

// Wait until the animation has been applied.
await animation.ready;
await UIHelper.ensureStablePresentationUpdate();

// Change the transform property.
target.style.transform = "translateY(100px)";

// Wait until that change was made.
await UIHelper.ensureStablePresentationUpdate();

if (window.testRunner)
testRunner.notifyDone();
})();

</script>
@@ -0,0 +1,14 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
transform: translateX(150px);
}

</style>
<div id="target"></div>
@@ -0,0 +1,35 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
transform: translateX(50px);
}

</style>
<div id="target"></div>
<script src="../resources/ui-helper.js"></script>
<script>

(async () => {
if (window.testRunner)
testRunner.waitUntilDone();

// Start an animation that lasts a day.
const duration = 24 * 60 * 60 * 1000;
const animation = document.getElementById("target").animate({ translate: "200px" }, duration);
animation.currentTime = duration / 2;

// Wait until the animation has been applied.
await animation.ready;
await UIHelper.ensureStablePresentationUpdate();

if (window.testRunner)
testRunner.notifyDone();
})();

</script>
@@ -0,0 +1,34 @@
<style>

#target {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: black;
}

</style>
<div id="target"></div>
<script src="../resources/ui-helper.js"></script>
<script>

(async () => {
if (window.testRunner)
testRunner.waitUntilDone();

// Start an animation that lasts a day.
const duration = 24 * 60 * 60 * 1000;
const animation = document.getElementById("target").animate({ translate: "200px" }, duration);
animation.currentTime = duration / 2;

// Wait until the animation has been applied.
await animation.ready;
await UIHelper.ensureStablePresentationUpdate();

if (window.testRunner)
testRunner.notifyDone();
})();

</script>

0 comments on commit 50cf5fe

Please sign in to comment.