Changeset 272201 in webkit
- Timestamp:
- Feb 2, 2021 2:59:00 AM (18 months ago)
- Location:
- trunk
- Files:
-
- 4 added
- 4 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/webanimations/relative-ordering-of-translate-and-rotate-properties-accelerated-expected.html (added)
-
LayoutTests/webanimations/relative-ordering-of-translate-and-rotate-properties-accelerated.html (added)
-
LayoutTests/webanimations/relative-ordering-of-translate-and-scale-properties-accelerated-expected.html (added)
-
LayoutTests/webanimations/relative-ordering-of-translate-and-scale-properties-accelerated.html (added)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp (modified) (8 diffs)
-
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r272198 r272201 1 2021-02-01 Antoine Quint <graouts@webkit.org> 2 3 Animation of "rotate" or "scale" property does not correctly account for static "translate" property 4 https://bugs.webkit.org/show_bug.cgi?id=219894 5 <rdar://problem/72342798> 6 7 Reviewed by Dean Jackson. 8 9 Add two new tests that ensure that translate is indeed applied before rotate and scale. 10 11 * webanimations/relative-ordering-of-translate-and-rotate-properties-accelerated-expected.html: Added. 12 * webanimations/relative-ordering-of-translate-and-rotate-properties-accelerated.html: Added. 13 * webanimations/relative-ordering-of-translate-and-scale-properties-accelerated-expected.html: Added. 14 * webanimations/relative-ordering-of-translate-and-scale-properties-accelerated.html: Added. 15 1 16 2021-02-02 Rob Buis <rbuis@igalia.com> 2 17 -
trunk/Source/WebCore/ChangeLog
r272200 r272201 1 2021-02-01 Antoine Quint <graouts@webkit.org> 2 3 Animation of "rotate" or "scale" property does not correctly account for static "translate" property 4 https://bugs.webkit.org/show_bug.cgi?id=219894 5 <rdar://problem/72342798> 6 7 Reviewed by Dean Jackson. 8 9 The CSS transform-related properties are designed to be applied in a specific order, guaranteeing that 10 "translate" is applied prior to both "scale" and "rotate". Since Core Animation has no concept of these 11 individual transform-related CSS properties, we use additive Core Animation animations to apply the value 12 of each CSS property, using non-interpolating animations set to start at the earliest time in the Core 13 Animation timeline and lasting forever to set the value of any underlying, non-animated value. 14 15 As such, in an example where an element would have a static "translate" property set as well as a "rotate" 16 or "scale" animation, we would yield the following animations, added in this order: 17 18 1. non-interpolating animation beginning at 1s setting the identity transform (the "clean slate" animation) 19 2. interpolating animation beginning at a time > 1s for the "scale" or "rotate" animation 20 3. non-interpolating animation beginning at 1s setting the "translate" value 21 22 Note that animations 2 and 3 are additive and thus added in the inverse order that we expect animations to be 23 applied. Due to a peculiarity of Core Animation (introduced in macOS 10.15), additive animations are applied 24 in an inverse order, hence the build-time flag CA_WHERE_ADDITIVE_TRANSFORMS_ARE_REVERSED. 25 26 However, Core Animation will first sort all animations based on their begin time, only respecting the order 27 in which animations are added when their begin time is equal. This means that in practice, our animations were 28 applied in the order 1, 3, 2, and thus the "translate" property was applied after the "rotate" or "scale" animation. 29 30 In order to address this, we now create a CAAnimationGroup for each set of animations created for a given CSS 31 property. Each of these groups shares the same begin time, 1s, to allow for "forever" non-interpolating animations 32 to be applied, but also to set a common base time for animations to be applied in the expected order. 33 34 Tests: webanimations/relative-ordering-of-translate-and-rotate-properties-accelerated.html 35 webanimations/relative-ordering-of-translate-and-scale-properties-accelerated.html 36 37 * platform/graphics/ca/GraphicsLayerCA.cpp: 38 (WebCore::GraphicsLayerCA::updateAnimations): 39 * platform/graphics/ca/GraphicsLayerCA.h: 40 (WebCore::GraphicsLayerCA::LayerPropertyAnimation::computedBeginTime const): 41 1 42 2021-02-02 Youenn Fablet <youenn@apple.com> 2 43 -
trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp
r272004 r272201 2889 2889 void GraphicsLayerCA::updateAnimations() 2890 2890 { 2891 auto baseTransformAnimationBeginTime = 1_s; 2891 // In order to guarantee that transform animations are applied in the expected order (translate, rotate, scale and transform), 2892 // we need to have them wrapped individually in an animation group because Core Animation sorts animations first by their begin 2893 // time, and then by the order in which they were added (for those with the same begin time). Since a rotate animation can have 2894 // an earlier begin time than a translate animation, we cannot rely on adding the animations in the correct order. 2895 // 2896 // Having an animation group wrapping each animation means that we can guarantee the order in which animations are applied by 2897 // ensuring they each have the same begin time. We set this begin time to be the smallest value possible, ensuring that base 2898 // transform animations are applied continuously. We'll then set the begin time of interpolating animations to be local to the 2899 // animation group, which means subtracting the group's begin time. 2900 2901 // We use 1_s here because 0_s would have special meaning in Core Animation, meaning that the animation would have its begin 2902 // time set to the current time when it's committed. 2903 auto animationGroupBeginTime = 1_s; 2904 auto infiniteDuration = std::numeric_limits<double>::max(); 2892 2905 auto currentTime = Seconds(CACurrentMediaTime()); 2893 auto updateBeginTimes = [&](LayerPropertyAnimation& animation) 2894 { 2895 if (animation.m_pendingRemoval) 2896 return; 2897 2898 // In case we have an offset, and we haven't set an explicit begin time previously, 2899 // we need to record the beginTime now. 2900 if (animation.m_timeOffset && !animation.m_beginTime) 2901 animation.m_beginTime = currentTime; 2902 2903 // Now check if we have a resolved begin time and ensure the begin time we'll use for 2904 // base transform animations matches the smallest known begin time to guarantee that 2905 // such animations can combine with other explicit transform animations correctly. 2906 if (auto computedBeginTime = animation.computedBeginTime()) 2907 baseTransformAnimationBeginTime = std::min(baseTransformAnimationBeginTime, *computedBeginTime); 2906 2907 auto addAnimationGroup = [&](AnimatedPropertyID property, const Vector<RefPtr<PlatformCAAnimation>>& animations) { 2908 auto caAnimationGroup = createPlatformCAAnimation(PlatformCAAnimation::Group, ""); 2909 caAnimationGroup->setDuration(infiniteDuration); 2910 caAnimationGroup->setAnimations(animations); 2911 2912 auto animationGroup = LayerPropertyAnimation(WTFMove(caAnimationGroup), "group-" + createCanonicalUUIDString(), property, 0, 0, 0_s); 2913 animationGroup.m_beginTime = animationGroupBeginTime; 2914 2915 setAnimationOnLayer(animationGroup); 2916 m_animationGroups.append(WTFMove(animationGroup)); 2908 2917 }; 2909 2918 2910 2919 enum class Additive { Yes, No }; 2911 auto addAnimation = [&](LayerPropertyAnimation& animation, Additive additive = Additive::Yes) { 2912 animation.m_animation->setAdditive(additive == Additive::Yes); 2913 setAnimationOnLayer(animation); 2920 auto prepareAnimationForAddition = [&](LayerPropertyAnimation& animation, Additive additive = Additive::Yes) { 2921 auto caAnim = animation.m_animation; 2922 caAnim->setAdditive(additive == Additive::Yes); 2923 if (auto beginTime = animation.computedBeginTime()) 2924 caAnim->setBeginTime(beginTime->seconds()); 2925 2914 2926 if (animation.m_playState == PlayState::PausePending || animation.m_playState == PlayState::Paused) { 2915 pauseCAAnimationOnLayer(animation); 2927 caAnim->setSpeed(0); 2928 caAnim->setTimeOffset(animation.m_timeOffset.seconds()); 2916 2929 animation.m_playState = PlayState::Paused; 2917 2930 } else … … 2919 2932 }; 2920 2933 2934 auto addAnimation = [&](LayerPropertyAnimation& animation, Additive additive = Additive::Yes) { 2935 prepareAnimationForAddition(animation, additive); 2936 addAnimationGroup(animation.m_property, { animation.m_animation }); 2937 }; 2938 2921 2939 enum class TransformationMatrixSource { UseIdentityMatrix, AskClient }; 2922 auto addBaseValueTransformAnimation = [&](AnimatedPropertyID property, TransformationMatrixSource matrixSource = TransformationMatrixSource::AskClient, Seconds beginTimeOfEarliestPropertyAnimation = 0_s){2940 auto makeBaseValueTransformAnimation = [&](AnimatedPropertyID property, TransformationMatrixSource matrixSource = TransformationMatrixSource::AskClient, Seconds beginTimeOfEarliestPropertyAnimation = 0_s) -> LayerPropertyAnimation* { 2923 2941 // A base value transform animation can either be set to the identity matrix or to read the underlying 2924 2942 // value from the GraphicsLayerClient. If we didn't explicitly ask for an identity matrix, we can skip … … 2926 2944 auto matrix = matrixSource == TransformationMatrixSource::UseIdentityMatrix ? TransformationMatrix() : client().transformMatrixForProperty(property); 2927 2945 if (matrixSource == TransformationMatrixSource::AskClient && matrix.isIdentity()) 2928 return ;2946 return nullptr; 2929 2947 2930 2948 auto delay = beginTimeOfEarliestPropertyAnimation > currentTime ? beginTimeOfEarliestPropertyAnimation - currentTime : 0_s; … … 2934 2952 // of the delay until that animation. 2935 2953 auto caAnimation = createPlatformCAAnimation(PlatformCAAnimation::Basic, propertyIdToString(property)); 2936 caAnimation->setDuration( (delay ? delay : Seconds::infinity()).seconds());2954 caAnimation->setDuration(delay ? delay.seconds() : infiniteDuration); 2937 2955 caAnimation->setFromValue(matrix); 2938 2956 caAnimation->setToValue(matrix); 2939 2957 2940 2958 auto animation = LayerPropertyAnimation(WTFMove(caAnimation), "base-transform-" + createCanonicalUUIDString(), property, 0, 0, 0_s); 2941 // To ensure the base value transform is applied along with all the interpolating animations, we set it to have started 2942 // as early as possible, which combined with the infinite duration ensures it's current for any given CA media time, 2943 // unless we're just filling until an animation for this property starts, in which case it must start now. 2944 animation.m_beginTime = delay ? currentTime : baseTransformAnimationBeginTime; 2945 2959 if (delay) 2960 animation.m_beginTime = currentTime - animationGroupBeginTime; 2961 2962 m_baseValueTransformAnimations.append(WTFMove(animation)); 2963 return &m_baseValueTransformAnimations.last(); 2964 }; 2965 2966 auto addBaseValueTransformAnimation = [&](AnimatedPropertyID property, TransformationMatrixSource matrixSource = TransformationMatrixSource::AskClient, Seconds beginTimeOfEarliestPropertyAnimation = 0_s) { 2946 2967 // Additivity will depend on the source of the matrix, if it was explicitly provided as an identity matrix, it 2947 2968 // is the initial base value transform animation and must override the current transform value for this layer. 2948 2969 // Otherwise, it is meant to apply the underlying value for one specific transform-related property and be additive 2949 2970 // to be combined with the other base value transform animations and interpolating animations. 2950 addAnimation(animation, matrixSource == TransformationMatrixSource::AskClient ? Additive::Yes : Additive::No);2951 m_baseValueTransformAnimations.append(WTFMove(animation));2971 if (auto* animation = makeBaseValueTransformAnimation(property, matrixSource, beginTimeOfEarliestPropertyAnimation)) 2972 addAnimation(*animation, matrixSource == TransformationMatrixSource::AskClient ? Additive::Yes : Additive::No); 2952 2973 }; 2953 2974 2954 // Iterate through all animations to update each animation's begin time, if necessary, 2955 // compute the base transform animation begin times and remove all running CA animations. 2975 // Iterate through all animations to set the begin time of any new animations. 2956 2976 for (auto& animation : m_animations) { 2957 updateBeginTimes(animation); 2958 if (animation.m_playState == PlayState::Playing || animation.m_playState == PlayState::Paused) 2959 removeCAAnimationFromLayer(animation); 2960 } 2961 2962 // Also remove all the base value transform CA animations. 2963 for (auto& animation : m_baseValueTransformAnimations) 2964 removeCAAnimationFromLayer(animation); 2965 2966 // Now remove all the animations marked as pending removal and all base value transform animations. 2977 if (!animation.m_pendingRemoval && !animation.m_beginTime) 2978 animation.m_beginTime = currentTime - animationGroupBeginTime; 2979 } 2980 2981 // Now, remove all animation groups from the layer so that we no longer have any layer animations. 2982 for (auto& animationGroup : m_animationGroups) 2983 removeCAAnimationFromLayer(animationGroup); 2984 2985 // We can remove all previously-created base value transform animations and animation groups. 2986 m_baseValueTransformAnimations.clear(); 2987 m_animationGroups.clear(); 2988 2989 // Now remove all the animations marked as pending removal. 2967 2990 m_animations.removeAllMatching([&](LayerPropertyAnimation animation) { 2968 2991 return animation.m_pendingRemoval; 2969 2992 }); 2970 m_baseValueTransformAnimations.clear();2971 2993 2972 2994 // Now that our list of animations is current, we can separate animations by property so that … … 3050 3072 } 3051 3073 3052 if (earliestBeginTime > currentTime) 3053 addBaseValueTransformAnimation(property, TransformationMatrixSource::AskClient, earliestBeginTime); 3054 3055 for (auto* animation : animations) 3056 addAnimation(*animation); 3074 if (earliestBeginTime) 3075 earliestBeginTime += animationGroupBeginTime; 3076 3077 if (earliestBeginTime > currentTime) { 3078 if (auto* baseValueTransformAnimation = makeBaseValueTransformAnimation(property, TransformationMatrixSource::AskClient, earliestBeginTime)) { 3079 prepareAnimationForAddition(*baseValueTransformAnimation); 3080 caAnimations.append(baseValueTransformAnimation->m_animation); 3081 } 3082 } 3083 3084 for (auto* animation : animations) { 3085 prepareAnimationForAddition(*animation); 3086 caAnimations.append(animation->m_animation); 3087 } 3088 3089 addAnimationGroup(property, caAnimations); 3057 3090 }; 3058 3091 … … 3069 3102 3070 3103 Seconds earliestBeginTime = 0_s; 3104 Vector<RefPtr<PlatformCAAnimation>> caAnimations; 3071 3105 for (auto* animation : WTF::makeReversedRange(animations)) { 3072 3106 if (auto beginTime = animation->computedBeginTime()) { … … 3074 3108 earliestBeginTime = *beginTime; 3075 3109 } 3076 addAnimation(*animation); 3110 prepareAnimationForAddition(*animation); 3111 caAnimations.append(animation->m_animation); 3077 3112 } 3078 3113 3079 if (earliestBeginTime > currentTime) 3080 addBaseValueTransformAnimation(property, TransformationMatrixSource::AskClient, earliestBeginTime); 3114 if (earliestBeginTime) 3115 earliestBeginTime += animationGroupBeginTime; 3116 3117 if (earliestBeginTime > currentTime) { 3118 if (auto* baseValueTransformAnimation = makeBaseValueTransformAnimation(property, TransformationMatrixSource::AskClient, earliestBeginTime)) { 3119 prepareAnimationForAddition(*baseValueTransformAnimation); 3120 caAnimations.append(baseValueTransformAnimation->m_animation); 3121 } 3122 } 3123 3124 addAnimationGroup(property, caAnimations); 3081 3125 }; 3082 3126 … … 4441 4485 4442 4486 for (auto& animation : m_animations) { 4443 auto caAnimation = animatedLayer(animation.m_property)->animationForKey(animation.animationIdentifier()); 4444 animations.append({ animatedPropertyIDAsString(animation.m_property), caAnimation->speed() }); 4487 if (auto caAnimation = animatedLayer(animation.m_property)->animationForKey(animation.animationIdentifier())) 4488 animations.append({ animatedPropertyIDAsString(animation.m_property), caAnimation->speed() }); 4489 else 4490 animations.append({ animatedPropertyIDAsString(animation.m_property), (animation.m_playState == PlayState::Playing || animation.m_playState == PlayState::PlayPending) ? 1 : 0 }); 4445 4491 } 4446 4492 -
trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h
r271699 r272201 475 475 { 476 476 if (m_beginTime) 477 return m_beginTime - m_timeOffset;477 return *m_beginTime - m_timeOffset; 478 478 return WTF::nullopt; 479 479 } … … 485 485 int m_subIndex; 486 486 Seconds m_timeOffset { 0_s }; 487 Seconds m_beginTime { 0_s };487 Optional<Seconds> m_beginTime; 488 488 PlayState m_playState { PlayState::PlayPending }; 489 489 bool m_pendingRemoval { false }; … … 617 617 Vector<LayerPropertyAnimation> m_animations; 618 618 Vector<LayerPropertyAnimation> m_baseValueTransformAnimations; 619 Vector<LayerPropertyAnimation> m_animationGroups; 619 620 620 621 Vector<FloatRect> m_dirtyRects;
Note: See TracChangeset
for help on using the changeset viewer.