add benchmark for picture recording (#54908)

diff --git a/dev/benchmarks/macrobenchmarks/lib/src/web/bench_picture_recording.dart b/dev/benchmarks/macrobenchmarks/lib/src/web/bench_picture_recording.dart
new file mode 100644
index 0000000..1e45f9a
--- /dev/null
+++ b/dev/benchmarks/macrobenchmarks/lib/src/web/bench_picture_recording.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';
+
+import 'package:macrobenchmarks/src/web/recorder.dart';
+
+/// Measure the performance of paint bounds estimation by recording a picture
+/// without actually rendering it.
+///
+/// Bounds estimation is done in two phases:
+///
+/// * As we call drawing methods on `Canvas` we grow bounds with every paint op.
+/// * When we're done recording a picture we call `PictureRecorder.endRecording`
+///   at which point we compute the overall picture bounds and cache the result.
+///
+/// This benchmarks puts emphasis on paint operations that trigger expensive
+/// math such as `transformLTRB`. To do that we push non-identity transforms
+/// and rotations before calling drawing methods.
+class BenchPictureRecording extends RawRecorder {
+  BenchPictureRecording() : super(name: benchmarkName);
+
+  static const String benchmarkName = 'bench_picture_recording';
+
+  /// Cached paint used for drawing.
+  ///
+  /// We want to avoid polluting the results with paint initialization logic.
+  Paint paint;
+
+  /// A prelaid out and cached paragraph.
+  ///
+  /// This is cached to remove text layout time from the benchmark time.
+  Paragraph paragraph;
+
+  @override
+  Future<void> setUpAll() async {
+    paint = Paint();
+    paragraph = (ParagraphBuilder(ParagraphStyle())
+        ..addText('abcd edfh ijkl mnop qrst uvwx yz'))
+      .build()
+        ..layout(const ParagraphConstraints(width: 50));
+  }
+
+  @override
+  void body(Profile profile) {
+    final PictureRecorder recorder = PictureRecorder();
+    final Canvas canvas = Canvas(recorder);
+    profile.record('recordPaintCommands', () {
+      for (int i = 1; i <= 100; i++) {
+        canvas.translate((10 + i).toDouble(), (10 + i).toDouble());
+
+        canvas.save();
+        for (int j = 0; j < 10; j++) {
+          canvas.drawRect(const Rect.fromLTWH(10, 10, 10, 10), paint);
+          canvas.drawCircle(const Offset(50, 50), 50, paint);
+          canvas.rotate(1.0);
+        }
+        canvas.restore();
+
+        canvas.save();
+        for (int j = 0; j < 10; j++) {
+          canvas.translate(1, 1);
+          canvas.clipRect(Rect.fromLTWH(20, 20, 40 / i, 40));
+          canvas.drawRRect(RRect.fromRectAndRadius(const Rect.fromLTWH(10, 10, 10, 10), const Radius.circular(2)), paint);
+          canvas.drawParagraph(paragraph, Offset.zero);
+        }
+        canvas.restore();
+      }
+    });
+    profile.record('estimatePaintBounds', () {
+      recorder.endRecording();
+    });
+  }
+}
diff --git a/dev/benchmarks/macrobenchmarks/lib/web_benchmarks.dart b/dev/benchmarks/macrobenchmarks/lib/web_benchmarks.dart
index e306199..06e4a99 100644
--- a/dev/benchmarks/macrobenchmarks/lib/web_benchmarks.dart
+++ b/dev/benchmarks/macrobenchmarks/lib/web_benchmarks.dart
@@ -14,6 +14,7 @@
 import 'src/web/bench_card_infinite_scroll.dart';
 import 'src/web/bench_draw_rect.dart';
 import 'src/web/bench_dynamic_clip_on_static_picture.dart';
+import 'src/web/bench_picture_recording.dart';
 import 'src/web/bench_simple_lazy_text_scroll.dart';
 import 'src/web/bench_text_out_of_picture_bounds.dart';
 import 'src/web/recorder.dart';
@@ -33,6 +34,7 @@
   BenchSimpleLazyTextScroll.benchmarkName: () => BenchSimpleLazyTextScroll(),
   BenchBuildMaterialCheckbox.benchmarkName: () => BenchBuildMaterialCheckbox(),
   BenchDynamicClipOnStaticPicture.benchmarkName: () => BenchDynamicClipOnStaticPicture(),
+  BenchPictureRecording.benchmarkName: () => BenchPictureRecording(),
   if (isCanvasKit)
     BenchBuildColorsGrid.canvasKitBenchmarkName: () => BenchBuildColorsGrid.canvasKit(),