New benchmark to measure performance of animations after removing a BackdropFilter. (#46924)

diff --git a/dev/benchmarks/macrobenchmarks/README.md b/dev/benchmarks/macrobenchmarks/README.md
index 529a45b..d8bbe9f 100644
--- a/dev/benchmarks/macrobenchmarks/README.md
+++ b/dev/benchmarks/macrobenchmarks/README.md
@@ -37,3 +37,15 @@
 Results should be in the file `build/backdrop_filter_perf.timeline_summary.json`.
 
 More detailed logs should be in `build/backdrop_filter_perf.timeline.json`.
+
+## Post Backdrop filter benchmark
+
+To run the post-backdrop filter benchmark on a device:
+
+```
+flutter drive --profile test_driver/post_backdrop_filter_perf.dart
+```
+
+Results should be in the file `build/post_backdrop_filter_perf.timeline_summary.json`.
+
+More detailed logs should be in `build/post_backdrop_filter_perf.timeline.json`.
diff --git a/dev/benchmarks/macrobenchmarks/lib/common.dart b/dev/benchmarks/macrobenchmarks/lib/common.dart
index c8d24dc..d75ce70 100644
--- a/dev/benchmarks/macrobenchmarks/lib/common.dart
+++ b/dev/benchmarks/macrobenchmarks/lib/common.dart
@@ -5,6 +5,7 @@
 const String kCullOpacityRouteName = '/cull_opacity';
 const String kCubicBezierRouteName = '/cubic_bezier';
 const String kBackdropFilterRouteName = '/backdrop_filter';
+const String kPostBackdropFilterRouteName = '/post_backdrop_filter';
 const String kSimpleAnimationRouteName = '/simple_animation';
 const String kPictureCacheRouteName = '/picture_cache';
 const String kLargeImagesRouteName = '/large_images';
diff --git a/dev/benchmarks/macrobenchmarks/lib/main.dart b/dev/benchmarks/macrobenchmarks/lib/main.dart
index 5a2160c..d7877bd 100644
--- a/dev/benchmarks/macrobenchmarks/lib/main.dart
+++ b/dev/benchmarks/macrobenchmarks/lib/main.dart
@@ -10,6 +10,7 @@
 import 'src/backdrop_filter.dart';
 import 'src/cubic_bezier.dart';
 import 'src/cull_opacity.dart';
+import 'src/post_backdrop_filter.dart';
 import 'src/simple_animation.dart';
 
 const String kMacrobenchmarks ='Macrobenchmarks';
@@ -29,6 +30,7 @@
         kCullOpacityRouteName: (BuildContext context) => CullOpacityPage(),
         kCubicBezierRouteName: (BuildContext context) => CubicBezierPage(),
         kBackdropFilterRouteName: (BuildContext context) => BackdropFilterPage(),
+        kPostBackdropFilterRouteName: (BuildContext context) => PostBackdropFilterPage(),
         kSimpleAnimationRouteName: (BuildContext conttext) => SimpleAnimationPage(),
         kPictureCacheRouteName: (BuildContext context) => PictureCachePage(),
         kLargeImagesRouteName: (BuildContext context) => LargeImagesPage(),
@@ -68,6 +70,13 @@
             },
           ),
           RaisedButton(
+            key: const Key(kPostBackdropFilterRouteName),
+            child: const Text('Post Backdrop Filter'),
+            onPressed: () {
+              Navigator.pushNamed(context, kPostBackdropFilterRouteName);
+            },
+          ),
+          RaisedButton(
             key: const Key(kSimpleAnimationRouteName),
             child: const Text('Simple Animation'),
             onPressed: () {
diff --git a/dev/benchmarks/macrobenchmarks/lib/src/post_backdrop_filter.dart b/dev/benchmarks/macrobenchmarks/lib/src/post_backdrop_filter.dart
new file mode 100644
index 0000000..4f348e6
--- /dev/null
+++ b/dev/benchmarks/macrobenchmarks/lib/src/post_backdrop_filter.dart
@@ -0,0 +1,101 @@
+// 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';
+
+import 'package:flutter/material.dart';
+
+class PostBackdropFilterPage extends StatefulWidget {
+  @override
+  _PostBackdropFilterPageState createState() => _PostBackdropFilterPageState();
+}
+
+class _PostBackdropFilterPageState extends State<PostBackdropFilterPage> with TickerProviderStateMixin {
+  bool _includeBackdropFilter = false;
+  AnimationController animation;
+
+  @override
+  void initState() {
+    super.initState();
+    animation = AnimationController(vsync: this, duration: const Duration(seconds: 1));
+  }
+
+  @override
+  void dispose() {
+    animation.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    Widget getConditionalBackdrop() {
+      if (_includeBackdropFilter) {
+        return Column(
+          children: <Widget>[
+            const SizedBox(height: 20),
+            ClipRect(
+              child: BackdropFilter(
+                filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
+                child: const Text('BackdropFilter'),
+              ),
+            ),
+            const SizedBox(height: 20),
+          ],
+        );
+      } else {
+        return const SizedBox(height: 20);
+      }
+    }
+
+    return Scaffold(
+      backgroundColor: Colors.grey,
+      body: Stack(
+        children: <Widget>[
+          Text('0' * 10000, style: TextStyle(color: Colors.yellow)),
+          Column(
+            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+            children: <Widget>[
+              Expanded(
+                child: RepaintBoundary(
+                    child: Center(
+                      child: AnimatedBuilder(
+                          animation: animation,
+                          builder: (BuildContext c, Widget w) {
+                            final int val = (animation.value * 255).round();
+                            return Container(
+                                width: 50,
+                                height: 50,
+                                color: Color.fromARGB(255, val, val, val));
+                          }),
+                    )),
+              ),
+              getConditionalBackdrop(),
+              RepaintBoundary(
+                child: Container(
+                  color: Colors.white,
+                  child:Row(
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    children: <Widget>[
+                      const Text('Include BackdropFilter:'),
+                      Checkbox(
+                        key: const Key('bdf-checkbox'), // this key is used by the driver test
+                        value: _includeBackdropFilter,
+                        onChanged: (bool v) => setState(() { _includeBackdropFilter = v; }),
+                      ),
+                      MaterialButton(
+                        key: const Key('bdf-animate'), // this key is used by the driver test
+                        child: const Text('Animate'),
+                        onPressed: () => setState(() { animation.repeat(); }),
+                      ),
+                    ],
+                  ),
+                ),
+              )
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/dev/benchmarks/macrobenchmarks/test_driver/post_backdrop_filter_perf.dart b/dev/benchmarks/macrobenchmarks/test_driver/post_backdrop_filter_perf.dart
new file mode 100644
index 0000000..8169d13
--- /dev/null
+++ b/dev/benchmarks/macrobenchmarks/test_driver/post_backdrop_filter_perf.dart
@@ -0,0 +1,11 @@
+// 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/driver_extension.dart';
+import 'package:macrobenchmarks/main.dart' as app;
+
+void main() {
+  enableFlutterDriverExtension();
+  app.main();
+}
diff --git a/dev/benchmarks/macrobenchmarks/test_driver/post_backdrop_filter_perf_test.dart b/dev/benchmarks/macrobenchmarks/test_driver/post_backdrop_filter_perf_test.dart
new file mode 100644
index 0000000..78c6807
--- /dev/null
+++ b/dev/benchmarks/macrobenchmarks/test_driver/post_backdrop_filter_perf_test.dart
@@ -0,0 +1,28 @@
+// 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:macrobenchmarks/common.dart';
+
+import 'util.dart';
+
+void main() {
+  macroPerfTest(
+    'post_backdrop_filter_perf',
+    kPostBackdropFilterRouteName,
+    pageDelay: const Duration(seconds: 1),
+    duration: const Duration(seconds: 10),
+    setupOps: (FlutterDriver driver) async {
+      final SerializableFinder backdropFilterCheckbox = find.byValueKey('bdf-checkbox');
+      await driver.tap(backdropFilterCheckbox);
+      await Future<void>.delayed(const Duration(milliseconds: 500)); // BackdropFilter on
+      await driver.tap(backdropFilterCheckbox);
+      await Future<void>.delayed(const Duration(milliseconds: 500)); // BackdropFilter off
+
+      final SerializableFinder animateButton = find.byValueKey('bdf-animate');
+      await driver.tap(animateButton);
+      await Future<void>.delayed(const Duration(milliseconds: 1000)); // Now animate
+    },
+  );
+}
diff --git a/dev/benchmarks/macrobenchmarks/test_driver/util.dart b/dev/benchmarks/macrobenchmarks/test_driver/util.dart
index 4597c16..22fb2fd 100644
--- a/dev/benchmarks/macrobenchmarks/test_driver/util.dart
+++ b/dev/benchmarks/macrobenchmarks/test_driver/util.dart
@@ -13,6 +13,7 @@
     { Duration pageDelay,
       Duration duration = const Duration(seconds: 3),
       Future<void> driverOps(FlutterDriver driver),
+      Future<void> setupOps(FlutterDriver driver),
     }) {
   test(testName, () async {
     final FlutterDriver driver = await FlutterDriver.connect();
@@ -34,6 +35,10 @@
       await Future<void>.delayed(pageDelay);
     }
 
+    if (setupOps != null) {
+      await setupOps(driver);
+    }
+
     final Timeline timeline = await driver.traceAction(() async {
       final Future<void> durationFuture = Future<void>.delayed(duration);
       if (driverOps != null) {
diff --git a/dev/devicelab/bin/tasks/post_backdrop_filter_perf_ios__timeline_summary.dart b/dev/devicelab/bin/tasks/post_backdrop_filter_perf_ios__timeline_summary.dart
new file mode 100644
index 0000000..dc3b0b3
--- /dev/null
+++ b/dev/devicelab/bin/tasks/post_backdrop_filter_perf_ios__timeline_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/tasks/perf_tests.dart';
+import 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+
+Future<void> main() async {
+  deviceOperatingSystem = DeviceOperatingSystem.ios;
+  await task(createPostBackdropFilterPerfTest(needsMeasureCpuGpu: true));
+}
diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart
index 0a1d858..fc3c57a 100644
--- a/dev/devicelab/lib/tasks/perf_tests.dart
+++ b/dev/devicelab/lib/tasks/perf_tests.dart
@@ -63,6 +63,15 @@
   ).run;
 }
 
+TaskFunction createPostBackdropFilterPerfTest({bool needsMeasureCpuGpu = false}) {
+  return PerfTest(
+    '${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
+    'test_driver/post_backdrop_filter_perf.dart',
+    'post_backdrop_filter_perf',
+    needsMeasureCpuGPu: needsMeasureCpuGpu,
+  ).run;
+}
+
 TaskFunction createSimpleAnimationPerfTest({bool needsMeasureCpuGpu = false}) {
   return PerfTest(
     '${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index a0a389a..e02bea0 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -463,6 +463,12 @@
     stage: devicelab_ios
     required_agent_capabilities: ["mac/ios"]
 
+  post_backdrop_filter_perf_ios__timeline_summary:
+    description: >
+      Measures the runtime performance of animations after a backdrop filter is removed on iOS.
+    stage: devicelab_ios
+    required_agent_capabilities: ["mac/ios"]
+
   complex_layout_scroll_perf_ios__timeline_summary:
     description: >
       Measures the runtime performance of the Complex Layout sample app on