script: fix spurious animation checks to correctly invoke rAF callbacks (#35387)

After running an `rAF` callback, if no new callbacks were registered, we
send a `NoAnimationFramesCallback` to the compositor to stop ticking
animations using video refresh callbacks. This interacts badly with the
mechanism to track spurious animations frames i.e. rAF callbacks that
don't mutate the DOM. Such 'faked' rAF callbacks are triggered by
registering a oneshot timer instead of the compositor callback.

The compositor's refresh callback is never enabled back again once a
non-spurious rAF callback runs and registers a new rAF callback. If the
former callback resets the `spurious_animations_frames` counter, then when
the latter rAF callback runs, it will not schedule a OneShotTimer timer
for any rAF callback that itself registers, since the counter was reset
previously. Hence that third rAF callback that never runs as it relies
on the compsitor's refresh callback, which was disabled previously.

The current logic also doesn't actually recognize spurious animation
frames because the `spurious_animations_frames` counter is updated at
the end of the `run_the_animation_frame_callbacks`, effectively meaning
`was_faking_animation_frames` and `self.is_faking_animation_frames` will
always be the same value but the logic effectively only runs when
`(!was_faking && is_faking)` is true.

This patch fixes the logic to detect spurious animations frames by
moving logic to update the counter to be before the check for spurious
frames. It also ensures that the compositor's refesh callbacks is
re-enabled once we see a non-spurious callback.


Fixes #35386

Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
Mukilan Thiyagarajan 2025-02-12 14:41:19 +05:30 committed by GitHub
parent ed597d8137
commit bcbb1d67d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 87 additions and 17 deletions

View file

@ -729584,6 +729584,13 @@
null,
{}
]
],
"spurious-frame-callbacks-optimization.html": [
"b4c582cd5edce8f92a2da0b44131a8dbdff60f11",
[
null,
{}
]
]
},
"atob": {

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<head>
<title>Optimization of requestAnimationFrame callbacks that don't modify the DOM shouldn't break animations</title>
<link rel="author" title="Mukilan Thiyagarajan" href="mailto:mukilan@igalia.com">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="target">0</div>
</body>
<script>
"use strict";
let frame = 0;
const draw = (t) => {
frame += 1;
if (frame < 11) {
// Don't mutate the DOM for 10 frames to meet the threshold for Servo's
// spurious frame optimization to kick in.
requestAnimationFrame(draw);
} else if (frame == 11) {
// Don't schedule next rAF so the compositor's tick is disabled.
// This is specific to Servo as the spurious frame detection at the
// time of this test was broken.
setTimeout(() => {
requestAnimationFrame(draw);
}, 10);
} else {
// Normal frames.
document.getElementById('target').innerText = t;
requestAnimationFrame(draw);
}
};
async_test(function(test) {
let target = document.getElementById('target');
setTimeout(test.step_func_done(() => {
assert_greater_than(parseInt(target.innerText), 500);
}), 550);
requestAnimationFrame(draw);
});
</script>