[Impeller] add mechanism for sharing bdf inputs. (#55701)

Introduces a mechanism to allow backdrop filters to 1) share backdrop inputs and 2) fuse filter applications for faster blurs.

This is a proposed solution to https://github.com/flutter/flutter/issues/131568

Implemented:
* Developer can specify a "backdrop id" which indicates that a backdrop layer should share the input texture and potentially cached filter for a layer.
* Removes second save layer for each backdrop filter
* Removes save layer trace event for backdrop filter
* Can fuse backdrop filters if there is more than one identical filter

TBD:
* Adjust heruristic to avoid applying bdf filter to entire screen

Suggestions: applying a bdf should be a distinct operation from a save layer in the DL builder/dispatcher. The saveLayer implmenentation in the impeller dispatcher is super convoluted because it needs to handle both.

### Video

Video starts with normal bdf then I hot reload to specify that the bdfs share inputs/filters. This is running on a pixel 8 pro

Change to the macrobenchmark app is just:
```dart
  Widget build(BuildContext context) {
    Widget addBlur(Widget child, bool shouldBlur) {
      if (shouldBlur) {
        return ClipRect(
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
            backdropId: 1, // Added ID
            child: child,
          ),
        );
      } else {
        return child;
      }
    }
```

https://github.com/user-attachments/assets/22707f97-5825-43f1-91b4-1a02a43437f5

Requires framework changes in https://github.com/jonahwilliams/flutter/pull/new/backdrop_id
diff --git a/display_list/benchmarking/dl_complexity_gl.cc b/display_list/benchmarking/dl_complexity_gl.cc
index 44f658b..5da3703 100644
--- a/display_list/benchmarking/dl_complexity_gl.cc
+++ b/display_list/benchmarking/dl_complexity_gl.cc
@@ -51,7 +51,8 @@
 void DisplayListGLComplexityCalculator::GLHelper::saveLayer(
     const DlRect& bounds,
     const SaveLayerOptions options,
-    const DlImageFilter* backdrop) {
+    const DlImageFilter* backdrop,
+    std::optional<int64_t> backdrop_id) {
   if (IsComplex()) {
     return;
   }
@@ -627,7 +628,8 @@
   GLHelper helper(Ceiling() - CurrentComplexityScore());
   if (opacity < SK_Scalar1 && !display_list->can_apply_group_opacity()) {
     auto bounds = display_list->GetBounds();
-    helper.saveLayer(bounds, SaveLayerOptions::kWithAttributes, nullptr);
+    helper.saveLayer(bounds, SaveLayerOptions::kWithAttributes, nullptr,
+                     /*backdrop_id=*/-1);
   }
   display_list->Dispatch(helper);
   AccumulateComplexity(helper.ComplexityScore());
diff --git a/display_list/benchmarking/dl_complexity_gl.h b/display_list/benchmarking/dl_complexity_gl.h
index d0fcbb9..3041f90 100644
--- a/display_list/benchmarking/dl_complexity_gl.h
+++ b/display_list/benchmarking/dl_complexity_gl.h
@@ -37,7 +37,8 @@
 
     void saveLayer(const DlRect& bounds,
                    const SaveLayerOptions options,
-                   const DlImageFilter* backdrop) override;
+                   const DlImageFilter* backdrop,
+                   std::optional<int64_t> backdrop_id) override;
 
     void drawLine(const DlPoint& p0, const DlPoint& p1) override;
     void drawDashedLine(const DlPoint& p0,
diff --git a/display_list/benchmarking/dl_complexity_metal.cc b/display_list/benchmarking/dl_complexity_metal.cc
index 82f641e..25fa25a 100644
--- a/display_list/benchmarking/dl_complexity_metal.cc
+++ b/display_list/benchmarking/dl_complexity_metal.cc
@@ -65,7 +65,8 @@
 void DisplayListMetalComplexityCalculator::MetalHelper::saveLayer(
     const DlRect& bounds,
     const SaveLayerOptions options,
-    const DlImageFilter* backdrop) {
+    const DlImageFilter* backdrop,
+    std::optional<int64_t> backdrop_id) {
   if (IsComplex()) {
     return;
   }
@@ -571,7 +572,8 @@
   MetalHelper helper(Ceiling() - CurrentComplexityScore());
   if (opacity < SK_Scalar1 && !display_list->can_apply_group_opacity()) {
     auto bounds = display_list->GetBounds();
-    helper.saveLayer(bounds, SaveLayerOptions::kWithAttributes, nullptr);
+    helper.saveLayer(bounds, SaveLayerOptions::kWithAttributes, nullptr,
+                     /*backdrop_id=*/-1);
   }
   display_list->Dispatch(helper);
   AccumulateComplexity(helper.ComplexityScore());
diff --git a/display_list/benchmarking/dl_complexity_metal.h b/display_list/benchmarking/dl_complexity_metal.h
index d11d0ff..79dd5c3 100644
--- a/display_list/benchmarking/dl_complexity_metal.h
+++ b/display_list/benchmarking/dl_complexity_metal.h
@@ -37,7 +37,8 @@
 
     void saveLayer(const DlRect& bounds,
                    const SaveLayerOptions options,
-                   const DlImageFilter* backdrop) override;
+                   const DlImageFilter* backdrop,
+                   std::optional<int64_t> backdrop_id) override;
 
     void drawLine(const DlPoint& p0, const DlPoint& p1) override;
     void drawDashedLine(const DlPoint& p0,
diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc
index 0477bf2..524a5aa 100644
--- a/display_list/display_list_unittests.cc
+++ b/display_list/display_list_unittests.cc
@@ -1483,7 +1483,8 @@
 
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override {
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     FML_UNREACHABLE();
   }
 
@@ -1491,7 +1492,8 @@
                          const SaveLayerOptions& options,
                          uint32_t total_content_depth,
                          DlBlendMode max_content_blend_mode,
-                         const DlImageFilter* backdrop = nullptr) {
+                         const DlImageFilter* backdrop = nullptr,
+                         std::optional<int64_t> backdrop_id = std::nullopt) {
     ASSERT_LT(save_layer_count_, expected_.size()) << label();
     auto expect = expected_[save_layer_count_];
     if (expect.options.has_value()) {
@@ -3666,7 +3668,8 @@
 
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override {
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     ASSERT_LT(save_layer_count_, expected_.size());
     auto expected = expected_[save_layer_count_];
     EXPECT_EQ(options.bounds_from_caller(),
@@ -4131,7 +4134,8 @@
 
   void saveLayer(const DlRect& bounds,
                  SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override {
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     // This method should not be called since we override the variant with
     // the total_content_depth parameter.
     FAIL() << "saveLayer(no depth parameter) method should not be called";
@@ -4141,7 +4145,8 @@
                  const SaveLayerOptions& options,
                  uint32_t total_content_depth,
                  DlBlendMode max_content_mode,
-                 const DlImageFilter* backdrop) override {
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     ASSERT_LT(index_, depth_expectations_.size());
     EXPECT_EQ(depth_expectations_[index_], total_content_depth)
         << "at index " << index_;
diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc
index 7db556f..39953df 100644
--- a/display_list/dl_builder.cc
+++ b/display_list/dl_builder.cc
@@ -409,7 +409,8 @@
 
 void DisplayListBuilder::saveLayer(const DlRect& bounds,
                                    const SaveLayerOptions in_options,
-                                   const DlImageFilter* backdrop) {
+                                   const DlImageFilter* backdrop,
+                                   std::optional<int64_t> backdrop_id) {
   SaveLayerOptions options = in_options.without_optimizations();
   DisplayListAttributeFlags flags = options.renders_with_attributes()
                                         ? kSaveLayerWithPaintFlags
@@ -524,7 +525,8 @@
     }
 
     if (backdrop) {
-      Push<SaveLayerBackdropOp>(0, options, record_bounds, backdrop);
+      Push<SaveLayerBackdropOp>(0, options, record_bounds, backdrop,
+                                backdrop_id);
     } else {
       Push<SaveLayerOp>(0, options, record_bounds);
     }
@@ -542,7 +544,8 @@
 }
 void DisplayListBuilder::SaveLayer(std::optional<const DlRect>& bounds,
                                    const DlPaint* paint,
-                                   const DlImageFilter* backdrop) {
+                                   const DlImageFilter* backdrop,
+                                   std::optional<int64_t> backdrop_id) {
   SaveLayerOptions options;
   DlRect temp_bounds;
   if (bounds.has_value()) {
@@ -556,7 +559,7 @@
     SetAttributesFromPaint(*paint,
                            DisplayListOpFlags::kSaveLayerWithPaintFlags);
   }
-  saveLayer(temp_bounds, options, backdrop);
+  saveLayer(temp_bounds, options, backdrop, backdrop_id);
 }
 
 void DisplayListBuilder::Restore() {
diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h
index c7ac969..58d343d 100644
--- a/display_list/dl_builder.h
+++ b/display_list/dl_builder.h
@@ -54,7 +54,8 @@
   // |DlCanvas|
   void SaveLayer(std::optional<const DlRect>& bounds,
                  const DlPaint* paint = nullptr,
-                 const DlImageFilter* backdrop = nullptr) override;
+                 const DlImageFilter* backdrop = nullptr,
+                 std::optional<int64_t> backdrop_id = std::nullopt) override;
   // |DlCanvas|
   void Restore() override;
   // |DlCanvas|
@@ -354,7 +355,8 @@
   // |DlOpReceiver|
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override;
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override;
   // |DlOpReceiver|
   void restore() override { Restore(); }
 
diff --git a/display_list/dl_canvas.h b/display_list/dl_canvas.h
index ff0ea7e..80076fc 100644
--- a/display_list/dl_canvas.h
+++ b/display_list/dl_canvas.h
@@ -62,7 +62,8 @@
   virtual void Save() = 0;
   virtual void SaveLayer(std::optional<const DlRect>& bounds,
                          const DlPaint* paint = nullptr,
-                         const DlImageFilter* backdrop = nullptr) = 0;
+                         const DlImageFilter* backdrop = nullptr,
+                         std::optional<int64_t> backdrop_id = std::nullopt) = 0;
   virtual void Restore() = 0;
   virtual int GetSaveCount() const = 0;
   virtual void RestoreToCount(int restore_count) = 0;
@@ -233,9 +234,10 @@
 
   void SaveLayer(const SkRect* bounds,
                  const DlPaint* paint = nullptr,
-                 const DlImageFilter* backdrop = nullptr) {
+                 const DlImageFilter* backdrop = nullptr,
+                 std::optional<int64_t> backdrop_id = std::nullopt) {
     auto optional_bounds = ToOptDlRect(bounds);
-    SaveLayer(optional_bounds, paint, backdrop);
+    SaveLayer(optional_bounds, paint, backdrop, backdrop_id);
   }
 
   void Transform(const SkMatrix* matrix) {
diff --git a/display_list/dl_op_receiver.h b/display_list/dl_op_receiver.h
index 091ed6f..79d5d28 100644
--- a/display_list/dl_op_receiver.h
+++ b/display_list/dl_op_receiver.h
@@ -17,8 +17,6 @@
 #include "flutter/display_list/effects/dl_mask_filter.h"
 #include "flutter/display_list/image/dl_image.h"
 
-#include "flutter/impeller/geometry/path.h"
-
 namespace flutter {
 
 class DisplayList;
@@ -143,15 +141,17 @@
   // layer before further rendering happens.
   virtual void saveLayer(const DlRect& bounds,
                          const SaveLayerOptions options,
-                         const DlImageFilter* backdrop = nullptr) = 0;
+                         const DlImageFilter* backdrop = nullptr,
+                         std::optional<int64_t> backdrop_id = std::nullopt) = 0;
   // Optional variant of saveLayer() that passes the total depth count of
   // all rendering operations that occur until the next restore() call.
   virtual void saveLayer(const DlRect& bounds,
                          const SaveLayerOptions& options,
                          uint32_t total_content_depth,
                          DlBlendMode max_content_blend_mode,
-                         const DlImageFilter* backdrop = nullptr) {
-    saveLayer(bounds, options, backdrop);
+                         const DlImageFilter* backdrop = nullptr,
+                         std::optional<int64_t> backdrop_id = std::nullopt) {
+    saveLayer(bounds, options, backdrop, backdrop_id);
   }
   virtual void restore() = 0;
 
@@ -170,13 +170,17 @@
   // public DisplayListBuilder/DlCanvas public interfaces where possible,
   // as tracked in:
   // https://github.com/flutter/flutter/issues/144070
-  virtual void saveLayer(const DlRect* bounds,
-                         const SaveLayerOptions options,
-                         const DlImageFilter* backdrop = nullptr) final {
+  virtual void saveLayer(
+      const DlRect* bounds,
+      const SaveLayerOptions options,
+      const DlImageFilter* backdrop = nullptr,
+      std::optional<int64_t> backdrop_id = std::nullopt) final {
     if (bounds) {
-      saveLayer(*bounds, options.with_bounds_from_caller(), backdrop);
+      saveLayer(*bounds, options.with_bounds_from_caller(), backdrop,
+                backdrop_id);
     } else {
-      saveLayer(DlRect(), options.without_bounds_from_caller(), backdrop);
+      saveLayer(DlRect(), options.without_bounds_from_caller(), backdrop,
+                backdrop_id);
     }
   }
   // ---------------------------------------------------------------------
diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h
index 592c31e..189038b 100644
--- a/display_list/dl_op_records.h
+++ b/display_list/dl_op_records.h
@@ -310,19 +310,24 @@
 
   SaveLayerBackdropOp(const SaveLayerOptions& options,
                       const DlRect& rect,
-                      const DlImageFilter* backdrop)
-      : SaveLayerOpBase(options, rect), backdrop(backdrop->shared()) {}
+                      const DlImageFilter* backdrop,
+                      std::optional<int64_t> backdrop_id)
+      : SaveLayerOpBase(options, rect),
+        backdrop(backdrop->shared()),
+        backdrop_id_(backdrop_id) {}
 
   const std::shared_ptr<DlImageFilter> backdrop;
+  std::optional<int64_t> backdrop_id_;
 
   void dispatch(DlOpReceiver& receiver) const {
     receiver.saveLayer(rect, options, total_content_depth, max_blend_mode,
-                       backdrop.get());
+                       backdrop.get(), backdrop_id_);
   }
 
   DisplayListCompare equals(const SaveLayerBackdropOp* other) const {
     return (options == other->options && rect == other->rect &&
-            Equals(backdrop, other->backdrop))
+            Equals(backdrop, other->backdrop) &&
+            backdrop_id_ == other->backdrop_id_)
                ? DisplayListCompare::kEqual
                : DisplayListCompare::kNotEqual;
   }
diff --git a/display_list/effects/dl_image_filter.h b/display_list/effects/dl_image_filter.h
index f98010f..1e6b015 100644
--- a/display_list/effects/dl_image_filter.h
+++ b/display_list/effects/dl_image_filter.h
@@ -281,7 +281,8 @@
   bool equals_(const DlImageFilter& other) const override {
     FML_DCHECK(other.type() == DlImageFilterType::kBlur);
     auto that = static_cast<const DlBlurImageFilter*>(&other);
-    return (sigma_x_ == that->sigma_x_ && sigma_y_ == that->sigma_y_ &&
+    return (SkScalarNearlyEqual(sigma_x_, that->sigma_x_) &&
+            SkScalarNearlyEqual(sigma_y_, that->sigma_y_) &&
             tile_mode_ == that->tile_mode_);
   }
 
diff --git a/display_list/skia/dl_sk_canvas.cc b/display_list/skia/dl_sk_canvas.cc
index 8b19d74..a190519 100644
--- a/display_list/skia/dl_sk_canvas.cc
+++ b/display_list/skia/dl_sk_canvas.cc
@@ -54,7 +54,8 @@
 
 void DlSkCanvasAdapter::SaveLayer(std::optional<const DlRect>& bounds,
                                   const DlPaint* paint,
-                                  const DlImageFilter* backdrop) {
+                                  const DlImageFilter* backdrop,
+                                  std::optional<int64_t> backdrop_id) {
   sk_sp<SkImageFilter> sk_backdrop = ToSk(backdrop);
   SkOptionalPaint sk_paint(paint);
   TRACE_EVENT0("flutter", "Canvas::saveLayer");
diff --git a/display_list/skia/dl_sk_canvas.h b/display_list/skia/dl_sk_canvas.h
index 804752c..7047f41 100644
--- a/display_list/skia/dl_sk_canvas.h
+++ b/display_list/skia/dl_sk_canvas.h
@@ -32,7 +32,8 @@
   void Save() override;
   void SaveLayer(std::optional<const DlRect>& bounds,
                  const DlPaint* paint = nullptr,
-                 const DlImageFilter* backdrop = nullptr) override;
+                 const DlImageFilter* backdrop = nullptr,
+                 std::optional<int64_t> backdrop_id = std::nullopt) override;
   void Restore() override;
   int GetSaveCount() const override;
   void RestoreToCount(int restore_count) override;
diff --git a/display_list/skia/dl_sk_dispatcher.cc b/display_list/skia/dl_sk_dispatcher.cc
index 33b1116..45066eb 100644
--- a/display_list/skia/dl_sk_dispatcher.cc
+++ b/display_list/skia/dl_sk_dispatcher.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "flutter/display_list/skia/dl_sk_dispatcher.h"
+#include <cstdint>
 
 #include "flutter/display_list/dl_blend_mode.h"
 #include "flutter/display_list/skia/dl_sk_conversions.h"
@@ -46,7 +47,8 @@
 }
 void DlSkCanvasDispatcher::saveLayer(const DlRect& bounds,
                                      const SaveLayerOptions options,
-                                     const DlImageFilter* backdrop) {
+                                     const DlImageFilter* backdrop,
+                                     std::optional<int64_t> backdrop_id) {
   if (!options.content_is_clipped() && options.can_distribute_opacity() &&
       backdrop == nullptr) {
     // We know that:
diff --git a/display_list/skia/dl_sk_dispatcher.h b/display_list/skia/dl_sk_dispatcher.h
index fa5fbdd..1455479 100644
--- a/display_list/skia/dl_sk_dispatcher.h
+++ b/display_list/skia/dl_sk_dispatcher.h
@@ -31,7 +31,8 @@
   void restore() override;
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override;
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override;
 
   void translate(DlScalar tx, DlScalar ty) override;
   void scale(DlScalar sx, DlScalar sy) override;
diff --git a/display_list/testing/dl_test_snippets.cc b/display_list/testing/dl_test_snippets.cc
index 05ca54d..64ce2a0 100644
--- a/display_list/testing/dl_test_snippets.cc
+++ b/display_list/testing/dl_test_snippets.cc
@@ -327,7 +327,7 @@
               r.drawRect(DlRect::MakeLTRB(10, 10, 20, 20));
               r.restore();
             }},
-           {5, 136, 3,
+           {5, 152, 3,
             [](DlOpReceiver& r) {
               r.saveLayer(nullptr, SaveLayerOptions::kNoAttributes,
                           &kTestCFImageFilter1);
@@ -337,7 +337,7 @@
               r.drawRect(DlRect::MakeLTRB(10, 10, 20, 20));
               r.restore();
             }},
-           {5, 136, 3,
+           {5, 152, 3,
             [](DlOpReceiver& r) {
               r.saveLayer(nullptr, SaveLayerOptions::kWithAttributes,
                           &kTestCFImageFilter1);
@@ -347,7 +347,7 @@
               r.drawRect(DlRect::MakeLTRB(10, 10, 20, 20));
               r.restore();
             }},
-           {5, 136, 3,
+           {5, 152, 3,
             [](DlOpReceiver& r) {
               r.saveLayer(&kTestBounds, SaveLayerOptions::kNoAttributes,
                           &kTestCFImageFilter1);
@@ -357,7 +357,7 @@
               r.drawRect(DlRect::MakeLTRB(10, 10, 20, 20));
               r.restore();
             }},
-           {5, 136, 3,
+           {5, 152, 3,
             [](DlOpReceiver& r) {
               r.saveLayer(&kTestBounds, SaveLayerOptions::kWithAttributes,
                           &kTestCFImageFilter1);
diff --git a/display_list/utils/dl_receiver_utils.h b/display_list/utils/dl_receiver_utils.h
index 257d276..fccc04c 100644
--- a/display_list/utils/dl_receiver_utils.h
+++ b/display_list/utils/dl_receiver_utils.h
@@ -82,7 +82,8 @@
   void save() override {}
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override {}
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {}
   void restore() override {}
   void drawColor(DlColor color, DlBlendMode mode) override {}
   void drawPaint() override {}
diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc
index 32707c4..aec04ba 100644
--- a/flow/layers/backdrop_filter_layer.cc
+++ b/flow/layers/backdrop_filter_layer.cc
@@ -8,8 +8,11 @@
 
 BackdropFilterLayer::BackdropFilterLayer(
     std::shared_ptr<const DlImageFilter> filter,
-    DlBlendMode blend_mode)
-    : filter_(std::move(filter)), blend_mode_(blend_mode) {}
+    DlBlendMode blend_mode,
+    std::optional<int64_t> backdrop_id)
+    : filter_(std::move(filter)),
+      blend_mode_(blend_mode),
+      backdrop_id_(backdrop_id) {}
 
 void BackdropFilterLayer::Diff(DiffContext* context, const Layer* old_layer) {
   DiffContext::AutoSubtreeRestore subtree(context);
@@ -57,7 +60,8 @@
   FML_DCHECK(needs_painting(context));
 
   auto mutator = context.state_stack.save();
-  mutator.applyBackdropFilter(paint_bounds(), filter_, blend_mode_);
+  mutator.applyBackdropFilter(paint_bounds(), filter_, blend_mode_,
+                              backdrop_id_);
 
   PaintChildren(context);
 }
diff --git a/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h
index d17fed1..180c5a6 100644
--- a/flow/layers/backdrop_filter_layer.h
+++ b/flow/layers/backdrop_filter_layer.h
@@ -13,7 +13,8 @@
 class BackdropFilterLayer : public ContainerLayer {
  public:
   BackdropFilterLayer(std::shared_ptr<const DlImageFilter> filter,
-                      DlBlendMode blend_mode);
+                      DlBlendMode blend_mode,
+                      std::optional<int64_t> backdrop_id = std::nullopt);
 
   void Diff(DiffContext* context, const Layer* old_layer) override;
 
@@ -24,6 +25,7 @@
  private:
   std::shared_ptr<const DlImageFilter> filter_;
   DlBlendMode blend_mode_;
+  std::optional<int64_t> backdrop_id_;
 
   FML_DISALLOW_COPY_AND_ASSIGN(BackdropFilterLayer);
 };
diff --git a/flow/layers/layer_state_stack.cc b/flow/layers/layer_state_stack.cc
index a3908df..15cf795 100644
--- a/flow/layers/layer_state_stack.cc
+++ b/flow/layers/layer_state_stack.cc
@@ -52,7 +52,8 @@
   void saveLayer(const SkRect& bounds,
                  LayerStateStack::RenderingAttributes& attributes,
                  DlBlendMode blend,
-                 const DlImageFilter* backdrop) override {}
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {}
   void restore() override {}
 
   void translate(SkScalar tx, SkScalar ty) override {}
@@ -99,10 +100,13 @@
   void saveLayer(const SkRect& bounds,
                  LayerStateStack::RenderingAttributes& attributes,
                  DlBlendMode blend_mode,
-                 const DlImageFilter* backdrop) override {
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     TRACE_EVENT0("flutter", "Canvas::saveLayer");
     DlPaint paint;
-    canvas_->SaveLayer(&bounds, attributes.fill(paint, blend_mode), backdrop);
+    std::optional<const DlRect> rect = ToDlRect(bounds);
+    canvas_->SaveLayer(rect, attributes.fill(paint, blend_mode), backdrop,
+                       backdrop_id);
   }
   void restore() override { canvas_->Restore(); }
 
@@ -157,7 +161,8 @@
   void saveLayer(const SkRect& bounds,
                  LayerStateStack::RenderingAttributes& attributes,
                  DlBlendMode blend,
-                 const DlImageFilter* backdrop) override {
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     save_stack_.emplace_back(state());
   }
   void restore() override { save_stack_.pop_back(); }
@@ -343,13 +348,16 @@
   BackdropFilterEntry(const SkRect& bounds,
                       const std::shared_ptr<const DlImageFilter>& filter,
                       DlBlendMode blend_mode,
+                      std::optional<int64_t> backdrop_id,
                       const LayerStateStack::RenderingAttributes& prev)
-      : SaveLayerEntry(bounds, blend_mode, prev), filter_(filter) {}
+      : SaveLayerEntry(bounds, blend_mode, prev),
+        filter_(filter),
+        backdrop_id_(backdrop_id) {}
   ~BackdropFilterEntry() override = default;
 
   void apply(LayerStateStack* stack) const override {
     stack->delegate_->saveLayer(bounds_, stack->outstanding_, blend_mode_,
-                                filter_.get());
+                                filter_.get(), backdrop_id_);
     stack->outstanding_ = {};
   }
 
@@ -366,6 +374,7 @@
 
  private:
   const std::shared_ptr<const DlImageFilter> filter_;
+  std::optional<int64_t> backdrop_id_;
 
   FML_DISALLOW_COPY_ASSIGN_AND_MOVE(BackdropFilterEntry);
 };
@@ -558,8 +567,9 @@
 void MutatorContext::applyBackdropFilter(
     const SkRect& bounds,
     const std::shared_ptr<const DlImageFilter>& filter,
-    DlBlendMode blend_mode) {
-  layer_state_stack_->push_backdrop(bounds, filter, blend_mode);
+    DlBlendMode blend_mode,
+    std::optional<int64_t> backdrop_id) {
+  layer_state_stack_->push_backdrop(bounds, filter, blend_mode, backdrop_id);
 }
 
 void MutatorContext::translate(SkScalar tx, SkScalar ty) {
@@ -706,9 +716,10 @@
 void LayerStateStack::push_backdrop(
     const SkRect& bounds,
     const std::shared_ptr<const DlImageFilter>& filter,
-    DlBlendMode blend_mode) {
+    DlBlendMode blend_mode,
+    std::optional<int64_t> backdrop_id) {
   state_stack_.emplace_back(std::make_unique<BackdropFilterEntry>(
-      bounds, filter, blend_mode, outstanding_));
+      bounds, filter, blend_mode, backdrop_id, outstanding_));
   apply_last_entry();
 }
 
diff --git a/flow/layers/layer_state_stack.h b/flow/layers/layer_state_stack.h
index c9e606e..a5cec34 100644
--- a/flow/layers/layer_state_stack.h
+++ b/flow/layers/layer_state_stack.h
@@ -197,7 +197,8 @@
     // will only see a saveLayer with the indicated blend_mode.
     void applyBackdropFilter(const SkRect& bounds,
                              const std::shared_ptr<const DlImageFilter>& filter,
-                             DlBlendMode blend_mode);
+                             DlBlendMode blend_mode,
+                             std::optional<int64_t> backdrop_id);
 
     void translate(SkScalar tx, SkScalar ty);
     void translate(SkPoint tp) { translate(tp.fX, tp.fY); }
@@ -334,7 +335,8 @@
                          const std::shared_ptr<const DlImageFilter>& filter);
   void push_backdrop(const SkRect& bounds,
                      const std::shared_ptr<const DlImageFilter>& filter,
-                     DlBlendMode blend_mode);
+                     DlBlendMode blend_mode,
+                     std::optional<int64_t> backdrop_id);
 
   void push_translate(SkScalar tx, SkScalar ty);
   void push_transform(const SkM44& matrix);
@@ -444,10 +446,12 @@
     virtual bool content_culled(const SkRect& content_bounds) const = 0;
 
     virtual void save() = 0;
-    virtual void saveLayer(const SkRect& bounds,
-                           RenderingAttributes& attributes,
-                           DlBlendMode blend,
-                           const DlImageFilter* backdrop) = 0;
+    virtual void saveLayer(
+        const SkRect& bounds,
+        RenderingAttributes& attributes,
+        DlBlendMode blend,
+        const DlImageFilter* backdrop,
+        std::optional<int64_t> backdrop_id = std::nullopt) = 0;
     virtual void restore() = 0;
 
     virtual void translate(SkScalar tx, SkScalar ty) = 0;
diff --git a/impeller/display_list/aiks_dl_blur_unittests.cc b/impeller/display_list/aiks_dl_blur_unittests.cc
index c224ac7..96f852b 100644
--- a/impeller/display_list/aiks_dl_blur_unittests.cc
+++ b/impeller/display_list/aiks_dl_blur_unittests.cc
@@ -268,6 +268,87 @@
   ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
 }
 
+TEST_P(AiksTest, CanRenderBackdropBlurWithSingleBackdropId) {
+  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
+
+  DisplayListBuilder builder;
+
+  DlPaint paint;
+  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
+                    DlImageSampling::kNearestNeighbor, &paint);
+
+  SkRRect rrect =
+      SkRRect::MakeRectXY(SkRect::MakeXYWH(50, 250, 100, 100), 20, 20);
+  builder.Save();
+  builder.ClipRRect(rrect);
+
+  DlPaint save_paint;
+  save_paint.setBlendMode(DlBlendMode::kSrc);
+  auto backdrop_filter = DlBlurImageFilter::Make(30, 30, DlTileMode::kClamp);
+  builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
+                    /*backdrop_id=*/1);
+  builder.Restore();
+  builder.Restore();
+
+  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
+}
+
+TEST_P(AiksTest, CanRenderMultipleBackdropBlurWithSingleBackdropId) {
+  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
+
+  DisplayListBuilder builder;
+
+  DlPaint paint;
+  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
+                    DlImageSampling::kNearestNeighbor, &paint);
+
+  for (int i = 0; i < 6; i++) {
+    SkRRect rrect = SkRRect::MakeRectXY(
+        SkRect::MakeXYWH(50 + (i * 100), 250, 100, 100), 20, 20);
+    builder.Save();
+    builder.ClipRRect(rrect);
+
+    DlPaint save_paint;
+    save_paint.setBlendMode(DlBlendMode::kSrc);
+    auto backdrop_filter = DlBlurImageFilter::Make(30, 30, DlTileMode::kClamp);
+    builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
+                      /*backdrop_id=*/1);
+    builder.Restore();
+    builder.Restore();
+  }
+
+  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
+}
+
+TEST_P(AiksTest,
+       CanRenderMultipleBackdropBlurWithSingleBackdropIdAndDistinctFilters) {
+  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
+
+  DisplayListBuilder builder;
+
+  DlPaint paint;
+  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
+                    DlImageSampling::kNearestNeighbor, &paint);
+
+  for (int i = 0; i < 6; i++) {
+    SkRRect rrect = SkRRect::MakeRectXY(
+        SkRect::MakeXYWH(50 + (i * 100), 250, 100, 100), 20, 20);
+    builder.Save();
+    builder.ClipRRect(rrect);
+
+    DlPaint save_paint;
+    save_paint.setBlendMode(DlBlendMode::kSrc);
+    auto backdrop_filter =
+        DlBlurImageFilter::Make(30 + i, 30, DlTileMode::kClamp);
+    builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
+                      /*backdrop_id=*/1);
+    builder.Restore();
+    builder.Restore();
+  }
+
+  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
+}
+
 TEST_P(AiksTest, CanRenderBackdropBlurHugeSigma) {
   DisplayListBuilder builder;
 
@@ -1258,5 +1339,41 @@
   }
 }
 
