Changeset 230574 in webkit


Ignore:
Timestamp:
Apr 12, 2018 8:14:31 AM (6 years ago)
Author:
graouts@webkit.org
Message:

[Web Animations] Enable seeking for hardware animations
https://bugs.webkit.org/show_bug.cgi?id=184518

Reviewed by Dean Jackson.

LayoutTests/imported/w3c:

Track a small regression in the Web Animations WPT tests.

  • web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt:

Source/WebCore:

Hardware animations had lagged behind software animations in support and this patch bring their respective level
of support closer to one another. Importantly, we add the ability to seek an animation added to a GraphicsLayerCA
since many tests pause and seek animations to test their state. Animations may also have their playback state changed
along with their current time in the same run loop, so we now maintain a list of pending processing actions for
hardware animations.

  • animation/DocumentTimeline.cpp:

(WebCore::DocumentTimeline::updateAnimationSchedule): If we have animations queued up for updates to their accelerated
state we can schedule animation resolution immediately since we've already established we'll have work to do.
(WebCore::DocumentTimeline::updateAnimations): Factor the updates of pending accelerated animations out in a dedicated
method.
(WebCore::DocumentTimeline::applyPendingAcceleratedAnimations): Go through all pending accelerated animations and update
their state.

  • animation/KeyframeEffectReadOnly.cpp:

(WebCore::KeyframeEffectReadOnly::setBlendingKeyframes): Update the m_shouldRunAccelerated flag when setting blending keyframes.
(WebCore::KeyframeEffectReadOnly::apply): Ensure we finish accelerated animations if the progress is 1 or null (no longer active).
start accelerated animations if the animation is starting and always resolve styles in software as well to ensure that
hit testing will work as expected.
(WebCore::KeyframeEffectReadOnly::computeShouldRunAccelerated):
(WebCore::KeyframeEffectReadOnly::animationPlayStateDidChange): Called by WebAnimation when play() or pause() is called
with the appropriate flag.
(WebCore::KeyframeEffectReadOnly::animationDidSeek): Called by WebAnimation when the currentTime property is set.
(WebCore::KeyframeEffectReadOnly::addPendingAcceleratedAction): Add the provided action to the list of pending accelerated
actions and notify the animation that the accelerated state needs changing.
(WebCore::KeyframeEffectReadOnly::applyPendingAcceleratedActions): Called by DocumentTimeline, through WebAnimation, to apply
all pending accelerated actions.
(WebCore::KeyframeEffectReadOnly::backingAnimationForCompositedRenderer const): If we're dealing with a declarative animation,
we already have a backing Animation object, so use it directly. Otherwise, create one and ensure it reflects all timing properties
for the animation.
(WebCore::KeyframeEffectReadOnly::shouldRunAccelerated): Deleted.
(WebCore::KeyframeEffectReadOnly::startOrStopAccelerated): Deleted.

  • animation/WebAnimation.cpp:

(WebCore::WebAnimation::setCurrentTime): Call animationDidSeek() on the effect to ensure its accelerated animation gets seeked.
(WebCore::WebAnimation::play): Call animationPlayStateDidChange() on the effect to ensure its accelerated animation is started or resumed.
(WebCore::WebAnimation::pause): Call animationPlayStateDidChange() on the effect to ensure its accelerated animation gets paused.
(WebCore::WebAnimation::acceleratedStateDidChange):
(WebCore::WebAnimation::applyPendingAcceleratedActions):
(WebCore::WebAnimation::acceleratedRunningStateDidChange): Deleted.
(WebCore::WebAnimation::startOrStopAccelerated): Deleted.

  • platform/graphics/GraphicsLayer.h:

(WebCore::GraphicsLayer::seekAnimation):

  • platform/graphics/ca/GraphicsLayerCA.cpp:

(WebCore::GraphicsLayerCA::addProcessingActionForAnimation): Add an AnimationProcessingAction to the list of such actions for a
given animation name. In case we already have a Remove action, we ignore the action since the hardware animation will have been
removed by the time we try to apply this processing action.
(WebCore::GraphicsLayerCA::pauseAnimation): Add a Pause processing action.
(WebCore::GraphicsLayerCA::seekAnimation): Add a Seek processing action.
(WebCore::GraphicsLayerCA::removeAnimation): Add a Remove processing action.
(WebCore::GraphicsLayerCA::updateAnimations): First ensure that all animations pending commit are committed and then update
all animations based on the actions added through addProcessingActionForAnimation().
(WebCore::GraphicsLayerCA::seekCAAnimationOnLayer): Generate a new animation based on the new seek time provided.

  • platform/graphics/ca/GraphicsLayerCA.h:

(WebCore::GraphicsLayerCA::AnimationProcessingAction::AnimationProcessingAction):

  • rendering/RenderBoxModelObject.cpp:

(WebCore::RenderBoxModelObject::animationSeeked):

  • rendering/RenderLayerBacking.cpp:

(WebCore::RenderLayerBacking::animationSeeked):

  • rendering/RenderLayerCompositor.cpp:

(WebCore::RenderLayerCompositor::requiresCompositingForAnimation const): Fix an issue where we would run the CSSAnimationController
logic even when the legacy animation engine was disabled.

LayoutTests:

Update current test expectations, some tests have regressed because they weren't probably running their hardware
animations. Followup patches will make them opt into CSS Animations and CSS Transitions as Web Animations again.

  • animations/3d/transform-origin-vs-functions.html:
  • animations/change-completed-animation-transform.html:
  • animations/missing-values-first-keyframe.html:
  • animations/missing-values-last-keyframe.html:
  • animations/play-state-start-paused.html:
  • compositing/contents-scale/animating.html:
  • compositing/layer-creation/animation-overlap-with-children.html:
  • compositing/overflow/overflow-positioning.html:
  • compositing/visible-rect/animated.html:
  • css3/filters/filter-animation-from-none-hw.html:
  • css3/filters/filter-animation-from-none-multi-hw.html:
  • css3/filters/filter-animation-from-none-multi.html:
  • css3/filters/filter-animation-from-none.html:
  • platform/mac-sierra/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt:
