1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'timeline.dart';
/// Event name for refresh rate related timeline events.
const String kUIThreadVsyncProcessEvent = 'VsyncProcessCallback';
/// A summary of [TimelineEvents]s corresponding to `kUIThreadVsyncProcessEvent` events.
///
/// `RefreshRate` is the time between the start of a vsync pulse and the target time of that vsync.
class RefreshRateSummary {
/// Creates a [RefreshRateSummary] given the timeline events.
RefreshRateSummary({required List<TimelineEvent> vsyncEvents}) {
final List<double> refreshRates = _computeRefreshRates(vsyncEvents);
_numberOfTotalFrames = refreshRates.length;
for (final double refreshRate in refreshRates) {
if ((refreshRate - 30).abs() < _kErrorMargin) {
_numberOf30HzFrames++;
continue;
}
if ((refreshRate - 60).abs() < _kErrorMargin) {
_numberOf60HzFrames++;
continue;
}
if ((refreshRate - 80).abs() < _kErrorMargin) {
_numberOf80HzFrames++;
continue;
}
if ((refreshRate - 90).abs() < _kErrorMargin) {
_numberOf90HzFrames++;
continue;
}
if ((refreshRate - 120).abs() < _kErrorMargin) {
_numberOf120HzFrames++;
continue;
}
_framesWithIllegalRefreshRate.add(refreshRate);
}
assert(_numberOfTotalFrames ==
_numberOf30HzFrames +
_numberOf60HzFrames +
_numberOf80HzFrames +
_numberOf90HzFrames +
_numberOf120HzFrames +
_framesWithIllegalRefreshRate.length);
}
// The error margin to determine the frame refresh rate.
// For example, when we calculated a frame that has a refresh rate of 65, we consider the frame to be a 60Hz frame.
// Can be adjusted if necessary.
static const double _kErrorMargin = 6.0;
/// The percentage of 30hz frames.
///
/// For example, if this value is 20, it means there are 20 percent of total
/// frames are 30hz. 0 means no frames are 30hz, 100 means all frames are 30hz.
double get percentageOf30HzFrames => _numberOfTotalFrames > 0
? _numberOf30HzFrames / _numberOfTotalFrames * 100
: 0;
/// The percentage of 60hz frames.
///
/// For example, if this value is 20, it means there are 20 percent of total
/// frames are 60hz. 0 means no frames are 60hz, 100 means all frames are 60hz.
double get percentageOf60HzFrames => _numberOfTotalFrames > 0
? _numberOf60HzFrames / _numberOfTotalFrames * 100
: 0;
/// The percentage of 80hz frames.
///
/// For example, if this value is 20, it means there are 20 percent of total
/// frames are 80hz. 0 means no frames are 80hz, 100 means all frames are 80hz.
double get percentageOf80HzFrames => _numberOfTotalFrames > 0
? _numberOf80HzFrames / _numberOfTotalFrames * 100
: 0;
/// The percentage of 90hz frames.
///
/// For example, if this value is 20, it means there are 20 percent of total
/// frames are 90hz. 0 means no frames are 90hz, 100 means all frames are 90hz.
double get percentageOf90HzFrames => _numberOfTotalFrames > 0
? _numberOf90HzFrames / _numberOfTotalFrames * 100
: 0;
/// The percentage of 120hz frames.
///
/// For example, if this value is 20, it means there are 20 percent of total
/// frames are 120hz. 0 means no frames are 120hz, 100 means all frames are 120hz.
double get percentageOf120HzFrames => _numberOfTotalFrames > 0
? _numberOf120HzFrames / _numberOfTotalFrames * 100
: 0;
/// A list of all the frames with Illegal refresh rate.
///
/// A refresh rate is consider illegal if it does not belong to anyone of the refresh rate this class is
/// explicitly tracking.
List<double> get framesWithIllegalRefreshRate =>
_framesWithIllegalRefreshRate;
int _numberOf30HzFrames = 0;
int _numberOf60HzFrames = 0;
int _numberOf80HzFrames = 0;
int _numberOf90HzFrames = 0;
int _numberOf120HzFrames = 0;
int _numberOfTotalFrames = 0;
final List<double> _framesWithIllegalRefreshRate = <double>[];
static List<double> _computeRefreshRates(List<TimelineEvent> vsyncEvents) {
final List<double> result = <double>[];
for (int i = 0; i < vsyncEvents.length; i++) {
final TimelineEvent event = vsyncEvents[i];
if (event.phase != 'B') {
continue;
}
assert(event.name == kUIThreadVsyncProcessEvent);
assert(event.arguments != null);
final Map<String, dynamic> arguments = event.arguments!;
const double nanosecondsPerSecond = 1e+9;
final int startTimeInNanoseconds = int.parse(arguments['StartTime'] as String);
final int targetTimeInNanoseconds = int.parse(arguments['TargetTime'] as String);
final int frameDurationInNanoseconds = targetTimeInNanoseconds - startTimeInNanoseconds;
final double refreshRate = nanosecondsPerSecond /
frameDurationInNanoseconds;
result.add(refreshRate);
}
return result;
}
}