+TEST_P(AiksTest,
+       CanRenderMultipleBackdropBlurWithSingleBackdropIdDifferentLayers) {
+  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
+
+  DisplayListBuilder builder;
+
+  DlPaint paint;
+  builder.DrawImage(image, SkPoint::Make(50.0, 50.0),
+                    DlImageSampling::kNearestNeighbor, &paint);
+
+  for (int i = 0; i < 6; i++) {
+    if (i != 0) {
+      DlPaint paint;
+      paint.setColor(DlColor::kWhite().withAlphaF(0.95));
+      builder.SaveLayer(nullptr, &paint);
+    }
+    SkRRect rrect = SkRRect::MakeRectXY(
+        SkRect::MakeXYWH(50 + (i * 100), 250, 100, 100), 20, 20);
+    builder.Save();
+    builder.ClipRRect(rrect);
+
+    DlPaint save_paint;
+    save_paint.setBlendMode(DlBlendMode::kSrc);
+    auto backdrop_filter = DlBlurImageFilter::Make(30, 30, DlTileMode::kClamp);
+    builder.SaveLayer(nullptr, &save_paint, backdrop_filter.get(),
+                      /*backdrop_id=*/1);
+    builder.Restore();
+    builder.Restore();
+    if (i != 0) {
+      builder.Restore();
+    }
+  }
+
+  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
+}
+
 }  // namespace testing
 }  // namespace impeller
