Changeset 252957 in webkit
- Timestamp:
- Nov 30, 2019 5:15:48 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r252951 r252957 1 2019-11-30 Antoine Quint <graouts@apple.com> 2 3 [Web Animations] Forward-filling animations should not schedule updates while filling 4 https://bugs.webkit.org/show_bug.cgi?id=204697 5 <rdar://problem/57534005> 6 7 Reviewed by Dean Jackson. 8 9 Adding tests checking that we don't schedule animation updates while filling for accelerated and non-accelerated animations alike. 10 11 * webanimations/no-scheduling-while-filling-accelerated-expected.txt: Added. 12 * webanimations/no-scheduling-while-filling-accelerated.html: Added. 13 * webanimations/no-scheduling-while-filling-non-accelerated-expected.txt: Added. 14 * webanimations/no-scheduling-while-filling-non-accelerated.html: Added. 15 1 16 2019-11-29 Commit Queue <commit-queue@webkit.org> 2 17 -
trunk/Source/WebCore/ChangeLog
r252956 r252957 1 2019-11-30 Antoine Quint <graouts@apple.com> 2 3 [Web Animations] Forward-filling animations should not schedule updates while filling 4 https://bugs.webkit.org/show_bug.cgi?id=204697 5 <rdar://problem/57534005> 6 7 Reviewed by Dean Jackson. 8 9 Tests: webanimations/no-scheduling-while-filling-accelerated.html 10 webanimations/no-scheduling-while-filling-non-accelerated.html 11 12 For two different reasons, we would continuously schedule updates for animations in their "after" phase, ie. when they would have "fill: forwards" 13 and be finished. 14 15 In the case of non-accelerated animations, that was because we would also schedule an immedate update in DocumentTimeline::scheduleNextTick() provided 16 we had relevant animations that weren't accelerated. But since animations in their "after" phase are still considered relevant, which means that they 17 would override the base style with an animated value, this caused the unnecessary scheduled updates. 18 19 To address this, we run WebAnimation::timeToNextTick() in DocumentTimeline::scheduleNextTick() for all relevant animations and we teach that function 20 to schedule an immediate update only during the "active" phase, and to schedule a timed update only in the "before" phase computing the delay until 21 the animation enters the "active" phase. 22 23 While performing this work, we found a couple of other issues that weren't apparent until we had this more efficient scheduling in place. 24 25 First, we would not allow any additional calls to DocumentTimeline::scheduleAnimationResolution() to actually schedule an update while we were in the 26 process of updating animations. While this wasn't a problem before because the mere presence of relevant animations would cause an upadte, we now had 27 to allow any animation changing timing properties as promises would resolve and events would be dispatched during the animation update to cause further 28 animation updates to be scheduled. So we moved the "m_animationResolutionScheduled" flag reset earlier in DocumentTimeline::updateAnimationsAndSendEvents() 29 before we actually run the animation update procedure. 30 31 Following that change, we also had to make sure that timing changes made through the evaluation of the pending play and pause tasks would _not_ cause 32 animations to be scheduled, which meant that an animation that became non-pending (ie. its first tick occured) would always schedule one immediate 33 animation update. So now we have an extra "silent" flag to WebAnimation::timingDidChange() which is only set to true when called from either 34 WebAnimation::runPendingPlayTask() or WebAnimation::runPendingPauseTask(). 35 36 Finally, one existing test showed that calling KeyframeEffect::setKeyframes() while running animations didn't cause an animation update to be scheduled 37 since a call to WebAnimation::effectTimingDidChange() was lacking. This is now addressed. 38 39 As for accelerated animations, the extraneous animation scheduling occured because we would get in a state where we would record an "accelerated action" 40 to stop the accelerated animation but the accelerated animation had already completed and the renderer for the target element was no longer composited. 41 This meant that KeyframeEffect::applyPendingAcceleratedActions() would not perform any work and the pending accelerated action would remain in the 42 DocumentTimeline queue and cause an update to be scheduled to try again, endlessly. 43 44 To address that, we first check in KeyframeEffect::addPendingAcceleratedAction() if the recorded action differs from the last recorded action, avoiding 45 unnecessary work, and in KeyframeEffect::applyPendingAcceleratedActions(), if our last recorded action was "Stop" and the renderer was not composited, 46 we discard all pending accelerated actions. 47 48 * animation/DocumentTimeline.cpp: 49 (WebCore::DocumentTimeline::updateAnimationsAndSendEvents): 50 (WebCore::DocumentTimeline::scheduleNextTick): 51 * animation/KeyframeEffect.cpp: 52 (WebCore::KeyframeEffect::setKeyframes): 53 (WebCore::KeyframeEffect::addPendingAcceleratedAction): 54 (WebCore::KeyframeEffect::applyPendingAcceleratedActions): 55 * animation/WebAnimation.cpp: 56 (WebCore::WebAnimation::timingDidChange): 57 (WebCore::WebAnimation::runPendingPlayTask): 58 (WebCore::WebAnimation::runPendingPauseTask): 59 (WebCore::WebAnimation::timeToNextTick const): 60 * animation/WebAnimation.h: 61 1 62 2019-11-30 youenn fablet <youenn@apple.com> 2 63 -
trunk/Source/WebCore/animation/DocumentTimeline.cpp
r252951 r252957 351 351 return; 352 352 353 // Updating animations and sending events may invalidate the timing of some animations, so we must set the m_animationResolutionScheduled 354 // flag to false prior to running that procedure to allow animation with timing model updates to schedule updates. 355 m_animationResolutionScheduled = false; 356 353 357 internalUpdateAnimationsAndSendEvents(); 354 358 applyPendingAcceleratedAnimations(); 355 359 356 m_animationResolutionScheduled = false;357 scheduleNextTick();360 if (!m_animationResolutionScheduled) 361 scheduleNextTick(); 358 362 } 359 363 … … 530 534 return; 531 535 532 for (const auto& animation : m_animations) {533 if (!animation->isRunningAccelerated()) {534 scheduleAnimationResolution();535 return;536 }537 }538 539 536 Seconds scheduleDelay = Seconds::infinity(); 540 537 -
trunk/Source/WebCore/animation/KeyframeEffect.cpp
r252951 r252957 664 664 ExceptionOr<void> KeyframeEffect::setKeyframes(JSGlobalObject& lexicalGlobalObject, Strong<JSObject>&& keyframesInput) 665 665 { 666 return processKeyframes(lexicalGlobalObject, WTFMove(keyframesInput)); 666 auto processKeyframesResult = processKeyframes(lexicalGlobalObject, WTFMove(keyframesInput)); 667 if (!processKeyframesResult.hasException() && animation()) 668 animation()->effectTimingDidChange(); 669 return processKeyframesResult; 667 670 } 668 671 … … 1343 1346 void KeyframeEffect::addPendingAcceleratedAction(AcceleratedAction action) 1344 1347 { 1348 if (action == m_lastRecordedAcceleratedAction) 1349 return; 1350 1345 1351 if (action == AcceleratedAction::Stop) 1346 1352 m_pendingAcceleratedActions.clear(); … … 1382 1388 1383 1389 auto* renderer = this->renderer(); 1384 if (!renderer || !renderer->isComposited()) 1385 return; 1390 if (!renderer || !renderer->isComposited()) { 1391 // The renderer may no longer be composited because the accelerated animation ended before we had a chance to update it, 1392 // in which case if we asked for the animation to stop, we can discard the current set of accelerated actions. 1393 if (m_lastRecordedAcceleratedAction == AcceleratedAction::Stop) 1394 m_pendingAcceleratedActions.clear(); 1395 return; 1396 } 1386 1397 1387 1398 auto pendingAcceleratedActions = m_pendingAcceleratedActions; -
trunk/Source/WebCore/animation/WebAnimation.cpp
r252951 r252957 718 718 } 719 719 720 void WebAnimation::timingDidChange(DidSeek didSeek, SynchronouslyNotify synchronouslyNotify )720 void WebAnimation::timingDidChange(DidSeek didSeek, SynchronouslyNotify synchronouslyNotify, Silently silently) 721 721 { 722 722 m_shouldSkipUpdatingFinishedStateWhenResolving = false; … … 728 728 } 729 729 730 if ( m_timeline)730 if (silently == Silently::No && m_timeline) 731 731 m_timeline->animationTimingDidChange(*this); 732 732 }; … … 980 980 981 981 // 5. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the synchronously notify flag set to false. 982 timingDidChange(DidSeek::No, SynchronouslyNotify::No );982 timingDidChange(DidSeek::No, SynchronouslyNotify::No, Silently::Yes); 983 983 984 984 invalidateEffect(); … … 1106 1106 // 6. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the 1107 1107 // synchronously notify flag set to false. 1108 timingDidChange(DidSeek::No, SynchronouslyNotify::No );1108 timingDidChange(DidSeek::No, SynchronouslyNotify::No, Silently::Yes); 1109 1109 1110 1110 invalidateEffect(); … … 1290 1290 Seconds WebAnimation::timeToNextTick() const 1291 1291 { 1292 ASSERT(isRunningAccelerated()); 1293 1292 // Any animation that is pending needs immediate resolution. 1294 1293 if (pending()) 1295 1294 return 0_s; 1296 1295 1297 // If we're not running, there's no telling when we'll end. 1298 if (playState() != PlayState::Running) 1296 // If we're not running, or time is not advancing for this animation, there's no telling when we'll end. 1297 auto playbackRate = effectivePlaybackRate(); 1298 if (playState() != PlayState::Running || !playbackRate) 1299 1299 return Seconds::infinity(); 1300 1300 1301 // CSS Animations dispatch events for each iteration, so compute the time until 1302 // the end of this iteration. Any other animation only cares about remaning total time. 1303 if (isCSSAnimation()) { 1304 auto* animationEffect = effect(); 1305 auto timing = animationEffect->getComputedTiming(); 1306 // If we're actively running, we need the time until the next iteration. 1307 if (auto iterationProgress = timing.simpleIterationProgress) 1308 return animationEffect->iterationDuration() * (1 - *iterationProgress); 1309 1310 // Otherwise we're probably in the before phase waiting to reach our start time. 1311 if (auto animationCurrentTime = currentTime()) { 1312 // If our current time is negative, we need to be scheduled to be resolved at the inverse 1313 // of our current time, unless we fill backwards, in which case we want to invalidate as 1314 // soon as possible. 1315 auto localTime = animationCurrentTime.value(); 1316 if (localTime < 0_s) 1317 return -localTime; 1318 if (localTime < animationEffect->delay()) 1319 return animationEffect->delay() - localTime; 1301 auto& effect = *this->effect(); 1302 auto timing = effect.getBasicTiming(); 1303 switch (timing.phase) { 1304 case AnimationEffectPhase::Before: 1305 // The animation is in its "before" phase, in this case we can wait until it enters its "active" phase. 1306 return (effect.delay() - timing.localTime.value()) / playbackRate; 1307 case AnimationEffectPhase::Active: 1308 // Non-accelerated animations in the "active" phase will need to update their animated value at the immediate next opportunity. 1309 if (!isRunningAccelerated()) 1310 return 0_s; 1311 // Accelerated CSS Animations need to trigger "animationiteration" events, in this case we can wait until the next iteration. 1312 if (isCSSAnimation()) { 1313 if (auto iterationProgress = effect.getComputedTiming().simpleIterationProgress) 1314 return effect.iterationDuration() * (1 - *iterationProgress); 1320 1315 } 1321 } else if (auto animationCurrentTime = currentTime()) 1322 return effect()->endTime() - *animationCurrentTime; 1316 // Accelerated animations in the "active" phase can wait until they ended. 1317 return (effect.endTime() - timing.localTime.value()) / playbackRate; 1318 case AnimationEffectPhase::After: 1319 // The animation is in its after phase, which means it will no longer update its value, so it doens't need a tick. 1320 return Seconds::infinity(); 1321 case AnimationEffectPhase::Idle: 1322 ASSERT_NOT_REACHED(); 1323 return Seconds::infinity(); 1324 } 1323 1325 1324 1326 ASSERT_NOT_REACHED(); -
trunk/Source/WebCore/animation/WebAnimation.h
r252951 r252957 155 155 enum class TimeToRunPendingTask : uint8_t { NotScheduled, ASAP, WhenReady }; 156 156 157 void timingDidChange(DidSeek, SynchronouslyNotify );157 void timingDidChange(DidSeek, SynchronouslyNotify, Silently = Silently::No); 158 158 void updateFinishedState(DidSeek, SynchronouslyNotify); 159 159 Seconds effectEndTime() const;
Note: See TracChangeset
for help on using the changeset viewer.