Changeset 261926 in webkit
- Timestamp:
- May 20, 2020 9:26:16 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 8 added
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r261924 r261926 1 2020-05-20 Antoine Quint <graouts@apple.com> 2 3 [Web Animations] Animation engine should not wake up every tick for steps timing functions 4 https://bugs.webkit.org/show_bug.cgi?id=212103 5 <rdar://problem/62737868> 6 7 Reviewed by Simon Fraser. 8 9 Add tests that check that an animation using a steps() timing function correctly computes the time to 10 the next tick accouning for the fact that it won't compute a different iteration progress until the 11 next step. 12 13 * webanimations/scheduling-of-animation-with-steps-timing-function-on-effect-expected.txt: Added. 14 * webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html: Added. 15 * webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe-expected.txt: Added. 16 * webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html: Added. 17 * webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes-expected.txt: Added. 18 * webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html: Added. 19 * webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function-expected.txt: Added. 20 * webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html: Added. 21 1 22 2020-05-20 Noam Rosenthal <noam@webkit.org> 2 23 -
trunk/Source/WTF/ChangeLog
r261915 r261926 1 2020-05-20 Antoine Quint <graouts@apple.com> 2 3 [Web Animations] Animation engine should not wake up every tick for steps timing functions 4 https://bugs.webkit.org/show_bug.cgi?id=212103 5 <rdar://problem/62737868> 6 7 Reviewed by Simon Fraser. 8 9 Allow Seconds to be divided or multiplied by a double with operands in any order. 10 11 * wtf/Seconds.h: 12 (WTF::operator*): 13 (WTF::operator/): 14 1 15 2020-05-20 Youenn Fablet <youenn@apple.com> 2 16 -
trunk/Source/WTF/wtf/Seconds.h
r259917 r261926 333 333 } // inline seconds_literals 334 334 335 inline Seconds operator*(double scalar, Seconds seconds) 336 { 337 return Seconds(scalar * seconds.value()); 338 } 339 340 inline Seconds operator/(double scalar, Seconds seconds) 341 { 342 return Seconds(scalar / seconds.value()); 343 } 344 335 345 WTF_EXPORT_PRIVATE TextStream& operator<<(TextStream&, Seconds); 336 346 -
trunk/Source/WebCore/ChangeLog
r261924 r261926 1 2020-05-20 Antoine Quint <graouts@apple.com> 2 3 [Web Animations] Animation engine should not wake up every tick for steps timing functions 4 https://bugs.webkit.org/show_bug.cgi?id=212103 5 <rdar://problem/62737868> 6 7 Reviewed by Simon Fraser. 8 9 Tests: webanimations/scheduling-of-animation-with-steps-timing-function-on-effect.html 10 webanimations/scheduling-of-animation-with-steps-timing-function-on-keyframe.html 11 webanimations/scheduling-of-css-animation-with-explicit-steps-timing-function-on-some-keyframes.html 12 webanimations/scheduling-of-css-animation-with-implicit-steps-timing-function.html 13 14 When an animation uses a steps() timing function, it will appear to animate discretely between values such 15 that there is only n visual changes, where n is the number of steps provided. This gives us an opportunity 16 to be more efficient when scheduling animations using steps() timing functions. 17 18 In WebAnimation::timeToNextTick() we now ask the associated effect for the amount of progress until the next 19 step. For an effect-wide steps() timing function, we can use the provided iteration progress. For animations 20 with a linear effect-wide timing function (the default), we have to map the provided iteration progress to 21 a keyframe interval, provided that interval uses a steps() timing function. 22 23 The new {Animation|Keyframe}Effect::progressUntilNextStep() method returns WTF::nullopt for any other case. 24 25 In order to test this, we add a new internals.timeToNextAnimationTick(animation) method which we use in the 26 two new tests. 27 28 * animation/AnimationEffect.cpp: 29 (WebCore::AnimationEffect::progressUntilNextStep const): 30 * animation/AnimationEffect.h: 31 * animation/KeyframeEffect.cpp: 32 (WebCore::KeyframeEffect::setBlendingKeyframes): 33 (WebCore::KeyframeEffect::computeSomeKeyframesUseStepsTimingFunction): 34 (WebCore::KeyframeEffect::timingFunctionForKeyframeAtIndex const): Avoid any out-of-bounds use of the underlying data 35 structures by returning nullptr for cases where we don't have an explicit keyframe. We also make the function const 36 such that it may be called from progressUntilNextStep(), it always was const but wasn't marked as such. 37 (WebCore::KeyframeEffect::progressUntilNextStep const): 38 * animation/KeyframeEffect.h: 39 * animation/WebAnimation.cpp: 40 (WebCore::WebAnimation::timeToNextTick const): 41 * animation/WebAnimation.h: 42 * animation/WebAnimation.idl: 43 * testing/Internals.cpp: 44 (WebCore::Internals::timeToNextAnimationTick const): 45 * testing/Internals.h: 46 * testing/Internals.idl: 47 1 48 2020-05-20 Noam Rosenthal <noam@webkit.org> 2 49 -
trunk/Source/WebCore/animation/AnimationEffect.cpp
r261637 r261926 542 542 } 543 543 544 Optional<double> AnimationEffect::progressUntilNextStep(double iterationProgress) const 545 { 546 if (!is<StepsTimingFunction>(m_timingFunction)) 547 return WTF::nullopt; 548 549 auto numberOfSteps = downcast<StepsTimingFunction>(*m_timingFunction).numberOfSteps(); 550 auto nextStepProgress = ceil(iterationProgress * numberOfSteps) / numberOfSteps; 551 return nextStepProgress - iterationProgress; 552 } 553 544 554 } // namespace WebCore -
trunk/Source/WebCore/animation/AnimationEffect.h
r261637 r261926 103 103 void updateStaticTimingProperties(); 104 104 105 virtual Optional<double> progressUntilNextStep(double) const; 106 105 107 protected: 106 108 explicit AnimationEffect(); -
trunk/Source/WebCore/animation/AnimationTimeline.cpp
r261029 r261926 400 400 // start value of the transition shoud be to make sure that we don't account for animated values that would have been blended onto 401 401 // the style applied during the last style resolution. 402 403 // FIXME: NO. We should be applying animations with the current time. 404 402 405 if (auto* unanimatedStyle = keyframeEffect->unanimatedStyle()) 403 406 return RenderStyle::clone(*unanimatedStyle); -
trunk/Source/WebCore/animation/KeyframeEffect.cpp
r261756 r261926 847 847 computeStackingContextImpact(); 848 848 computeAcceleratedPropertiesState(); 849 computeSomeKeyframesUseStepsTimingFunction(); 849 850 850 851 checkForMatchingTransformFunctionLists(); … … 1264 1265 else 1265 1266 m_acceleratedPropertiesState = AcceleratedProperties::All; 1267 } 1268 1269 void KeyframeEffect::computeSomeKeyframesUseStepsTimingFunction() 1270 { 1271 m_someKeyframesUseStepsTimingFunction = false; 1272 1273 size_t numberOfKeyframes = m_blendingKeyframes.size(); 1274 1275 // If we're dealing with a CSS Animation and it specifies a default steps() timing function, 1276 // we need to check that any of the specified keyframes either does not have an explicit timing 1277 // function or specifies an explicit steps() timing function. 1278 if (is<CSSAnimation>(animation()) && is<StepsTimingFunction>(downcast<DeclarativeAnimation>(*animation()).backingAnimation().timingFunction())) { 1279 for (size_t i = 0; i < numberOfKeyframes; i++) { 1280 auto* timingFunction = m_blendingKeyframes[i].timingFunction(); 1281 if (!timingFunction || is<StepsTimingFunction>(timingFunction)) { 1282 m_someKeyframesUseStepsTimingFunction = true; 1283 return; 1284 } 1285 } 1286 return; 1287 } 1288 1289 // For any other type of animation, we just need to check whether any of the keyframes specify 1290 // an explicit steps() timing function. 1291 for (size_t i = 0; i < numberOfKeyframes; i++) { 1292 if (is<StepsTimingFunction>(m_blendingKeyframes[i].timingFunction())) { 1293 m_someKeyframesUseStepsTimingFunction = true; 1294 return; 1295 } 1296 } 1266 1297 } 1267 1298 … … 1440 1471 } 1441 1472 1442 TimingFunction* KeyframeEffect::timingFunctionForKeyframeAtIndex(size_t index) 1443 { 1444 if (!m_parsedKeyframes.isEmpty()) 1473 TimingFunction* KeyframeEffect::timingFunctionForKeyframeAtIndex(size_t index) const 1474 { 1475 if (!m_parsedKeyframes.isEmpty()) { 1476 if (index >= m_parsedKeyframes.size()) 1477 return nullptr; 1445 1478 return m_parsedKeyframes[index].timingFunction.get(); 1479 } 1446 1480 1447 1481 auto effectAnimation = animation(); … … 1449 1483 // If we're dealing with a CSS Animation, the timing function is specified either on the keyframe itself. 1450 1484 if (is<CSSAnimation>(effectAnimation)) { 1485 if (index >= m_blendingKeyframes.size()) 1486 return nullptr; 1451 1487 if (auto* timingFunction = m_blendingKeyframes[index].timingFunction()) 1452 1488 return timingFunction; … … 1796 1832 } 1797 1833 1834 Optional<double> KeyframeEffect::progressUntilNextStep(double iterationProgress) const 1835 { 1836 ASSERT(iterationProgress >= 0 && iterationProgress <= 1); 1837 1838 if (auto progress = AnimationEffect::progressUntilNextStep(iterationProgress)) 1839 return progress; 1840 1841 if (!is<LinearTimingFunction>(timingFunction()) || !m_someKeyframesUseStepsTimingFunction) 1842 return WTF::nullopt; 1843 1844 if (m_blendingKeyframes.isEmpty()) 1845 return WTF::nullopt; 1846 1847 auto progressUntilNextStepInInterval = [iterationProgress](double intervalStartProgress, double intervalEndProgress, TimingFunction* timingFunction) -> Optional<double> { 1848 if (!is<StepsTimingFunction>(timingFunction)) 1849 return WTF::nullopt; 1850 1851 auto numberOfSteps = downcast<StepsTimingFunction>(*timingFunction).numberOfSteps(); 1852 auto intervalProgress = intervalEndProgress - intervalStartProgress; 1853 auto iterationProgressMappedToCurrentInterval = (iterationProgress - intervalStartProgress) / intervalProgress; 1854 auto nextStepProgress = ceil(iterationProgressMappedToCurrentInterval * numberOfSteps) / numberOfSteps; 1855 return (nextStepProgress - iterationProgressMappedToCurrentInterval) * intervalProgress; 1856 }; 1857 1858 for (size_t i = 0; i < m_blendingKeyframes.size(); ++i) { 1859 auto intervalEndProgress = m_blendingKeyframes[i].key(); 1860 // We can stop once we find a keyframe for which the progress is more than the provided iteration progress. 1861 if (intervalEndProgress <= iterationProgress) 1862 continue; 1863 1864 // In case we're on the first keyframe, then this means we are dealing with an implicit 0% keyframe. 1865 // This will be a linear timing function unless we're dealing with a CSS Animation which might have 1866 // the default timing function for its keyframes defined on its backing Animation object. 1867 if (!i) { 1868 if (is<CSSAnimation>(animation())) 1869 return progressUntilNextStepInInterval(0, intervalEndProgress, downcast<DeclarativeAnimation>(*animation()).backingAnimation().timingFunction()); 1870 return WTF::nullopt; 1871 } 1872 1873 return progressUntilNextStepInInterval(m_blendingKeyframes[i - 1].key(), intervalEndProgress, timingFunctionForKeyframeAtIndex(i - 1)); 1874 } 1875 1876 // If we end up here, then this means we are dealing with an implicit 100% keyframe. 1877 // This will be a linear timing function unless we're dealing with a CSS Animation which might have 1878 // the default timing function for its keyframes defined on its backing Animation object. 1879 auto& lastExplicitKeyframe = m_blendingKeyframes[m_blendingKeyframes.size() - 1]; 1880 if (is<CSSAnimation>(animation())) 1881 return progressUntilNextStepInInterval(lastExplicitKeyframe.key(), 1, downcast<DeclarativeAnimation>(*animation()).backingAnimation().timingFunction()); 1882 1883 // In any other case, we are not dealing with an interval with a steps() timing function. 1884 return WTF::nullopt; 1885 } 1886 1798 1887 } // namespace WebCore -
trunk/Source/WebCore/animation/KeyframeEffect.h
r261637 r261926 182 182 void updateAcceleratedActions(); 183 183 void setAnimatedPropertiesInStyle(RenderStyle&, double); 184 TimingFunction* timingFunctionForKeyframeAtIndex(size_t) ;184 TimingFunction* timingFunctionForKeyframeAtIndex(size_t) const; 185 185 Ref<const Animation> backingAnimationForCompositedRenderer() const; 186 186 void computedNeedsForcedLayout(); 187 187 void computeStackingContextImpact(); 188 void computeSomeKeyframesUseStepsTimingFunction(); 188 189 void clearBlendingKeyframes(); 189 190 void updateBlendingKeyframes(RenderStyle&); … … 192 193 void computeAcceleratedPropertiesState(); 193 194 void setBlendingKeyframes(KeyframeList&); 195 Optional<double> progressUntilNextStep(double) const final; 194 196 void checkForMatchingTransformFunctionLists(); 195 197 void checkForMatchingFilterFunctionLists(); … … 223 225 bool m_colorFilterFunctionListsMatch { false }; 224 226 bool m_inTargetEffectStack { false }; 227 bool m_someKeyframesUseStepsTimingFunction { false }; 225 228 }; 226 229 -
trunk/Source/WebCore/animation/WebAnimation.cpp
r261637 r261926 1483 1483 return (effect.endTime() - timing.localTime.value()) / playbackRate; 1484 1484 } 1485 if (auto iterationProgress = effect.getComputedTiming().simpleIterationProgress) { 1486 // In case we're in a range that uses a steps() timing function, we can compute the time until the next step starts. 1487 if (auto progressUntilNextStep = effect.progressUntilNextStep(*iterationProgress)) 1488 return effect.iterationDuration() * *progressUntilNextStep / playbackRate; 1489 } 1485 1490 // Other animations in the "active" phase will need to update their animated value at the immediate next opportunity. 1486 1491 return 0_s; -
trunk/Source/WebCore/animation/WebAnimation.h
r261637 r261926 121 121 bool needsTick() const; 122 122 virtual void tick(); 123 Seconds timeToNextTick() const;123 WEBCORE_EXPORT Seconds timeToNextTick() const; 124 124 virtual void resolve(RenderStyle&); 125 125 void effectTargetDidChange(Element* previousTarget, Element* newTarget); -
trunk/Source/WebCore/animation/WebAnimation.idl
r260671 r261926 42 42 InterfaceName=Animation, 43 43 CustomConstructor(), 44 CustomToJSObject 44 CustomToJSObject, 45 ExportMacro=WEBCORE_EXPORT 45 46 ] interface WebAnimation : EventTarget { 46 47 attribute DOMString id; -
trunk/Source/WebCore/style/StyleTreeResolver.cpp
r260774 r261926 318 318 m_document.timeline().updateCSSTransitionsForElement(element, *oldStyle, *newStyle); 319 319 320 // FIXME: Maybe need to do this first such that CSS Transitions are aware of newly created or canceled animations 320 321 if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations()) 321 322 m_document.timeline().updateCSSAnimationsForElement(element, oldStyle, *newStyle); -
trunk/Source/WebCore/testing/Internals.cpp
r261897 r261926 197 197 #include "VoidCallback.h" 198 198 #include "WebAnimation.h" 199 #include "WebAnimationUtilities.h" 199 200 #include "WebCoreJSClientData.h" 200 201 #include "WindowProxy.h" … … 1199 1200 return frame()->document()->timeline().numberOfAnimationTimelineInvalidationsForTesting(); 1200 1201 return 0; 1202 } 1203 1204 double Internals::timeToNextAnimationTick(WebAnimation& animation) const 1205 { 1206 return secondsToWebAnimationsAPITime(animation.timeToNextTick()); 1201 1207 } 1202 1208 -
trunk/Source/WebCore/testing/Internals.h
r261897 r261926 107 107 class UnsuspendableActiveDOMObject; 108 108 class VoidCallback; 109 class WebAnimation; 109 110 class WebGLRenderingContext; 110 111 class WindowProxy; … … 240 241 Vector<AcceleratedAnimation> acceleratedAnimationsForElement(Element&); 241 242 unsigned numberOfAnimationTimelineInvalidations() const; 243 double timeToNextAnimationTick(WebAnimation&) const; 242 244 243 245 // For animations testing, we need a way to get at pseudo elements. -
trunk/Source/WebCore/testing/Internals.idl
r261897 r261926 298 298 sequence<AcceleratedAnimation> acceleratedAnimationsForElement(Element element); 299 299 unsigned long numberOfAnimationTimelineInvalidations(); 300 double timeToNextAnimationTick(WebAnimation animation); 300 301 301 302 // For animations testing, we need a way to get at pseudo elements.
Note: See TracChangeset
for help on using the changeset viewer.