diff --git a/impeller/display_list/aiks_playground.cc b/impeller/display_list/aiks_playground.cc
index ae42bf9..6b2ac34 100644
--- a/impeller/display_list/aiks_playground.cc
+++ b/impeller/display_list/aiks_playground.cc
@@ -10,6 +10,7 @@
 #include "impeller/display_list/dl_dispatcher.h"
 #include "impeller/typographer/backends/skia/typographer_context_skia.h"
 #include "impeller/typographer/typographer_context.h"
+#include "include/core/SkRect.h"
 
 namespace impeller {
 
@@ -49,22 +50,14 @@
 
   return Playground::OpenPlaygroundHere(
       [&renderer, &callback](RenderTarget& render_target) -> bool {
-        auto display_list = callback();
-        TextFrameDispatcher collector(renderer.GetContentContext(),  //
-                                      Matrix(),                      //
-                                      Rect::MakeMaximum()            //
+        return RenderToOnscreen(
+            renderer.GetContentContext(),  //
+            render_target,                 //
+            callback(),                    //
+            SkIRect::MakeWH(render_target.GetRenderTargetSize().width,
+                            render_target.GetRenderTargetSize().height),  //
+            /*reset_host_buffer=*/true                                    //
         );
-        display_list->Dispatch(collector);
-
-        CanvasDlDispatcher impeller_dispatcher(
-            renderer.GetContentContext(), render_target,
-            display_list->root_has_backdrop_filter(),
-            display_list->max_root_blend_mode(), IRect::MakeMaximum());
-        display_list->Dispatch(impeller_dispatcher);
-        impeller_dispatcher.FinishRecording();
-        renderer.GetContentContext().GetTransientsBuffer().Reset();
-        renderer.GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
-        return true;
       });
 }
 
diff --git a/impeller/display_list/canvas.cc b/impeller/display_list/canvas.cc
index dedb9c3..f052634 100644
--- a/impeller/display_list/canvas.cc
+++ b/impeller/display_list/canvas.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <optional>
+#include <unordered_map>
 #include <utility>
 
 #include "display_list/effects/dl_color_source.h"
@@ -93,11 +94,17 @@
 ///
 /// Returns the previous render pass stored as a texture, or nullptr if there
 /// was a validation failure.
+///
+/// [should_remove_texture] defaults to false. If true, the render target
+/// texture is removed from the entity pass target. This allows the texture to
+/// be cached by the canvas dispatcher for usage in the backdrop filter reuse
+/// mechanism.
 static std::shared_ptr<Texture> FlipBackdrop(
     std::vector<LazyRenderingConfig>& render_passes,
     Point global_pass_position,
     EntityPassClipStack& clip_coverage_stack,
-    ContentContext& renderer) {
+    ContentContext& renderer,
+    bool should_remove_texture = false) {
   auto rendering_config = std::move(render_passes.back());
   render_passes.pop_back();
 
@@ -144,6 +151,14 @@
   render_passes.push_back(LazyRenderingConfig(
       renderer, std::move(rendering_config.entity_pass_target),
       std::move(rendering_config.inline_pass_context)));
+  // If the current texture is being cached for a BDF we need to ensure we
+  // don't recycle it during recording; remove it from the entity pass target.
+  if (should_remove_texture) {
+    render_passes.back().entity_pass_target->RemoveSecondary();
+  }
+  RenderPass& current_render_pass =
+      *render_passes.back().inline_pass_context->GetRenderPass(0).pass;
+
   // Eagerly restore the BDF contents.
 
   // If the pass context returns a backdrop texture, we need to draw it to the
@@ -162,9 +177,7 @@
   msaa_backdrop_entity.SetContents(std::move(msaa_backdrop_contents));
   msaa_backdrop_entity.SetBlendMode(BlendMode::kSource);
   msaa_backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
-  if (!msaa_backdrop_entity.Render(
-          renderer,
-          *render_passes.back().inline_pass_context->GetRenderPass(0).pass)) {
+  if (!msaa_backdrop_entity.Render(renderer, current_render_pass)) {
     VALIDATION_LOG << "Failed to render MSAA backdrop entity.";
     return nullptr;
   }
@@ -173,13 +186,9 @@
   // applied.
   auto& replay_entities = clip_coverage_stack.GetReplayEntities();
   for (const auto& replay : replay_entities) {
-    SetClipScissor(
-        replay.clip_coverage,
-        *render_passes.back().inline_pass_context->GetRenderPass(0).pass,
-        global_pass_position);
-    if (!replay.entity.Render(
-            renderer,
-            *render_passes.back().inline_pass_context->GetRenderPass(0).pass)) {
+    SetClipScissor(replay.clip_coverage, current_render_pass,
+                   global_pass_position);
+    if (!replay.entity.Render(renderer, current_render_pass)) {
       VALIDATION_LOG << "Failed to render entity for clip restore.";
     }
   }
@@ -984,7 +993,8 @@
                        const flutter::DlImageFilter* backdrop_filter,
                        ContentBoundsPromise bounds_promise,
                        uint32_t total_content_depth,
-                       bool can_distribute_opacity) {
+                       bool can_distribute_opacity,
+                       std::optional<int64_t> backdrop_id) {
   TRACE_EVENT0("flutter", "Canvas::saveLayer");
   if (IsSkipping()) {
     return SkipUntilMatchingRestore(total_content_depth);
@@ -1055,7 +1065,7 @@
 
   // Backdrop filter state, ignored if there is no BDF.
   std::shared_ptr<FilterContents> backdrop_filter_contents;
-  Point local_position = {0, 0};
+  Point local_position = Point(0, 0);
   if (backdrop_filter) {
     local_position = subpass_coverage.GetOrigin() - GetGlobalPassPosition();
     Canvas::BackdropFilterProc backdrop_filter_proc =
@@ -1068,14 +1078,41 @@
           return filter;
         };
 
-    auto input_texture = FlipBackdrop(render_passes_,           //
-                                      GetGlobalPassPosition(),  //
-                                      clip_coverage_stack_,     //
-                                      renderer_                 //
-    );
-    if (!input_texture) {
-      // Validation failures are logged in FlipBackdrop.
-      return;
+    std::shared_ptr<Texture> input_texture;
+
+    // If the backdrop ID is not the no-op id, and there is more than one usage
+    // of it in the current scene, cache the backdrop texture and remove it from
+    // the current entity pass flip.
+    bool will_cache_backdrop_texture = false;
+    BackdropData* backdrop_data = nullptr;
+    if (backdrop_id.has_value()) {
+      std::unordered_map<int64_t, BackdropData>::iterator backdrop_data_it =
+          backdrop_data_.find(backdrop_id.value());
+      if (backdrop_data_it != backdrop_data_.end()) {
+        backdrop_data = &backdrop_data_it->second;
+        will_cache_backdrop_texture =
+            backdrop_data_it->second.backdrop_count > 1;
+      }
+    }
+
+    if (!will_cache_backdrop_texture ||
+        (will_cache_backdrop_texture && !backdrop_data->texture_slot)) {
+      input_texture = FlipBackdrop(render_passes_,              //
+                                   GetGlobalPassPosition(),     //
+                                   clip_coverage_stack_,        //
+                                   renderer_,                   //
+                                   will_cache_backdrop_texture  //
+      );
+      if (!input_texture) {
+        // Validation failures are logged in FlipBackdrop.
+        return;
+      }
+
+      if (will_cache_backdrop_texture) {
+        backdrop_data->texture_slot = input_texture;
+      }
+    } else {
+      input_texture = backdrop_data->texture_slot;
     }
 
     backdrop_filter_contents = backdrop_filter_proc(
@@ -1086,6 +1123,42 @@
         transform_stack_.back().transform.HasTranslation()
             ? Entity::RenderingMode::kSubpassPrependSnapshotTransform
             : Entity::RenderingMode::kSubpassAppendSnapshotTransform);
+
+    if (will_cache_backdrop_texture) {
+      FML_DCHECK(backdrop_data);
+      // If all filters on the shared backdrop layer are equal, process the
+      // layer once.
+      if (backdrop_data->all_filters_equal &&
+          !backdrop_data->shared_filter_snapshot.has_value()) {
+        // TODO(157110): compute minimum input hint.
+        backdrop_data->shared_filter_snapshot =
+            backdrop_filter_contents->RenderToSnapshot(renderer_, {});
+      }
+
+      std::optional<Snapshot> maybe_snapshot =
+          backdrop_data->shared_filter_snapshot;
+      if (maybe_snapshot.has_value()) {
+        Snapshot snapshot = maybe_snapshot.value();
+        std::shared_ptr<TextureContents> contents = TextureContents::MakeRect(
+            subpass_coverage.Shift(-GetGlobalPassPosition()));
+        auto scaled =
+            subpass_coverage.TransformBounds(snapshot.transform.Invert());
+        contents->SetTexture(snapshot.texture);
+        contents->SetSourceRect(scaled);
+        contents->SetSamplerDescriptor(snapshot.sampler_descriptor);
+
+        // This backdrop entity sets a depth value as it is written to the newly
+        // flipped backdrop and not into a new saveLayer.
+        Entity backdrop_entity;
+        backdrop_entity.SetContents(std::move(contents));
+        backdrop_entity.SetClipDepth(++current_depth_);
+        backdrop_entity.SetBlendMode(paint.blend_mode);
+
+        backdrop_entity.Render(renderer_, GetCurrentRenderPass());
+        Save(0);
+        return;
+      }
+    }
   }
 
   // When applying a save layer, absorb any pending distributed opacity.
@@ -1119,18 +1192,18 @@
   // the subpass will affect in the parent pass.
   clip_coverage_stack_.PushSubpass(subpass_coverage, GetClipHeight());
 
-  if (backdrop_filter_contents) {
-    // Render the backdrop entity.
-    Entity backdrop_entity;
-    backdrop_entity.SetContents(std::move(backdrop_filter_contents));
-    backdrop_entity.SetTransform(
-        Matrix::MakeTranslation(Vector3(-local_position)));
-    backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
-
-    backdrop_entity.Render(
-        renderer_,
-        *render_passes_.back().inline_pass_context->GetRenderPass(0).pass);
+  if (!backdrop_filter_contents) {
+    return;
   }
+
+  // Render the backdrop entity.
+  Entity backdrop_entity;
+  backdrop_entity.SetContents(std::move(backdrop_filter_contents));
+  backdrop_entity.SetTransform(
+      Matrix::MakeTranslation(Vector3(-local_position)));
+  backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
+
+  backdrop_entity.Render(renderer_, GetCurrentRenderPass());
 }
 
 bool Canvas::Restore() {
@@ -1300,9 +1373,7 @@
       return true;
     }
 
-    entity.Render(
-        renderer_,
-        *render_passes_.back().inline_pass_context->GetRenderPass(0).pass);
+    entity.Render(renderer_, GetCurrentRenderPass());
   }
 
   return true;
@@ -1564,19 +1635,24 @@
 
   if (clip_state_result.clip_did_change) {
     // We only need to update the pass scissor if the clip state has changed.
-    SetClipScissor(
-        clip_coverage_stack_.CurrentClipCoverage(),
-        *render_passes_.back().inline_pass_context->GetRenderPass(0).pass,
-        GetGlobalPassPosition());
+    SetClipScissor(clip_coverage_stack_.CurrentClipCoverage(),
+                   GetCurrentRenderPass(), GetGlobalPassPosition());
   }
 
   if (!clip_state_result.should_render) {
     return;
   }
 
-  entity.Render(
-      renderer_,
-      *render_passes_.back().inline_pass_context->GetRenderPass(0).pass);
+  entity.Render(renderer_, GetCurrentRenderPass());
+}
+
+RenderPass& Canvas::GetCurrentRenderPass() const {
+  return *render_passes_.back().inline_pass_context->GetRenderPass(0).pass;
+}
+
+void Canvas::SetBackdropData(
+    std::unordered_map<int64_t, BackdropData> backdrop_data) {
+  backdrop_data_ = std::move(backdrop_data);
 }
 
 bool Canvas::BlitToOnscreen() {
@@ -1642,6 +1718,7 @@
   FML_DCHECK(render_passes_.size() == 1u);
   render_passes_.back().inline_pass_context->GetRenderPass(0);
   render_passes_.back().inline_pass_context->EndPass();
+  backdrop_data_.clear();
 
   // If requires_readback_ was true, then we rendered to an offscreen texture
   // instead of to the onscreen provided in the render target. Now we need to
diff --git a/impeller/display_list/canvas.h b/impeller/display_list/canvas.h
index 387cb5c..f856c8c 100644
--- a/impeller/display_list/canvas.h
+++ b/impeller/display_list/canvas.h
@@ -9,6 +9,7 @@
 #include <functional>
 #include <memory>
 #include <optional>
+#include <utility>
 #include <vector>
 
 #include "display_list/effects/dl_image_filter.h"
@@ -24,10 +25,21 @@
 #include "impeller/geometry/path.h"
 #include "impeller/geometry/point.h"
 #include "impeller/geometry/vector.h"
+#include "impeller/renderer/snapshot.h"
 #include "impeller/typographer/text_frame.h"
 
 namespace impeller {
 
+struct BackdropData {
+  size_t backdrop_count = 0;
+  bool all_filters_equal = true;
+  std::shared_ptr<Texture> texture_slot;
+  // A single snapshot of the backdrop filter that is used when there are
+  // multiple backdrops that share an identical filter.
+  std::optional<Snapshot> shared_filter_snapshot;
+  std::shared_ptr<flutter::DlImageFilter> last_backdrop;
+};
+
 struct CanvasStackEntry {
   Matrix transform;
   uint32_t clip_depth = 0u;
@@ -123,6 +135,10 @@
 
   ~Canvas() = default;
 
+  /// @brief Update the backdrop data used to group together backdrop filters
+  ///        within the same layer.
+  void SetBackdropData(std::unordered_map<int64_t, BackdropData> backdrop_data);
+
   /// @brief Return the culling bounds of the current render target, or nullopt
   ///        if there is no coverage.
   std::optional<Rect> GetLocalCoverageLimit() const;
@@ -135,7 +151,8 @@
       const flutter::DlImageFilter* backdrop_filter = nullptr,
       ContentBoundsPromise bounds_promise = ContentBoundsPromise::kUnknown,
       uint32_t total_content_depth = kMaxDepth,
-      bool can_distribute_opacity = false);
+      bool can_distribute_opacity = false,
+      std::optional<int64_t> backdrop_id = std::nullopt);
 
   bool Restore();
 
@@ -232,6 +249,7 @@
   std::optional<Rect> initial_cull_rect_;
   std::vector<LazyRenderingConfig> render_passes_;
   std::vector<SaveLayerState> save_layer_state_;
+  std::unordered_map<int64_t, BackdropData> backdrop_data_;
 
   // All geometry objects created for regular draws can be stack allocated,
   // but clip geometries must be cached for record/replay for backdrop filters
@@ -277,6 +295,8 @@
                                Size corner_radii,
                                const Paint& paint);
 
+  RenderPass& GetCurrentRenderPass() const;
+
   Canvas(const Canvas&) = delete;
 
   Canvas& operator=(const Canvas&) = delete;
diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc
index ffdd0c6..895a4eb 100644
--- a/impeller/display_list/dl_dispatcher.cc
+++ b/impeller/display_list/dl_dispatcher.cc
@@ -8,14 +8,14 @@
 #include <cstring>
 #include <memory>
 #include <optional>
-#include <utility>
 #include <vector>
 
 #include "display_list/effects/dl_color_source.h"
+#include "display_list/effects/dl_image_filter.h"
 #include "flutter/fml/logging.h"
 #include "impeller/core/formats.h"
 #include "impeller/display_list/aiks_context.h"
-#include "impeller/display_list/color_filter.h"
+#include "impeller/display_list/canvas.h"
 #include "impeller/display_list/dl_atlas_geometry.h"
 #include "impeller/display_list/dl_vertices_geometry.h"
 #include "impeller/display_list/nine_patch_converter.h"
@@ -302,7 +302,8 @@
                                  const flutter::SaveLayerOptions& options,
                                  uint32_t total_content_depth,
                                  flutter::DlBlendMode max_content_mode,
-                                 const flutter::DlImageFilter* backdrop) {
+                                 const flutter::DlImageFilter* backdrop,
+                                 std::optional<int64_t> backdrop_id) {
   AUTO_DEPTH_WATCHER(1u);
 
   auto paint = options.renders_with_attributes() ? paint_ : Paint{};
@@ -320,7 +321,9 @@
       paint, impeller_bounds, backdrop, promise, total_content_depth,
       // Unbounded content can still have user specified bounds that require a
       // saveLayer to be created to perform the clip.
-      options.can_distribute_opacity() && !options.content_is_unbounded());
+      options.can_distribute_opacity() && !options.content_is_unbounded(),
+      backdrop_id  //
+  );
 }
 
 // |flutter::DlOpReceiver|
