| // 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; |
| } |
| } |