From 6291a51ce29179191f3c28b44904390d91aa9a9d Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 10 Feb 2025 18:12:49 -0800 Subject: [PATCH] [Android] add HCPP platform views benchmark and integration test. (#163018) Adds a benchmark and integration test of HCPP using a Pixel 7. --- .ci.yaml | 10 ++ TESTOWNERS | 1 + .../platform_views_layout/lib/main_hcpp.dart | 126 ++++++++++++++++++ .../test_driver/scroll_perf_hcpp.dart | 13 ++ .../test_driver/scroll_perf_hcpp_test.dart | 61 +++++++++ ...ws_hcpp_scroll_perf__timeline_summary.dart | 12 ++ dev/devicelab/lib/tasks/perf_tests.dart | 30 ++++- 7 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 dev/benchmarks/platform_views_layout/lib/main_hcpp.dart create mode 100644 dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp.dart create mode 100644 dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp_test.dart create mode 100644 dev/devicelab/bin/tasks/platform_views_hcpp_scroll_perf__timeline_summary.dart diff --git a/.ci.yaml b/.ci.yaml index 51eb0f667f..e0b7593799 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -3210,6 +3210,16 @@ targets: ["devicelab", "android", "linux", "pixel", "7pro"] task_name: platform_views_scroll_perf_impeller__timeline_summary + - name: Linux_pixel_7pro platform_views_hcpp_scroll_perf__timeline_summary + recipe: devicelab/devicelab_drone + presubmit: false + bringup: true + timeout: 60 + properties: + tags: > + ["devicelab", "android", "linux", "pixel", "7pro"] + task_name: platform_views_hcpp_scroll_perf__timeline_summary + # linux mokey benchmark - name: Linux_mokey platform_view__start_up recipe: devicelab/devicelab_drone diff --git a/TESTOWNERS b/TESTOWNERS index ccc0617479..aa93e814ba 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -108,6 +108,7 @@ /dev/devicelab/bin/tasks/complex_layout_scroll_perf_impeller__timeline_summary.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/complex_layout_scroll_perf_impeller_gles__timeline_summary.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/rrect_blur_perf__timeline_summary.dart @gaaclarke @flutter/engine +/dev/devicelab/bin/tasks/platform_views_hcpp_scroll_perf__timeline_summary.dart @jonahwilliams @flutter/engine ## Windows Android DeviceLab tests /dev/devicelab/bin/tasks/basic_material_app_win__compile.dart @bkonyi @flutter/tool diff --git a/dev/benchmarks/platform_views_layout/lib/main_hcpp.dart b/dev/benchmarks/platform_views_layout/lib/main_hcpp.dart new file mode 100644 index 0000000000..e50212e0eb --- /dev/null +++ b/dev/benchmarks/platform_views_layout/lib/main_hcpp.dart @@ -0,0 +1,126 @@ +// 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/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; + +void main() { + runApp(const PlatformViewApp()); +} + +class PlatformViewApp extends StatefulWidget { + const PlatformViewApp({super.key}); + + @override + PlatformViewAppState createState() => PlatformViewAppState(); +} + +class PlatformViewAppState extends State { + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData.light(), + title: 'Advanced Layout', + home: const PlatformViewLayout(), + ); + } +} + +class PlatformViewLayout extends StatelessWidget { + const PlatformViewLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Platform View Scrolling Layout')), + body: ListView.builder( + key: const Key('platform-views-scroll'), // This key is used by the driver test. + itemCount: 200, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(5.0), + child: Material( + elevation: (index % 5 + 1).toDouble(), + color: Colors.white, + child: const Stack(children: [_AndroidPlatformView(), RotationContainer()]), + ), + ); + }, + ), + ); + } +} + +final class _AndroidPlatformView extends StatelessWidget { + const _AndroidPlatformView(); + + static const String viewType = 'benchmarks/platform_views_layout/DummyPlatformView'; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 400, + height: 200, + child: PlatformViewLink( + viewType: viewType, + surfaceFactory: (BuildContext context, PlatformViewController controller) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return PlatformViewsService.initHybridAndroidView( + id: params.id, + viewType: viewType, + layoutDirection: TextDirection.ltr, + creationParamsCodec: const StandardMessageCodec(), + ) + ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) + ..create(); + }, + ), + ); + } +} + +class RotationContainer extends StatefulWidget { + const RotationContainer({super.key}); + + @override + State createState() => _RotationContainerState(); +} + +class _RotationContainerState extends State with SingleTickerProviderStateMixin { + late AnimationController _rotationController; + + @override + void initState() { + super.initState(); + _rotationController = AnimationController( + vsync: this, + duration: const Duration(seconds: 1), + value: 1, + ); + _rotationController.repeat(); + } + + @override + void dispose() { + _rotationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RotationTransition( + turns: Tween(begin: 0.0, end: 1.0).animate(_rotationController), + child: Container(color: Colors.purple, width: 50.0, height: 50.0), + ); + } +} diff --git a/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp.dart b/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp.dart new file mode 100644 index 0000000000..a788f89f94 --- /dev/null +++ b/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp.dart @@ -0,0 +1,13 @@ +// 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/widgets.dart'; +import 'package:flutter_driver/driver_extension.dart'; + +import 'package:platform_views_layout/main_hcpp.dart' as app; + +void main() { + enableFlutterDriverExtension(); + runApp(const app.PlatformViewApp()); +} diff --git a/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp_test.dart b/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp_test.dart new file mode 100644 index 0000000000..7705c92e64 --- /dev/null +++ b/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_hcpp_test.dart @@ -0,0 +1,61 @@ +// 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_driver/flutter_driver.dart'; +import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; + +void main() { + group('scrolling performance test', () { + late FlutterDriver driver; + + setUpAll(() async { + driver = await FlutterDriver.connect(); + + await driver.waitUntilFirstFrameRasterized(); + }); + + tearDownAll(() async { + driver.close(); + }); + + Future testScrollPerf(String listKey, String summaryName) async { + // The slight initial delay avoids starting the timing during a + // period of increased load on the device. Without this delay, the + // benchmark has greater noise. + // See: https://github.com/flutter/flutter/issues/19434 + await Future.delayed(const Duration(milliseconds: 250)); + + await driver.forceGC(); + + final Timeline timeline = await driver.traceAction(() async { + // Find the scrollable stock list + final SerializableFinder list = find.byValueKey(listKey); + + for (int j = 0; j < 5; j += 1) { + // Scroll down + for (int i = 0; i < 5; i += 1) { + await driver.scroll(list, 0.0, -300.0, const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: 500)); + } + + // Scroll up + for (int i = 0; i < 5; i += 1) { + await driver.scroll(list, 0.0, 300.0, const Duration(milliseconds: 300)); + await Future.delayed(const Duration(milliseconds: 500)); + } + } + }); + + final TimelineSummary summary = TimelineSummary.summarize(timeline); + await summary.writeTimelineToFile(summaryName, pretty: true); + } + + test('platform_views_scroll_perf', () async { + // Disable frame sync, since there are ongoing animations. + await driver.runUnsynchronized(() async { + await testScrollPerf('platform-views-scroll', 'platform_views_hcpp_scroll_perf'); + }); + }, timeout: Timeout.none); + }); +} diff --git a/dev/devicelab/bin/tasks/platform_views_hcpp_scroll_perf__timeline_summary.dart b/dev/devicelab/bin/tasks/platform_views_hcpp_scroll_perf__timeline_summary.dart new file mode 100644 index 0000000000..85091c4639 --- /dev/null +++ b/dev/devicelab/bin/tasks/platform_views_hcpp_scroll_perf__timeline_summary.dart @@ -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 main() async { + deviceOperatingSystem = DeviceOperatingSystem.android; + await task(createAndroidHCPPScrollPerfTest()); +} diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index aa080f0b6c..41bdfa230b 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -107,6 +107,19 @@ TaskFunction createAndroidTextureScrollPerfTest({bool? enableImpeller}) { ).run; } +TaskFunction createAndroidHCPPScrollPerfTest() { + return PerfTest( + '${flutterDirectory.path}/dev/benchmarks/platform_views_layout', + 'test_driver/scroll_perf_hcpp.dart', + 'platform_views_hcpp_scroll_perf', + testDriver: 'test_driver/scroll_perf_hcpp_test.dart', + needsFullTimeline: false, + enableImpeller: true, + enableSurfaceControl: true, + enableMergedPlatformThread: true, + ).run; +} + TaskFunction createAndroidViewScrollPerfTest() { return PerfTest( '${flutterDirectory.path}/dev/benchmarks/platform_views_layout_hybrid_composition', @@ -847,6 +860,13 @@ void _addMetadataToManifest(String testDirectory, List<(String, String)> keyPair file.writeAsStringSync(xmlDoc.toXmlString(pretty: true, indent: ' ')); } +void _addSurfaceControlSupportToManifest(String testDirectory) { + final List<(String, String)> keyPairs = <(String, String)>[ + ('io.flutter.embedding.android.EnableSurfaceControl', 'true'), + ]; + _addMetadataToManifest(testDirectory, keyPairs); +} + void _addMergedPlatformThreadSupportToManifest(String testDirectory) { final List<(String, String)> keyPairs = <(String, String)>[ ('io.flutter.embedding.android.EnableMergedPlatformUIThread', 'true'), @@ -1204,6 +1224,7 @@ class PerfTest { this.forceOpenGLES, this.disablePartialRepaint = false, this.enableMergedPlatformThread = false, + this.enableSurfaceControl = false, this.createPlatforms = const [], }) : _resultFilename = resultFilename; @@ -1225,6 +1246,7 @@ class PerfTest { this.forceOpenGLES, this.disablePartialRepaint = false, this.enableMergedPlatformThread = false, + this.enableSurfaceControl = false, this.createPlatforms = const [], }) : saveTraceFile = false, timelineFileName = null, @@ -1281,6 +1303,9 @@ class PerfTest { /// Whether the UI thread should be the platform thread. final bool enableMergedPlatformThread; + /// Whether to enable SurfaceControl swapchain. + final bool enableSurfaceControl; + /// Number of seconds to time out the test after, allowing debug callbacks to run. final int? timeoutSeconds; @@ -1376,6 +1401,9 @@ class PerfTest { if (enableMergedPlatformThread) { _addMergedPlatformThreadSupportToManifest(testDirectory); } + if (enableSurfaceControl) { + _addSurfaceControlSupportToManifest(testDirectory); + } } if (disablePartialRepaint || enableMergedPlatformThread) { changedPlist = true; @@ -1450,8 +1478,6 @@ class PerfTest { recordGPU = false; } - // TODO(liyuqian): Remove isAndroid restriction once - // https://github.com/flutter/flutter/issues/61567 is fixed. final bool isAndroid = deviceOperatingSystem == DeviceOperatingSystem.android; return TaskResult.success( data,