@@ -974,6 +977,11 @@
       skia_conversions::ToBlendMode(dl_mode), paint_);
 }
 
+void CanvasDlDispatcher::SetBackdropData(
+    std::unordered_map<int64_t, BackdropData> backdrop) {
+  GetCanvas().SetBackdropData(std::move(backdrop));
+}
+
 //// Text Frame Dispatcher
 
 TextFrameDispatcher::TextFrameDispatcher(const ContentContext& renderer,
@@ -994,9 +1002,28 @@
 
 void TextFrameDispatcher::saveLayer(const DlRect& bounds,
                                     const flutter::SaveLayerOptions options,
-                                    const flutter::DlImageFilter* backdrop) {
+                                    const flutter::DlImageFilter* backdrop,
+                                    std::optional<int64_t> backdrop_id) {
   save();
 
+  if (backdrop != nullptr && backdrop_id.has_value()) {
+    std::shared_ptr<flutter::DlImageFilter> shared_backdrop =
+        backdrop->shared();
+    std::unordered_map<int64_t, BackdropData>::iterator existing =
+        backdrop_data_.find(backdrop_id.value());
+    if (existing == backdrop_data_.end()) {
+      backdrop_data_[backdrop_id.value()] =
+          BackdropData{.backdrop_count = 1, .last_backdrop = shared_backdrop};
+    } else {
+      BackdropData& data = existing->second;
+      data.backdrop_count++;
+      if (data.all_filters_equal) {
+        data.all_filters_equal = (*data.last_backdrop == *shared_backdrop);
+        data.last_backdrop = shared_backdrop;
+      }
+    }
+  }
+
   // This dispatcher does not track enough state to accurately compute
   // cull rects with image filters.
   auto global_cull_rect = cull_rect_state_.back();
@@ -1192,6 +1219,13 @@
   }
 }
 
+std::unordered_map<int64_t, BackdropData>
+TextFrameDispatcher::TakeBackdropData() {
+  std::unordered_map<int64_t, BackdropData> temp;
+  std::swap(temp, backdrop_data_);
+  return temp;
+}
+
 std::shared_ptr<Texture> DisplayListToTexture(
     const sk_sp<flutter::DisplayList>& display_list,
     ISize size,
@@ -1239,6 +1273,7 @@
       display_list->max_root_blend_mode(),       //
       impeller::IRect::MakeSize(size)            //
   );
+  impeller_dispatcher.SetBackdropData(collector.TakeBackdropData());
   display_list->Dispatch(impeller_dispatcher, sk_cull_rect);
   impeller_dispatcher.FinishRecording();
 
@@ -1267,6 +1302,7 @@
       display_list->max_root_blend_mode(),       //
       IRect::RoundOut(ip_cull_rect)              //
   );
