Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
0ce527eb
Unverified
Commit
0ce527eb
authored
Feb 09, 2022
by
Chris Yang
Committed by
GitHub
Feb 09, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_driver] show refresh rate status in timeline summary (#95699)
parent
5f3bee55
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
323 additions
and
2 deletions
+323
-2
refresh_rate_summarizer.dart
...lutter_driver/lib/src/driver/refresh_rate_summarizer.dart
+130
-0
timeline_summary.dart
packages/flutter_driver/lib/src/driver/timeline_summary.dart
+7
-0
timeline_summary_test.dart
...ter_driver/test/src/real_tests/timeline_summary_test.dart
+186
-2
No files found.
packages/flutter_driver/lib/src/driver/refresh_rate_summarizer.dart
0 → 100644
View file @
0ce527eb
// 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
=
1
e
+
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
;
}
}
packages/flutter_driver/lib/src/driver/timeline_summary.dart
View file @
0ce527eb
...
...
@@ -13,6 +13,7 @@ import 'gc_summarizer.dart';
import
'percentile_utils.dart'
;
import
'profiling_summarizer.dart'
;
import
'raster_cache_summarizer.dart'
;
import
'refresh_rate_summarizer.dart'
;
import
'scene_display_lag_summarizer.dart'
;
import
'timeline.dart'
;
import
'vsync_frame_lag_summarizer.dart'
;
...
...
@@ -220,6 +221,7 @@ class TimelineSummary {
final
Map
<
String
,
dynamic
>
profilingSummary
=
_profilingSummarizer
().
summarize
();
final
RasterCacheSummarizer
rasterCacheSummarizer
=
_rasterCacheSummarizer
();
final
GCSummarizer
gcSummarizer
=
_gcSummarizer
();
final
RefreshRateSummary
refreshRateSummary
=
RefreshRateSummary
(
vsyncEvents:
_extractNamedEvents
(
kUIThreadVsyncProcessEvent
));
final
Map
<
String
,
dynamic
>
timelineSummary
=
<
String
,
dynamic
>{
'average_frame_build_time_millis'
:
computeAverageFrameBuildTimeMillis
(),
...
...
@@ -271,6 +273,11 @@ class TimelineSummary {
'99th_percentile_picture_cache_memory'
:
rasterCacheSummarizer
.
computePercentilePictureMemory
(
99.0
),
'worst_picture_cache_memory'
:
rasterCacheSummarizer
.
computeWorstPictureMemory
(),
'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
);
...
...
packages/flutter_driver/test/src/real_tests/timeline_summary_test.dart
View file @
0ce527eb
...
...
@@ -3,10 +3,12 @@
// found in the LICENSE file.
import
'dart:convert'
show
json
;
import
'dart:math'
;
import
'package:file/file.dart'
;
import
'package:flutter_driver/flutter_driver.dart'
;
import
'package:flutter_driver/src/driver/profiling_summarizer.dart'
;
import
'package:flutter_driver/src/driver/refresh_rate_summarizer.dart'
;
import
'package:flutter_driver/src/driver/scene_display_lag_summarizer.dart'
;
import
'package:flutter_driver/src/driver/vsync_frame_lag_summarizer.dart'
;
import
'package:path/path.dart'
as
path
;
...
...
@@ -89,10 +91,14 @@ void main() {
'ts'
:
timeStamp
,
};
Map
<
String
,
dynamic
>
vsyncCallback
(
int
timeStamp
)
=>
<
String
,
dynamic
>{
Map
<
String
,
dynamic
>
vsyncCallback
(
int
timeStamp
,
{
String
phase
=
'B'
,
String
startTime
=
'2750850055428'
,
String
endTime
=
'2750866722095'
}
)
=>
<
String
,
dynamic
>{
'name'
:
'VsyncProcessCallback'
,
'ph'
:
'B'
,
'ph'
:
phase
,
'ts'
:
timeStamp
,
'args'
:
<
String
,
dynamic
>{
'StartTime'
:
startTime
,
'TargetTime'
:
endTime
,
}
};
List
<
Map
<
String
,
dynamic
>>
_genGC
(
String
name
,
int
count
,
int
startTime
,
int
timeDiff
)
{
...
...
@@ -467,6 +473,11 @@ void main() {
'99th_percentile_picture_cache_memory'
:
0.0
,
'worst_picture_cache_memory'
:
0.0
,
'total_ui_gc_time'
:
0.4
,
'30hz_frame_percentage'
:
0
,
'60hz_frame_percentage'
:
0
,
'90hz_frame_percentage'
:
0
,
'120hz_frame_percentage'
:
0
,
'illegal_refresh_rate_frame_count'
:
0
,
},
);
});
...
...
@@ -582,6 +593,11 @@ void main() {
'99th_percentile_picture_cache_memory'
:
0.0
,
'worst_picture_cache_memory'
:
0.0
,
'total_ui_gc_time'
:
0.4
,
'30hz_frame_percentage'
:
0
,
'60hz_frame_percentage'
:
100
,
'90hz_frame_percentage'
:
0
,
'120hz_frame_percentage'
:
0
,
'illegal_refresh_rate_frame_count'
:
0
,
});
});
});
...
...
@@ -734,5 +750,173 @@ void main() {
expect
(
summarizer
.
computePercentileVsyncFrameLag
(
99
),
990
);
});
});
group
(
'RefreshRateSummarizer tests'
,
()
{
const
double
kCompareDelta
=
0.01
;
RefreshRateSummary
_summarize
(
List
<
Map
<
String
,
dynamic
>>
traceEvents
)
{
final
Timeline
timeline
=
Timeline
.
fromJson
(<
String
,
dynamic
>{
'traceEvents'
:
traceEvents
,
});
return
RefreshRateSummary
(
vsyncEvents:
timeline
.
events
!);
}
List
<
Map
<
String
,
dynamic
>>
_populateEvents
({
required
int
numberOfEvents
,
required
int
startTime
,
required
int
interval
,
required
int
margin
})
{
final
List
<
Map
<
String
,
dynamic
>>
events
=
<
Map
<
String
,
dynamic
>>[];
int
startTimeInNanoseconds
=
startTime
;
for
(
int
i
=
0
;
i
<
numberOfEvents
;
i
++)
{
final
int
randomMargin
=
margin
>=
1
?
(-
margin
+
Random
().
nextInt
(
margin
*
2
))
:
0
;
final
int
endTime
=
startTimeInNanoseconds
+
interval
+
randomMargin
;
events
.
add
(
vsyncCallback
(
0
,
startTime:
startTimeInNanoseconds
.
toString
(),
endTime:
endTime
.
toString
()));
startTimeInNanoseconds
=
endTime
;
}
return
events
;
}
test
(
'Recognize 30 hz frames.'
,
()
async
{
const
int
startTimeInNanoseconds
=
2750850055430
;
const
int
intervalInNanoseconds
=
33333333
;
// allow some margins
const
int
margin
=
3000000
;
final
List
<
Map
<
String
,
dynamic
>>
events
=
_populateEvents
(
numberOfEvents:
100
,
startTime:
startTimeInNanoseconds
,
interval:
intervalInNanoseconds
,
margin:
margin
,
);
final
RefreshRateSummary
summary
=
_summarize
(
events
);
expect
(
summary
.
percentageOf30HzFrames
,
closeTo
(
100
,
kCompareDelta
));
expect
(
summary
.
percentageOf60HzFrames
,
0
);
expect
(
summary
.
percentageOf90HzFrames
,
0
);
expect
(
summary
.
percentageOf120HzFrames
,
0
);
expect
(
summary
.
framesWithIllegalRefreshRate
,
isEmpty
);
});
test
(
'Recognize 60 hz frames.'
,
()
async
{
const
int
startTimeInNanoseconds
=
2750850055430
;
const
int
intervalInNanoseconds
=
16666666
;
// allow some margins
const
int
margin
=
1200000
;
final
List
<
Map
<
String
,
dynamic
>>
events
=
_populateEvents
(
numberOfEvents:
100
,
startTime:
startTimeInNanoseconds
,
interval:
intervalInNanoseconds
,
margin:
margin
,
);
final
RefreshRateSummary
summary
=
_summarize
(
events
);
expect
(
summary
.
percentageOf30HzFrames
,
0
);
expect
(
summary
.
percentageOf60HzFrames
,
closeTo
(
100
,
kCompareDelta
));
expect
(
summary
.
percentageOf90HzFrames
,
0
);
expect
(
summary
.
percentageOf120HzFrames
,
0
);
expect
(
summary
.
framesWithIllegalRefreshRate
,
isEmpty
);
});
test
(
'Recognize 90 hz frames.'
,
()
async
{
const
int
startTimeInNanoseconds
=
2750850055430
;
const
int
intervalInNanoseconds
=
11111111
;
// allow some margins
const
int
margin
=
500000
;
final
List
<
Map
<
String
,
dynamic
>>
events
=
_populateEvents
(
numberOfEvents:
100
,
startTime:
startTimeInNanoseconds
,
interval:
intervalInNanoseconds
,
margin:
margin
,
);
final
RefreshRateSummary
summary
=
_summarize
(
events
);
expect
(
summary
.
percentageOf30HzFrames
,
0
);
expect
(
summary
.
percentageOf60HzFrames
,
0
);
expect
(
summary
.
percentageOf90HzFrames
,
closeTo
(
100
,
kCompareDelta
));
expect
(
summary
.
percentageOf120HzFrames
,
0
);
expect
(
summary
.
framesWithIllegalRefreshRate
,
isEmpty
);
});
test
(
'Recognize 120 hz frames.'
,
()
async
{
const
int
startTimeInNanoseconds
=
2750850055430
;
const
int
intervalInNanoseconds
=
8333333
;
// allow some margins
const
int
margin
=
300000
;
final
List
<
Map
<
String
,
dynamic
>>
events
=
_populateEvents
(
numberOfEvents:
100
,
startTime:
startTimeInNanoseconds
,
interval:
intervalInNanoseconds
,
margin:
margin
,
);
final
RefreshRateSummary
summary
=
_summarize
(
events
);
expect
(
summary
.
percentageOf30HzFrames
,
0
);
expect
(
summary
.
percentageOf60HzFrames
,
0
);
expect
(
summary
.
percentageOf90HzFrames
,
0
);
expect
(
summary
.
percentageOf120HzFrames
,
closeTo
(
100
,
kCompareDelta
));
expect
(
summary
.
framesWithIllegalRefreshRate
,
isEmpty
);
});
test
(
'Identify illegal refresh rates.'
,
()
async
{
const
int
startTimeInNanoseconds
=
2750850055430
;
const
int
intervalInNanoseconds
=
10000000
;
final
List
<
Map
<
String
,
dynamic
>>
events
=
_populateEvents
(
numberOfEvents:
1
,
startTime:
startTimeInNanoseconds
,
interval:
intervalInNanoseconds
,
margin:
0
,
);
final
RefreshRateSummary
summary
=
_summarize
(
events
);
expect
(
summary
.
percentageOf30HzFrames
,
0
);
expect
(
summary
.
percentageOf60HzFrames
,
0
);
expect
(
summary
.
percentageOf90HzFrames
,
0
);
expect
(
summary
.
percentageOf120HzFrames
,
0
);
expect
(
summary
.
framesWithIllegalRefreshRate
,
isNotEmpty
);
expect
(
summary
.
framesWithIllegalRefreshRate
.
first
,
closeTo
(
100
,
kCompareDelta
));
});
test
(
'Mixed refresh rates.'
,
()
async
{
final
List
<
Map
<
String
,
dynamic
>>
events
=
<
Map
<
String
,
dynamic
>>[];
const
int
num30Hz
=
10
;
const
int
num60Hz
=
20
;
const
int
num90Hz
=
20
;
const
int
num120Hz
=
40
;
const
int
numIllegal
=
10
;
// Add 30hz frames
events
.
addAll
(
_populateEvents
(
numberOfEvents:
num30Hz
,
startTime:
0
,
interval:
32000000
,
margin:
0
,
));
// Add 60hz frames
events
.
addAll
(
_populateEvents
(
numberOfEvents:
num60Hz
,
startTime:
0
,
interval:
16000000
,
margin:
0
,
));
// Add 90hz frames
events
.
addAll
(
_populateEvents
(
numberOfEvents:
num90Hz
,
startTime:
0
,
interval:
11000000
,
margin:
0
,
));
// Add 120hz frames
events
.
addAll
(
_populateEvents
(
numberOfEvents:
num120Hz
,
startTime:
0
,
interval:
8000000
,
margin:
0
,
));
// Add illegal refresh rate frames
events
.
addAll
(
_populateEvents
(
numberOfEvents:
numIllegal
,
startTime:
0
,
interval:
60000
,
margin:
0
,
));
final
RefreshRateSummary
summary
=
_summarize
(
events
);
expect
(
summary
.
percentageOf30HzFrames
,
closeTo
(
num30Hz
,
kCompareDelta
));
expect
(
summary
.
percentageOf60HzFrames
,
closeTo
(
num60Hz
,
kCompareDelta
));
expect
(
summary
.
percentageOf90HzFrames
,
closeTo
(
num90Hz
,
kCompareDelta
));
expect
(
summary
.
percentageOf120HzFrames
,
closeTo
(
num120Hz
,
kCompareDelta
));
expect
(
summary
.
framesWithIllegalRefreshRate
,
isNotEmpty
);
expect
(
summary
.
framesWithIllegalRefreshRate
.
length
,
10
);
});
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment