From 08a2635e2b725c951ff0471eb9c556b375aa5d7a Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 2 Dec 2022 13:37:45 -0800 Subject: [PATCH] [devicelab] add benchmark for complex non-intersecting widgets with platform views (#116436) --- .ci.yaml | 10 ++ TESTOWNERS | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 17 ++- .../lib/main_non_intersecting.dart | 115 ++++++++++++++++++ .../scroll_perf_non_intersecting_test.dart | 62 ++++++++++ ...kit_view_scroll_perf_non_intersecting.dart | 13 ++ ...ecting_impeller_ios__timeline_summary.dart | 12 ++ dev/devicelab/lib/tasks/perf_tests.dart | 11 ++ 8 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 dev/benchmarks/platform_views_layout/lib/main_non_intersecting.dart create mode 100644 dev/benchmarks/platform_views_layout/test_driver/scroll_perf_non_intersecting_test.dart create mode 100644 dev/benchmarks/platform_views_layout/test_driver/uikit_view_scroll_perf_non_intersecting.dart create mode 100644 dev/devicelab/bin/tasks/platform_views_scroll_perf_non_intersecting_impeller_ios__timeline_summary.dart diff --git a/.ci.yaml b/.ci.yaml index 2d67d350b740..cee0d85b77c5 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -3714,6 +3714,16 @@ targets: ["devicelab", "ios", "mac"] task_name: platform_views_scroll_perf_impeller_ios__timeline_summary + - name: Mac_ios platform_views_scroll_perf_non_intersecting_impeller_ios__timeline_summary + recipe: devicelab/devicelab_drone + presubmit: false + bringup: true + timeout: 60 + properties: + tags: > + ["devicelab", "ios", "mac"] + task_name: platform_views_scroll_perf_non_intersecting_impeller_ios__timeline_summary + - name: Mac_ios post_backdrop_filter_perf_ios__timeline_summary recipe: devicelab/devicelab_drone presubmit: false diff --git a/TESTOWNERS b/TESTOWNERS index 5cf3ab653c92..6611c44e6961 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -196,6 +196,7 @@ /dev/devicelab/bin/tasks/platform_interaction_test_ios.dart @zanderso @flutter/engine /dev/devicelab/bin/tasks/platform_view_ios__start_up.dart @stuartmorgan @flutter/plugin /dev/devicelab/bin/tasks/platform_views_scroll_perf_impeller_ios__timeline_summary.dart @zanderso @flutter/engine +/dev/devicelab/bin/tasks/platform_views_scroll_perf_non_intersecting_impeller_ios__timeline_summary.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/platform_views_scroll_perf_ios__timeline_summary.dart @zanderso @flutter/engine /dev/devicelab/bin/tasks/post_backdrop_filter_perf_ios__timeline_summary.dart @zanderso @flutter/engine /dev/devicelab/bin/tasks/route_test_ios.dart @jasguerrero @flutter/tool diff --git a/dev/benchmarks/platform_views_layout/ios/Runner.xcodeproj/project.pbxproj b/dev/benchmarks/platform_views_layout/ios/Runner.xcodeproj/project.pbxproj index 5b596fab048e..958b67cb47d8 100644 --- a/dev/benchmarks/platform_views_layout/ios/Runner.xcodeproj/project.pbxproj +++ b/dev/benchmarks/platform_views_layout/ios/Runner.xcodeproj/project.pbxproj @@ -77,7 +77,6 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, ); sourceTree = ""; }; @@ -309,7 +308,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.platformViewsLayout; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; @@ -427,9 +429,13 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.platformViewsLayout; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; @@ -444,7 +450,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.platformViewsLayout; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/dev/benchmarks/platform_views_layout/lib/main_non_intersecting.dart b/dev/benchmarks/platform_views_layout/lib/main_non_intersecting.dart new file mode 100644 index 000000000000..63156e8f9924 --- /dev/null +++ b/dev/benchmarks/platform_views_layout/lib/main_non_intersecting.dart @@ -0,0 +1,115 @@ +// 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 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart' show timeDilation; + +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(), + ); + } + + void toggleAnimationSpeed() { + setState(() { + timeDilation = (timeDilation != 1.0) ? 1.0 : 5.0; + }); + } +} + +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: index.isEven + ? CustomPaint(painter: ExpensivePainter(), size: const Size(400, 200)) + : const DummyPlatformView() + ), + ); + }, + ), + ); + } +} + +class DummyPlatformView extends StatelessWidget { + const DummyPlatformView({super.key}); + + @override + Widget build(BuildContext context) { + const String viewType = 'benchmarks/platform_views_layout/DummyPlatformView'; + late Widget nativeView; + if (Platform.isIOS) { + nativeView = const UiKitView( + viewType: viewType, + ); + } else if (Platform.isAndroid) { + nativeView = const AndroidView( + viewType: viewType, + ); + } else { + assert(false, 'Invalid platform'); + } + return Container( + color: Colors.purple, + height: 200.0, + child: nativeView, + ); + } +} + +class ExpensivePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final double boxWidth = size.width / 50; + final double boxHeight = size.height / 50; + for (int i = 0; i < 50; i++) { + for (int j = 0; j < 50; j++) { + final Rect rect = Rect.fromLTWH(i * boxWidth, j * boxHeight, boxWidth, boxHeight); + canvas.drawRect(rect, Paint() + ..style = PaintingStyle.fill + ..color = Colors.red + ); + } + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} diff --git a/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_non_intersecting_test.dart b/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_non_intersecting_test.dart new file mode 100644 index 000000000000..eb8fed0fbd4a --- /dev/null +++ b/dev/benchmarks/platform_views_layout/test_driver/scroll_perf_non_intersecting_test.dart @@ -0,0 +1,62 @@ +// 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); + expect(list, isNotNull); + + for (int j = 0; j < 5; j ++) { + // 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_scroll_perf_non_intersecting'); + }); + }, timeout: Timeout.none); + }); +} diff --git a/dev/benchmarks/platform_views_layout/test_driver/uikit_view_scroll_perf_non_intersecting.dart b/dev/benchmarks/platform_views_layout/test_driver/uikit_view_scroll_perf_non_intersecting.dart new file mode 100644 index 000000000000..4e2431c2391a --- /dev/null +++ b/dev/benchmarks/platform_views_layout/test_driver/uikit_view_scroll_perf_non_intersecting.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_non_intersecting.dart' as app; + +void main() { + enableFlutterDriverExtension(); + runApp(const app.PlatformViewApp()); +} diff --git a/dev/devicelab/bin/tasks/platform_views_scroll_perf_non_intersecting_impeller_ios__timeline_summary.dart b/dev/devicelab/bin/tasks/platform_views_scroll_perf_non_intersecting_impeller_ios__timeline_summary.dart new file mode 100644 index 000000000000..3be1c768c44d --- /dev/null +++ b/dev/devicelab/bin/tasks/platform_views_scroll_perf_non_intersecting_impeller_ios__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.ios; + await task(createUiKitViewScrollPerfNonIntersectingTest(enableImpeller: true)); +} diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index 2557285b3b5e..df82422afef4 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -59,6 +59,17 @@ TaskFunction createUiKitViewScrollPerfTest({bool enableImpeller = false}) { ).run; } +TaskFunction createUiKitViewScrollPerfNonIntersectingTest({bool enableImpeller = false}) { + return PerfTest( + '${flutterDirectory.path}/dev/benchmarks/platform_views_layout', + 'test_driver/uikit_view_scroll_perf_non_intersecting.dart', + 'platform_views_scroll_perf_non_intersecting', + testDriver: 'test_driver/scroll_perf_non_intersecting_test.dart', + needsFullTimeline: false, + enableImpeller: enableImpeller, + ).run; +} + TaskFunction createAndroidTextureScrollPerfTest() { return PerfTest( '${flutterDirectory.path}/dev/benchmarks/platform_views_layout',