forked from firka/flutter
[Impeller] add a configuration option that allows defering all PSO construction until needed. (#165261)
The cost of bootstapping the initial PSOs can regress cold startup time for customer money. As an experiment, attempt to defer PSO construction to skia like. --------- Co-authored-by: Aaron Clarke <aaclarke@google.com> Co-authored-by: gaaclarke <30870216+gaaclarke@users.noreply.github.com>
This commit is contained in:
12
dev/devicelab/bin/tasks/flutter_gallery_lazy__start_up.dart
Normal file
12
dev/devicelab/bin/tasks/flutter_gallery_lazy__start_up.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
// 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 'package:flutter_devicelab/framework/devices.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/tasks/perf_tests.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.android;
|
||||
await task(createFlutterGalleryStartupTest(enableLazyShaderMode: true));
|
||||
}
|
||||
@@ -270,11 +270,13 @@ TaskFunction createOpenPayScrollPerfTest({bool measureCpuGpu = true}) {
|
||||
TaskFunction createFlutterGalleryStartupTest({
|
||||
String target = 'lib/main.dart',
|
||||
Map<String, String>? runEnvironment,
|
||||
bool enableLazyShaderMode = false,
|
||||
}) {
|
||||
return StartupTest(
|
||||
'${flutterDirectory.path}/dev/integration_tests/flutter_gallery',
|
||||
target: target,
|
||||
runEnvironment: runEnvironment,
|
||||
enableLazyShaderMode: enableLazyShaderMode,
|
||||
).run;
|
||||
}
|
||||
|
||||
@@ -840,6 +842,17 @@ void _addVulkanGPUTracingToManifest(String testDirectory) {
|
||||
_addMetadataToManifest(testDirectory, keyPairs);
|
||||
}
|
||||
|
||||
/// Opens the file at testDirectory + 'android/app/src/main/AndroidManifest.xml'
|
||||
/// <meta-data
|
||||
/// android:name="io.flutter.embedding.android.ImpellerShaderMode"
|
||||
/// android:value="lazy" />
|
||||
void _addLazyShaderMode(String testDirectory) {
|
||||
final List<(String, String)> keyPairs = <(String, String)>[
|
||||
('io.flutter.embedding.android.ImpellerLazyShaderInitialization', 'true'),
|
||||
];
|
||||
_addMetadataToManifest(testDirectory, keyPairs);
|
||||
}
|
||||
|
||||
/// Opens the file at testDirectory + 'android/app/src/main/AndroidManifest.xml'
|
||||
/// and adds the following entry to the application.
|
||||
/// <meta-data
|
||||
@@ -881,10 +894,12 @@ class StartupTest {
|
||||
this.reportMetrics = true,
|
||||
this.target = 'lib/main.dart',
|
||||
this.runEnvironment,
|
||||
this.enableLazyShaderMode = false,
|
||||
});
|
||||
|
||||
final String testDirectory;
|
||||
final bool reportMetrics;
|
||||
final bool enableLazyShaderMode;
|
||||
final String target;
|
||||
final Map<String, String>? runEnvironment;
|
||||
|
||||
@@ -895,144 +910,155 @@ class StartupTest {
|
||||
const int iterations = 5;
|
||||
final List<Map<String, dynamic>> results = <Map<String, dynamic>>[];
|
||||
|
||||
section('Building application');
|
||||
String? applicationBinaryPath;
|
||||
switch (deviceOperatingSystem) {
|
||||
case DeviceOperatingSystem.android:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target-platform=android-arm,android-arm64',
|
||||
'--target=$target',
|
||||
],
|
||||
);
|
||||
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||
case DeviceOperatingSystem.androidArm:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target-platform=android-arm',
|
||||
'--target=$target',
|
||||
],
|
||||
);
|
||||
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||
case DeviceOperatingSystem.androidArm64:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target-platform=android-arm64',
|
||||
'--target=$target',
|
||||
],
|
||||
);
|
||||
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||
case DeviceOperatingSystem.fake:
|
||||
case DeviceOperatingSystem.fuchsia:
|
||||
case DeviceOperatingSystem.linux:
|
||||
break;
|
||||
case DeviceOperatingSystem.ios:
|
||||
case DeviceOperatingSystem.macos:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
if (deviceOperatingSystem == DeviceOperatingSystem.ios) 'ios' else 'macos',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target=$target',
|
||||
if (deviceOperatingSystem == DeviceOperatingSystem.ios) '--no-publish-port',
|
||||
],
|
||||
);
|
||||
final String buildRoot = path.join(testDirectory, 'build');
|
||||
applicationBinaryPath = _findDarwinAppInBuildDirectory(buildRoot);
|
||||
case DeviceOperatingSystem.windows:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>['windows', '-v', '--profile', '--target=$target'],
|
||||
);
|
||||
final String basename = path.basename(testDirectory);
|
||||
final String arch = Abi.current() == Abi.windowsX64 ? 'x64' : 'arm64';
|
||||
applicationBinaryPath = path.join(
|
||||
testDirectory,
|
||||
'build',
|
||||
'windows',
|
||||
arch,
|
||||
'runner',
|
||||
'Profile',
|
||||
'$basename.exe',
|
||||
);
|
||||
if (enableLazyShaderMode) {
|
||||
_addLazyShaderMode(testDirectory);
|
||||
}
|
||||
|
||||
const int maxFailures = 3;
|
||||
int currentFailures = 0;
|
||||
for (int i = 0; i < iterations; i += 1) {
|
||||
// Startup should not take more than a few minutes. After 10 minutes,
|
||||
// take a screenshot to help debug.
|
||||
final Timer timer = Timer(const Duration(minutes: 10), () async {
|
||||
print('Startup not completed within 10 minutes. Taking a screenshot...');
|
||||
await _flutterScreenshot(
|
||||
device.deviceId,
|
||||
'screenshot_startup_${DateTime.now().toLocal().toIso8601String()}.png',
|
||||
);
|
||||
});
|
||||
final int result = await flutter(
|
||||
'run',
|
||||
options: <String>[
|
||||
'--no-android-gradle-daemon',
|
||||
'--no-publish-port',
|
||||
'--verbose',
|
||||
'--profile',
|
||||
'--trace-startup',
|
||||
'--target=$target',
|
||||
'-d',
|
||||
device.deviceId,
|
||||
if (applicationBinaryPath != null) '--use-application-binary=$applicationBinaryPath',
|
||||
],
|
||||
environment: runEnvironment,
|
||||
canFail: true,
|
||||
);
|
||||
timer.cancel();
|
||||
if (result == 0) {
|
||||
final Map<String, dynamic> data =
|
||||
json.decode(
|
||||
file(
|
||||
'${testOutputDirectory(testDirectory)}/start_up_info.json',
|
||||
).readAsStringSync(),
|
||||
)
|
||||
as Map<String, dynamic>;
|
||||
results.add(data);
|
||||
} else {
|
||||
currentFailures += 1;
|
||||
await _flutterScreenshot(
|
||||
device.deviceId,
|
||||
'screenshot_startup_failure_$currentFailures.png',
|
||||
);
|
||||
i -= 1;
|
||||
if (currentFailures == maxFailures) {
|
||||
return TaskResult.failure('Application failed to start $maxFailures times');
|
||||
}
|
||||
try {
|
||||
section('Building application');
|
||||
String? applicationBinaryPath;
|
||||
switch (deviceOperatingSystem) {
|
||||
case DeviceOperatingSystem.android:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target-platform=android-arm,android-arm64',
|
||||
'--target=$target',
|
||||
],
|
||||
);
|
||||
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||
case DeviceOperatingSystem.androidArm:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target-platform=android-arm',
|
||||
'--target=$target',
|
||||
],
|
||||
);
|
||||
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||
case DeviceOperatingSystem.androidArm64:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target-platform=android-arm64',
|
||||
'--target=$target',
|
||||
],
|
||||
);
|
||||
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||
case DeviceOperatingSystem.fake:
|
||||
case DeviceOperatingSystem.fuchsia:
|
||||
case DeviceOperatingSystem.linux:
|
||||
break;
|
||||
case DeviceOperatingSystem.ios:
|
||||
case DeviceOperatingSystem.macos:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
if (deviceOperatingSystem == DeviceOperatingSystem.ios) 'ios' else 'macos',
|
||||
'-v',
|
||||
'--profile',
|
||||
'--target=$target',
|
||||
if (deviceOperatingSystem == DeviceOperatingSystem.ios) '--no-publish-port',
|
||||
],
|
||||
);
|
||||
final String buildRoot = path.join(testDirectory, 'build');
|
||||
applicationBinaryPath = _findDarwinAppInBuildDirectory(buildRoot);
|
||||
case DeviceOperatingSystem.windows:
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>['windows', '-v', '--profile', '--target=$target'],
|
||||
);
|
||||
final String basename = path.basename(testDirectory);
|
||||
final String arch = Abi.current() == Abi.windowsX64 ? 'x64' : 'arm64';
|
||||
applicationBinaryPath = path.join(
|
||||
testDirectory,
|
||||
'build',
|
||||
'windows',
|
||||
arch,
|
||||
'runner',
|
||||
'Profile',
|
||||
'$basename.exe',
|
||||
);
|
||||
}
|
||||
|
||||
await device.uninstallApp();
|
||||
const int maxFailures = 3;
|
||||
int currentFailures = 0;
|
||||
for (int i = 0; i < iterations; i += 1) {
|
||||
// Startup should not take more than a few minutes. After 10 minutes,
|
||||
// take a screenshot to help debug.
|
||||
final Timer timer = Timer(const Duration(minutes: 10), () async {
|
||||
print('Startup not completed within 10 minutes. Taking a screenshot...');
|
||||
await _flutterScreenshot(
|
||||
device.deviceId,
|
||||
'screenshot_startup_${DateTime.now().toLocal().toIso8601String()}.png',
|
||||
);
|
||||
});
|
||||
final int result = await flutter(
|
||||
'run',
|
||||
options: <String>[
|
||||
'--no-android-gradle-daemon',
|
||||
'--no-publish-port',
|
||||
'--verbose',
|
||||
'--profile',
|
||||
'--trace-startup',
|
||||
'--target=$target',
|
||||
'-d',
|
||||
device.deviceId,
|
||||
if (applicationBinaryPath != null) '--use-application-binary=$applicationBinaryPath',
|
||||
],
|
||||
environment: runEnvironment,
|
||||
canFail: true,
|
||||
);
|
||||
timer.cancel();
|
||||
if (result == 0) {
|
||||
final Map<String, dynamic> data =
|
||||
json.decode(
|
||||
file(
|
||||
'${testOutputDirectory(testDirectory)}/start_up_info.json',
|
||||
).readAsStringSync(),
|
||||
)
|
||||
as Map<String, dynamic>;
|
||||
results.add(data);
|
||||
} else {
|
||||
currentFailures += 1;
|
||||
await _flutterScreenshot(
|
||||
device.deviceId,
|
||||
'screenshot_startup_failure_$currentFailures.png',
|
||||
);
|
||||
i -= 1;
|
||||
if (currentFailures == maxFailures) {
|
||||
return TaskResult.failure('Application failed to start $maxFailures times');
|
||||
}
|
||||
}
|
||||
|
||||
await device.uninstallApp();
|
||||
}
|
||||
|
||||
final Map<String, dynamic> averageResults = _average(results, iterations);
|
||||
|
||||
if (!reportMetrics) {
|
||||
return TaskResult.success(averageResults);
|
||||
}
|
||||
|
||||
return TaskResult.success(
|
||||
averageResults,
|
||||
benchmarkScoreKeys: <String>[
|
||||
'timeToFirstFrameMicros',
|
||||
'timeToFirstFrameRasterizedMicros',
|
||||
],
|
||||
);
|
||||
} finally {
|
||||
await _resetManifest(testDirectory);
|
||||
}
|
||||
|
||||
final Map<String, dynamic> averageResults = _average(results, iterations);
|
||||
|
||||
if (!reportMetrics) {
|
||||
return TaskResult.success(averageResults);
|
||||
}
|
||||
|
||||
return TaskResult.success(
|
||||
averageResults,
|
||||
benchmarkScoreKeys: <String>['timeToFirstFrameMicros', 'timeToFirstFrameRasterizedMicros'],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1180,6 +1206,7 @@ class PerfTest {
|
||||
this.disablePartialRepaint = false,
|
||||
this.enableMergedPlatformThread = false,
|
||||
this.enableSurfaceControl = false,
|
||||
this.enableLazyShaderMode = false,
|
||||
this.createPlatforms = const <String>[],
|
||||
}) : _resultFilename = resultFilename;
|
||||
|
||||
@@ -1202,6 +1229,7 @@ class PerfTest {
|
||||
this.disablePartialRepaint = false,
|
||||
this.enableMergedPlatformThread = false,
|
||||
this.enableSurfaceControl = false,
|
||||
this.enableLazyShaderMode = false,
|
||||
this.createPlatforms = const <String>[],
|
||||
}) : saveTraceFile = false,
|
||||
timelineFileName = null,
|
||||
@@ -1261,6 +1289,9 @@ class PerfTest {
|
||||
/// Whether to enable SurfaceControl swapchain.
|
||||
final bool enableSurfaceControl;
|
||||
|
||||
/// Whether to defer construction of all PSO objects in the Impeller backend.
|
||||
final bool enableLazyShaderMode;
|
||||
|
||||
/// Number of seconds to time out the test after, allowing debug callbacks to run.
|
||||
final int? timeoutSeconds;
|
||||
|
||||
@@ -1359,6 +1390,9 @@ class PerfTest {
|
||||
if (enableSurfaceControl) {
|
||||
_addSurfaceControlSupportToManifest(testDirectory);
|
||||
}
|
||||
if (enableLazyShaderMode) {
|
||||
_addLazyShaderMode(testDirectory);
|
||||
}
|
||||
}
|
||||
if (disablePartialRepaint || enableMergedPlatformThread) {
|
||||
changedPlist = true;
|
||||
|
||||
Reference in New Issue
Block a user