Unverified Commit c5c63dfd authored by xster's avatar xster Committed by GitHub

Don't flush touch events during first launch frame pre-warm (#13990)

* Lock events during runApp's warm up frame

* move to scheduler binding

* let the one scheduleWarmUpFrame api always lock

* tweak test
parent 8a6e9737
...@@ -161,21 +161,25 @@ abstract class BindingBase { ...@@ -161,21 +161,25 @@ abstract class BindingBase {
/// callback's future completes. /// callback's future completes.
/// ///
/// This causes input lag and should therefore be avoided when possible. It is /// This causes input lag and should therefore be avoided when possible. It is
/// primarily intended for development features, in particular to allow /// primarily intended for use during non-user-interactive time such as to
/// [reassembleApplication] to block input while it walks the tree (which it /// allow [reassembleApplication] to block input while it walks the tree
/// partially does asynchronously). /// (which it partially does asynchronously).
/// ///
/// The [Future] returned by the `callback` argument is returned by [lockEvents]. /// The [Future] returned by the `callback` argument is returned by [lockEvents].
@protected @protected
Future<Null> lockEvents(Future<Null> callback()) { Future<Null> lockEvents(Future<Null> callback()) {
developer.Timeline.startSync('Lock events');
assert(callback != null); assert(callback != null);
_lockCount += 1; _lockCount += 1;
final Future<Null> future = callback(); final Future<Null> future = callback();
assert(future != null, 'The lockEvents() callback returned null; it should return a Future<Null> that completes when the lock is to expire.'); assert(future != null, 'The lockEvents() callback returned null; it should return a Future<Null> that completes when the lock is to expire.');
future.whenComplete(() { future.whenComplete(() {
_lockCount -= 1; _lockCount -= 1;
if (!locked) if (!locked) {
developer.Timeline.finishSync();
unlocked(); unlocked();
}
}); });
return future; return future;
} }
......
...@@ -666,6 +666,8 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding { ...@@ -666,6 +666,8 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
/// This is used during application startup so that the first frame (which is /// This is used during application startup so that the first frame (which is
/// likely to be quite expensive) gets a few extra milliseconds to run. /// likely to be quite expensive) gets a few extra milliseconds to run.
/// ///
/// Locks events dispatching until the scheduled frame has completed.
///
/// If a frame has already been scheduled with [scheduleFrame] or /// If a frame has already been scheduled with [scheduleFrame] or
/// [scheduleForcedFrame], this call may delay that frame. /// [scheduleForcedFrame], this call may delay that frame.
/// ///
...@@ -677,8 +679,9 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding { ...@@ -677,8 +679,9 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return; return;
final bool hadScheduledFrame = _hasScheduledFrame;
_warmUpFrame = true; _warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between. // We use timers here to ensure that microtasks flush in between.
Timer.run(() { Timer.run(() {
assert(_warmUpFrame); assert(_warmUpFrame);
...@@ -700,6 +703,13 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding { ...@@ -700,6 +703,13 @@ abstract class SchedulerBinding extends BindingBase with ServicesBinding {
if (hadScheduledFrame) if (hadScheduledFrame)
scheduleFrame(); scheduleFrame();
}); });
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
} }
Duration _firstRawTimeStampInEpoch; Duration _firstRawTimeStampInEpoch;
......
...@@ -92,11 +92,13 @@ void main() { ...@@ -92,11 +92,13 @@ void main() {
test('2 calls to scheduleWarmUpFrame just schedules it once', () { test('2 calls to scheduleWarmUpFrame just schedules it once', () {
final List<VoidCallback> timerQueueTasks = <VoidCallback>[]; final List<VoidCallback> timerQueueTasks = <VoidCallback>[];
bool taskExecuted = false;
runZoned( runZoned(
() { () {
// Run it twice without processing the queued tasks. // Run it twice without processing the queued tasks.
scheduler.scheduleWarmUpFrame(); scheduler.scheduleWarmUpFrame();
scheduler.scheduleWarmUpFrame(); scheduler.scheduleWarmUpFrame();
scheduler.scheduleTask(() { taskExecuted = true; }, Priority.touch);
}, },
zoneSpecification: new ZoneSpecification( zoneSpecification: new ZoneSpecification(
createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f()) { createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f()) {
...@@ -107,7 +109,9 @@ void main() { ...@@ -107,7 +109,9 @@ void main() {
), ),
); );
// A single call to scheduleWarmUpFrame queues up 2 Timer tasks. // scheduleWarmUpFrame scheduled 2 Timers, scheduleTask scheduled 0 because
// events are locked.
expect(timerQueueTasks.length, 2); expect(timerQueueTasks.length, 2);
expect(taskExecuted, false);
}); });
} }
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