diff --git a/.ci.yaml b/.ci.yaml index 2ad58d56176..a6b1f1fbdc2 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -1390,6 +1390,16 @@ targets: ["devicelab", "android", "linux"] task_name: animated_complex_opacity_perf__e2e_summary + - name: Linux_android animated_complex_image_filtered_perf__e2e_summary + recipe: devicelab/devicelab_drone + presubmit: false + bringup: true + timeout: 60 + properties: + tags: > + ["devicelab", "android", "linux"] + task_name: animated_complex_image_filtered_perf__e2e_summary + - name: Linux_android animated_placeholder_perf__e2e_summary recipe: devicelab/devicelab_drone presubmit: false diff --git a/TESTOWNERS b/TESTOWNERS index b7cbd4c4e30..5cf3ab653c9 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -85,6 +85,7 @@ /dev/devicelab/bin/tasks/gradient_dynamic_perf__e2e_summary.dart @flar @flutter/engine /dev/devicelab/bin/tasks/gradient_static_perf__e2e_summary.dart @flar @flutter/engine /dev/devicelab/bin/tasks/animated_complex_opacity_perf__e2e_summary.dart @jonahwilliams @flutter/engine +/dev/devicelab/bin/tasks/animated_complex_image_filtered_perf__e2e_summary.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/spell_check_test.dart @camsim99 @flutter/android ## Windows Android DeviceLab tests diff --git a/dev/benchmarks/macrobenchmarks/lib/common.dart b/dev/benchmarks/macrobenchmarks/lib/common.dart index ea29778b703..0b4681d61a8 100644 --- a/dev/benchmarks/macrobenchmarks/lib/common.dart +++ b/dev/benchmarks/macrobenchmarks/lib/common.dart @@ -31,6 +31,7 @@ const String kAnimatedImageRouteName = '/animated_image'; const String kOpacityPeepholeRouteName = '/opacity_peephole'; const String kGradientPerfRouteName = '/gradient_perf'; const String kAnimatedComplexOpacityPerfRouteName = '/animated_complex_opacity'; +const String kAnimatedComplexImageFilteredPerfRouteName = '/animated_complex_image_filtered'; const String kListTextLayoutRouteName = '/list_text_layout'; const String kOpacityPeepholeOneRectRouteName = '$kOpacityPeepholeRouteName/one_big_rect'; diff --git a/dev/benchmarks/macrobenchmarks/lib/main.dart b/dev/benchmarks/macrobenchmarks/lib/main.dart index d6f3022733b..1a6f020f733 100644 --- a/dev/benchmarks/macrobenchmarks/lib/main.dart +++ b/dev/benchmarks/macrobenchmarks/lib/main.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'common.dart'; +import 'src/animated_complex_image_filtered.dart'; import 'src/animated_complex_opacity.dart'; import 'src/animated_image.dart'; import 'src/animated_placeholder.dart'; @@ -82,6 +83,7 @@ class MacrobenchmarksApp extends StatelessWidget { ...gradientPerfRoutes, kAnimatedComplexOpacityPerfRouteName: (BuildContext context) => const AnimatedComplexOpacity(), kListTextLayoutRouteName: (BuildContext context) => const ColumnOfText(), + kAnimatedComplexImageFilteredPerfRouteName: (BuildContext context) => const AnimatedComplexImageFiltered(), }, ); } @@ -295,6 +297,13 @@ class HomePage extends StatelessWidget { Navigator.pushNamed(context, kAnimatedComplexOpacityPerfRouteName); }, ), + ElevatedButton( + key: const Key(kAnimatedComplexImageFilteredPerfRouteName), + child: const Text('Animated complex image filtered perf'), + onPressed: () { + Navigator.pushNamed(context, kAnimatedComplexImageFilteredPerfRouteName); + }, + ), ElevatedButton( key: const Key(kListTextLayoutRouteName), child: const Text('A list with lots of text'), diff --git a/dev/benchmarks/macrobenchmarks/lib/src/animated_complex_image_filtered.dart b/dev/benchmarks/macrobenchmarks/lib/src/animated_complex_image_filtered.dart new file mode 100644 index 00000000000..e7a508c8f8d --- /dev/null +++ b/dev/benchmarks/macrobenchmarks/lib/src/animated_complex_image_filtered.dart @@ -0,0 +1,75 @@ +// 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:ui' as ui; +import 'package:flutter/material.dart'; + +// Various tests to verify that Animated image filtered layers do not +// dirty children even without explicit repaint boundaries. These intentionally use +// text to ensure we don't measure the opacity peephole case. +class AnimatedComplexImageFiltered extends StatefulWidget { + const AnimatedComplexImageFiltered({ super.key }); + + @override + State createState() => _AnimatedComplexImageFilteredState(); +} + +class _AnimatedComplexImageFilteredState extends State with SingleTickerProviderStateMixin { + late final AnimationController controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 5000)); + late final Animation animation = controller.drive(Tween(begin: 0.0, end: 1.0)); + ui.ImageFilter imageFilter = ui.ImageFilter.blur(); + + @override + void initState() { + super.initState(); + controller.repeat(); + animation.addListener(() { + setState(() { + imageFilter = ui.ImageFilter.blur(sigmaX: animation.value * 5, sigmaY: animation.value * 5); + }); + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: ListView( + children: [ + for (int i = 0; i < 20; i++) + ImageFiltered( + imageFilter: imageFilter, + child: Center( + child: Transform.scale(scale: 1.01, child: const ModeratelyComplexWidget()), + ), + ), + ], + ), + ), + ); + } +} + +class ModeratelyComplexWidget extends StatelessWidget { + const ModeratelyComplexWidget({ super.key }); + + @override + Widget build(BuildContext context) { + return const Material( + elevation: 10, + clipBehavior: Clip.hardEdge, + child: ListTile( + leading: Icon(Icons.abc, size: 24), + title: DecoratedBox(decoration: BoxDecoration(color: Colors.red), child: Text('Hello World')), + trailing: FlutterLogo(), + ), + ); + } +} diff --git a/dev/benchmarks/macrobenchmarks/lib/src/animated_complex_opacity.dart b/dev/benchmarks/macrobenchmarks/lib/src/animated_complex_opacity.dart index 90c3bcd104c..3aecfcccc06 100644 --- a/dev/benchmarks/macrobenchmarks/lib/src/animated_complex_opacity.dart +++ b/dev/benchmarks/macrobenchmarks/lib/src/animated_complex_opacity.dart @@ -21,7 +21,13 @@ class _AnimatedComplexOpacityState extends State with Si @override void initState() { super.initState(); - controller.forward(from: 0.0); + controller.repeat(); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); } @override diff --git a/dev/benchmarks/macrobenchmarks/test/animated_complex_image_filtered_perf_e2e.dart b/dev/benchmarks/macrobenchmarks/test/animated_complex_image_filtered_perf_e2e.dart new file mode 100644 index 00000000000..b0494622f4b --- /dev/null +++ b/dev/benchmarks/macrobenchmarks/test/animated_complex_image_filtered_perf_e2e.dart @@ -0,0 +1,16 @@ +// 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:macrobenchmarks/common.dart'; + +import 'util.dart'; + +void main() { + macroPerfTestE2E( + 'animated_complex_image_filtered_perf', + kAnimatedComplexImageFilteredPerfRouteName, + pageDelay: const Duration(seconds: 1), + duration: const Duration(seconds: 10), + ); +} diff --git a/dev/benchmarks/macrobenchmarks/test/animated_complex_opacity_perf_e2e.dart b/dev/benchmarks/macrobenchmarks/test/animated_complex_opacity_perf_e2e.dart index cd6e4f92f5c..10e54478118 100644 --- a/dev/benchmarks/macrobenchmarks/test/animated_complex_opacity_perf_e2e.dart +++ b/dev/benchmarks/macrobenchmarks/test/animated_complex_opacity_perf_e2e.dart @@ -11,6 +11,6 @@ void main() { 'animated_complex_opacity_perf', kAnimatedComplexOpacityPerfRouteName, pageDelay: const Duration(seconds: 1), - duration: const Duration(seconds: 5), + duration: const Duration(seconds: 10), ); } diff --git a/dev/benchmarks/macrobenchmarks/test/animated_placeholder_perf_e2e.dart b/dev/benchmarks/macrobenchmarks/test/animated_placeholder_perf_e2e.dart index 9e5af923698..11ca3df3862 100644 --- a/dev/benchmarks/macrobenchmarks/test/animated_placeholder_perf_e2e.dart +++ b/dev/benchmarks/macrobenchmarks/test/animated_placeholder_perf_e2e.dart @@ -11,6 +11,6 @@ void main() { 'animated_placeholder_perf', kAnimatedPlaceholderRouteName, pageDelay: const Duration(seconds: 1), - duration: const Duration(seconds: 5), + duration: const Duration(seconds: 10), ); } diff --git a/dev/devicelab/bin/tasks/animated_complex_image_filtered_perf__e2e_summary.dart b/dev/devicelab/bin/tasks/animated_complex_image_filtered_perf__e2e_summary.dart new file mode 100644 index 00000000000..f3be29f9b99 --- /dev/null +++ b/dev/devicelab/bin/tasks/animated_complex_image_filtered_perf__e2e_summary.dart @@ -0,0 +1,14 @@ +// 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:async'; + +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(createAnimatedComplexImageFilteredPerfE2ETest()); +} diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index f52f8044854..8065cbf9903 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -627,6 +627,17 @@ TaskFunction createAnimatedComplexOpacityPerfE2ETest({ ).run; } +TaskFunction createAnimatedComplexImageFilteredPerfE2ETest({ + bool enableImpeller = false, +}) { + return PerfTest.e2e( + '${flutterDirectory.path}/dev/benchmarks/macrobenchmarks', + 'test/animated_complex_image_filtered_perf_e2e.dart', + enableImpeller: enableImpeller, + ).run; +} + + Map _average(List> results, int iterations) { final Map tally = {}; for (final Map item in results) {