mirror of
https://github.com/flutter/flutter
synced 2024-07-16 10:29:14 +00:00
Render the warm up frame in a proper rendering process (#143290)
_This PR requires https://github.com/flutter/engine/pull/50570._ This PR uses the new `PlatformDispatcher.scheduleWarmUpFrame` API to render warm up frames. For why the warm up frame must involve the engine to render, see https://github.com/flutter/flutter/issues/142851.
This commit is contained in:
parent
01963ea21a
commit
be2544ab59
|
@ -1025,28 +1025,29 @@ mixin SchedulerBinding on BindingBase {
|
|||
debugTimelineTask = TimelineTask()..start('Warm-up frame');
|
||||
}
|
||||
final bool hadScheduledFrame = _hasScheduledFrame;
|
||||
// We use timers here to ensure that microtasks flush in between.
|
||||
Timer.run(() {
|
||||
assert(_warmUpFrame);
|
||||
handleBeginFrame(null);
|
||||
});
|
||||
Timer.run(() {
|
||||
assert(_warmUpFrame);
|
||||
handleDrawFrame();
|
||||
// We call resetEpoch after this frame so that, in the hot reload case,
|
||||
// the very next frame pretends to have occurred immediately after this
|
||||
// warm-up frame. The warm-up frame's timestamp will typically be far in
|
||||
// the past (the time of the last real frame), so if we didn't reset the
|
||||
// epoch we would see a sudden jump from the old time in the warm-up frame
|
||||
// to the new time in the "real" frame. The biggest problem with this is
|
||||
// that implicit animations end up being triggered at the old time and
|
||||
// then skipping every frame and finishing in the new time.
|
||||
resetEpoch();
|
||||
_warmUpFrame = false;
|
||||
if (hadScheduledFrame) {
|
||||
scheduleFrame();
|
||||
}
|
||||
});
|
||||
PlatformDispatcher.instance.scheduleWarmUpFrame(
|
||||
beginFrame: () {
|
||||
assert(_warmUpFrame);
|
||||
handleBeginFrame(null);
|
||||
},
|
||||
drawFrame: () {
|
||||
assert(_warmUpFrame);
|
||||
handleDrawFrame();
|
||||
// We call resetEpoch after this frame so that, in the hot reload case,
|
||||
// the very next frame pretends to have occurred immediately after this
|
||||
// warm-up frame. The warm-up frame's timestamp will typically be far in
|
||||
// the past (the time of the last real frame), so if we didn't reset the
|
||||
// epoch we would see a sudden jump from the old time in the warm-up frame
|
||||
// to the new time in the "real" frame. The biggest problem with this is
|
||||
// that implicit animations end up being triggered at the old time and
|
||||
// then skipping every frame and finishing in the new time.
|
||||
resetEpoch();
|
||||
_warmUpFrame = false;
|
||||
if (hadScheduledFrame) {
|
||||
scheduleFrame();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Lock events so touch events etc don't insert themselves until the
|
||||
// scheduled frame has finished.
|
||||
|
|
|
@ -14,6 +14,21 @@ import 'scheduler_tester.dart';
|
|||
class TestSchedulerBinding extends BindingBase with SchedulerBinding, ServicesBinding {
|
||||
final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{};
|
||||
|
||||
VoidCallback? additionalHandleBeginFrame;
|
||||
VoidCallback? additionalHandleDrawFrame;
|
||||
|
||||
@override
|
||||
void handleBeginFrame(Duration? rawTimeStamp) {
|
||||
additionalHandleBeginFrame?.call();
|
||||
super.handleBeginFrame(rawTimeStamp);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleDrawFrame() {
|
||||
additionalHandleDrawFrame?.call();
|
||||
super.handleDrawFrame();
|
||||
}
|
||||
|
||||
@override
|
||||
void postEvent(String eventKind, Map<String, dynamic> eventData) {
|
||||
getEventsDispatched(eventKind).add(eventData);
|
||||
|
@ -39,6 +54,11 @@ void main() {
|
|||
scheduler = TestSchedulerBinding();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
scheduler.additionalHandleBeginFrame = null;
|
||||
scheduler.additionalHandleDrawFrame = null;
|
||||
});
|
||||
|
||||
test('Tasks are executed in the right order', () {
|
||||
final TestStrategy strategy = TestStrategy();
|
||||
scheduler.schedulingStrategy = strategy.shouldRunTaskWithPriority;
|
||||
|
@ -111,6 +131,25 @@ void main() {
|
|||
expect(executedTasks[0], equals(0));
|
||||
});
|
||||
|
||||
test('scheduleWarmUpFrame should flush microtasks between callbacks', () async {
|
||||
addTearDown(() => scheduler.handleEventLoopCallback());
|
||||
|
||||
bool microtaskDone = false;
|
||||
final Completer<void> drawFrameDone = Completer<void>();
|
||||
scheduler.additionalHandleBeginFrame = () {
|
||||
expect(microtaskDone, false);
|
||||
scheduleMicrotask(() {
|
||||
microtaskDone = true;
|
||||
});
|
||||
};
|
||||
scheduler.additionalHandleDrawFrame = () {
|
||||
expect(microtaskDone, true);
|
||||
drawFrameDone.complete();
|
||||
};
|
||||
scheduler.scheduleWarmUpFrame();
|
||||
await drawFrameDone.future;
|
||||
});
|
||||
|
||||
test('2 calls to scheduleWarmUpFrame just schedules it once', () {
|
||||
final List<VoidCallback> timerQueueTasks = <VoidCallback>[];
|
||||
bool taskExecuted = false;
|
||||
|
|
Loading…
Reference in a new issue