blob: bedd529d0bb2aad2dbd9a93b860a1ab9e5260ef9 [file] [log] [blame]
// 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.
factory RefreshRateSummary({required List<TimelineEvent> vsyncEvents}) {
return RefreshRateSummary._(refreshRates: _computeRefreshRates(vsyncEvents));
}
RefreshRateSummary._({required List<double> refreshRates}) {
_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;
}
}