+  impeller_dispatcher.SetBackdropData(collector.TakeBackdropData());
   display_list->Dispatch(impeller_dispatcher, cull_rect);
   impeller_dispatcher.FinishRecording();
   if (reset_host_buffer) {
diff --git a/impeller/display_list/dl_dispatcher.h b/impeller/display_list/dl_dispatcher.h
index 1418229..8302a1f 100644
--- a/impeller/display_list/dl_dispatcher.h
+++ b/impeller/display_list/dl_dispatcher.h
@@ -5,6 +5,8 @@
 #ifndef FLUTTER_IMPELLER_DISPLAY_LIST_DL_DISPATCHER_H_
 #define FLUTTER_IMPELLER_DISPLAY_LIST_DL_DISPATCHER_H_
 
+#include <memory>
+
 #include "flutter/display_list/dl_op_receiver.h"
 #include "flutter/display_list/geometry/dl_geometry_types.h"
 #include "flutter/display_list/geometry/dl_path.h"
@@ -73,7 +75,8 @@
                  const flutter::SaveLayerOptions& options,
                  uint32_t total_content_depth,
                  flutter::DlBlendMode max_content_mode,
-                 const flutter::DlImageFilter* backdrop) override;
+                 const flutter::DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override;
 
   // |flutter::DlOpReceiver|
   void restore() override;
@@ -253,6 +256,8 @@
 
   ~CanvasDlDispatcher() = default;
 
+  void SetBackdropData(std::unordered_map<int64_t, BackdropData> backdrop);
+
   // |flutter::DlOpReceiver|
   void save() override {
     // This dispatcher should never be used with the save() variant
@@ -264,7 +269,8 @@
   // |flutter::DlOpReceiver|
   void saveLayer(const DlRect& bounds,
                  const flutter::SaveLayerOptions options,
-                 const flutter::DlImageFilter* backdrop) override {
+                 const flutter::DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     // This dispatcher should never be used with the saveLayer() variant
     // that does not include the content_depth parameter.
     FML_UNREACHABLE();
@@ -299,7 +305,8 @@
 
   void saveLayer(const DlRect& bounds,
                  const flutter::SaveLayerOptions options,
-                 const flutter::DlImageFilter* backdrop) override;
+                 const flutter::DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override;
 
   void restore() override;
 
@@ -353,12 +360,15 @@
   // |flutter::DlOpReceiver|
   void setImageFilter(const flutter::DlImageFilter* filter) override;
 
+  std::unordered_map<int64_t, BackdropData> TakeBackdropData();
+
  private:
   const Rect GetCurrentLocalCullingBounds() const;
 
   const ContentContext& renderer_;
   Matrix matrix_;
   std::vector<Matrix> stack_;
+  std::unordered_map<int64_t, BackdropData> backdrop_data_;
   // note: cull rects are always in the global coordinate space.
   std::vector<Rect> cull_rect_state_;
   bool has_image_filter_ = false;
diff --git a/impeller/display_list/dl_playground.cc b/impeller/display_list/dl_playground.cc
index eab910d..9a05612 100644
--- a/impeller/display_list/dl_playground.cc
+++ b/impeller/display_list/dl_playground.cc
@@ -45,21 +45,14 @@
           wireframe = !wireframe;
           context.GetContentContext().SetWireframe(wireframe);
         }
-
-        auto list = callback();
-        TextFrameDispatcher collector(context.GetContentContext(), Matrix(),
-                                      Rect::MakeMaximum());
-        list->Dispatch(collector);
-
-        CanvasDlDispatcher impeller_dispatcher(
-            context.GetContentContext(), render_target,
-            list->root_has_backdrop_filter(), list->max_root_blend_mode(),
-            IRect::MakeMaximum());
-        list->Dispatch(impeller_dispatcher);
-        impeller_dispatcher.FinishRecording();
-        context.GetContentContext().GetTransientsBuffer().Reset();
-        context.GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
-        return true;
+        return RenderToOnscreen(
+            context.GetContentContext(),  //
+            render_target,                //
+            callback(),                   //
+            SkIRect::MakeWH(render_target.GetRenderTargetSize().width,
+                            render_target.GetRenderTargetSize().height),  //
+            /*reset_host_buffer=*/true                                    //
+        );
       });
 }
 
diff --git a/impeller/entity/entity_pass_target.cc b/impeller/entity/entity_pass_target.cc
index c92a84a..712645a 100644
--- a/impeller/entity/entity_pass_target.cc
+++ b/impeller/entity/entity_pass_target.cc
@@ -73,4 +73,8 @@
   return target_.IsValid();
 }
 
+void EntityPassTarget::RemoveSecondary() {
+  secondary_color_texture_ = nullptr;
+}
+
 }  // namespace impeller
diff --git a/impeller/entity/entity_pass_target.h b/impeller/entity/entity_pass_target.h
index e82bad5..2adf4ec 100644
--- a/impeller/entity/entity_pass_target.h
+++ b/impeller/entity/entity_pass_target.h
@@ -28,6 +28,9 @@
 
   RenderTarget& GetRenderTarget();
 
+  /// @brief Remove the cached secondary color texture.
+  void RemoveSecondary();
+
   bool IsValid() const;
 
  private:
diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart
index da23f53..d868427 100644
--- a/lib/ui/compositing.dart
+++ b/lib/ui/compositing.dart
@@ -401,6 +401,20 @@
   /// the most recent save layer and rendered back to the scene using the indicated
   /// [blendMode] prior to rasterizing the child layers.
   ///
+  /// If [backdropId] is provided and not null, then this value is treated
+  /// as a unique identifier for the backdrop. When the first backdrop filter with
+  /// a given id is processed during rasterization, the state of the backdrop is
+  /// recorded and cached. All subsequent backdrop filters with the same identifier
+  /// will apply their filter to the cached backdrop. The correct usage of the
+  /// backdrop id has the benefit of dramatically improving performance for
+  /// applications with multiple backdrop filters. For example, an application
+  /// that uses a backdrop blur filter for each item in a list view should set
+  /// all filters to have the same backdrop id.
+  ///
+  /// If overlapping backdrop filters use the same backdropId, then each filter
+  /// will apply to the backdrop before the overlapping filter components were
+  /// rendered.
+  ///
   /// {@macro dart.ui.sceneBuilder.oldLayer}
   ///
   /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained}
@@ -410,6 +424,7 @@
     ImageFilter filter, {
     BlendMode blendMode = BlendMode.srcOver,
     BackdropFilterEngineLayer? oldLayer,
+    int? backdropId,
   });
 
   /// Pushes a shader mask operation onto the operation stack.
@@ -772,18 +787,19 @@
   BackdropFilterEngineLayer pushBackdropFilter(
     ImageFilter filter, {
     BlendMode blendMode = BlendMode.srcOver,
+    int? backdropId,
     BackdropFilterEngineLayer? oldLayer,
   }) {
     assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter'));
     final EngineLayer engineLayer = _NativeEngineLayer._();
-    _pushBackdropFilter(engineLayer, filter._toNativeImageFilter(), blendMode.index, oldLayer?._nativeLayer);
+    _pushBackdropFilter(engineLayer, filter._toNativeImageFilter(), blendMode.index, backdropId, oldLayer?._nativeLayer);
     final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(engineLayer);
     assert(_debugPushLayer(layer));
     return layer;
   }
 
