Changeset 271524 in webkit


Ignore:
Timestamp:
Jan 15, 2021 9:47:18 AM (18 months ago)
Author:
graouts@webkit.org
Message:

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.
Location:
trunk
Files:
6 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r271523 r271524  
     12021-01-15  Antoine Quint  <graouts@webkit.org>
     2
     3        Reversed transform animation not applied alongside other transform animations
     4        https://bugs.webkit.org/show_bug.cgi?id=218655
     5        <rdar://problem/71116284>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Add new tests that start a transform-related animation that runs accelerated, then add another
     10        transform-related animation that either initially or eventually is not accelerated. In all cases,
     11        we check that once the second animation is no longer accelerated that the first animation is also
     12        no longer accelerated.
     13
     14        * platform/win/TestExpectations:
     15        * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2-expected.txt: Added.
     16        * webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html: Added.
     17        * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3-expected.txt: Added.
     18        * webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html: Added.
     19        * webanimations/combining-transform-animations-with-different-acceleration-capabilities-expected.txt: Added.
     20        * webanimations/combining-transform-animations-with-different-acceleration-capabilities.html: Added.
     21
    1222021-01-15  Tomoki Imai  <Tomoki.Imai@sony.com>
    223
  • trunk/LayoutTests/platform/win/TestExpectations

    r271425 r271524  
    46164616
    46174617webkit.org/b/220536 fast/text/cjk-multi-codepoint-cluster-vertical.html [ ImageOnlyFailure ]
     4618
     4619# These tests time out on Windows
     4620webkit.org/b/220653 webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html [ Skip ]
     4621webkit.org/b/220653 webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html [ Skip ]
     4622webkit.org/b/220653 webanimations/combining-transform-animations-with-different-acceleration-capabilities.html [ Skip ]
  • trunk/Source/WebCore/ChangeLog

    r271523 r271524  
     12021-01-15  Antoine Quint  <graouts@webkit.org>
     2
     3        Reversed transform animation not applied alongside other transform animations
     4        https://bugs.webkit.org/show_bug.cgi?id=218655
     5        <rdar://problem/71116284>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Tests: webanimations/combining-transform-animations-with-different-acceleration-capabilities-2.html
     10               webanimations/combining-transform-animations-with-different-acceleration-capabilities-3.html
     11               webanimations/combining-transform-animations-with-different-acceleration-capabilities.html
     12
     13        While, in theory, animations for a transform-related CSS property (translate, rotate, scale and transform)
     14        can be accelerated, there are various reasons why it might not, in fact, run accelerated.
     15
     16        One example is that the timing function is not something we can translate in terms Core Animation can
     17        understand, such as the steps() timing function. In this case, the KeyframeEffect itself is aware of
     18        the limitation and the method KeyframeEffect::canBeAccelerated() returns false.
     19
     20        Another example is that the playback rate of the animation is not 1, which we currently don't support for
     21        Core Animation animations (see bug 211839). In this case, GraphicsLayerCA is where the impossibility to
     22        run an animation accelerated is determined.
     23
     24        While we support running transform-related animations with or without acceleration, one thing we cannot
     25        support is, for the same element, running some transform-related animations with acceleration, and some
     26        without.
     27
     28        Thus, regardless of where we determine that a transform-related animation cannot be accelerated, we need
     29        to send this information up to the KeyframeEffectStack in which this animation's effect belongs to make
     30        sure that any other transform-related animation that may already be running accelerated no longer does
     31        and continues running without acceleration.
     32
     33        There are two locations where we determine that a transform-related animation cannot be accelerated:
     34
     35            1. in DocumentTimeline::applyPendingAcceleratedAnimations() under which we start, update or stop
     36               accelerated animations that have been invalidated since the last page rendering,
     37            2. in KeyframeEffect::updateAcceleratedActions() which is called for each page rendering, including
     38               animations that cannot be accelerated.
     39
     40        In the first case, we catch situations where an animation that could have been accelerated but failed
     41        to be started due to the internal logic of GraphicsLayerCA. We use the new KeyframeEffect method
     42        applyPendingAcceleratedActions() return value to determine this, and for each effect where the result
     43        indicates that a transform-related animation could not be accelerated, we add the KeyframeEffectStack
     44        to which it belongs and, once we're done with updating all effects, call the new
     45        stopAcceleratingTransformRelatedProperties() method on the keyframe effect stack.
     46
     47        In the second case, we catch situations where an animation is known to not be able to run accelerated
     48        even without involving GraphicsLayerCA. We check whether the animation targets a transform-related
     49        property and if it is active, and if so call stopAcceleratingTransformRelatedProperties()
     50        on the keyframe effect stack there as well.
     51
     52        When KeyframeEffectStack::stopAcceleratingTransformRelatedProperties() is called, we go
     53        through all the registered effects and call stopAcceleratingTransformRelatedProperties(). This new
     54        KeyframeEffect method will either add a pending accelerated action to stop the accelerated animation,
     55        or if we're currently apply accelerated actions (ie. during DocumentTimeline::applyPendingAcceleratedAnimations()),
     56        we stop the accelerated animation right away.
     57
     58        In both cases we know not to try running this animation again with acceleration by setting m_runningAccelerated
     59        to RunningAccelerated::No.
     60
     61        * animation/DocumentTimeline.cpp:
     62        (WebCore::DocumentTimeline::applyPendingAcceleratedAnimations):
     63        * animation/KeyframeEffect.cpp:
     64        (WebCore::KeyframeEffect::isTargetingTransformRelatedProperty const):
     65        (WebCore::KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation const):
     66        (WebCore::KeyframeEffect::updateAcceleratedActions):
     67        (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
     68        (WebCore::KeyframeEffect::stopAcceleratingTransformRelatedProperties):
     69        * animation/KeyframeEffect.h:
     70        * animation/KeyframeEffectStack.cpp:
     71        (WebCore::KeyframeEffectStack::stopAcceleratingTransformRelatedProperties):
     72        * animation/KeyframeEffectStack.h:
     73        * animation/WebAnimation.cpp:
     74        (WebCore::WebAnimation::applyPendingAcceleratedActions): Deleted.
     75        * animation/WebAnimation.h:
     76        * animation/WebAnimationTypes.h:
     77
    1782021-01-15  Tomoki Imai  <Tomoki.Imai@sony.com>
    279
  • trunk/Source/WebCore/animation/DocumentTimeline.cpp

    r268809 r271524  
    4242#include "RenderLayer.h"
    4343#include "RenderLayerBacking.h"
     44#include "WebAnimationTypes.h"
    4445
    4546namespace WebCore {
     
    443444    m_acceleratedAnimationsPendingRunningStateChange.clear();
    444445
     446    // Animations may fail to run accelerated for reasons private to GraphicsLayerCA. If that happens, and the animation
     447    // in question targets a transform-related property, we must prevent all other transform-related animations for this
     448    // element to run accelerated since we can't run some transform-related animations accelerated, and some not. To do
     449    // this, we keep a list of all KeyframeEffectStack objects containing an effect that failed to start a transform-related
     450    // animation so that we can return any transform-related accelerated animation to run non-accelerated.
     451    HashSet<KeyframeEffectStack*> effectStacksContainingEffectThatFailedToRunAcceleratedTransformRelatedAnimation;
     452
    445453    bool hasForcedLayout = false;
    446454    for (auto& animation : acceleratedAnimationsPendingRunningStateChange) {
    447         if (!hasForcedLayout) {
    448             auto* effect = animation->effect();
    449             if (is<KeyframeEffect>(effect))
    450                 hasForcedLayout |= downcast<KeyframeEffect>(effect)->forceLayoutIfNeeded();
    451         }
    452         animation->applyPendingAcceleratedActions();
    453     }
     455        auto* effect = animation->effect();
     456        if (!is<KeyframeEffect>(effect))
     457            continue;
     458
     459        auto& keyframeEffect = downcast<KeyframeEffect>(*effect);
     460        if (!hasForcedLayout)
     461            hasForcedLayout |= keyframeEffect.forceLayoutIfNeeded();
     462        auto pendingAccelerationActionResult = keyframeEffect.applyPendingAcceleratedActions();
     463        if (pendingAccelerationActionResult.contains(AcceleratedActionApplicationResult::TransformRelatedAnimationCannotBeAccelerated)) {
     464            ASSERT(keyframeEffect.targetStyleable());
     465            ASSERT(keyframeEffect.targetStyleable()->keyframeEffectStack());
     466            effectStacksContainingEffectThatFailedToRunAcceleratedTransformRelatedAnimation.add(keyframeEffect.targetStyleable()->keyframeEffectStack());
     467        }
     468    }
     469
     470    for (auto& effectStack : effectStacksContainingEffectThatFailedToRunAcceleratedTransformRelatedAnimation)
     471        effectStack->stopAcceleratingTransformRelatedProperties(UseAcceleratedAction::No);
    454472}
    455473
  • trunk/Source/WebCore/animation/KeyframeEffect.cpp

    r271269 r271524  
    12821282}
    12831283
    1284 bool KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation() const
    1285 {
    1286     if (!isRunningAccelerated())
    1287         return false;
    1288 
     1284bool KeyframeEffect::isTargetingTransformRelatedProperty() const
     1285{
    12891286    return m_blendingKeyframes.properties().contains(CSSPropertyTranslate)
    12901287        || m_blendingKeyframes.properties().contains(CSSPropertyScale)
    12911288        || m_blendingKeyframes.properties().contains(CSSPropertyRotate)
    12921289        || m_blendingKeyframes.properties().contains(CSSPropertyTransform);
     1290}
     1291
     1292bool KeyframeEffect::isRunningAcceleratedTransformRelatedAnimation() const
     1293{
     1294    return isRunningAccelerated() && isTargetingTransformRelatedProperty();
    12931295}
    12941296
     
    15751577void KeyframeEffect::updateAcceleratedActions()
    15761578{
    1577     if (!canBeAccelerated())
    1578         return;
     1579    if (!canBeAccelerated()) {
     1580        // In the case where this animation is actively targeting a transform-related property and yet
     1581        // cannot be accelerated, we must notify the effect stack such that any running accelerated
     1582        // transform-related animation targeting this element reverts to running non-accelerated.
     1583        if (isTargetingTransformRelatedProperty()
     1584            && animation()->playState() == WebAnimation::PlayState::Running
     1585            && getComputedTiming().phase == AnimationEffectPhase::Active) {
     1586            ASSERT(targetStyleable());
     1587            ASSERT(targetStyleable()->keyframeEffectStack());
     1588            targetStyleable()->keyframeEffectStack()->stopAcceleratingTransformRelatedProperties(UseAcceleratedAction::Yes);
     1589        }
     1590        return;
     1591    }
    15791592
    15801593    auto computedTiming = getComputedTiming();
     
    16681681}
    16691682
    1670 void KeyframeEffect::applyPendingAcceleratedActions()
    1671 {
     1683OptionSet<AcceleratedActionApplicationResult> KeyframeEffect::applyPendingAcceleratedActions()
     1684{
     1685    OptionSet<AcceleratedActionApplicationResult> result;
     1686
    16721687    // Once an accelerated animation has been committed, we no longer want to force a layout.
    16731688    // This should have been performed by a call to forceLayoutIfNeeded() prior to applying
     
    16761691
    16771692    if (m_pendingAcceleratedActions.isEmpty())
    1678         return;
     1693        return result;
    16791694
    16801695    auto* renderer = this->renderer();
     
    16861701            m_runningAccelerated = RunningAccelerated::NotStarted;
    16871702        }
    1688         return;
     1703        return result;
    16891704    }
    16901705
     
    17201735            if (m_runningAccelerated == RunningAccelerated::No) {
    17211736                m_lastRecordedAcceleratedAction = AcceleratedAction::Stop;
    1722                 return;
     1737                if (isTargetingTransformRelatedProperty())
     1738                    result.add(AcceleratedActionApplicationResult::TransformRelatedAnimationCannotBeAccelerated);
     1739                return result;
    17231740            }
    17241741            break;
     
    17371754            if (!document()->renderTreeBeingDestroyed())
    17381755                m_target->invalidateStyleAndLayerComposition();
    1739             m_runningAccelerated = RunningAccelerated::NotStarted;
     1756            m_runningAccelerated = canBeAccelerated() ? RunningAccelerated::NotStarted : RunningAccelerated::No;
    17401757            break;
    17411758        case AcceleratedAction::TransformChange:
     
    17441761        }
    17451762    }
     1763
     1764    if (m_runningAccelerated == RunningAccelerated::No && isTargetingTransformRelatedProperty())
     1765        result.add(AcceleratedActionApplicationResult::TransformRelatedAnimationCannotBeAccelerated);
     1766
     1767    return result;
     1768}
     1769
     1770void KeyframeEffect::stopAcceleratingTransformRelatedProperties(UseAcceleratedAction useAcceleratedAction)
     1771{
     1772    if (!isRunningAcceleratedTransformRelatedAnimation())
     1773        return;
     1774
     1775    if (useAcceleratedAction == UseAcceleratedAction::Yes) {
     1776        addPendingAcceleratedAction(AcceleratedAction::Stop);
     1777        return;
     1778    }
     1779
     1780    auto* renderer = this->renderer();
     1781    if (!renderer || !renderer->isComposited())
     1782        return;
     1783
     1784    ASSERT(document());
     1785    renderer->animationFinished(m_blendingKeyframes.animationName());
     1786    if (!document()->renderTreeBeingDestroyed())
     1787        m_target->invalidateStyleAndLayerComposition();
     1788
     1789    m_runningAccelerated = RunningAccelerated::No;
    17461790}
    17471791
  • trunk/Source/WebCore/animation/KeyframeEffect.h

    r270837 r271524  
    134134    void animationTimingDidChange();
    135135    void transformRelatedPropertyDidChange();
    136     void applyPendingAcceleratedActions();
     136    OptionSet<AcceleratedActionApplicationResult> applyPendingAcceleratedActions();
    137137
    138138    void willChangeRenderer();
     
    172172    bool requiresPseudoElement() const;
    173173    bool hasImplicitKeyframes() const;
     174
     175    void stopAcceleratingTransformRelatedProperties(UseAcceleratedAction);
    174176
    175177private:
     
    204206    Seconds timeToNextTick() const final;
    205207    Optional<double> progressUntilNextStep(double) const final;
     208    bool isTargetingTransformRelatedProperty() const;
    206209    void checkForMatchingTransformFunctionLists();
    207210    void checkForMatchingFilterFunctionLists();
  • trunk/Source/WebCore/animation/KeyframeEffectStack.cpp

    r270837 r271524  
    149149}
    150150
     151void KeyframeEffectStack::stopAcceleratingTransformRelatedProperties(UseAcceleratedAction useAcceleratedAction)
     152{
     153    for (auto& effect : m_effects)
     154        effect->stopAcceleratingTransformRelatedProperties(useAcceleratedAction);
     155}
     156
    151157} // namespace WebCore
  • trunk/Source/WebCore/animation/KeyframeEffectStack.h

    r270837 r271524  
    5454    bool hasEffectWithImplicitKeyframes() const;
    5555
     56    void stopAcceleratingTransformRelatedProperties(UseAcceleratedAction);
     57
    5658private:
    5759    void ensureEffectsAreSorted();
  • trunk/Source/WebCore/animation/WebAnimation.cpp

    r269914 r271524  
    12541254}
    12551255
    1256 void WebAnimation::applyPendingAcceleratedActions()
    1257 {
    1258     if (is<KeyframeEffect>(m_effect))
    1259         downcast<KeyframeEffect>(*m_effect).applyPendingAcceleratedActions();
    1260 }
    1261 
    12621256WebAnimation& WebAnimation::readyPromiseResolve()
    12631257{
  • trunk/Source/WebCore/animation/WebAnimation.h

    r269914 r271524  
    125125    void effectTargetDidChange(const Optional<const Styleable>& previousTarget, const Optional<const Styleable>& newTarget);
    126126    void acceleratedStateDidChange();
    127     void applyPendingAcceleratedActions();
    128127    void willChangeRenderer();
    129128
  • trunk/Source/WebCore/animation/WebAnimationTypes.h

    r267188 r271524  
    5656};
    5757
     58enum class AcceleratedActionApplicationResult {
     59    TransformRelatedAnimationCannotBeAccelerated  = 1 << 0
     60};
     61
     62enum class UseAcceleratedAction : uint8_t { Yes, No };
     63
    5864using MarkableDouble = Markable<double, WebAnimationsMarkableDoubleTraits>;
    5965
Note: See TracChangeset for help on using the changeset viewer.