Add VMService command to get frame rasterization metrics (#100696)
This commit is contained in:
parent
329ceaef66
commit
aeaeded715
@ -118,6 +118,11 @@ class CommandHelp {
|
||||
'WidgetsApp.showWidgetInspectorOverride',
|
||||
);
|
||||
|
||||
late final CommandHelpOption j = _makeOption(
|
||||
'j',
|
||||
'Dump frame raster stats for the current frame.',
|
||||
);
|
||||
|
||||
late final CommandHelpOption k = _makeOption(
|
||||
'k',
|
||||
'Toggle CanvasKit rendering.',
|
||||
|
@ -674,6 +674,41 @@ abstract class ResidentHandlers {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Dump frame rasterization metrics for the last rendered frame.
|
||||
///
|
||||
/// The last frames gets re-painted while recording additional tracing info
|
||||
/// pertaining to the various draw calls issued by the frame. The timings
|
||||
/// recorded here are not indicative of production performance. The intended
|
||||
/// use case is to look at the various layers in proportion to see what
|
||||
/// contributes the most towards raster performance.
|
||||
Future<bool> debugFrameJankMetrics() async {
|
||||
if (!supportsServiceProtocol || !isRunningDebug) {
|
||||
return false;
|
||||
}
|
||||
for (final FlutterDevice device in flutterDevices) {
|
||||
final List<FlutterView> views = await device.vmService.getFlutterViews();
|
||||
for (final FlutterView view in views) {
|
||||
final Map<String, Object> rasterData =
|
||||
await device.vmService.renderFrameWithRasterStats(
|
||||
viewId: view.id,
|
||||
uiIsolateId: view.uiIsolate.id,
|
||||
);
|
||||
if (rasterData != null) {
|
||||
final File tempFile = globals.fsUtils.getUniqueFile(
|
||||
globals.fs.currentDirectory,
|
||||
'flutter_jank_metrics',
|
||||
'json',
|
||||
);
|
||||
tempFile.writeAsStringSync(jsonEncode(rasterData), flush: true);
|
||||
logger.printStatus('Wrote jank metrics to ${tempFile.absolute.path}');
|
||||
} else {
|
||||
logger.printWarning('Unable to get jank metrics.');
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Dump the application's current layer tree to the terminal.
|
||||
Future<bool> debugDumpLayerTree() async {
|
||||
if (!supportsServiceProtocol || !isRunningDebug) {
|
||||
@ -1439,6 +1474,7 @@ abstract class ResidentRunner extends ResidentHandlers {
|
||||
if (isRunningDebug) {
|
||||
commandHelp.g.print();
|
||||
}
|
||||
commandHelp.j.print();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1605,6 +1641,9 @@ class TerminalHandler {
|
||||
return residentRunner.debugToggleWidgetInspector();
|
||||
case 'I':
|
||||
return residentRunner.debugToggleInvertOversizedImages();
|
||||
case 'j':
|
||||
case 'J':
|
||||
return residentRunner.debugFrameJankMetrics();
|
||||
case 'L':
|
||||
return residentRunner.debugDumpLayerTree();
|
||||
case 'o':
|
||||
|
@ -23,6 +23,7 @@ const String kRunInViewMethod = '_flutter.runInView';
|
||||
const String kListViewsMethod = '_flutter.listViews';
|
||||
const String kScreenshotSkpMethod = '_flutter.screenshotSkp';
|
||||
const String kScreenshotMethod = '_flutter.screenshot';
|
||||
const String kRenderFrameWithRasterStatsMethod = '_flutter.renderFrameWithRasterStats';
|
||||
|
||||
/// The error response code from an unrecoverable compilation failure.
|
||||
const int kIsolateReloadBarred = 1005;
|
||||
@ -538,6 +539,26 @@ class FlutterVmService {
|
||||
await onRunnable;
|
||||
}
|
||||
|
||||
/// Renders the last frame with additional raster tracing enabled.
|
||||
///
|
||||
/// When a frame is rendered using this method it will incur additional cost
|
||||
/// for rasterization which is not reflective of how long the frame takes in
|
||||
/// production. This is primarily intended to be used to identify the layers
|
||||
/// that result in the most raster perf degradation.
|
||||
Future<Map<String, Object>?> renderFrameWithRasterStats({
|
||||
required String? viewId,
|
||||
required String? uiIsolateId,
|
||||
}) async {
|
||||
final vm_service.Response? response = await callMethodWrapper(
|
||||
kRenderFrameWithRasterStatsMethod,
|
||||
isolateId: uiIsolateId,
|
||||
args: <String, String?>{
|
||||
'viewId': viewId,
|
||||
},
|
||||
);
|
||||
return response?.json as Map<String, Object>?;
|
||||
}
|
||||
|
||||
Future<String> flutterDebugDumpApp({
|
||||
required String isolateId,
|
||||
}) async {
|
||||
|
@ -1330,6 +1330,7 @@ flutter:
|
||||
commandHelp.a,
|
||||
commandHelp.M,
|
||||
commandHelp.g,
|
||||
commandHelp.j,
|
||||
commandHelp.hWithDetails,
|
||||
commandHelp.c,
|
||||
commandHelp.q,
|
||||
|
@ -459,6 +459,14 @@ void main() {
|
||||
method: 'getVMTimeline',
|
||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: kRenderFrameWithRasterStatsMethod,
|
||||
args: <String, dynamic>{
|
||||
'viewId': '1',
|
||||
'isolateId': '12',
|
||||
},
|
||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
@ -482,6 +490,10 @@ void main() {
|
||||
final vm_service.Response timeline = await fakeVmServiceHost.vmService.getTimeline();
|
||||
expect(timeline, isNull);
|
||||
|
||||
final Map<String, Object> rasterStats =
|
||||
await fakeVmServiceHost.vmService.renderFrameWithRasterStats(viewId: '1', uiIsolateId: '12');
|
||||
expect(rasterStats, isNull);
|
||||
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
@ -502,6 +514,35 @@ void main() {
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('renderWithStats forwards stats correctly', () async {
|
||||
// ignore: always_specify_types
|
||||
const Map<String, dynamic> response = {
|
||||
'type': 'RenderFrameWithRasterStats',
|
||||
'snapshots':<dynamic>[
|
||||
// ignore: always_specify_types
|
||||
{
|
||||
'layer_unique_id':1512,
|
||||
'duration_micros':477,
|
||||
'snapshot':''
|
||||
},
|
||||
],
|
||||
};
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(method: kRenderFrameWithRasterStatsMethod, args: <String, Object>{
|
||||
'isolateId': 'isolate/123',
|
||||
'viewId': 'view/1',
|
||||
}, jsonResponse: response),
|
||||
]
|
||||
);
|
||||
|
||||
final Map<String, Object> rasterStats =
|
||||
await fakeVmServiceHost.vmService.renderFrameWithRasterStats(viewId: 'view/1', uiIsolateId: 'isolate/123');
|
||||
expect(rasterStats, equals(response));
|
||||
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('getFlutterViews polls until a view is returned', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
|
@ -612,6 +612,7 @@ void main() {
|
||||
'a Toggle timeline events for all widget build methods. (debugProfileWidgetBuilds)',
|
||||
'M Write SkSL shaders to a unique file in the project directory.',
|
||||
'g Run source code generators.',
|
||||
'j Dump frame raster stats for the current frame.',
|
||||
'h Repeat this help message.',
|
||||
'd Detach (terminate "flutter run" but leave application running).',
|
||||
'c Clear the screen',
|
||||
|
Loading…
x
Reference in New Issue
Block a user