-  @Native<Void Function(Pointer<Void>, Handle, Pointer<Void>, Int32, Handle)>(symbol: 'SceneBuilder::pushBackdropFilter')
-  external void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter, int blendMode, EngineLayer? oldLayer);
+  @Native<Void Function(Pointer<Void>, Handle, Pointer<Void>, Int32, Handle, Handle)>(symbol: 'SceneBuilder::pushBackdropFilter')
+  external void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter, int blendMode, int? backdropId, EngineLayer? oldLayer);
 
   @override
   ShaderMaskEngineLayer pushShaderMask(
diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc
index b89a7f7..a8e3a29 100644
--- a/lib/ui/compositing/scene_builder.cc
+++ b/lib/ui/compositing/scene_builder.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "flutter/lib/ui/compositing/scene_builder.h"
+#include <cstdint>
 
+#include "dart_api.h"
 #include "flutter/flow/layers/backdrop_filter_layer.h"
 #include "flutter/flow/layers/clip_path_layer.h"
 #include "flutter/flow/layers/clip_rect_layer.h"
@@ -13,7 +15,6 @@
 #include "flutter/flow/layers/display_list_layer.h"
 #include "flutter/flow/layers/image_filter_layer.h"
 #include "flutter/flow/layers/layer.h"
-#include "flutter/flow/layers/layer_tree.h"
 #include "flutter/flow/layers/opacity_layer.h"
 #include "flutter/flow/layers/performance_overlay_layer.h"
 #include "flutter/flow/layers/platform_view_layer.h"
@@ -21,13 +22,10 @@
 #include "flutter/flow/layers/texture_layer.h"
 #include "flutter/flow/layers/transform_layer.h"
 #include "flutter/fml/build_config.h"
+#include "flutter/lib/ui/compositing/scene.h"
 #include "flutter/lib/ui/floating_point.h"
 #include "flutter/lib/ui/painting/matrix.h"
 #include "flutter/lib/ui/painting/shader.h"
-#include "third_party/tonic/converter/dart_converter.h"
-#include "third_party/tonic/dart_args.h"
-#include "third_party/tonic/dart_binding_macros.h"
-#include "third_party/tonic/dart_library_natives.h"
 
 namespace flutter {
 
@@ -43,7 +41,7 @@
 
 void SceneBuilder::pushTransform(Dart_Handle layer_handle,
                                  tonic::Float64List& matrix4,
-                                 const fml::RefPtr<EngineLayer>& oldLayer) {
+                                 const fml::RefPtr<EngineLayer>& old_layer) {
   SkM44 sk_matrix = ToSkM44(matrix4);
   auto layer = std::make_shared<flutter::TransformLayer>(sk_matrix);
   PushLayer(layer);
@@ -51,22 +49,22 @@
   matrix4.Release();
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
 void SceneBuilder::pushOffset(Dart_Handle layer_handle,
                               double dx,
                               double dy,
-                              const fml::RefPtr<EngineLayer>& oldLayer) {
+                              const fml::RefPtr<EngineLayer>& old_layer) {
   SkMatrix sk_matrix = SkMatrix::Translate(SafeNarrow(dx), SafeNarrow(dy));
   auto layer = std::make_shared<flutter::TransformLayer>(sk_matrix);
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
@@ -75,49 +73,48 @@
                                 double right,
                                 double top,
                                 double bottom,
-                                int clipBehavior,
-                                const fml::RefPtr<EngineLayer>& oldLayer) {
-  SkRect clipRect = SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
-                                     SafeNarrow(right), SafeNarrow(bottom));
-  flutter::Clip clip_behavior = static_cast<flutter::Clip>(clipBehavior);
-  auto layer =
-      std::make_shared<flutter::ClipRectLayer>(clipRect, clip_behavior);
+                                int clip_behavior,
+                                const fml::RefPtr<EngineLayer>& old_layer) {
+  SkRect clip_rect = SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
+                                      SafeNarrow(right), SafeNarrow(bottom));
+  auto layer = std::make_shared<flutter::ClipRectLayer>(
+      clip_rect, static_cast<flutter::Clip>(clip_behavior));
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
 void SceneBuilder::pushClipRRect(Dart_Handle layer_handle,
                                  const RRect& rrect,
-                                 int clipBehavior,
-                                 const fml::RefPtr<EngineLayer>& oldLayer) {
-  flutter::Clip clip_behavior = static_cast<flutter::Clip>(clipBehavior);
-  auto layer =
-      std::make_shared<flutter::ClipRRectLayer>(rrect.sk_rrect, clip_behavior);
+                                 int clip_behavior,
+                                 const fml::RefPtr<EngineLayer>& old_layer) {
+  auto layer = std::make_shared<flutter::ClipRRectLayer>(
+      rrect.sk_rrect, static_cast<flutter::Clip>(clip_behavior));
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
 void SceneBuilder::pushClipPath(Dart_Handle layer_handle,
                                 const CanvasPath* path,
-                                int clipBehavior,
-                                const fml::RefPtr<EngineLayer>& oldLayer) {
-  flutter::Clip clip_behavior = static_cast<flutter::Clip>(clipBehavior);
-  FML_DCHECK(clip_behavior != flutter::Clip::kNone);
-  auto layer =
-      std::make_shared<flutter::ClipPathLayer>(path->path(), clip_behavior);
+                                int clip_behavior,
+                                const fml::RefPtr<EngineLayer>& old_layer) {
+  flutter::Clip flutter_clip_behavior =
+      static_cast<flutter::Clip>(clip_behavior);
+  FML_DCHECK(flutter_clip_behavior != flutter::Clip::kNone);
+  auto layer = std::make_shared<flutter::ClipPathLayer>(
+      path->path(), static_cast<flutter::Clip>(flutter_clip_behavior));
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
@@ -125,27 +122,27 @@
                                int alpha,
                                double dx,
                                double dy,
-                               const fml::RefPtr<EngineLayer>& oldLayer) {
+                               const fml::RefPtr<EngineLayer>& old_layer) {
   auto layer = std::make_shared<flutter::OpacityLayer>(
       alpha, SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)));
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
 void SceneBuilder::pushColorFilter(Dart_Handle layer_handle,
                                    const ColorFilter* color_filter,
-                                   const fml::RefPtr<EngineLayer>& oldLayer) {
+                                   const fml::RefPtr<EngineLayer>& old_layer) {
   auto layer =
       std::make_shared<flutter::ColorFilterLayer>(color_filter->filter());
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
@@ -153,57 +150,66 @@
                                    const ImageFilter* image_filter,
                                    double dx,
                                    double dy,
-                                   const fml::RefPtr<EngineLayer>& oldLayer) {
+                                   const fml::RefPtr<EngineLayer>& old_layer) {
   auto layer = std::make_shared<flutter::ImageFilterLayer>(
       image_filter->filter(), SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)));
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
 void SceneBuilder::pushBackdropFilter(
     Dart_Handle layer_handle,
     ImageFilter* filter,
-    int blendMode,
-    const fml::RefPtr<EngineLayer>& oldLayer) {
+    int blend_mode,
+    Dart_Handle backdrop_id,
+    const fml::RefPtr<EngineLayer>& old_layer) {
+  std::optional<int64_t> converted_backdrop_id;
+  if (Dart_IsInteger(backdrop_id)) {
+    int64_t out;
+    Dart_IntegerToInt64(backdrop_id, &out);
+    converted_backdrop_id = out;
+  }
+
   auto layer = std::make_shared<flutter::BackdropFilterLayer>(
-      filter->filter(), static_cast<DlBlendMode>(blendMode));
+      filter->filter(), static_cast<DlBlendMode>(blend_mode),
+      converted_backdrop_id);
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
 void SceneBuilder::pushShaderMask(Dart_Handle layer_handle,
                                   Shader* shader,
-                                  double maskRectLeft,
-                                  double maskRectRight,
-                                  double maskRectTop,
-                                  double maskRectBottom,
-                                  int blendMode,
-                                  int filterQualityIndex,
-                                  const fml::RefPtr<EngineLayer>& oldLayer) {
+                                  double mask_rect_left,
+                                  double mask_rect_right,
+                                  double mask_rect_top,
+                                  double mask_Rect_bottom,
+                                  int blend_mode,
+                                  int filter_quality_index,
+                                  const fml::RefPtr<EngineLayer>& old_layer) {
   SkRect rect =
-      SkRect::MakeLTRB(SafeNarrow(maskRectLeft), SafeNarrow(maskRectTop),
-                       SafeNarrow(maskRectRight), SafeNarrow(maskRectBottom));
-  auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
+      SkRect::MakeLTRB(SafeNarrow(mask_rect_right), SafeNarrow(mask_rect_right),
+                       SafeNarrow(mask_rect_top), SafeNarrow(mask_Rect_bottom));
+  auto sampling = ImageFilter::SamplingFromIndex(filter_quality_index);
   auto layer = std::make_shared<flutter::ShaderMaskLayer>(
-      shader->shader(sampling), rect, static_cast<DlBlendMode>(blendMode));
+      shader->shader(sampling), rect, static_cast<DlBlendMode>(blend_mode));
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 
-  if (oldLayer && oldLayer->Layer()) {
-    layer->AssignOldLayer(oldLayer->Layer().get());
+  if (old_layer && old_layer->Layer()) {
+    layer->AssignOldLayer(old_layer->Layer().get());
   }
 }
 
-void SceneBuilder::addRetained(const fml::RefPtr<EngineLayer>& retainedLayer) {
-  AddLayer(retainedLayer->Layer());
+void SceneBuilder::addRetained(const fml::RefPtr<EngineLayer>& retained_layer) {
+  AddLayer(retained_layer->Layer());
 }
 
 void SceneBuilder::pop() {
@@ -233,13 +239,13 @@
                               double dy,
                               double width,
                               double height,
-                              int64_t textureId,
+                              int64_t texture_id,
                               bool freeze,
-                              int filterQualityIndex) {
-  auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
+                              int filter_quality_index) {
+  auto sampling = ImageFilter::SamplingFromIndex(filter_quality_index);
   auto layer = std::make_unique<flutter::TextureLayer>(
       SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)),
-      SkSize::Make(SafeNarrow(width), SafeNarrow(height)), textureId, freeze,
+      SkSize::Make(SafeNarrow(width), SafeNarrow(height)), texture_id, freeze,
       sampling);
   AddLayer(std::move(layer));
 }
@@ -248,14 +254,14 @@
                                    double dy,
                                    double width,
                                    double height,
-                                   int64_t viewId) {
+                                   int64_t view_id) {
   auto layer = std::make_unique<flutter::PlatformViewLayer>(
       SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)),
-      SkSize::Make(SafeNarrow(width), SafeNarrow(height)), viewId);
+      SkSize::Make(SafeNarrow(width), SafeNarrow(height)), view_id);
   AddLayer(std::move(layer));
 }
 
-void SceneBuilder::addPerformanceOverlay(uint64_t enabledOptions,
+void SceneBuilder::addPerformanceOverlay(uint64_t enabled_options,
                                          double left,
                                          double right,
                                          double top,
@@ -263,7 +269,7 @@
   SkRect rect = SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
                                  SafeNarrow(right), SafeNarrow(bottom));
   auto layer =
-      std::make_unique<flutter::PerformanceOverlayLayer>(enabledOptions);
+      std::make_unique<flutter::PerformanceOverlayLayer>(enabled_options);
   layer->set_paint_bounds(rect);
   AddLayer(std::move(layer));
 }
diff --git a/lib/ui/compositing/scene_builder.h b/lib/ui/compositing/scene_builder.h
index d13f4d0..717129a 100644
--- a/lib/ui/compositing/scene_builder.h
+++ b/lib/ui/compositing/scene_builder.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "flutter/flow/layers/container_layer.h"
-#include "flutter/lib/ui/compositing/scene.h"
 #include "flutter/lib/ui/dart_wrapper.h"
 #include "flutter/lib/ui/painting/color_filter.h"
 #include "flutter/lib/ui/painting/engine_layer.h"
@@ -38,64 +37,65 @@
 
   void pushTransformHandle(Dart_Handle layer_handle,
                            Dart_Handle matrix4_handle,
-                           const fml::RefPtr<EngineLayer>& oldLayer) {
+                           const fml::RefPtr<EngineLayer>& old_layer) {
     tonic::Float64List matrix4(matrix4_handle);
-    pushTransform(layer_handle, matrix4, oldLayer);
+    pushTransform(layer_handle, matrix4, old_layer);
   }
   void pushTransform(Dart_Handle layer_handle,
                      tonic::Float64List& matrix4,
-                     const fml::RefPtr<EngineLayer>& oldLayer);
+                     const fml::RefPtr<EngineLayer>& old_layer);
   void pushOffset(Dart_Handle layer_handle,
                   double dx,
                   double dy,
-                  const fml::RefPtr<EngineLayer>& oldLayer);
+                  const fml::RefPtr<EngineLayer>& old_layer);
   void pushClipRect(Dart_Handle layer_handle,
                     double left,
                     double right,
                     double top,
                     double bottom,
-                    int clipBehavior,
-                    const fml::RefPtr<EngineLayer>& oldLayer);
+                    int clip_behavior,
+                    const fml::RefPtr<EngineLayer>& old_layer);
   void pushClipRRect(Dart_Handle layer_handle,
                      const RRect& rrect,
-                     int clipBehavior,
-                     const fml::RefPtr<EngineLayer>& oldLayer);
+                     int clip_behavior,
+                     const fml::RefPtr<EngineLayer>& old_layer);
   void pushClipPath(Dart_Handle layer_handle,
                     const CanvasPath* path,
-                    int clipBehavior,
-                    const fml::RefPtr<EngineLayer>& oldLayer);
+                    int clip_behavior,
+                    const fml::RefPtr<EngineLayer>& old_layer);
   void pushOpacity(Dart_Handle layer_handle,
                    int alpha,
                    double dx,
                    double dy,
-                   const fml::RefPtr<EngineLayer>& oldLayer);
+                   const fml::RefPtr<EngineLayer>& old_layer);
   void pushColorFilter(Dart_Handle layer_handle,
                        const ColorFilter* color_filter,
-                       const fml::RefPtr<EngineLayer>& oldLayer);
+                       const fml::RefPtr<EngineLayer>& old_layer);
   void pushImageFilter(Dart_Handle layer_handle,
                        const ImageFilter* image_filter,
                        double dx,
                        double dy,
-                       const fml::RefPtr<EngineLayer>& oldLayer);
+                       const fml::RefPtr<EngineLayer>& old_layer);
   void pushBackdropFilter(Dart_Handle layer_handle,
                           ImageFilter* filter,
-                          int blendMode,
-                          const fml::RefPtr<EngineLayer>& oldLayer);
+                          int blend_mode,
+                          Dart_Handle backdrop_id,
+                          const fml::RefPtr<EngineLayer>& old_layer);
   void pushShaderMask(Dart_Handle layer_handle,
                       Shader* shader,
-                      double maskRectLeft,
-                      double maskRectRight,
-                      double maskRectTop,
-                      double maskRectBottom,
-                      int blendMode,
-                      int filterQualityIndex,
-                      const fml::RefPtr<EngineLayer>& oldLayer);
+                      double mask_rect_left,
+                      double mask_rect_right,
+                      double mask_rect_top,
+                      double mask_rect_bottom,
+                      int blend_mode,
+                      int filter_quality_index,
+                      const fml::RefPtr<EngineLayer>& old_layer);
 
