Unverified Commit 67908dfb authored by Yegor's avatar Yegor Committed by GitHub

Reschedule engine frame if it arrives in the middle of warm-up (#72115)

* Reschedule engine frame if it arrives in the middle of warm-up

* user post-frame callback instead
parent 795b23ef
......@@ -954,20 +954,45 @@ mixin SchedulerBinding on BindingBase {
int _debugFrameNumber = 0;
String? _debugBanner;
bool _ignoreNextEngineDrawFrame = false;
// Whether the current engine frame needs to be postponed till after the
// warm-up frame.
//
// Engine may begin a frame in the middle of the warm-up frame because the
// warm-up frame is scheduled by timers while the engine frame is scheduled
// by platform specific frame scheduler (e.g. `requestAnimationFrame` on the
// web). When this happens, we let the warm-up frame finish, and postpone the
// engine frame.
bool _rescheduleAfterWarmUpFrame = false;
void _handleBeginFrame(Duration rawTimeStamp) {
if (_warmUpFrame) {
assert(!_ignoreNextEngineDrawFrame);
_ignoreNextEngineDrawFrame = true;
// "begin frame" and "draw frame" must strictly alternate. Therefore
// _rescheduleAfterWarmUpFrame cannot possibly be true here as it is
// reset by _handleDrawFrame.
assert(!_rescheduleAfterWarmUpFrame);
_rescheduleAfterWarmUpFrame = true;
return;
}
handleBeginFrame(rawTimeStamp);
}
void _handleDrawFrame() {
if (_ignoreNextEngineDrawFrame) {
_ignoreNextEngineDrawFrame = false;
if (_rescheduleAfterWarmUpFrame) {
_rescheduleAfterWarmUpFrame = false;
// Reschedule in a post-frame callback to allow the draw-frame phase of
// the warm-up frame to finish.
addPostFrameCallback((Duration timeStamp) {
// Force an engine frame.
//
// We need to reset _hasScheduledFrame here because we cancelled the
// original engine frame, and therefore did not run handleBeginFrame
// who is responsible for resetting it. So if a frame callback set this
// to true in the "begin frame" part of the warm-up frame, it will
// still be true here and cause us to skip scheduling an engine frame.
_hasScheduledFrame = false;
scheduleFrame();
});
return;
}
handleDrawFrame();
......
......@@ -129,6 +129,11 @@ void main() {
// events are locked.
expect(timerQueueTasks.length, 2);
expect(taskExecuted, false);
// Run the timers so that the scheduler is no longer in warm-up state.
for (final VoidCallback timer in timerQueueTasks) {
timer();
}
});
test('Flutter.Frame event fired', () async {
......@@ -165,6 +170,9 @@ void main() {
});
test('currentSystemFrameTimeStamp is the raw timestamp', () {
// Undo epoch set by previous tests.
scheduler.resetEpoch();
late Duration lastTimeStamp;
late Duration lastSystemTimeStamp;
......@@ -195,6 +203,40 @@ void main() {
expect(lastTimeStamp, const Duration(seconds: 3)); // 2s + (8 - 6)s / 2
expect(lastSystemTimeStamp, const Duration(seconds: 8));
});
test('Animation frame scheduled in the middle of the warm-up frame', () {
expect(scheduler.schedulerPhase, SchedulerPhase.idle);
final List<VoidCallback> timers = <VoidCallback>[];
final ZoneSpecification timerInterceptor = ZoneSpecification(
createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void Function() callback) {
timers.add(callback);
return DummyTimer();
},
);
// Schedule a warm-up frame.
// Expect two timers, one for begin frame, and one for draw frame.
runZoned<void>(scheduler.scheduleWarmUpFrame, zoneSpecification: timerInterceptor);
expect(timers.length, 2);
final VoidCallback warmUpBeginFrame = timers.first;
final VoidCallback warmUpDrawFrame = timers.last;
timers.clear();
warmUpBeginFrame();
// Simulate an animation frame firing between warm-up begin frame and warm-up draw frame.
// Expect a timer that reschedules the frame.
expect(scheduler.hasScheduledFrame, isFalse);
window.onBeginFrame!(Duration.zero);
expect(scheduler.hasScheduledFrame, isFalse);
window.onDrawFrame!();
expect(scheduler.hasScheduledFrame, isFalse);
// The draw frame part of the warm-up frame will run the post-frame
// callback that reschedules the engine frame.
warmUpDrawFrame();
expect(scheduler.hasScheduledFrame, isTrue);
});
}
class DummyTimer implements Timer {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment