Skip to content
Permalink
Browse files
Tiled layer flicker when an animation ends
https://bugs.webkit.org/show_bug.cgi?id=244017
<rdar://89111231>

Reviewed by Tim Horton.

The test page (internal to Apple) had a tiled layer whose transform was animated via a transition.
When the animation finished, there was sometimes a frame where tiles were missing, causing a visual
flash. When tiled layers are being animated, we wake up every frame to re-evaluate the tile
coverage.

Transform animations are implemented via Core Animation; the code makes a fill-forwards animation on
the layer. For such animations, `DocumentTimeline::timeToNextTick()` ensures that we don't waste
power doing rendering updates on every frame, scheduling a timer to fire for the next relevant time.

However, it was possible for this timer to fire late; this resulted in a frame where the tile
coverage logic, which consults DocumentTimeline animation state, saw an animation that had completed
(and computed tile coverage on that basis), but the CAAnimation was still affecting the rendered
result, putting the tiled layer in a position where some areas without tile coverage were exposed.

Fix by having the m_tickScheduleTimer fire slightly (~16ms) earlier, so that DocumentTimeline
animations have their correct state when consulted for tile coverage computation. This may result in
a few more rendering updates at the ends of accelerated animations (as detected by layout tests),
but that should be benign.

* LayoutTests/animations/no-style-recalc-during-accelerated-animation.html:
* LayoutTests/animations/steps-transform-rendering-updates-expected.txt:
* LayoutTests/animations/steps-transform-rendering-updates.html:
* Source/WebCore/animation/DocumentTimeline.cpp:
(WebCore::DocumentTimeline::scheduleAnimationResolution):
(WebCore::DocumentTimeline::scheduleNextTick):

Canonical link: https://commits.webkit.org/253549@main
  • Loading branch information
smfr committed Aug 18, 2022
1 parent 2469848 commit 3292b5d3e18271e9e376a51d251674f5b7bc2862
Show file tree
Hide file tree
Showing 5 changed files with 18 additions and 9 deletions.
@@ -1,2 +1,2 @@
Got iteration event.
PASS: saw three or fewer style recalcs during the animation.
PASS: saw five or fewer style recalcs during the animation.
@@ -35,10 +35,10 @@
box.addEventListener("animationiteration", () => result.innerText = "Got iteration event.\n");
box.addEventListener("animationend", () => {
const numRecalcs = internals.styleRecalcCount();
if (numRecalcs > 3)
result.innerText += "FAIL: saw " + numRecalcs + " style recalcs during the animation, should only see three."
if (numRecalcs > 5)
result.innerText += "FAIL: saw " + numRecalcs + " style recalcs during the animation, should only see five."
else
result.innerText += "PASS: saw three or fewer style recalcs during the animation."
result.innerText += "PASS: saw five or fewer style recalcs during the animation."

if (window.testRunner)
testRunner.notifyDone();
@@ -1,5 +1,5 @@
PASS count is 0
PASS count < 6 is true
PASS count < 7 is true
PASS successfullyParsed is true

TEST COMPLETE
@@ -35,7 +35,7 @@
}, false);
box.addEventListener('animationend', () => {
count = internals.renderingUpdateCount();
shouldBeTrue('count < 6');
shouldBeTrue('count < 7');
finishJSTest();
}, false);

@@ -167,8 +167,11 @@ void DocumentTimeline::scheduleAnimationResolution()
if (animationsAreSuspended() || m_animationResolutionScheduled || !m_document || !m_document->page())
return;

bool havePendingActivity = shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState();
LOG_WITH_STREAM(Animations, stream << "DocumentTimeline " << this << " scheduleAnimationResolution - havePendingActivity " << havePendingActivity);

// We need some relevant animations or pending events to proceed.
if (!shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState())
if (!havePendingActivity)
return;

m_document->page()->scheduleRenderingUpdate(RenderingUpdateStep::Animations);
@@ -342,6 +345,8 @@ void DocumentTimeline::scheduleNextTick()
return std::nullopt;
};

auto animationInterval = this->animationInterval();

for (const auto& animation : m_animations) {
if (!animation->isRelevant())
continue;
@@ -364,15 +369,19 @@ void DocumentTimeline::scheduleNextTick()
animationTimeToNextRequiredTick = *timeToNextPossibleTickAccountingForFrameRate - nextTickTimeEpsilon;
}

if (animationTimeToNextRequiredTick < animationInterval()) {
if (animationTimeToNextRequiredTick < animationInterval) {
LOG_WITH_STREAM(Animations, stream << "DocumentTimeline " << this << " scheduleNextTick - scheduleAnimationResolution now");
scheduleAnimationResolution();
return;
}
scheduleDelay = std::min(scheduleDelay, animationTimeToNextRequiredTick);
}

if (scheduleDelay < Seconds::infinity())
if (scheduleDelay < Seconds::infinity()) {
scheduleDelay = std::max(scheduleDelay - animationInterval, 0_s);
LOG_WITH_STREAM(Animations, stream << "DocumentTimeline " << this << " scheduleNextTick - scheduleDelay " << scheduleDelay);
m_tickScheduleTimer.startOneShot(scheduleDelay);
}
}

void DocumentTimeline::animationAcceleratedRunningStateDidChange(WebAnimation& animation)

0 comments on commit 3292b5d

Please sign in to comment.