mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Use async timeline events for the phases of the scheduler binding (#88825)
This commit is contained in:
parent
7736b9eef0
commit
2004afae06
@ -37,11 +37,11 @@ Future<void> saveDurationsHistogram(List<Map<String, dynamic>> events, String ou
|
||||
} else if (startEvent != null && eventName == 'Frame') {
|
||||
final String phase = event['ph'] as String;
|
||||
final int timestamp = event['ts'] as int;
|
||||
if (phase == 'B') {
|
||||
if (phase == 'B' || phase == 'b') {
|
||||
assert(frameStart == null);
|
||||
frameStart = timestamp;
|
||||
} else {
|
||||
assert(phase == 'E');
|
||||
assert(phase == 'E' || phase == 'e');
|
||||
final String routeName = (startEvent['args'] as Map<String, dynamic>)['to'] as String;
|
||||
durations[routeName] ??= <int>[];
|
||||
durations[routeName]!.add(timestamp - frameStart!);
|
||||
|
||||
@ -298,7 +298,7 @@ abstract class BindingBase {
|
||||
/// The [Future] returned by the `callback` argument is returned by [lockEvents].
|
||||
@protected
|
||||
Future<void> lockEvents(Future<void> Function() callback) {
|
||||
developer.Timeline.startSync('Lock events');
|
||||
final developer.TimelineTask timelineTask = developer.TimelineTask()..start('Lock events');
|
||||
|
||||
assert(callback != null);
|
||||
_lockCount += 1;
|
||||
@ -307,7 +307,7 @@ abstract class BindingBase {
|
||||
future.whenComplete(() {
|
||||
_lockCount -= 1;
|
||||
if (!locked) {
|
||||
developer.Timeline.finishSync();
|
||||
timelineTask.finish();
|
||||
unlocked();
|
||||
}
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:developer' show Flow, Timeline;
|
||||
import 'dart:developer' show Flow, Timeline, TimelineTask;
|
||||
import 'dart:ui' show AppLifecycleState, FramePhase, FrameTiming, TimingsCallback, PlatformDispatcher;
|
||||
|
||||
import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
|
||||
@ -851,7 +851,7 @@ mixin SchedulerBinding on BindingBase {
|
||||
return;
|
||||
|
||||
_warmUpFrame = true;
|
||||
Timeline.startSync('Warm-up frame');
|
||||
final TimelineTask timelineTask = TimelineTask()..start('Warm-up frame');
|
||||
final bool hadScheduledFrame = _hasScheduledFrame;
|
||||
// We use timers here to ensure that microtasks flush in between.
|
||||
Timer.run(() {
|
||||
@ -879,7 +879,7 @@ mixin SchedulerBinding on BindingBase {
|
||||
// scheduled frame has finished.
|
||||
lockEvents(() async {
|
||||
await endOfFrame;
|
||||
Timeline.finishSync();
|
||||
timelineTask.finish();
|
||||
});
|
||||
}
|
||||
|
||||
@ -996,6 +996,9 @@ mixin SchedulerBinding on BindingBase {
|
||||
handleDrawFrame();
|
||||
}
|
||||
|
||||
TimelineTask? _frameTimelineTask;
|
||||
TimelineTask? _animateTimelineTask;
|
||||
|
||||
/// Called by the engine to prepare the framework to produce a new frame.
|
||||
///
|
||||
/// This function calls all the transient frame callbacks registered by
|
||||
@ -1020,7 +1023,7 @@ mixin SchedulerBinding on BindingBase {
|
||||
/// statements printed during a frame from those printed between frames (e.g.
|
||||
/// in response to events or timers).
|
||||
void handleBeginFrame(Duration? rawTimeStamp) {
|
||||
Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
|
||||
_frameTimelineTask = TimelineTask()..start('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
|
||||
_firstRawTimeStampInEpoch ??= rawTimeStamp;
|
||||
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
|
||||
if (rawTimeStamp != null)
|
||||
@ -1047,7 +1050,7 @@ mixin SchedulerBinding on BindingBase {
|
||||
_hasScheduledFrame = false;
|
||||
try {
|
||||
// TRANSIENT FRAME CALLBACKS
|
||||
Timeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
|
||||
_animateTimelineTask = TimelineTask()..start('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
|
||||
_schedulerPhase = SchedulerPhase.transientCallbacks;
|
||||
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
|
||||
_transientCallbacks = <int, _FrameCallbackEntry>{};
|
||||
@ -1072,7 +1075,8 @@ mixin SchedulerBinding on BindingBase {
|
||||
/// useful when working with frame callbacks.
|
||||
void handleDrawFrame() {
|
||||
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
|
||||
Timeline.finishSync(); // end the "Animate" phase
|
||||
_animateTimelineTask?.finish(); // end the "Animate" phase
|
||||
_animateTimelineTask = null;
|
||||
try {
|
||||
// PERSISTENT FRAME CALLBACKS
|
||||
_schedulerPhase = SchedulerPhase.persistentCallbacks;
|
||||
@ -1088,7 +1092,8 @@ mixin SchedulerBinding on BindingBase {
|
||||
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
|
||||
} finally {
|
||||
_schedulerPhase = SchedulerPhase.idle;
|
||||
Timeline.finishSync(); // end the Frame
|
||||
_frameTimelineTask?.finish(); // end the Frame
|
||||
_frameTimelineTask = null;
|
||||
assert(() {
|
||||
if (debugPrintEndFrameBanner)
|
||||
debugPrint('▀' * _debugBanner!.length);
|
||||
|
||||
@ -351,7 +351,7 @@ class TimelineSummary {
|
||||
// Timeline does not guarantee that the first event is the "begin" event.
|
||||
TimelineEvent? begin;
|
||||
for (final TimelineEvent event in events) {
|
||||
if (event.phase == 'B') {
|
||||
if (event.phase == 'B' || event.phase == 'b') {
|
||||
begin = event;
|
||||
} else {
|
||||
if (begin != null) {
|
||||
|
||||
@ -79,5 +79,26 @@ void main() {
|
||||
expect(response.json['value'], 'Brightness.light');
|
||||
}
|
||||
timer.cancel();
|
||||
|
||||
// Verify that all duration events on the timeline are properly nested.
|
||||
final Response response = await vmService.callServiceExtension('getVMTimeline');
|
||||
final List<TimelineEvent> events = (response as Timeline).traceEvents;
|
||||
final Map<int, List<String>> threadDurationEventStack = <int, List<String>>{};
|
||||
for (final TimelineEvent e in events) {
|
||||
final Map<String, dynamic> event = e.json;
|
||||
final String phase = event['ph'] as String;
|
||||
final int tid = event['tid'] as int;
|
||||
final String name = event['name'] as String;
|
||||
final List<String> stack = threadDurationEventStack.putIfAbsent(tid, () => <String>[]);
|
||||
if (phase == 'B') {
|
||||
stack.add(name);
|
||||
} else if (phase == 'E') {
|
||||
// The downloaded part of the timeline may contain an end event whose
|
||||
// corresponding begin event happened before the start of the timeline.
|
||||
if (stack.isNotEmpty) {
|
||||
expect(stack.removeLast(), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user