Changeset 238128 in webkit
- Timestamp:
- Nov 13, 2018 2:22:56 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r238125 r238128 1 2018-11-12 Antoine Quint <graouts@apple.com> 2 3 [Web Animations] Don't schedule animation frames or update style while an accelerated animation is running 4 https://bugs.webkit.org/show_bug.cgi?id=191542 5 <rdar://problem/45356027> 6 7 Reviewed by Simon Fraser. 8 9 Add a test that checks that we make only minimal style updates and still dispatch events while an accelerated animation is running. 10 11 * animations/no-style-recalc-during-accelerated-animation-expected.txt: Added. 12 * animations/no-style-recalc-during-accelerated-animation.html: Added. 13 * fast/layers/no-clipping-overflow-hidden-added-after-transform-expected.html: 14 * fast/layers/no-clipping-overflow-hidden-added-after-transform.html: Change the colors to avoid a tiny ImageOnlyFailure. 15 * platform/win/TestExpectations: Mark some regressions tracked by webkit.org/b/191584. 16 1 17 2018-11-12 Darshan Kadu <darsh7807@gmail.com> 2 18 -
trunk/LayoutTests/fast/layers/no-clipping-overflow-hidden-added-after-transform-expected.html
r154281 r238128 10 10 #overflowHidden { 11 11 overflow: hidden; 12 background: purple;12 background: white; 13 13 } 14 14 15 15 #transformed { 16 16 -webkit-transform: rotate(45deg) translate3d(0, 0, 0); 17 background: green;17 background: black; 18 18 } 19 19 </style> -
trunk/LayoutTests/fast/layers/no-clipping-overflow-hidden-added-after-transform.html
r237587 r238128 10 10 #overflowHidden { 11 11 overflow: hidden; 12 background: purple;12 background: white; 13 13 } 14 14 … … 16 16 -webkit-transform: rotate(0deg) translate3d(0, 0, 0); 17 17 -webkit-transition: -webkit-transform linear 1ms; 18 background: green;18 background: black; 19 19 } 20 20 … … 33 33 function transitionFinished() { 34 34 if (window.testRunner) 35 requestAnimationFrame(() => window.testRunner.notifyDone());35 window.testRunner.notifyDone(); 36 36 } 37 37 -
trunk/LayoutTests/platform/win/TestExpectations
r238071 r238128 4252 4252 webkit.org/b/191368 imported/blink/fast/text/international/complex-text-trailing-space.html [ Crash ] 4253 4253 webkit.org/b/191368 imported/blink/fast/text/sub-pixel/complex-text-preferred-width.html [ Crash ] 4254 4255 webkit.org/b/191584 animations/animation-direction-normal.html [ Failure ] 4256 webkit.org/b/191584 animations/animation-direction-reverse.html [ Failure ] 4257 webkit.org/b/191584 animations/dynamic-stylesheet-loading.html [ Failure ] 4258 webkit.org/b/191584 animations/play-state-paused.html [ Failure ] 4259 webkit.org/b/191584 animations/transform-non-accelerated.html [ Failure ] 4260 webkit.org/b/191584 transitions/start-transform-transition.html [ Failure ] 4261 webkit.org/b/191584 http/wpt/css/css-animations/set-animation-play-state-to-paused-001.html [ ImageOnlyFailure ] 4262 webkit.org/b/191584 webanimations/accelerated-animation-with-delay.html [ ImageOnlyFailure ] 4263 webkit.org/b/191584 webanimations/accelerated-transition-by-removing-property.html [ ImageOnlyFailure ] 4264 webkit.org/b/191584 fast/animation/css-animation-resuming-when-visible-with-style-change.html [ Timeout ] -
trunk/Source/WebCore/ChangeLog
r238126 r238128 1 2018-11-12 Antoine Quint <graouts@apple.com> 2 3 [Web Animations] Don't schedule animation frames or update style while an accelerated animation is running 4 https://bugs.webkit.org/show_bug.cgi?id=191542 5 <rdar://problem/45356027> 6 7 Reviewed by Simon Fraser. 8 9 Test: animations/no-style-recalc-during-accelerated-animation.html 10 11 In order to be more power-efficient, we stop scheduling calls to updateAnimationsAndSendEvents() when running only accelerated 12 animations. To do that, we prevent scheduling further animation resolution if we're in the process of updating animations, and 13 when we are done, call the new DocumentTimeline::scheduleNextTick() method that will check whether we have only accelerated 14 animations running, and in that case check which of those animations needs an update the soonest and starts a timer scheduled 15 for that time when we'll schedule animation resolution. 16 17 By default, animations compute the time until their natural completion but in the case of CSS Animations, we want to make sure 18 we also update animations in-flight to dispatch "animationiteration" events. 19 20 * animation/AnimationEffect.h: Make the simpleIterationProgress() public so it can be called by WebAnimation::timeToNextTick(). 21 * animation/DocumentTimeline.cpp: 22 (WebCore::DocumentTimeline::DocumentTimeline): Create the m_tickScheduleTimer and set it up to call scheduleAnimationResolutionIfNeeded(). 23 (WebCore::DocumentTimeline::suspendAnimations): If we don't already have a cached current time, cache the current time. 24 (WebCore::DocumentTimeline::resumeAnimations): Reset the cached current time to ensure we'll get a fresh one when updating animations next. 25 (WebCore::DocumentTimeline::liveCurrentTime const): Factor the code to compute the current time out of currentTime() so that we can 26 cache the current time in suspendAnimations() without also automatically clearing the current time. 27 (WebCore::DocumentTimeline::currentTime): Use liveCurrentTime() and cacheCurrentTime() since much of the code from this function has been 28 factored out into those. Additionally, we were failing to clear the current time if called inside an animation frame, which we now do correctly 29 by virtue of using cacheCurrentTime(). This fixes some flakiness. 30 (WebCore::DocumentTimeline::cacheCurrentTime): Factor the code to cache the current time out of currentTime(). 31 (WebCore::DocumentTimeline::maybeClearCachedCurrentTime): No need to clear the current time if we get suspended. 32 (WebCore::DocumentTimeline::scheduleAnimationResolutionIfNeeded): Prevent scheduling an animation update if we're in the middle of one already, 33 scheduleNextTick() will be called after animations are updated to see if we should schedule an animation update instead. 34 (WebCore::DocumentTimeline::unscheduleAnimationResolution): Cancel the m_tickScheduleTimer if we need to unschedule animation resolution. 35 (WebCore::DocumentTimeline::animationResolutionTimerFired): Factor the call to applyPendingAcceleratedAnimations() out of updateAnimationsAndSendEvents() 36 and call scheduleNextTick(). 37 (WebCore::DocumentTimeline::updateAnimationsAndSendEvents): Set the new m_isUpdatingAnimations member variable to true while this function is running. 38 (WebCore::DocumentTimeline::scheduleNextTick): Schedule an animation update immediately if we have any relevant animation that is not accelerated. 39 Otherwise, iterate through all animations to figure out the earliest moment at which we need to update animations. 40 (WebCore::DocumentTimeline::updateListOfElementsWithRunningAcceleratedAnimationsForElement): Use the new WebAnimation::isRunningAccelerated() function. 41 * animation/DocumentTimeline.h: 42 * animation/WebAnimation.cpp: 43 (WebCore::WebAnimation::isRunningAccelerated const): Since we end up checking if an animation is running with an accelerated effect, we introduce a new 44 function to get that information directly through the WebAnimation object without bothering about its effect. 45 (WebCore::WebAnimation::resolve): We should only call updateFinishedState() here since timingDidChange() would also notify the timeline about a potential 46 change in relevance, which is not necessary and which would schedule an animation frame even for animations that are accelerated. 47 (WebCore::WebAnimation::timeToNextTick const): Compute the time until our animation completion or, in the case of CSS animations, the next iteration. 48 * animation/WebAnimation.h: 49 1 50 2018-11-13 Miguel Gomez <magomez@igalia.com> 2 51 -
trunk/Source/WebCore/animation/AnimationEffect.h
r237853 r238128 91 91 std::optional<Seconds> activeTime() const; 92 92 Seconds endTime() const; 93 std::optional<double> simpleIterationProgress() const; 93 94 std::optional<double> iterationProgress() const; 94 95 std::optional<double> currentIteration() const; … … 105 106 106 107 std::optional<double> overallProgress() const; 107 std::optional<double> simpleIterationProgress() const;108 108 AnimationEffect::ComputedDirection currentDirection() const; 109 109 std::optional<double> directedProgress() const; -
trunk/Source/WebCore/animation/DocumentTimeline.cpp
r237868 r238128 64 64 , m_document(&document) 65 65 , m_originTime(originTime) 66 , m_tickScheduleTimer(*this, &DocumentTimeline::scheduleAnimationResolutionIfNeeded) 66 67 #if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 67 68 , m_animationResolutionTimer(*this, &DocumentTimeline::animationResolutionTimerFired) … … 209 210 return; 210 211 212 if (!m_cachedCurrentTime) 213 m_cachedCurrentTime = liveCurrentTime(); 214 211 215 for (const auto& animation : m_animations) 212 216 animation->setSuspended(true); … … 223 227 if (!animationsAreSuspended()) 224 228 return; 229 230 m_cachedCurrentTime = std::nullopt; 225 231 226 232 m_isSuspended = false; … … 245 251 } 246 252 return count; 253 } 254 255 Seconds DocumentTimeline::liveCurrentTime() const 256 { 257 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 258 return m_document->animationScheduler().lastTimestamp(); 259 #else 260 return Seconds(m_document->domWindow()->nowTimestamp()); 261 #endif 247 262 } 248 263 … … 260 275 } 261 276 277 auto currentTime = liveCurrentTime(); 278 262 279 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 263 280 // If we're in the middle of firing a frame, either due to a requestAnimationFrame callback … … 265 282 // the timestamp for requestAnimationFrame() callbacks. 266 283 if (m_document->animationScheduler().isFiring()) 267 m_cachedCurrentTime = m_document->animationScheduler().lastTimestamp();284 cacheCurrentTime(currentTime); 268 285 #endif 269 286 … … 274 291 // like setTimeout() or handling events will get a time that's only updating at around 60fps, or less if 275 292 // we're throttled. 276 auto lastAnimationSchedulerTimestamp = m_document->animationScheduler().lastTimestamp();293 auto lastAnimationSchedulerTimestamp = currentTime; 277 294 auto delta = Seconds(m_document->domWindow()->nowTimestamp()) - lastAnimationSchedulerTimestamp; 278 295 int frames = std::floor(delta.seconds() / animationInterval().seconds()); 279 m_cachedCurrentTime = lastAnimationSchedulerTimestamp + Seconds(frames * animationInterval().seconds());296 cacheCurrentTime(lastAnimationSchedulerTimestamp + Seconds(frames * animationInterval().seconds())); 280 297 #else 281 m_cachedCurrentTime = Seconds(m_document->domWindow()->nowTimestamp());298 cacheCurrentTime(currentTime); 282 299 #endif 283 // We want to be sure to keep this time cached until we've both finished running JS and finished updating284 // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will285 // fire syncronously if no JS is running.286 m_waitingOnVMIdle = true;287 if (!m_currentTimeClearingTaskQueue.hasPendingTasks())288 m_currentTimeClearingTaskQueue.enqueueTask(std::bind(&DocumentTimeline::maybeClearCachedCurrentTime, this));289 m_document->vm().whenIdle([this, protectedThis = makeRefPtr(this)]() {290 m_waitingOnVMIdle = false;291 maybeClearCachedCurrentTime();292 });293 300 } 294 301 return m_cachedCurrentTime.value() - m_originTime; 302 } 303 304 void DocumentTimeline::cacheCurrentTime(Seconds newCurrentTime) 305 { 306 m_cachedCurrentTime = newCurrentTime; 307 // We want to be sure to keep this time cached until we've both finished running JS and finished updating 308 // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will 309 // fire syncronously if no JS is running. 310 m_waitingOnVMIdle = true; 311 if (!m_currentTimeClearingTaskQueue.hasPendingTasks()) 312 m_currentTimeClearingTaskQueue.enqueueTask(std::bind(&DocumentTimeline::maybeClearCachedCurrentTime, this)); 313 m_document->vm().whenIdle([this, protectedThis = makeRefPtr(this)]() { 314 m_waitingOnVMIdle = false; 315 maybeClearCachedCurrentTime(); 316 }); 295 317 } 296 318 … … 301 323 // we're guaranteed to have a consistent current time reported for all work happening in a given 302 324 // JS frame or throughout updating animations in WebCore. 303 if (!m_ waitingOnVMIdle && !m_currentTimeClearingTaskQueue.hasPendingTasks())325 if (!m_isSuspended && !m_waitingOnVMIdle && !m_currentTimeClearingTaskQueue.hasPendingTasks()) 304 326 m_cachedCurrentTime = std::nullopt; 305 327 } … … 307 329 void DocumentTimeline::scheduleAnimationResolutionIfNeeded() 308 330 { 309 if (!m_is Suspended && !m_animations.isEmpty())331 if (!m_isUpdatingAnimations && !m_isSuspended && !m_animations.isEmpty()) 310 332 scheduleAnimationResolution(); 311 333 } … … 338 360 void DocumentTimeline::unscheduleAnimationResolution() 339 361 { 362 m_tickScheduleTimer.stop(); 340 363 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 341 364 m_document->animationScheduler().unscheduleWebAnimationsResolution(); … … 354 377 { 355 378 updateAnimationsAndSendEvents(); 379 applyPendingAcceleratedAnimations(); 380 scheduleNextTick(); 356 381 } 357 382 … … 359 384 { 360 385 m_numberOfAnimationTimelineInvalidationsForTesting++; 386 387 m_isUpdatingAnimations = true; 361 388 362 389 // https://drafts.csswg.org/web-animations/#update-animations-and-send-events … … 423 450 transitionDidComplete(completedTransition); 424 451 425 applyPendingAcceleratedAnimations();452 m_isUpdatingAnimations = false; 426 453 } 427 454 … … 437 464 } 438 465 } 466 } 467 468 void DocumentTimeline::scheduleNextTick() 469 { 470 // There is no tick to schedule if we don't have any relevant animations. 471 if (m_animations.isEmpty()) 472 return; 473 474 for (const auto& animation : m_animations) { 475 if (!animation->isRunningAccelerated()) { 476 scheduleAnimationResolutionIfNeeded(); 477 return; 478 } 479 } 480 481 Seconds scheduleDelay = Seconds::infinity(); 482 483 for (const auto& animation : m_animations) { 484 auto animationTimeToNextRequiredTick = animation->timeToNextTick(); 485 if (animationTimeToNextRequiredTick < animationInterval()) { 486 scheduleAnimationResolutionIfNeeded(); 487 return; 488 } 489 scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick); 490 } 491 492 if (scheduleDelay < Seconds::infinity()) 493 m_tickScheduleTimer.startOneShot(scheduleDelay); 439 494 } 440 495 … … 541 596 bool runningAnimationsForElementAreAllAccelerated = !animations.isEmpty(); 542 597 for (const auto& animation : animations) { 543 if ( is<KeyframeEffect>(animation->effect()) && !downcast<KeyframeEffect>(animation->effect())->isRunningAccelerated()) {598 if (!animation->isRunningAccelerated()) { 544 599 runningAnimationsForElementAreAllAccelerated = false; 545 600 break; -
trunk/Source/WebCore/animation/DocumentTimeline.h
r237726 r238128 86 86 DocumentTimeline(Document&, Seconds); 87 87 88 Seconds liveCurrentTime() const; 89 void cacheCurrentTime(Seconds); 88 90 void scheduleAnimationResolutionIfNeeded(); 89 91 void scheduleInvalidationTaskIfNeeded(); … … 97 99 void updateListOfElementsWithRunningAcceleratedAnimationsForElement(Element&); 98 100 void transitionDidComplete(RefPtr<CSSTransition>); 101 void scheduleNextTick(); 99 102 100 103 RefPtr<Document> m_document; … … 102 105 bool m_isSuspended { false }; 103 106 bool m_waitingOnVMIdle { false }; 107 bool m_isUpdatingAnimations { false }; 104 108 std::optional<Seconds> m_cachedCurrentTime; 105 109 GenericTaskQueue<Timer> m_currentTimeClearingTaskQueue; … … 108 112 unsigned m_numberOfAnimationTimelineInvalidationsForTesting { 0 }; 109 113 HashSet<Element*> m_elementsWithRunningAcceleratedAnimations; 114 Timer m_tickScheduleTimer; 110 115 111 116 #if !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) -
trunk/Source/WebCore/animation/WebAnimation.cpp
r237868 r238128 1095 1095 } 1096 1096 1097 bool WebAnimation::isRunningAccelerated() const 1098 { 1099 return is<KeyframeEffect>(m_effect) && downcast<KeyframeEffect>(*m_effect).isRunningAccelerated(); 1100 } 1101 1097 1102 bool WebAnimation::needsTick() const 1098 1103 { … … 1115 1120 void WebAnimation::resolve(RenderStyle& targetStyle) 1116 1121 { 1117 timingDidChange(DidSeek::No, SynchronouslyNotify::Yes);1122 updateFinishedState(DidSeek::No, SynchronouslyNotify::Yes); 1118 1123 if (m_effect) 1119 1124 m_effect->apply(targetStyle); … … 1195 1200 } 1196 1201 1202 Seconds WebAnimation::timeToNextTick() const 1203 { 1204 ASSERT(isRunningAccelerated()); 1205 1206 if (pending()) 1207 return 0_s; 1208 1209 // If we're not running, there's no telling when we'll end. 1210 if (playState() != PlayState::Running) 1211 return Seconds::infinity(); 1212 1213 // CSS Animations dispatch events for each iteration, so compute the time until 1214 // the end of this iteration. Any other animation only cares about remaning total time. 1215 if (isCSSAnimation()) { 1216 // If we're actively running, we need the time until the next iteration. 1217 if (auto iterationProgress = effect()->simpleIterationProgress()) 1218 return effect()->iterationDuration() * (1 - iterationProgress.value()); 1219 1220 // Otherwise we're probably in the before phase waiting to reach our start time. 1221 if (auto animationCurrentTime = currentTime()) { 1222 // If our current time is negative, we need to be scheduled to be resolved at the inverse 1223 // of our current time, unless we fill backwards, in which case we want to invalidate as 1224 // soon as possible. 1225 auto localTime = animationCurrentTime.value(); 1226 if (localTime < 0_s) 1227 return -localTime; 1228 if (localTime < effect()->delay()) 1229 return effect()->delay() - localTime; 1230 } 1231 } else if (auto animationCurrentTime = currentTime()) 1232 return effect()->endTime() - animationCurrentTime.value(); 1233 1234 ASSERT_NOT_REACHED(); 1235 return Seconds::infinity(); 1236 } 1237 1197 1238 } // namespace WebCore -
trunk/Source/WebCore/animation/WebAnimation.h
r237854 r238128 105 105 virtual bool needsTick() const; 106 106 virtual void tick(); 107 Seconds timeToNextTick() const; 107 108 virtual void resolve(RenderStyle&); 108 109 void effectTargetDidChange(Element* previousTarget, Element* newTarget); … … 110 111 void applyPendingAcceleratedActions(); 111 112 113 bool isRunningAccelerated() const; 112 114 bool isRelevant() const { return m_isRelevant; } 113 115 void effectTimingDidChange();
Note: See TracChangeset
for help on using the changeset viewer.