Unverified Commit 0ce527eb authored by Chris Yang's avatar Chris Yang Committed by GitHub

[flutter_driver] show refresh rate status in timeline summary (#95699)

parent 5f3bee55
// 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 - 90).abs() < _kErrorMargin) {
_numberOf90HzFrames++;
continue;
}
if ((refreshRate - 120).abs() < _kErrorMargin) {
_numberOf120HzFrames++;
continue;
}
_framesWithIllegalRefreshRate.add(refreshRate);
}
assert(_numberOfTotalFrames ==
_numberOf30HzFrames +
_numberOf60HzFrames +
_numberOf90HzFrames +
_numberOf120HzFrames +
_framesWithIllegalRefreshRate.length);
}
static const double _kErrorMargin = 6.0;
/// Number of frames with 30hz refresh rate
int get numberOf30HzFrames => _numberOf30HzFrames;
/// Number of frames with 60hz refresh rate
int get numberOf60HzFrames => _numberOf60HzFrames;
/// Number of frames with 90hz refresh rate
int get numberOf90HzFrames => _numberOf90HzFrames;
/// Number of frames with 120hz refresh rate
int get numberOf120HzFrames => _numberOf120HzFrames;
/// 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 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 90hz 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 below:
/// 30hz, 60hz, 90hz or 120hz.
List<double> get framesWithIllegalRefreshRate =>
_framesWithIllegalRefreshRate;
int _numberOf30HzFrames = 0;
int _numberOf60HzFrames = 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;
}
}
...@@ -13,6 +13,7 @@ import 'gc_summarizer.dart'; ...@@ -13,6 +13,7 @@ import 'gc_summarizer.dart';
import 'percentile_utils.dart'; import 'percentile_utils.dart';
import 'profiling_summarizer.dart'; import 'profiling_summarizer.dart';
import 'raster_cache_summarizer.dart'; import 'raster_cache_summarizer.dart';
import 'refresh_rate_summarizer.dart';
import 'scene_display_lag_summarizer.dart'; import 'scene_display_lag_summarizer.dart';
import 'timeline.dart'; import 'timeline.dart';
import 'vsync_frame_lag_summarizer.dart'; import 'vsync_frame_lag_summarizer.dart';
...@@ -220,6 +221,7 @@ class TimelineSummary { ...@@ -220,6 +221,7 @@ class TimelineSummary {
final Map<String, dynamic> profilingSummary = _profilingSummarizer().summarize(); final Map<String, dynamic> profilingSummary = _profilingSummarizer().summarize();
final RasterCacheSummarizer rasterCacheSummarizer = _rasterCacheSummarizer(); final RasterCacheSummarizer rasterCacheSummarizer = _rasterCacheSummarizer();
final GCSummarizer gcSummarizer = _gcSummarizer(); final GCSummarizer gcSummarizer = _gcSummarizer();
final RefreshRateSummary refreshRateSummary = RefreshRateSummary(vsyncEvents: _extractNamedEvents(kUIThreadVsyncProcessEvent));
final Map<String, dynamic> timelineSummary = <String, dynamic>{ final Map<String, dynamic> timelineSummary = <String, dynamic>{
'average_frame_build_time_millis': computeAverageFrameBuildTimeMillis(), 'average_frame_build_time_millis': computeAverageFrameBuildTimeMillis(),
...@@ -271,6 +273,11 @@ class TimelineSummary { ...@@ -271,6 +273,11 @@ class TimelineSummary {
'99th_percentile_picture_cache_memory': rasterCacheSummarizer.computePercentilePictureMemory(99.0), '99th_percentile_picture_cache_memory': rasterCacheSummarizer.computePercentilePictureMemory(99.0),
'worst_picture_cache_memory': rasterCacheSummarizer.computeWorstPictureMemory(), 'worst_picture_cache_memory': rasterCacheSummarizer.computeWorstPictureMemory(),
'total_ui_gc_time': gcSummarizer.totalGCTimeMillis, 'total_ui_gc_time': gcSummarizer.totalGCTimeMillis,
'30hz_frame_percentage': refreshRateSummary.percentageOf30HzFrames,
'60hz_frame_percentage': refreshRateSummary.percentageOf60HzFrames,
'90hz_frame_percentage': refreshRateSummary.percentageOf90HzFrames,
'120hz_frame_percentage': refreshRateSummary.percentageOf120HzFrames,
'illegal_refresh_rate_frame_count': refreshRateSummary.framesWithIllegalRefreshRate.length,
}; };
timelineSummary.addAll(profilingSummary); timelineSummary.addAll(profilingSummary);
......
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