-  void addRetained(const fml::RefPtr<EngineLayer>& retainedLayer);
+  void addRetained(const fml::RefPtr<EngineLayer>& retained_layer);
 
   void pop();
 
-  void addPerformanceOverlay(uint64_t enabledOptions,
+  void addPerformanceOverlay(uint64_t enabled_options,
                              double left,
                              double right,
                              double top,
@@ -107,15 +107,15 @@
                   double dy,
                   double width,
                   double height,
-                  int64_t textureId,
+                  int64_t texture_id,
                   bool freeze,
-                  int filterQuality);
+                  int filter_quality);
 
   void addPlatformView(double dx,
                        double dy,
                        double width,
                        double height,
-                       int64_t viewId);
+                       int64_t view_id);
 
   void build(Dart_Handle scene_handle);
 
diff --git a/lib/ui/compositing/scene_builder_unittests.cc b/lib/ui/compositing/scene_builder_unittests.cc
index 702d83b..cbd2a3b 100644
--- a/lib/ui/compositing/scene_builder_unittests.cc
+++ b/lib/ui/compositing/scene_builder_unittests.cc
@@ -12,6 +12,7 @@
 #include "flutter/shell/common/shell_test.h"
 #include "flutter/shell/common/thread_host.h"
 #include "flutter/testing/testing.h"
+#include "lib/ui/compositing/scene.h"
 
 // CREATE_NATIVE_ENTRY is leaky by design
 // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
diff --git a/lib/web_ui/lib/compositing.dart b/lib/web_ui/lib/compositing.dart
index 09a4d25..2f0d558 100644
--- a/lib/web_ui/lib/compositing.dart
+++ b/lib/web_ui/lib/compositing.dart
@@ -76,6 +76,7 @@
     ImageFilter filter, {
     BlendMode blendMode = BlendMode.srcOver,
     BackdropFilterEngineLayer? oldLayer,
+    int? backdropId,
   });
   ShaderMaskEngineLayer pushShaderMask(
     Shader shader,
diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart
index 183180e..d2f34bc 100644
--- a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart
+++ b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart
@@ -110,6 +110,7 @@
     ui.ImageFilter filter, {
     ui.BlendMode blendMode = ui.BlendMode.srcOver,
     ui.EngineLayer? oldLayer,
+    int? backdropId,
   }) {
     return pushLayer<BackdropFilterEngineLayer>(BackdropFilterEngineLayer(
       filter,
diff --git a/lib/web_ui/lib/src/engine/html/scene_builder.dart b/lib/web_ui/lib/src/engine/html/scene_builder.dart
index ff63108..88644ad 100644
--- a/lib/web_ui/lib/src/engine/html/scene_builder.dart
+++ b/lib/web_ui/lib/src/engine/html/scene_builder.dart
@@ -240,6 +240,7 @@
     ui.ImageFilter filter, {
     ui.BlendMode blendMode = ui.BlendMode.srcOver,
     ui.BackdropFilterEngineLayer? oldLayer,
+    int? backdropId,
   }) {
     return _pushSurface<PersistedBackdropFilter>(PersistedBackdropFilter(
         oldLayer as PersistedBackdropFilter?, filter));
diff --git a/lib/web_ui/lib/src/engine/scene_builder.dart b/lib/web_ui/lib/src/engine/scene_builder.dart
index af84cb0..37d29e6 100644
--- a/lib/web_ui/lib/src/engine/scene_builder.dart
+++ b/lib/web_ui/lib/src/engine/scene_builder.dart
@@ -131,7 +131,8 @@
   ui.BackdropFilterEngineLayer pushBackdropFilter(
     ui.ImageFilter filter, {
     ui.BlendMode blendMode = ui.BlendMode.srcOver,
-    ui.BackdropFilterEngineLayer? oldLayer
+    ui.BackdropFilterEngineLayer? oldLayer,
+    int? backdropId,
   }) => pushLayer<BackdropFilterLayer>(
       BackdropFilterLayer(),
       BackdropFilterOperation(filter, blendMode),
diff --git a/shell/common/dl_op_spy.cc b/shell/common/dl_op_spy.cc
index 19f54d1..77fb209 100644
--- a/shell/common/dl_op_spy.cc
+++ b/shell/common/dl_op_spy.cc
@@ -31,7 +31,8 @@
 void DlOpSpy::save() {}
 void DlOpSpy::saveLayer(const DlRect& bounds,
                         const SaveLayerOptions options,
-                        const DlImageFilter* backdrop) {}
+                        const DlImageFilter* backdrop,
+                        std::optional<int64_t> backdrop_id) {}
 void DlOpSpy::restore() {}
 void DlOpSpy::drawColor(DlColor color, DlBlendMode mode) {
   did_draw_ |= !color.isTransparent();
diff --git a/shell/common/dl_op_spy.h b/shell/common/dl_op_spy.h
index 6d3d9c9..82c2dfc 100644
--- a/shell/common/dl_op_spy.h
+++ b/shell/common/dl_op_spy.h
@@ -41,7 +41,8 @@
   void save() override;
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override;
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override;
   void restore() override;
   void drawColor(DlColor color, DlBlendMode mode) override;
   void drawPaint() override;
diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc
index d21b586..4f90651 100644
--- a/testing/display_list_testing.cc
+++ b/testing/display_list_testing.cc
@@ -4,6 +4,7 @@
 
 #include "flutter/testing/display_list_testing.h"
 
+#include <cstdint>
 #include <iomanip>
 
 #include "flutter/display_list/display_list.h"
@@ -701,12 +702,17 @@
 }
 void DisplayListStreamDispatcher::saveLayer(const DlRect& bounds,
                                             const SaveLayerOptions options,
-                                            const DlImageFilter* backdrop) {
+                                            const DlImageFilter* backdrop,
+                                            std::optional<int64_t> backdrop_id) {
   startl() << "saveLayer(" << bounds << ", " << options;
   if (backdrop) {
     os_ << "," << std::endl;
     indent(10);
-    startl() << "backdrop: ";
+    if (backdrop_id.has_value()) {
+      startl() << "backdrop: " << backdrop_id.value();
+    } else {
+      startl() << "backdrop: (no id)";
+    }
     out(backdrop);
     outdent(10);
   } else {
diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h
index 8dcaa7d..33fcd6b 100644
--- a/testing/display_list_testing.h
+++ b/testing/display_list_testing.h
@@ -110,7 +110,8 @@
   void save() override;
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override;
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override;
   void restore() override;
 
   void translate(DlScalar tx, DlScalar ty) override;
@@ -397,7 +398,8 @@
   void save() override { RecordByType(DisplayListOpType::kSave); }
   void saveLayer(const DlRect& bounds,
                  const SaveLayerOptions options,
-                 const DlImageFilter* backdrop) override {
+                 const DlImageFilter* backdrop,
+                 std::optional<int64_t> backdrop_id) override {
     if (backdrop) {
       RecordByType(DisplayListOpType::kSaveLayerBackdrop);
     } else {
diff --git a/testing/impeller_golden_tests_output.txt b/testing/impeller_golden_tests_output.txt
index ef3b466..60d7faf 100644
--- a/testing/impeller_golden_tests_output.txt
+++ b/testing/impeller_golden_tests_output.txt
@@ -254,6 +254,9 @@
 impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Metal.png
 impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_OpenGLES.png
 impeller_Play_AiksTest_CanRenderBackdropBlurInteractive_Vulkan.png
+impeller_Play_AiksTest_CanRenderBackdropBlurWithSingleBackdropId_Metal.png
+impeller_Play_AiksTest_CanRenderBackdropBlurWithSingleBackdropId_OpenGLES.png
+impeller_Play_AiksTest_CanRenderBackdropBlurWithSingleBackdropId_Vulkan.png
 impeller_Play_AiksTest_CanRenderBackdropBlur_Metal.png
 impeller_Play_AiksTest_CanRenderBackdropBlur_OpenGLES.png
 impeller_Play_AiksTest_CanRenderBackdropBlur_Vulkan.png
@@ -382,6 +385,15 @@
 impeller_Play_AiksTest_CanRenderMaskBlurHugeSigma_Metal.png
 impeller_Play_AiksTest_CanRenderMaskBlurHugeSigma_OpenGLES.png
 impeller_Play_AiksTest_CanRenderMaskBlurHugeSigma_Vulkan.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropIdAndDistinctFilters_Metal.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropIdAndDistinctFilters_OpenGLES.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropIdAndDistinctFilters_Vulkan.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropIdDifferentLayers_Metal.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropIdDifferentLayers_OpenGLES.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropIdDifferentLayers_Vulkan.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropId_Metal.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropId_OpenGLES.png
+impeller_Play_AiksTest_CanRenderMultipleBackdropBlurWithSingleBackdropId_Vulkan.png
 impeller_Play_AiksTest_CanRenderNestedClips_Metal.png
 impeller_Play_AiksTest_CanRenderNestedClips_OpenGLES.png
 impeller_Play_AiksTest_CanRenderNestedClips_Vulkan.png