Location:
trunk
Files:
33 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r230572 r230574  
     12018-04-11  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Enable seeking for hardware animations
     4        https://bugs.webkit.org/show_bug.cgi?id=184518
     5
     6        Reviewed by Dean Jackson.
     7
     8        Update current test expectations, some tests have regressed because they weren't probably running their hardware
     9        animations. Followup patches will make them opt into CSS Animations and CSS Transitions as Web Animations again.
     10
     11        * animations/3d/transform-origin-vs-functions.html:
     12        * animations/change-completed-animation-transform.html:
     13        * animations/missing-values-first-keyframe.html:
     14        * animations/missing-values-last-keyframe.html:
     15        * animations/play-state-start-paused.html:
     16        * compositing/contents-scale/animating.html:
     17        * compositing/layer-creation/animation-overlap-with-children.html:
     18        * compositing/overflow/overflow-positioning.html:
     19        * compositing/visible-rect/animated.html:
     20        * css3/filters/filter-animation-from-none-hw.html:
     21        * css3/filters/filter-animation-from-none-multi-hw.html:
     22        * css3/filters/filter-animation-from-none-multi.html:
     23        * css3/filters/filter-animation-from-none.html:
     24        * platform/mac-sierra/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt:
     25
    1262018-04-12  Per Arne Vollan  <pvollan@apple.com>
    227
  • trunk/LayoutTests/animations/3d/transform-origin-vs-functions.html

    r119985 r230574  
    1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
    2   "http://www.w3.org/TR/html4/strict.dtd">
     1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
    32<html>
    43  <head>
  • trunk/LayoutTests/animations/change-completed-animation-transform.html

    r200047 r230574  
    1 <!DOCTYPE html>
     1<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
    22<html>
    33<head>
  • trunk/LayoutTests/animations/missing-values-first-keyframe.html

    r230000 r230574  
    1 <!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
     1<!DOCTYPE html>
    22<html>
    33<head>
  • trunk/LayoutTests/animations/missing-values-last-keyframe.html

    r230000 r230574  
    1 <!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
     1<!DOCTYPE html>
    22<html>
    33<head>
  • trunk/LayoutTests/animations/play-state-start-paused.html

    r204052 r230574  
    1 <!DOCTYPE html>
     1<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
    22<html>
    33<head>
  • trunk/LayoutTests/compositing/contents-scale/animating.html

    r180441 r230574  
    1 <!DOCTYPE html>
     1<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
    22
    33<html>
  • trunk/LayoutTests/compositing/layer-creation/animation-overlap-with-children.html

    r180441 r230574  
    1 <!DOCTYPE html>
     1<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
    22
    33<html>
  • trunk/LayoutTests/compositing/overflow/overflow-positioning.html

    r119992 r230574  
    1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    2    "http://www.w3.org/TR/html4/loose.dtd">
     1<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
    32
    43<html lang="en">
  • trunk/LayoutTests/compositing/visible-rect/animated.html

    r180441 r230574  
    1 <!DOCTYPE html>
     1<!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
    22
    33<html>
  • trunk/LayoutTests/css3/filters/filter-animation-from-none-hw.html

    r230068 r230574  
    1 <!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
     1<!DOCTYPE html>
    22
    33<html>
  • trunk/LayoutTests/css3/filters/filter-animation-from-none-multi-hw.html

    r230068 r230574  
    1 <!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
     1<!DOCTYPE html>
    22
    33<html>
  • trunk/LayoutTests/css3/filters/filter-animation-from-none-multi.html

    r230068 r230574  
    1 <!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
     1<!DOCTYPE html>
    22
    33<html>
  • trunk/LayoutTests/css3/filters/filter-animation-from-none.html

    r230068 r230574  
    1 <!DOCTYPE html><!-- webkit-test-runner [ enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations=true ] -->
     1<!DOCTYPE html>
    22
    33<html>
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r230524 r230574  
     12018-04-11  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Enable seeking for hardware animations
     4        https://bugs.webkit.org/show_bug.cgi?id=184518
     5
     6        Reviewed by Dean Jackson.
     7
     8        Track a small regression in the Web Animations WPT tests.
     9
     10        * web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt:
     11
    1122018-04-11  Jianjun Zhu  <jianjun.zhu@intel.com>
    213
  • trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt

    r230068 r230574  
    141141FAIL filter: percentage or numeric-specifiable functions (percentage value) assert_equals: The value should be brightness(0.3) contrast(0.3) grayscale(0.3) invert(0.3) opacity(0.3) saturate(0.3) sepia(0.3) at 500ms expected "brightness(0.3) contrast(0.3) grayscale(0.3) invert(0.3) opacity(0.3) saturate(0.3) sepia(0.3)" but got "brightness(0.30000000000000004) contrast(0.30000000000000004) grayscale(0.30000000000000004) invert(0.30000000000000004) opacity(0.30000000000000004) saturate(0.30000000000000004) sepia(0.30000000000000004)"
    142142FAIL filter: interpolate different length of filter-function-list with function which lacuna value is 1 assert_equals: The value should be grayscale(0.5) brightness(0.5) contrast(0.5) opacity(0.5) saturate(0.5) at 500ms expected "grayscale(0.5) brightness(0.5) contrast(0.5) opacity(0.5) saturate(0.5)" but got "grayscale(1) brightness(0) contrast(0) opacity(0) saturate(0)"
    143 PASS filter: interpolate different length of filter-function-list with function which lacuna value is 0
     143FAIL filter: interpolate different length of filter-function-list with function which lacuna value is 0 assert_equals: The value should be opacity(0.5) grayscale(0.5) invert(0.5) sepia(0.5) blur(5px) at 500ms expected "opacity(0.5) grayscale(0.5) invert(0.5) sepia(0.5) blur(5px)" but got "opacity(0.25) grayscale(0.75) invert(0.75) sepia(0.75) blur(7.5px)"
    144144FAIL filter: interpolate different length of filter-function-list with drop-shadow function assert_equals: The value should be blur(5px) drop-shadow(rgba(85, 0, 170, 0.6) 5px 5px 5px at 500ms expected "blur(5px) drop-shadow(rgba(85, 0, 170, 0.6) 5px 5px 5px" but got "blur(10px) drop-shadow(rgba(0, 0, 255, 0.8) 10px 10px 10px)"
    145145PASS filter: interpolate from none
  • trunk/LayoutTests/platform/mac-sierra/imported/w3c/web-platform-tests/web-animations/animation-model/animation-types/interpolation-per-property-expected.txt

    r230068 r230574  
    141141FAIL filter: percentage or numeric-specifiable functions (percentage value) assert_equals: The value should be brightness(0.3) contrast(0.3) grayscale(0.3) invert(0.3) opacity(0.3) saturate(0.3) sepia(0.3) at 500ms expected "brightness(0.3) contrast(0.3) grayscale(0.3) invert(0.3) opacity(0.3) saturate(0.3) sepia(0.3)" but got "brightness(0.30000000000000004) contrast(0.30000000000000004) grayscale(0.30000000000000004) invert(0.30000000000000004) opacity(0.30000000000000004) saturate(0.30000000000000004) sepia(0.30000000000000004)"
    142142FAIL filter: interpolate different length of filter-function-list with function which lacuna value is 1 assert_equals: The value should be grayscale(0.5) brightness(0.5) contrast(0.5) opacity(0.5) saturate(0.5) at 500ms expected "grayscale(0.5) brightness(0.5) contrast(0.5) opacity(0.5) saturate(0.5)" but got "grayscale(1) brightness(0) contrast(0) opacity(0) saturate(0)"
    143 PASS filter: interpolate different length of filter-function-list with function which lacuna value is 0
     143FAIL filter: interpolate different length of filter-function-list with function which lacuna value is 0 assert_equals: The value should be opacity(0.5) grayscale(0.5) invert(0.5) sepia(0.5) blur(5px) at 500ms expected "opacity(0.5) grayscale(0.5) invert(0.5) sepia(0.5) blur(5px)" but got "opacity(0.25) grayscale(0.75) invert(0.75) sepia(0.75) blur(7.5px)"
    144144FAIL filter: interpolate different length of filter-function-list with drop-shadow function assert_equals: The value should be blur(5px) drop-shadow(rgba(85, 0, 170, 0.6) 5px 5px 5px at 500ms expected "blur(5px) drop-shadow(rgba(85, 0, 170, 0.6) 5px 5px 5px" but got "blur(10px) drop-shadow(rgba(0, 0, 255, 0.8) 10px 10px 10px)"
    145145PASS filter: interpolate from none
  • trunk/Source/WebCore/ChangeLog

    r230563 r230574  
     12018-04-11  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Enable seeking for hardware animations
     4        https://bugs.webkit.org/show_bug.cgi?id=184518
     5
     6        Reviewed by Dean Jackson.
     7
     8        Hardware animations had lagged behind software animations in support and this patch bring their respective level
     9        of support closer to one another. Importantly, we add the ability to seek an animation added to a GraphicsLayerCA
     10        since many tests pause and seek animations to test their state. Animations may also have their playback state changed
     11        along with their current time in the same run loop, so we now maintain a list of pending processing actions for
     12        hardware animations.
     13
     14        * animation/DocumentTimeline.cpp:
     15        (WebCore::DocumentTimeline::updateAnimationSchedule): If we have animations queued up for updates to their accelerated
     16        state we can schedule animation resolution immediately since we've already established we'll have work to do.
     17        (WebCore::DocumentTimeline::updateAnimations): Factor the updates of pending accelerated animations out in a dedicated
     18        method.
     19        (WebCore::DocumentTimeline::applyPendingAcceleratedAnimations): Go through all pending accelerated animations and update
     20        their state.
     21        * animation/KeyframeEffectReadOnly.cpp:
     22        (WebCore::KeyframeEffectReadOnly::setBlendingKeyframes): Update the m_shouldRunAccelerated flag when setting blending keyframes.
     23        (WebCore::KeyframeEffectReadOnly::apply): Ensure we finish accelerated animations if the progress is 1 or null (no longer active).
     24        start accelerated animations if the animation is starting and always resolve styles in software as well to ensure that
     25        hit testing will work as expected.
     26        (WebCore::KeyframeEffectReadOnly::computeShouldRunAccelerated):
     27        (WebCore::KeyframeEffectReadOnly::animationPlayStateDidChange): Called by WebAnimation when play() or pause() is called
     28        with the appropriate flag.
     29        (WebCore::KeyframeEffectReadOnly::animationDidSeek): Called by WebAnimation when the currentTime property is set.
     30        (WebCore::KeyframeEffectReadOnly::addPendingAcceleratedAction): Add the provided action to the list of pending accelerated
     31        actions and notify the animation that the accelerated state needs changing.
     32        (WebCore::KeyframeEffectReadOnly::applyPendingAcceleratedActions): Called by DocumentTimeline, through WebAnimation, to apply
     33        all pending accelerated actions.
     34        (WebCore::KeyframeEffectReadOnly::backingAnimationForCompositedRenderer const): If we're dealing with a declarative animation,
     35        we already have a backing Animation object, so use it directly. Otherwise, create one and ensure it reflects all timing properties
     36        for the animation.
     37        (WebCore::KeyframeEffectReadOnly::shouldRunAccelerated): Deleted.
     38        (WebCore::KeyframeEffectReadOnly::startOrStopAccelerated): Deleted.
     39        * animation/WebAnimation.cpp:
     40        (WebCore::WebAnimation::setCurrentTime): Call animationDidSeek() on the effect to ensure its accelerated animation gets seeked.
     41        (WebCore::WebAnimation::play): Call animationPlayStateDidChange() on the effect to ensure its accelerated animation is started or resumed.
     42        (WebCore::WebAnimation::pause): Call animationPlayStateDidChange() on the effect to ensure its accelerated animation gets paused.
     43        (WebCore::WebAnimation::acceleratedStateDidChange):
     44        (WebCore::WebAnimation::applyPendingAcceleratedActions):
     45        (WebCore::WebAnimation::acceleratedRunningStateDidChange): Deleted.
     46        (WebCore::WebAnimation::startOrStopAccelerated): Deleted.
     47        * platform/graphics/GraphicsLayer.h:
     48        (WebCore::GraphicsLayer::seekAnimation):
     49        * platform/graphics/ca/GraphicsLayerCA.cpp:
     50        (WebCore::GraphicsLayerCA::addProcessingActionForAnimation): Add an AnimationProcessingAction to the list of such actions for a
     51        given animation name. In case we already have a Remove action, we ignore the action since the hardware animation will have been
     52        removed by the time we try to apply this processing action.
     53        (WebCore::GraphicsLayerCA::pauseAnimation): Add a Pause processing action.
     54        (WebCore::GraphicsLayerCA::seekAnimation): Add a Seek processing action.
     55        (WebCore::GraphicsLayerCA::removeAnimation): Add a Remove processing action.
     56        (WebCore::GraphicsLayerCA::updateAnimations): First ensure that all animations pending commit are committed and then update
     57        all animations based on the actions added through addProcessingActionForAnimation().
     58        (WebCore::GraphicsLayerCA::seekCAAnimationOnLayer): Generate a new animation based on the new seek time provided.
     59        * platform/graphics/ca/GraphicsLayerCA.h:
     60        (WebCore::GraphicsLayerCA::AnimationProcessingAction::AnimationProcessingAction):
     61        * rendering/RenderBoxModelObject.cpp:
     62        (WebCore::RenderBoxModelObject::animationSeeked):
     63        * rendering/RenderLayerBacking.cpp:
     64        (WebCore::RenderLayerBacking::animationSeeked):
     65        * rendering/RenderLayerCompositor.cpp:
     66        (WebCore::RenderLayerCompositor::requiresCompositingForAnimation const): Fix an issue where we would run the CSSAnimationController
     67        logic even when the legacy animation engine was disabled.
     68
    1692018-04-12  Xabier Rodriguez Calvar  <calvaris@igalia.com>
    270
  • trunk/Source/WebCore/animation/AnimationEffectReadOnly.h

    r229818 r230574  
    4747    virtual void apply(RenderStyle&) = 0;
    4848    virtual void invalidate() = 0;
     49    virtual void animationPlayStateDidChange(WebAnimation::PlayState) = 0;
     50    virtual void animationDidSeek() = 0;
    4951
    5052    WebAnimation* animation() const { return m_animation.get(); }
  • trunk/Source/WebCore/animation/DocumentTimeline.cpp

    r229890 r230574  
    128128    m_needsUpdateAnimationSchedule = false;
    129129
     130    if (!m_acceleratedAnimationsPendingRunningStateChange.isEmpty()) {
     131        scheduleAnimationResolution();
     132        return;
     133    }
     134
    130135    Seconds scheduleDelay = Seconds::infinity();
    131136
     
    180185    }
    181186
    182     for (auto& animation : m_acceleratedAnimationsPendingRunningStateChange)
    183         animation->startOrStopAccelerated();
    184     m_acceleratedAnimationsPendingRunningStateChange.clear();
     187    applyPendingAcceleratedAnimations();
    185188
    186189    // Time has advanced, the timing model requires invalidation now.
     
    208211{
    209212    m_acceleratedAnimationsPendingRunningStateChange.add(&animation);
     213}
     214
     215void DocumentTimeline::applyPendingAcceleratedAnimations()
     216{
     217    for (auto& animation : m_acceleratedAnimationsPendingRunningStateChange)
     218        animation->applyPendingAcceleratedActions();
     219    m_acceleratedAnimationsPendingRunningStateChange.clear();
    210220}
    211221
  • trunk/Source/WebCore/animation/DocumentTimeline.h

    r229069 r230574  
    6060    std::unique_ptr<RenderStyle> animatedStyleForRenderer(RenderElement& renderer);
    6161    void animationAcceleratedRunningStateDidChange(WebAnimation&);
     62    void applyPendingAcceleratedAnimations();
    6263    bool runningAnimationsForElementAreAllAccelerated(Element&);
    6364    void detachFromDocument();
  • trunk/Source/WebCore/animation/KeyframeEffectReadOnly.cpp

    r230112 r230574  
    696696
    697697    computeStackingContextImpact();
     698    computeShouldRunAccelerated();
    698699
    699700    checkForMatchingTransformFunctionLists();
     
    925926
    926927    auto progress = iterationProgress();
     928    if (m_startedAccelerated && (!progress || progress.value() >= 1)) {
     929        m_startedAccelerated = false;
     930        animation()->acceleratedStateDidChange();
     931    }
     932
    927933    if (!progress)
    928934        return;
    929935
    930     if (m_startedAccelerated && progress.value() >= 1) {
    931         m_startedAccelerated = false;
    932         animation()->acceleratedRunningStateDidChange();
    933     }
    934 
    935     bool needsToStartAccelerated = false;
    936 
    937     if (!m_started && !m_startedAccelerated) {
    938         needsToStartAccelerated = shouldRunAccelerated();
    939         m_startedAccelerated = needsToStartAccelerated;
    940         if (needsToStartAccelerated)
    941             animation()->acceleratedRunningStateDidChange();
     936    if (!m_started && !m_startedAccelerated && m_shouldRunAccelerated) {
     937        m_startedAccelerated = true;
     938        animation()->acceleratedStateDidChange();
    942939    }
    943940    m_started = true;
    944941
    945     if (!needsToStartAccelerated && !m_startedAccelerated)
    946         setAnimatedPropertiesInStyle(targetStyle, progress.value());
     942    setAnimatedPropertiesInStyle(targetStyle, progress.value());
    947943
    948944    // https://w3c.github.io/web-animations/#side-effects-section
     
    958954}
    959955
    960 bool KeyframeEffectReadOnly::shouldRunAccelerated()
    961 {
     956void KeyframeEffectReadOnly::computeShouldRunAccelerated()
     957{
     958    m_shouldRunAccelerated = hasBlendingKeyframes();
    962959    for (auto cssPropertyId : m_blendingKeyframes.properties()) {
    963         if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(cssPropertyId))
    964             return false;
    965     }
    966     return hasBlendingKeyframes();
     960        if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(cssPropertyId)) {
     961            m_shouldRunAccelerated = false;
     962            return;
     963        }
     964    }
    967965}
    968966
     
    11441142}
    11451143
    1146 void KeyframeEffectReadOnly::startOrStopAccelerated()
     1144void KeyframeEffectReadOnly::animationPlayStateDidChange(WebAnimation::PlayState playState)
     1145{
     1146    if (playState == WebAnimation::PlayState::Running)
     1147        addPendingAcceleratedAction(AcceleratedAction::Play);
     1148    else if (playState == WebAnimation::PlayState::Paused)
     1149        addPendingAcceleratedAction(AcceleratedAction::Pause);
     1150};
     1151
     1152void KeyframeEffectReadOnly::animationDidSeek()
     1153{
     1154    addPendingAcceleratedAction(AcceleratedAction::Seek);
     1155}
     1156
     1157void KeyframeEffectReadOnly::addPendingAcceleratedAction(AcceleratedAction action)
     1158{
     1159    if (!m_shouldRunAccelerated)
     1160        return;
     1161
     1162    m_pendingAcceleratedActions.append(action);
     1163    animation()->acceleratedStateDidChange();
     1164}
     1165
     1166void KeyframeEffectReadOnly::applyPendingAcceleratedActions()
    11471167{
    11481168    auto* renderer = this->renderer();
     
    11521172    auto* compositedRenderer = downcast<RenderBoxModelObject>(renderer);
    11531173    if (m_startedAccelerated) {
    1154         auto animation = Animation::create();
    1155         animation->setDuration(timing()->iterationDuration().seconds());
    1156         compositedRenderer->startAnimation(0, animation.ptr(), m_blendingKeyframes);
     1174        auto timeOffset = animation()->currentTime().value().seconds();
     1175        if (timing()->delay() < 0_s)
     1176            timeOffset = -timing()->delay().seconds();
     1177
     1178        for (const auto& action : m_pendingAcceleratedActions) {
     1179            switch (action) {
     1180            case AcceleratedAction::Play:
     1181                compositedRenderer->startAnimation(timeOffset, backingAnimationForCompositedRenderer().ptr(), m_blendingKeyframes);
     1182                break;
     1183            case AcceleratedAction::Pause:
     1184                compositedRenderer->animationPaused(timeOffset, m_blendingKeyframes.animationName());
     1185                break;
     1186            case AcceleratedAction::Seek:
     1187                compositedRenderer->animationSeeked(timeOffset, m_blendingKeyframes.animationName());
     1188                break;
     1189            }
     1190        }
     1191        m_pendingAcceleratedActions.clear();
    11571192    } else {
    11581193        compositedRenderer->animationFinished(m_blendingKeyframes.animationName());
     
    11621197}
    11631198
     1199Ref<const Animation> KeyframeEffectReadOnly::backingAnimationForCompositedRenderer() const
     1200{
     1201    auto effectAnimation = animation();
     1202    if (is<DeclarativeAnimation>(effectAnimation))
     1203        return downcast<DeclarativeAnimation>(effectAnimation)->backingAnimation();
     1204
     1205    // FIXME: The iterationStart and endDelay AnimationEffectTimingReadOnly properties do not have
     1206    // corresponding Animation properties.
     1207    auto effectTiming = timing();
     1208    auto animation = Animation::create();
     1209    animation->setDuration(effectTiming->iterationDuration().seconds());
     1210    animation->setDelay(effectTiming->delay().seconds());
     1211    animation->setIterationCount(effectTiming->iterations());
     1212    animation->setTimingFunction(effectTiming->timingFunction()->clone());
     1213
     1214    switch (effectTiming->fill()) {
     1215    case FillMode::None:
     1216    case FillMode::Auto:
     1217        animation->setFillMode(AnimationFillModeNone);
     1218        break;
     1219    case FillMode::Backwards:
     1220        animation->setFillMode(AnimationFillModeBackwards);
     1221        break;
     1222    case FillMode::Forwards:
     1223        animation->setFillMode(AnimationFillModeForwards);
     1224        break;
     1225    case FillMode::Both:
     1226        animation->setFillMode(AnimationFillModeBoth);
     1227        break;
     1228    }
     1229
     1230    switch (effectTiming->direction()) {
     1231    case PlaybackDirection::Normal:
     1232        animation->setDirection(Animation::AnimationDirectionNormal);
     1233        break;
     1234    case PlaybackDirection::Alternate:
     1235        animation->setDirection(Animation::AnimationDirectionAlternate);
     1236        break;
     1237    case PlaybackDirection::Reverse:
     1238        animation->setDirection(Animation::AnimationDirectionReverse);
     1239        break;
     1240    case PlaybackDirection::AlternateReverse:
     1241        animation->setDirection(Animation::AnimationDirectionAlternateReverse);
     1242        break;
     1243    }
     1244
     1245    return WTFMove(animation);
     1246}
     1247
    11641248RenderElement* KeyframeEffectReadOnly::renderer() const
    11651249{
  • trunk/Source/WebCore/animation/KeyframeEffectReadOnly.h

    r230068 r230574  
    9999    void apply(RenderStyle&) override;
    100100    void invalidate() override;
    101     void startOrStopAccelerated();
     101    void animationPlayStateDidChange(WebAnimation::PlayState) final;
     102    void animationDidSeek() final;
     103    void applyPendingAcceleratedActions();
    102104    bool isRunningAccelerated() const { return m_startedAccelerated; }
    103105
    104106    RenderElement* renderer() const override;
    105107    const RenderStyle& currentStyle() const override;
    106     bool isAccelerated() const override { return false; }
     108    bool isAccelerated() const override { return m_startedAccelerated; }
    107109    bool filterFunctionListsMatch() const override { return m_filterFunctionListsMatch; }
    108110    bool transformFunctionListsMatch() const override { return m_transformFunctionListsMatch; }
     
    126128
    127129private:
     130    enum class AcceleratedAction { Play, Pause, Seek };
     131    void addPendingAcceleratedAction(AcceleratedAction);
    128132    void setAnimatedPropertiesInStyle(RenderStyle&, double);
    129133    TimingFunction* timingFunctionForKeyframeAtIndex(size_t);
     134    Ref<const Animation> backingAnimationForCompositedRenderer() const;
    130135    void computeStackingContextImpact();
    131136    void updateBlendingKeyframes();
    132     bool shouldRunAccelerated();
     137    void computeShouldRunAccelerated();
    133138    void setBlendingKeyframes(KeyframeList&);
    134139    void checkForMatchingTransformFunctionLists();
     
    138143#endif
    139144
     145    bool m_shouldRunAccelerated { false };
    140146    bool m_triggersStackingContext { false };
    141147    bool m_started { false };
     
    150156    KeyframeList m_blendingKeyframes;
    151157    Vector<ParsedKeyframe> m_parsedKeyframes;
     158
     159    Vector<AcceleratedAction> m_pendingAcceleratedActions;
    152160};
    153161
  • trunk/Source/WebCore/animation/WebAnimation.cpp

    r229864 r230574  
    383383    // 3. Run the procedure to update an animation's finished state for animation with the did seek flag set to true, and the synchronously notify flag set to false.
    384384    updateFinishedState(DidSeek::Yes, SynchronouslyNotify::No);
     385
     386    if (m_effect)
     387        m_effect->animationDidSeek();
    385388
    386389    return { };
     
    780783    updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
    781784
     785    if (m_effect)
     786        m_effect->animationPlayStateDidChange(PlayState::Running);
     787
    782788    return { };
    783789}
     
    876882    updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
    877883
     884    if (m_effect)
     885        m_effect->animationPlayStateDidChange(PlayState::Paused);
     886
    878887    return { };
    879888}
     
    10091018}
    10101019
    1011 void WebAnimation::acceleratedRunningStateDidChange()
     1020void WebAnimation::acceleratedStateDidChange()
    10121021{
    10131022    if (is<DocumentTimeline>(m_timeline))
     
    10151024}
    10161025
    1017 void WebAnimation::startOrStopAccelerated()
     1026void WebAnimation::applyPendingAcceleratedActions()
    10181027{
    10191028    if (is<KeyframeEffectReadOnly>(m_effect))
    1020         downcast<KeyframeEffectReadOnly>(*m_effect).startOrStopAccelerated();
     1029        downcast<KeyframeEffectReadOnly>(*m_effect).applyPendingAcceleratedActions();
    10211030}
    10221031
  • trunk/Source/WebCore/animation/WebAnimation.h

    r229983 r230574  
    101101    void resolve(RenderStyle&);
    102102    void effectTargetDidChange(Element* previousTarget, Element* newTarget);
    103     void acceleratedRunningStateDidChange();
    104     void startOrStopAccelerated();
     103    void acceleratedStateDidChange();
     104    void applyPendingAcceleratedActions();
    105105
    106106    void timingModelDidChange();
  • trunk/Source/WebCore/platform/graphics/GraphicsLayer.h

    r229174 r230574  
    453453    virtual bool addAnimation(const KeyframeValueList&, const FloatSize& /*boxSize*/, const Animation*, const String& /*animationName*/, double /*timeOffset*/)  { return false; }
    454454    virtual void pauseAnimation(const String& /*animationName*/, double /*timeOffset*/) { }
     455    virtual void seekAnimation(const String& /*animationName*/, double /*timeOffset*/) { }
    455456    virtual void removeAnimation(const String& /*animationName*/) { }
    456457
  • trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp

    r230347 r230574  
    10071007}
    10081008
     1009void GraphicsLayerCA::addProcessingActionForAnimation(const String& animationName, AnimationProcessingAction processingAction)
     1010{
     1011    auto& processingActions = m_animationsToProcess.ensure(animationName, [] {
     1012        return Vector<AnimationProcessingAction> { };
     1013    }).iterator->value;
     1014
     1015    if (!processingActions.isEmpty() && processingActions.last().action == Remove)
     1016        return;
     1017
     1018    processingActions.append(processingAction);
     1019}
     1020
    10091021bool GraphicsLayerCA::addAnimation(const KeyframeValueList& valueList, const FloatSize& boxSize, const Animation* anim, const String& animationName, double timeOffset)
    10101022{
     
    10421054    LOG(Animations, "GraphicsLayerCA %p pauseAnimation %s (running %d)", this, animationName.utf8().data(), animationIsRunning(animationName));
    10431055
     1056    // Call add since if there is already a Remove in there, we don't want to overwrite it with a Pause.
     1057    addProcessingActionForAnimation(animationName, AnimationProcessingAction { Pause, Seconds { timeOffset } });
     1058
     1059    noteLayerPropertyChanged(AnimationChanged);
     1060}
     1061
     1062void GraphicsLayerCA::seekAnimation(const String& animationName, double timeOffset)
     1063{
     1064    LOG(Animations, "GraphicsLayerCA %p seekAnimation %s (running %d)", this, animationName.utf8().data(), animationIsRunning(animationName));
     1065
     1066    // Call add since if there is already a Remove in there, we don't want to overwrite it with a Pause.
     1067    addProcessingActionForAnimation(animationName, AnimationProcessingAction { Seek, Seconds { timeOffset } });
     1068
     1069    noteLayerPropertyChanged(AnimationChanged);
     1070}
     1071
     1072void GraphicsLayerCA::removeAnimation(const String& animationName)
     1073{
     1074    LOG(Animations, "GraphicsLayerCA %p removeAnimation %s (running %d)", this, animationName.utf8().data(), animationIsRunning(animationName));
     1075
    10441076    if (!animationIsRunning(animationName))
    10451077        return;
    10461078
    1047     // Call add since if there is already a Remove in there, we don't want to overwrite it with a Pause.
    1048     m_animationsToProcess.add(animationName, AnimationProcessingAction { Pause, Seconds { timeOffset } });
    1049 
    1050     noteLayerPropertyChanged(AnimationChanged);
    1051 }
    1052 
    1053 void GraphicsLayerCA::removeAnimation(const String& animationName)
    1054 {
    1055     LOG(Animations, "GraphicsLayerCA %p removeAnimation %s (running %d)", this, animationName.utf8().data(), animationIsRunning(animationName));
    1056 
    1057     if (!animationIsRunning(animationName))
    1058         return;
    1059 
    1060     m_animationsToProcess.add(animationName, AnimationProcessingAction(Remove));
     1079    addProcessingActionForAnimation(animationName, AnimationProcessingAction(Remove));
    10611080    noteLayerPropertyChanged(AnimationChanged);
    10621081}
     
    27622781void GraphicsLayerCA::updateAnimations()
    27632782{
    2764     if (m_animationsToProcess.size()) {
    2765         AnimationsToProcessMap::const_iterator end = m_animationsToProcess.end();
    2766         for (AnimationsToProcessMap::const_iterator it = m_animationsToProcess.begin(); it != end; ++it) {
    2767             const String& currAnimationName = it->key;
    2768             AnimationsMap::iterator animationIt = m_runningAnimations.find(currAnimationName);
    2769             if (animationIt == m_runningAnimations.end())
    2770                 continue;
    2771 
    2772             const AnimationProcessingAction& processingInfo = it->value;
    2773             const Vector<LayerPropertyAnimation>& animations = animationIt->value;
    2774             for (size_t i = 0; i < animations.size(); ++i) {
    2775                 const LayerPropertyAnimation& currAnimation = animations[i];
    2776                 switch (processingInfo.action) {
    2777                 case Remove:
    2778                     removeCAAnimationFromLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index, currAnimation.m_subIndex);
    2779                     break;
    2780                 case Pause:
    2781                     pauseCAAnimationOnLayer(currAnimation.m_property, currAnimationName, currAnimation.m_index, currAnimation.m_subIndex, processingInfo.timeOffset);
    2782                     break;
    2783                 }
    2784             }
    2785 
    2786             if (processingInfo.action == Remove)
    2787                 m_runningAnimations.remove(currAnimationName);
    2788         }
    2789    
    2790         m_animationsToProcess.clear();
    2791     }
    2792    
    27932783    size_t numAnimations;
    27942784    if ((numAnimations = m_uncomittedAnimations.size())) {
     
    27962786            const LayerPropertyAnimation& pendingAnimation = m_uncomittedAnimations[i];
    27972787            setAnimationOnLayer(*pendingAnimation.m_animation, pendingAnimation.m_property, pendingAnimation.m_name, pendingAnimation.m_index, pendingAnimation.m_subIndex, pendingAnimation.m_timeOffset);
    2798            
     2788
    27992789            AnimationsMap::iterator it = m_runningAnimations.find(pendingAnimation.m_name);
    28002790            if (it == m_runningAnimations.end()) {
     
    28092799        m_uncomittedAnimations.clear();
    28102800    }
     2801
     2802    if (m_animationsToProcess.size()) {
     2803        AnimationsToProcessMap::const_iterator end = m_animationsToProcess.end();
     2804        for (AnimationsToProcessMap::const_iterator it = m_animationsToProcess.begin(); it != end; ++it) {
     2805            const String& currentAnimationName = it->key;
     2806            auto animationIterator = m_runningAnimations.find(currentAnimationName);
     2807            if (animationIterator == m_runningAnimations.end())
     2808                continue;
     2809
     2810            for (const auto& processingInfo : it->value) {
     2811                const Vector<LayerPropertyAnimation>& animations = animationIterator->value;
     2812                for (const auto& currentAnimation : animations) {
     2813                    switch (processingInfo.action) {
     2814                    case Remove:
     2815                        removeCAAnimationFromLayer(currentAnimation.m_property, currentAnimationName, currentAnimation.m_index, currentAnimation.m_subIndex);
     2816                        break;
     2817                    case Pause:
     2818                        pauseCAAnimationOnLayer(currentAnimation.m_property, currentAnimationName, currentAnimation.m_index, currentAnimation.m_subIndex, processingInfo.timeOffset);
     2819                        break;
     2820                    case Seek:
     2821                        seekCAAnimationOnLayer(currentAnimation.m_property, currentAnimationName, currentAnimation.m_index, currentAnimation.m_subIndex, processingInfo.timeOffset);
     2822                        break;
     2823                    }
     2824                }
     2825
     2826                if (processingInfo.action == Remove)
     2827                    m_runningAnimations.remove(currentAnimationName);
     2828            }
     2829
     2830        }
     2831
     2832        m_animationsToProcess.clear();
     2833    }
    28112834}
    28122835
     
    29042927    newAnim->setTimeOffset(timeOffset.seconds());
    29052928   
     2929    layer->addAnimationForKey(animationID, *newAnim); // This will replace the running animation.
     2930
     2931    // Pause the animations on the clones too.
     2932    if (LayerMap* layerCloneMap = animatedLayerClones(property)) {
     2933        for (auto& clone : *layerCloneMap) {
     2934            // Skip immediate replicas, since they move with the original.
     2935            if (m_replicaLayer && isReplicatedRootClone(clone.key))
     2936                continue;
     2937            clone.value->addAnimationForKey(animationID, *newAnim);
     2938        }
     2939    }
     2940}
     2941
     2942void GraphicsLayerCA::seekCAAnimationOnLayer(AnimatedPropertyID property, const String& animationName, int index, int subIndex, Seconds timeOffset)
     2943{
     2944    // FIXME: this can be refactored a fair bit or merged with pauseCAAnimationOnLayer() with an operation flag.
     2945    PlatformCALayer* layer = animatedLayer(property);
     2946
     2947    String animationID = animationIdentifier(animationName, property, index, subIndex);
     2948
     2949    RefPtr<PlatformCAAnimation> currentAnimation = layer->animationForKey(animationID);
     2950    if (!currentAnimation)
     2951        return;
     2952
     2953    // Animations on the layer are immutable, so we have to clone and modify.
     2954    RefPtr<PlatformCAAnimation> newAnim = currentAnimation->copy();
     2955
     2956    newAnim->setTimeOffset(timeOffset.seconds());
     2957
    29062958    layer->addAnimationForKey(animationID, *newAnim); // This will replace the running animation.
    29072959
  • trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h

    r229174 r230574  
    131131    WEBCORE_EXPORT bool addAnimation(const KeyframeValueList&, const FloatSize& boxSize, const Animation*, const String& animationName, double timeOffset) override;
    132132    WEBCORE_EXPORT void pauseAnimation(const String& animationName, double timeOffset) override;
     133    WEBCORE_EXPORT void seekAnimation(const String& animationName, double timeOffset) override;
    133134    WEBCORE_EXPORT void removeAnimation(const String& animationName) override;
    134135
     
    444445    bool removeCAAnimationFromLayer(AnimatedPropertyID, const String& animationName, int index, int subINdex);
    445446    void pauseCAAnimationOnLayer(AnimatedPropertyID, const String& animationName, int index, int subIndex, Seconds timeOffset);
     447    void seekCAAnimationOnLayer(AnimatedPropertyID, const String& animationName, int index, int subIndex, Seconds timeOffset);
    446448
    447449    enum MoveOrCopy { Move, Copy };
     
    519521    void repaintLayerDirtyRects();
    520522
     523    enum Action { Remove, Pause, Seek };
     524    struct AnimationProcessingAction {
     525        AnimationProcessingAction(Action action = Remove, Seconds timeOffset = 0_s)
     526            : action(action)
     527            , timeOffset(timeOffset)
     528        {
     529        }
     530        Action action;
     531        Seconds timeOffset; // Only used for pause.
     532    };
     533    void addProcessingActionForAnimation(const String&, AnimationProcessingAction);
     534
    521535    RefPtr<PlatformCALayer> m_layer; // The main layer
    522536    RefPtr<PlatformCALayer> m_structuralLayer; // A layer used for structural reasons, like preserves-3d or replica-flattening. Is the parent of m_layer.
     
    582596    Vector<LayerPropertyAnimation> m_uncomittedAnimations;
    583597   
    584     enum Action { Remove, Pause };
    585     struct AnimationProcessingAction {
    586         AnimationProcessingAction(Action action = Remove, Seconds timeOffset = 0_s)
    587             : action(action)
    588             , timeOffset(timeOffset)
    589         {
    590         }
    591         Action action;
    592         Seconds timeOffset; // only used for pause
    593     };
    594     typedef HashMap<String, AnimationProcessingAction> AnimationsToProcessMap;
     598    typedef HashMap<String, Vector<AnimationProcessingAction>> AnimationsToProcessMap;
    595599    AnimationsToProcessMap m_animationsToProcess;
    596600
  • trunk/Source/WebCore/rendering/RenderBoxModelObject.cpp

    r229174 r230574  
    195195}
    196196
     197void RenderBoxModelObject::animationSeeked(double timeOffset, const String& name)
     198{
     199    ASSERT(hasLayer());
     200    ASSERT(isComposited());
     201    layer()->backing()->animationSeeked(timeOffset, name);
     202}
     203
    197204void RenderBoxModelObject::animationFinished(const String& name)
    198205{
  • trunk/Source/WebCore/rendering/RenderBoxModelObject.h

    r229174 r230574  
    230230    bool startAnimation(double timeOffset, const Animation*, const KeyframeList& keyframes);
    231231    void animationPaused(double timeOffset, const String& name);
     232    void animationSeeked(double timeOffset, const String& name);
    232233    void animationFinished(const String& name);
    233234
  • trunk/Source/WebCore/rendering/RenderLayerBacking.cpp

    r230334 r230574  
    27792779}
    27802780
     2781void RenderLayerBacking::animationSeeked(double timeOffset, const String& animationName)
     2782{
     2783    m_graphicsLayer->seekAnimation(animationName, timeOffset);
     2784}
     2785
    27812786void RenderLayerBacking::animationFinished(const String& animationName)
    27822787{
  • trunk/Source/WebCore/rendering/RenderLayerBacking.h

    r229174 r230574  
    168168    bool startAnimation(double timeOffset, const Animation* anim, const KeyframeList& keyframes);
    169169    void animationPaused(double timeOffset, const String& name);
     170    void animationSeeked(double timeOffset, const String& name);
    170171    void animationFinished(const String& name);
    171172
  • trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp

    r230211 r230574  
    5555#include "RenderVideo.h"
    5656#include "RenderView.h"
     57#include "RuntimeEnabledFeatures.h"
    5758#include "ScrollingConstraints.h"
    5859#include "ScrollingCoordinator.h"
     
    24782479    }
    24792480
     2481    if (RuntimeEnabledFeatures::sharedFeatures().cssAnimationsAndCSSTransitionsBackedByWebAnimationsEnabled())
     2482        return false;
     2483
    24802484    const AnimationBase::RunningState activeAnimationState = AnimationBase::Running | AnimationBase::Paused;
    24812485    auto& animController = renderer.animation();
Note: See TracChangeset for help on using the changeset viewer.