[framework] remove usage and deprecate physical model layer (#102274)

diff --git a/packages/flutter/lib/src/rendering/flex.dart b/packages/flutter/lib/src/rendering/flex.dart
index 99107df..04043d0 100644
--- a/packages/flutter/lib/src/rendering/flex.dart
+++ b/packages/flutter/lib/src/rendering/flex.dart
@@ -1083,20 +1083,14 @@
     if (size.isEmpty)
       return;
 
-    if (clipBehavior == Clip.none) {
-      _clipRectLayer.layer = null;
-      defaultPaint(context, offset);
-    } else {
-      // We have overflow and the clipBehavior isn't none. Clip it.
-      _clipRectLayer.layer = context.pushClipRect(
-        needsCompositing,
-        offset,
-        Offset.zero & size,
-        defaultPaint,
-        clipBehavior: clipBehavior,
-        oldLayer: _clipRectLayer.layer,
-      );
-    }
+    _clipRectLayer.layer = context.pushClipRect(
+      needsCompositing,
+      offset,
+      Offset.zero & size,
+      defaultPaint,
+      clipBehavior: clipBehavior,
+      oldLayer: _clipRectLayer.layer,
+    );
 
     assert(() {
       // Only set this if it's null to save work. It gets reset to null if the
diff --git a/packages/flutter/lib/src/rendering/flow.dart b/packages/flutter/lib/src/rendering/flow.dart
index 153fe3f..4655d06 100644
--- a/packages/flutter/lib/src/rendering/flow.dart
+++ b/packages/flutter/lib/src/rendering/flow.dart
@@ -388,19 +388,14 @@
 
   @override
   void paint(PaintingContext context, Offset offset) {
-    if (clipBehavior == Clip.none) {
-      _clipRectLayer.layer = null;
-      _paintWithDelegate(context, offset);
-    } else {
-      _clipRectLayer.layer = context.pushClipRect(
-        needsCompositing,
-        offset,
-        Offset.zero & size,
-        _paintWithDelegate,
-        clipBehavior: clipBehavior,
-        oldLayer: _clipRectLayer.layer,
-      );
-    }
+    _clipRectLayer.layer = context.pushClipRect(
+      needsCompositing,
+      offset,
+      Offset.zero & size,
+      _paintWithDelegate,
+      clipBehavior: clipBehavior,
+      oldLayer: _clipRectLayer.layer,
+    );
   }
 
   final LayerHandle<ClipRectLayer> _clipRectLayer = LayerHandle<ClipRectLayer>();
diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart
index 4644180..b7f468a 100644
--- a/packages/flutter/lib/src/rendering/layer.dart
+++ b/packages/flutter/lib/src/rendering/layer.dart
@@ -1972,12 +1972,20 @@
 /// When debugging, setting [debugDisablePhysicalShapeLayers] to true will cause this
 /// layer to be skipped (directly replaced by its children). This can be helpful
 /// to track down the cause of performance problems.
+@Deprecated(
+  'Use a clip and canvas operations directly (See RenderPhysicalModel). '
+  'This feature was deprecated after v2.13.0-0.0.pre.',
+)
 class PhysicalModelLayer extends ContainerLayer {
   /// Creates a composited layer that uses a physical model to producing
   /// lighting effects.
   ///
   /// The [clipPath], [clipBehavior], [elevation], [color], and [shadowColor]
   /// arguments must be non-null before the compositing phase of the pipeline.
+  @Deprecated(
+    'Use a clip and canvas operations directly (See RenderPhysicalModel). '
+    'This feature was deprecated after v2.13.0-0.0.pre.',
+  )
   PhysicalModelLayer({
     Path? clipPath,
     Clip clipBehavior = Clip.none,
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index c517ae1..73648a2 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -491,6 +491,10 @@
   /// (e.g. from opacity layer to a clip rect layer).
   /// {@endtemplate}
   ClipRectLayer? pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.hardEdge, ClipRectLayer? oldLayer }) {
+    if (clipBehavior == Clip.none) {
+      painter(this, offset);
+      return null;
+    }
     final Rect offsetClipRect = clipRect.shift(offset);
     if (needsCompositing) {
       final ClipRectLayer layer = oldLayer ?? ClipRectLayer();
@@ -526,6 +530,10 @@
   /// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer}
   ClipRRectLayer? pushClipRRect(bool needsCompositing, Offset offset, Rect bounds, RRect clipRRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.antiAlias, ClipRRectLayer? oldLayer }) {
     assert(clipBehavior != null);
+    if (clipBehavior == Clip.none) {
+      painter(this, offset);
+      return null;
+    }
     final Rect offsetBounds = bounds.shift(offset);
     final RRect offsetClipRRect = clipRRect.shift(offset);
     if (needsCompositing) {
@@ -562,6 +570,10 @@
   /// {@macro flutter.rendering.PaintingContext.pushClipRect.oldLayer}
   ClipPathLayer? pushClipPath(bool needsCompositing, Offset offset, Rect bounds, Path clipPath, PaintingContextCallback painter, { Clip clipBehavior = Clip.antiAlias, ClipPathLayer? oldLayer }) {
     assert(clipBehavior != null);
+    if (clipBehavior == Clip.none) {
+      painter(this, offset);
+      return null;
+    }
     final Rect offsetBounds = bounds.shift(offset);
     final Path offsetClipPath = clipPath.shift(offset);
     if (needsCompositing) {
diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart
index 7e45a9d..28b5990 100644
--- a/packages/flutter/lib/src/rendering/proxy_box.dart
+++ b/packages/flutter/lib/src/rendering/proxy_box.dart
@@ -1828,9 +1828,6 @@
   }
 
   @override
-  bool get alwaysNeedsCompositing => true;
-
-  @override
   void describeSemanticsConfiguration(SemanticsConfiguration config) {
     super.describeSemanticsConfiguration(config);
     config.elevation = elevation;
@@ -1845,6 +1842,8 @@
   }
 }
 
+final Paint _transparentPaint = Paint()..color = const Color(0x00000000);
+
 /// Creates a physical model layer that clips its child to a rounded
 /// rectangle.
 ///
@@ -1873,9 +1872,6 @@
        _shape = shape,
        _borderRadius = borderRadius;
 
-  @override
-  PhysicalModelLayer? get layer => super.layer as PhysicalModelLayer?;
-
   /// The shape of the layer.
   ///
   /// Defaults to [BoxShape.rectangle]. The [borderRadius] affects the corners
@@ -1933,42 +1929,79 @@
 
   @override
   void paint(PaintingContext context, Offset offset) {
-    if (child != null) {
-      _updateClip();
-      final RRect offsetRRect = _clip!.shift(offset);
-      final Rect offsetBounds = offsetRRect.outerRect;
-      final Path offsetRRectAsPath = Path()..addRRect(offsetRRect);
-      bool paintShadows = true;
-      assert(() {
-        if (debugDisableShadows) {
-          if (elevation > 0.0) {
-            context.canvas.drawRRect(
-              offsetRRect,
-              Paint()
-                ..color = shadowColor
-                ..style = PaintingStyle.stroke
-                ..strokeWidth = elevation * 2.0,
-            );
-          }
-          paintShadows = false;
-        }
-        return true;
-      }());
-      layer ??= PhysicalModelLayer();
-      layer!
-        ..clipPath = offsetRRectAsPath
-        ..clipBehavior = clipBehavior
-        ..elevation = paintShadows ? elevation : 0.0
-        ..color = color
-        ..shadowColor = shadowColor;
-      context.pushLayer(layer!, super.paint, offset, childPaintBounds: offsetBounds);
-      assert(() {
-        layer!.debugCreator = debugCreator;
-        return true;
-      }());
-    } else {
+    if (child == null) {
       layer = null;
+      return;
     }
+
+    _updateClip();
+    final RRect offsetRRect = _clip!.shift(offset);
+    final Rect offsetBounds = offsetRRect.outerRect;
+    final Path offsetRRectAsPath = Path()..addRRect(offsetRRect);
+    bool paintShadows = true;
+    assert(() {
+      if (debugDisableShadows) {
+        if (elevation > 0.0) {
+          context.canvas.drawRRect(
+            offsetRRect,
+            Paint()
+              ..color = shadowColor
+              ..style = PaintingStyle.stroke
+              ..strokeWidth = elevation * 2.0,
+          );
+        }
+        paintShadows = false;
+      }
+      return true;
+    }());
+
+    final Canvas canvas = context.canvas;
+    if (elevation != 0.0 && paintShadows) {
+      // The drawShadow call doesn't add the region of the shadow to the
+      // picture's bounds, so we draw a hardcoded amount of extra space to
+      // account for the maximum potential area of the shadow.
+      // TODO(jsimmons): remove this when Skia does it for us.
+      canvas.drawRect(
+        offsetBounds.inflate(20.0),
+        _transparentPaint,
+      );
+      canvas.drawShadow(
+        offsetRRectAsPath,
+        shadowColor,
+        elevation,
+        color.alpha != 0xFF,
+      );
+    }
+    final bool usesSaveLayer = clipBehavior == Clip.antiAliasWithSaveLayer;
+    if (!usesSaveLayer) {
+      canvas.drawRRect(
+        offsetRRect,
+        Paint()..color = color
+      );
+    }
+    layer = context.pushClipRRect(
+      needsCompositing,
+      offset,
+      Offset.zero & size,
+      _clip!,
+      (PaintingContext context, Offset offset) {
+        if (usesSaveLayer) {
+          // If we want to avoid the bleeding edge artifact
+          // (https://github.com/flutter/flutter/issues/18057#issue-328003931)
+          // using saveLayer, we have to call drawPaint instead of drawPath as
+          // anti-aliased drawPath will always have such artifacts.
+          context.canvas.drawPaint( Paint()..color = color);
+        }
+        super.paint(context, offset);
+      },
+      oldLayer: layer as ClipRRectLayer?,
+      clipBehavior: clipBehavior,
+    );
+
+    assert(() {
+      layer?.debugCreator = debugCreator;
+      return true;
+    }());
   }
 
   @override
@@ -2007,9 +2040,6 @@
        assert(shadowColor != null);
 
   @override
-  PhysicalModelLayer? get layer => super.layer as PhysicalModelLayer?;
-
-  @override
   Path get _defaultClip => Path()..addRect(Offset.zero & size);
 
   @override
@@ -2025,41 +2055,78 @@
 
   @override
   void paint(PaintingContext context, Offset offset) {
-    if (child != null) {
-      _updateClip();
-      final Rect offsetBounds = offset & size;
-      final Path offsetPath = _clip!.shift(offset);
-      bool paintShadows = true;
-      assert(() {
-        if (debugDisableShadows) {
-          if (elevation > 0.0) {
-            context.canvas.drawPath(
-              offsetPath,
-              Paint()
-                ..color = shadowColor
-                ..style = PaintingStyle.stroke
-                ..strokeWidth = elevation * 2.0,
-            );
-          }
-          paintShadows = false;
-        }
-        return true;
-      }());
-      layer ??= PhysicalModelLayer();
-      layer!
-        ..clipPath = offsetPath
-        ..clipBehavior = clipBehavior
-        ..elevation = paintShadows ? elevation : 0.0
-        ..color = color
-        ..shadowColor = shadowColor;
-      context.pushLayer(layer!, super.paint, offset, childPaintBounds: offsetBounds);
-      assert(() {
-        layer!.debugCreator = debugCreator;
-        return true;
-      }());
-    } else {
+    if (child == null) {
       layer = null;
+      return;
     }
+
+    _updateClip();
+    final Rect offsetBounds = offset & size;
+    final Path offsetPath = _clip!.shift(offset);
+    bool paintShadows = true;
+    assert(() {
+      if (debugDisableShadows) {
+        if (elevation > 0.0) {
+          context.canvas.drawPath(
+            offsetPath,
+            Paint()
+              ..color = shadowColor
+              ..style = PaintingStyle.stroke
+              ..strokeWidth = elevation * 2.0,
+          );
+        }
+        paintShadows = false;
+      }
+      return true;
+    }());
+
+    final Canvas canvas = context.canvas;
+    if (elevation != 0.0 && paintShadows) {
+      // The drawShadow call doesn't add the region of the shadow to the
+      // picture's bounds, so we draw a hardcoded amount of extra space to
+      // account for the maximum potential area of the shadow.
+      // TODO(jsimmons): remove this when Skia does it for us.
+      canvas.drawRect(
+        offsetBounds.inflate(20.0),
+        _transparentPaint,
+      );
+      canvas.drawShadow(
+        offsetPath,
+        shadowColor,
+        elevation,
+        color.alpha != 0xFF,
+      );
+    }
+    final bool usesSaveLayer = clipBehavior == Clip.antiAliasWithSaveLayer;
+    if (!usesSaveLayer) {
+      canvas.drawPath(
+        offsetPath,
+        Paint()..color = color
+      );
+    }
+    layer = context.pushClipPath(
+      needsCompositing,
+      offset,
+      Offset.zero & size,
+      _clip!,
+      (PaintingContext context, Offset offset) {
+        if (usesSaveLayer) {
+          // If we want to avoid the bleeding edge artifact
+          // (https://github.com/flutter/flutter/issues/18057#issue-328003931)
+          // using saveLayer, we have to call drawPaint instead of drawPath as
+          // anti-aliased drawPath will always have such artifacts.
+          context.canvas.drawPaint( Paint()..color = color);
+        }
+        super.paint(context, offset);
+      },
+      oldLayer: layer as ClipPathLayer?,
+      clipBehavior: clipBehavior,
+    );
+
+    assert(() {
+      layer?.debugCreator = debugCreator;
+      return true;
+    }());
   }
 
   @override
diff --git a/packages/flutter/lib/src/rendering/shifted_box.dart b/packages/flutter/lib/src/rendering/shifted_box.dart
index 7ecdbb2..1d989ad 100644
--- a/packages/flutter/lib/src/rendering/shifted_box.dart
+++ b/packages/flutter/lib/src/rendering/shifted_box.dart
@@ -799,20 +799,15 @@
       return;
     }
 
-    if (clipBehavior == Clip.none) {
-      _clipRectLayer.layer = null;
-      super.paint(context, offset);
-    } else {
-      // We have overflow and the clipBehavior isn't none. Clip it.
-      _clipRectLayer.layer = context.pushClipRect(
-        needsCompositing,
-        offset,
-        Offset.zero & size,
-        super.paint,
-        clipBehavior: clipBehavior,
-        oldLayer: _clipRectLayer.layer,
-      );
-    }
+    // We have overflow and the clipBehavior isn't none. Clip it.
+    _clipRectLayer.layer = context.pushClipRect(
+      needsCompositing,
+      offset,
+      Offset.zero & size,
+      super.paint,
+      clipBehavior: clipBehavior,
+      oldLayer: _clipRectLayer.layer,
+    );
 
     // Display the overflow indicator.
     assert(() {
diff --git a/packages/flutter/test/material/chip_filter_test.dart b/packages/flutter/test/material/chip_filter_test.dart
index 3405f3b..773aada 100644
--- a/packages/flutter/test/material/chip_filter_test.dart
+++ b/packages/flutter/test/material/chip_filter_test.dart
@@ -64,6 +64,8 @@
   expect(
     finder,
     paints
+      // Physical model path
+      ..path()
       // The first path that is painted is the selection overlay. We do not care
       // how it is painted but it has to be added it to this pattern so that the
       // check mark can be checked next.
diff --git a/packages/flutter/test/material/chip_input_test.dart b/packages/flutter/test/material/chip_input_test.dart
index 84929d3..6c6ca97 100644
--- a/packages/flutter/test/material/chip_input_test.dart
+++ b/packages/flutter/test/material/chip_input_test.dart
@@ -64,6 +64,8 @@
   expect(
     finder,
     paints
+      // Physical model layer path
+      ..path()
       // The first path that is painted is the selection overlay. We do not care
       // how it is painted but it has to be added it to this pattern so that the
       // check mark can be checked next.
diff --git a/packages/flutter/test/material/outlined_button_test.dart b/packages/flutter/test/material/outlined_button_test.dart
index ecbf00c..d899247 100644
--- a/packages/flutter/test/material/outlined_button_test.dart
+++ b/packages/flutter/test/material/outlined_button_test.dart
@@ -852,10 +852,6 @@
         ),
       );
     }
-
-    // 116 = 16 + 'button'.length * 14 + 16, horizontal padding = 16
-    const Rect clipRect = Rect.fromLTRB(0.0, 0.0, 116.0, 36.0);
-    final Path clipPath = Path()..addRect(clipRect);
     final Finder outlinedButton = find.byType(OutlinedButton);
 
     BorderSide getBorderSide() {
@@ -873,12 +869,6 @@
     // Expect that the button is disabled and painted with the disabled border color.
     expect(tester.widget<OutlinedButton>(outlinedButton).enabled, false);
     expect(getBorderSide(), disabledBorderSide);
-    _checkPhysicalLayer(
-      tester.element(outlinedButton),
-      fillColor,
-      clipPath: clipPath,
-      clipRect: clipRect,
-    );
 
     // Pump a new button with a no-op onPressed callback to make it enabled.
     await tester.pumpWidget(
@@ -896,23 +886,11 @@
     // Wait for the border's color to change to pressed
     await tester.pump(const Duration(milliseconds: 200));
     expect(getBorderSide(), pressedBorderSide);
-    _checkPhysicalLayer(
-      tester.element(outlinedButton),
-      fillColor,
-      clipPath: clipPath,
-      clipRect: clipRect,
-    );
 
     // Tap gesture completes, button returns to its initial configuration.
     await gesture.up();
     await tester.pumpAndSettle();
     expect(getBorderSide(), enabledBorderSide);
-    _checkPhysicalLayer(
-      tester.element(outlinedButton),
-      fillColor,
-      clipPath: clipPath,
-      clipRect: clipRect,
-    );
   });
 
   testWidgets('OutlinedButton has no clip by default', (WidgetTester tester) async {
@@ -1748,29 +1726,6 @@
   });
 }
 
-PhysicalModelLayer _findPhysicalLayer(Element element) {
-  expect(element, isNotNull);
-  RenderObject? object = element.renderObject;
-  while (object != null && object is! RenderRepaintBoundary && object is! RenderView) {
-    object = object.parent as RenderObject?;
-  }
-  expect(object!.debugLayer, isNotNull);
-  expect(object.debugLayer!.firstChild, isA<PhysicalModelLayer>());
-  final PhysicalModelLayer layer = object.debugLayer!.firstChild! as PhysicalModelLayer;
-  final Layer child = layer.firstChild!;
-  return child is PhysicalModelLayer ? child : layer;
-}
-
-void _checkPhysicalLayer(Element element, Color expectedColor, { Path? clipPath, Rect? clipRect }) {
-  final PhysicalModelLayer expectedLayer = _findPhysicalLayer(element);
-  expect(expectedLayer.elevation, 0.0);
-  expect(expectedLayer.color, expectedColor);
-  if (clipPath != null) {
-    expect(clipRect, isNotNull);
-    expect(expectedLayer.clipPath, coversSameAreaAs(clipPath, areaToCompare: clipRect!.inflate(10.0)));
-  }
-}
-
 TextStyle _iconStyle(WidgetTester tester, IconData icon) {
   final RichText iconRichText = tester.widget<RichText>(
     find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
diff --git a/packages/flutter/test/material/range_slider_test.dart b/packages/flutter/test/material/range_slider_test.dart
index 6e8595b..c3d0b69 100644
--- a/packages/flutter/test/material/range_slider_test.dart
+++ b/packages/flutter/test/material/range_slider_test.dart
@@ -1360,7 +1360,7 @@
     );
 
     // Represents the Raised Button and Range Slider.
-    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 3));
+    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 4));
     expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 3));
 
     await tester.tap(find.text('Next'));
@@ -1379,7 +1379,7 @@
     );
 
     // Represents the raised button with inner page text.
-    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 1));
+    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
     expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 1));
 
     // Don't stop holding the value indicator.
diff --git a/packages/flutter/test/material/slider_test.dart b/packages/flutter/test/material/slider_test.dart
index 5438310..c2c8b6d 100644
--- a/packages/flutter/test/material/slider_test.dart
+++ b/packages/flutter/test/material/slider_test.dart
@@ -784,6 +784,7 @@
     expect(
       valueIndicatorBox,
       paints
+        ..rrect(color: const Color(0xfffafafa))
         ..rrect(color: customColor1) // active track
         ..rrect(color: customColor2) // inactive track
         ..circle(color: customColor1.withOpacity(0.12)) // overlay
@@ -2415,7 +2416,7 @@
         ..paragraph(),
     );
 
-    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
+    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 3));
     expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 2));
 
     await tester.tap(find.text('Next'));
@@ -2432,7 +2433,7 @@
     );
 
     // Represents the ElevatedButton with inner Text, inner page.
-    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 1));
+    expect(valueIndicatorBox, paintsExactlyCountTimes(#drawPath, 2));
     expect(valueIndicatorBox, paintsExactlyCountTimes(#drawParagraph, 1));
 
     // Don't stop holding the value indicator.
diff --git a/packages/flutter/test/material/slider_theme_test.dart b/packages/flutter/test/material/slider_theme_test.dart
index 93e607c..e890fdd 100644
--- a/packages/flutter/test/material/slider_theme_test.dart
+++ b/packages/flutter/test/material/slider_theme_test.dart
@@ -1134,6 +1134,8 @@
     expect(
       valueIndicatorBox,
       paints
+        // physical model
+        ..rrect()
         ..rrect(rrect: RRect.fromLTRBAndCorners(
           24.0, 298.0, 24.0, 302.0,
           topLeft: const Radius.circular(2.0),
diff --git a/packages/flutter/test/material/toggle_buttons_test.dart b/packages/flutter/test/material/toggle_buttons_test.dart
index 7937117..55ddfa3 100644
--- a/packages/flutter/test/material/toggle_buttons_test.dart
+++ b/packages/flutter/test/material/toggle_buttons_test.dart
@@ -1015,6 +1015,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: theme.colorScheme.onSurface.withOpacity(0.12),
@@ -1042,6 +1044,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: theme.colorScheme.onSurface.withOpacity(0.12),
@@ -1068,6 +1072,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: theme.colorScheme.onSurface.withOpacity(0.12),
@@ -1108,6 +1114,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: borderColor,
@@ -1137,6 +1145,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: selectedBorderColor,
@@ -1165,6 +1175,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: disabledBorderColor,
@@ -1432,6 +1444,8 @@
       expect(
         toggleButtonRenderObject[0],
         paints
+          // physical model
+          ..path()
           // leading side, top and bottom - enabled
           ..path(
             style: PaintingStyle.stroke,
@@ -1445,6 +1459,8 @@
       expect(
         toggleButtonRenderObject[1],
         paints
+          // physical model
+          ..path()
           // leading side - selected
           ..path(
             style: PaintingStyle.stroke,
@@ -1464,6 +1480,8 @@
       expect(
         toggleButtonRenderObject[2],
         paints
+          // physical model
+          ..path()
           // leading side - selected, since previous button is selected
           ..path(
             style: PaintingStyle.stroke,
@@ -1515,6 +1533,8 @@
       expect(
         toggleButtonRenderObject[0],
         paints
+        // physical model
+          ..path()
         // left side, top and right - enabled.
           ..path(
             style: PaintingStyle.stroke,
@@ -1528,6 +1548,8 @@
       expect(
         toggleButtonRenderObject[1],
         paints
+        // physical model
+          ..path()
         // top side - selected.
           ..path(
             style: PaintingStyle.stroke,
@@ -1547,6 +1569,8 @@
       expect(
         toggleButtonRenderObject[2],
         paints
+        // physical model
+          ..path()
         // top side - selected, since previous button is selected.
           ..path(
             style: PaintingStyle.stroke,
@@ -1712,6 +1736,8 @@
     expect(
       toggleButtonRenderObject[0],
       paints
+      // physical model paints
+        ..path()
       // left side, top and right - enabled.
         ..path(
           style: PaintingStyle.stroke,
diff --git a/packages/flutter/test/material/toggle_buttons_theme_test.dart b/packages/flutter/test/material/toggle_buttons_theme_test.dart
index cd8246e..5d26f85 100644
--- a/packages/flutter/test/material/toggle_buttons_theme_test.dart
+++ b/packages/flutter/test/material/toggle_buttons_theme_test.dart
@@ -543,6 +543,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model layer paint
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: borderColor,
@@ -576,6 +578,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model layer paint
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: selectedBorderColor,
@@ -608,6 +612,8 @@
       expect(
         toggleButtonRenderObject,
         paints
+          // physical model layer paint
+          ..path()
           ..path(
             style: PaintingStyle.stroke,
             color: disabledBorderColor,
diff --git a/packages/flutter/test/rendering/debug_test.dart b/packages/flutter/test/rendering/debug_test.dart
index b31703e..74d80e4 100644
--- a/packages/flutter/test/rendering/debug_test.dart
+++ b/packages/flutter/test/rendering/debug_test.dart
@@ -222,7 +222,7 @@
     );
     final RenderOpacity root = RenderOpacity(
       opacity: .5,
-      child: blackBox,
+      child: RenderRepaintBoundary(child: blackBox),
     );
     layout(root, phase: EnginePhase.compositingBits);
 
diff --git a/packages/flutter/test/rendering/proxy_box_test.dart b/packages/flutter/test/rendering/proxy_box_test.dart
index 70dfff9..4957f2f 100644
--- a/packages/flutter/test/rendering/proxy_box_test.dart
+++ b/packages/flutter/test/rendering/proxy_box_test.dart
@@ -5,9 +5,8 @@
 import 'dart:typed_data';
 import 'dart:ui' as ui show Gradient, Image, ImageFilter;
 
-import 'package:flutter/animation.dart';
 import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
 import 'package:flutter_test/flutter_test.dart';
 
@@ -15,7 +14,6 @@
 
 void main() {
   TestRenderingFlutterBinding.ensureInitialized();
-
   test('RenderFittedBox handles applying paint transform and hit-testing with empty size', () {
     final RenderFittedBox fittedBox = RenderFittedBox(
       child: RenderCustomPaint(
@@ -61,47 +59,20 @@
     expect(painted, equals(false));
   });
 
-  test('RenderPhysicalModel compositing on Fuchsia', () {
-    debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
-
+  test('RenderPhysicalModel compositing', () {
     final RenderPhysicalModel root = RenderPhysicalModel(color: const Color(0xffff00ff));
     layout(root, phase: EnginePhase.composite);
-    expect(root.needsCompositing, isTrue);
+    expect(root.needsCompositing, isFalse);
 
     // On Fuchsia, the system compositor is responsible for drawing shadows
     // for physical model layers with non-zero elevation.
     root.elevation = 1.0;
     pumpFrame(phase: EnginePhase.composite);
-    expect(root.needsCompositing, isTrue);
+    expect(root.needsCompositing, isFalse);
 
     root.elevation = 0.0;
     pumpFrame(phase: EnginePhase.composite);
-    expect(root.needsCompositing, isTrue);
-
-    debugDefaultTargetPlatformOverride = null;
-  });
-
-  test('RenderPhysicalModel compositing on non-Fuchsia', () {
-    for (final TargetPlatform platform in TargetPlatform.values) {
-      if (platform == TargetPlatform.fuchsia) {
-        continue;
-      }
-      debugDefaultTargetPlatformOverride = platform;
-
-      final RenderPhysicalModel root = RenderPhysicalModel(color: const Color(0xffff00ff));
-      layout(root, phase: EnginePhase.composite);
-      expect(root.needsCompositing, isTrue);
-
-      // Flutter now composites physical shapes on all platforms.
-      root.elevation = 1.0;
-      pumpFrame(phase: EnginePhase.composite);
-      expect(root.needsCompositing, isTrue);
-
-      root.elevation = 0.0;
-      pumpFrame(phase: EnginePhase.composite);
-      expect(root.needsCompositing, isTrue);
-    }
-    debugDefaultTargetPlatformOverride = null;
+    expect(root.needsCompositing, isFalse);
   });
 
   test('RenderSemanticsGestureHandler adds/removes correct semantic actions', () {
@@ -128,9 +99,6 @@
   group('RenderPhysicalShape', () {
     test('shape change triggers repaint', () {
       for (final TargetPlatform platform in TargetPlatform.values) {
-        if (platform == TargetPlatform.fuchsia) {
-          continue;
-        }
         debugDefaultTargetPlatformOverride = platform;
 
         final RenderPhysicalShape root = RenderPhysicalShape(
@@ -151,27 +119,24 @@
       debugDefaultTargetPlatformOverride = null;
     });
 
-    test('compositing on non-Fuchsia', () {
+    test('compositing', () {
       for (final TargetPlatform platform in TargetPlatform.values) {
-        if (platform == TargetPlatform.fuchsia) {
-          continue;
-        }
         debugDefaultTargetPlatformOverride = platform;
         final RenderPhysicalShape root = RenderPhysicalShape(
           color: const Color(0xffff00ff),
           clipper: const ShapeBorderClipper(shape: CircleBorder()),
         );
         layout(root, phase: EnginePhase.composite);
-        expect(root.needsCompositing, isTrue);
+        expect(root.needsCompositing, isFalse);
 
         // On non-Fuchsia platforms, we composite physical shape layers
         root.elevation = 1.0;
         pumpFrame(phase: EnginePhase.composite);
-        expect(root.needsCompositing, isTrue);
+        expect(root.needsCompositing, isFalse);
 
         root.elevation = 0.0;
         pumpFrame(phase: EnginePhase.composite);
-        expect(root.needsCompositing, isTrue);
+        expect(root.needsCompositing, isFalse);
       }
       debugDefaultTargetPlatformOverride = null;
     });
@@ -287,7 +252,9 @@
   test('RenderOpacity reuses its layer', () {
     _testLayerReuse<OpacityLayer>(RenderOpacity(
       opacity: 0.5,  // must not be 0 or 1.0. Otherwise, it won't create a layer
-      child: RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
+      child: RenderRepaintBoundary(
+        child: RenderSizedBox(const Size(1.0, 1.0)),
+      ), // size doesn't matter
     ));
   });
 
@@ -353,9 +320,7 @@
   test('RenderClipRect reuses its layer', () {
     _testLayerReuse<ClipRectLayer>(RenderClipRect(
       clipper: _TestRectClipper(),
-      // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1.0, 1.0)),
       ), // size doesn't matter
     ));
@@ -364,9 +329,7 @@
   test('RenderClipRRect reuses its layer', () {
     _testLayerReuse<ClipRRectLayer>(RenderClipRRect(
       clipper: _TestRRectClipper(),
-      // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1.0, 1.0)),
       ), // size doesn't matter
     ));
@@ -375,9 +338,7 @@
   test('RenderClipOval reuses its layer', () {
     _testLayerReuse<ClipPathLayer>(RenderClipOval(
       clipper: _TestRectClipper(),
-      // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1.0, 1.0)),
       ), // size doesn't matter
     ));
@@ -386,32 +347,28 @@
   test('RenderClipPath reuses its layer', () {
     _testLayerReuse<ClipPathLayer>(RenderClipPath(
       clipper: _TestPathClipper(),
-      // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1.0, 1.0)),
       ), // size doesn't matter
     ));
   });
 
   test('RenderPhysicalModel reuses its layer', () {
-    _testLayerReuse<PhysicalModelLayer>(RenderPhysicalModel(
+    _testLayerReuse<ClipRRectLayer>(RenderPhysicalModel(
+      clipBehavior: Clip.hardEdge,
       color: const Color.fromRGBO(0, 0, 0, 1.0),
-      // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1.0, 1.0)),
       ), // size doesn't matter
     ));
   });
 
   test('RenderPhysicalShape reuses its layer', () {
-    _testLayerReuse<PhysicalModelLayer>(RenderPhysicalShape(
+    _testLayerReuse<ClipPathLayer>(RenderPhysicalShape(
       clipper: _TestPathClipper(),
+      clipBehavior: Clip.hardEdge,
       color: const Color.fromRGBO(0, 0, 0, 1.0),
-      // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1.0, 1.0)),
       ), // size doesn't matter
     ));
@@ -421,9 +378,7 @@
     _testLayerReuse<TransformLayer>(RenderTransform(
       // Use a 3D transform to force compositing.
       transform: Matrix4.rotationX(0.1),
-      // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1.0, 1.0)),
       ), // size doesn't matter
     ));
@@ -434,8 +389,7 @@
       fit: BoxFit.cover,
       clipBehavior: Clip.hardEdge,
       // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(100.0, 200.0)),
       ), // size doesn't matter
     ));
@@ -445,8 +399,7 @@
     _testLayerReuse<TransformLayer>(RenderFittedBox(
       fit: BoxFit.fill,
       // Inject opacity under the clip to force compositing.
-      child: RenderOpacity(
-        opacity: 0.5,
+      child: RenderRepaintBoundary(
         child: RenderSizedBox(const Size(1, 1)),
       ), // size doesn't matter
     ));
@@ -768,7 +721,7 @@
   expect(L, isNot(Layer));
   expect(renderObject.debugLayer, null);
   layout(renderObject, phase: EnginePhase.paint, constraints: BoxConstraints.tight(const Size(10, 10)));
-  final Layer layer = renderObject.debugLayer!;
+  final Layer? layer = renderObject.debugLayer;
   expect(layer, isA<L>());
   expect(layer, isNotNull);
 
diff --git a/packages/flutter/test/widgets/mouse_region_test.dart b/packages/flutter/test/widgets/mouse_region_test.dart
index 7da9407..929fe1b 100644
--- a/packages/flutter/test/widgets/mouse_region_test.dart
+++ b/packages/flutter/test/widgets/mouse_region_test.dart
@@ -793,7 +793,7 @@
     await tester.pumpWidget(
       MouseRegion(
         onEnter: (PointerEnterEvent _) {},
-        child: const Opacity(opacity: 0.5, child: Placeholder()),
+        child: const RepaintBoundary(child: Placeholder()),
       ),
     );
 
diff --git a/packages/flutter/test/widgets/nested_scroll_view_test.dart b/packages/flutter/test/widgets/nested_scroll_view_test.dart
index 62a257e..b29488d 100644
--- a/packages/flutter/test/widgets/nested_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/nested_scroll_view_test.dart
@@ -591,35 +591,28 @@
       )),
     );
 
-    PhysicalModelLayer? _dfsFindPhysicalLayer(ContainerLayer layer) {
-      expect(layer, isNotNull);
-      Layer? child = layer.firstChild;
-      while (child != null) {
-        if (child is PhysicalModelLayer) {
-          return child;
+    Object? _dfsFindPhysicalLayer(RenderObject object) {
+      expect(object, isNotNull);
+      if (object is RenderPhysicalModel || object is RenderPhysicalShape) {
+        return object;
+      }
+      final List<RenderObject> children = <RenderObject>[];
+      object.visitChildren(children.add);
+      for (final RenderObject child in children) {
+        final Object? result = _dfsFindPhysicalLayer(child);
+        if (result != null) {
+          return result;
         }
-        if (child is ContainerLayer) {
-          Layer? innerChild = child.firstChild;
-          while (innerChild != null) {
-            if (innerChild is ContainerLayer) {
-              final PhysicalModelLayer? candidate = _dfsFindPhysicalLayer(innerChild);
-                if (candidate != null) {
-                  return candidate;
-                }
-              }
-            innerChild = innerChild.nextSibling;
-          }
-        }
-        child = child.nextSibling;
       }
       return null;
     }
 
-    final ContainerLayer nestedScrollViewLayer = find.byType(NestedScrollView).evaluate().first.renderObject!.debugLayer!;
+    final RenderObject nestedScrollViewLayer = find.byType(NestedScrollView).evaluate().first.renderObject!;
     void _checkPhysicalLayer({required double elevation}) {
-      final PhysicalModelLayer? layer = _dfsFindPhysicalLayer(nestedScrollViewLayer);
-      expect(layer, isNotNull);
-      expect(layer!.elevation, equals(elevation));
+      final dynamic physicalModel = _dfsFindPhysicalLayer(nestedScrollViewLayer);
+      expect(physicalModel, isNotNull);
+      // ignore: avoid_dynamic_calls
+      expect(physicalModel.elevation, equals(elevation));
     }
 
     int expectedBuildCount = 0;
diff --git a/packages/flutter/test/widgets/physical_model_test.dart b/packages/flutter/test/widgets/physical_model_test.dart
index ccd8698..4591603 100644
--- a/packages/flutter/test/widgets/physical_model_test.dart
+++ b/packages/flutter/test/widgets/physical_model_test.dart
@@ -43,30 +43,6 @@
     expect(renderPhysicalShape.clipBehavior, equals(Clip.antiAlias));
   });
 
-  testWidgets('PhysicalModel - creates a physical model layer when it needs compositing', (WidgetTester tester) async {
-    debugDisableShadows = false;
-    await tester.pumpWidget(
-      MaterialApp(
-        home: PhysicalModel(
-          color: Colors.grey,
-          shadowColor: Colors.red,
-          elevation: 1.0,
-          child: Material(child: TextField(controller: TextEditingController())),
-        ),
-      ),
-    );
-    await tester.pump();
-
-    final RenderPhysicalModel renderPhysicalModel = tester.allRenderObjects.whereType<RenderPhysicalModel>().first;
-    expect(renderPhysicalModel.needsCompositing, true);
-
-    final PhysicalModelLayer physicalModelLayer = tester.layers.whereType<PhysicalModelLayer>().first;
-    expect(physicalModelLayer.shadowColor, Colors.red);
-    expect(physicalModelLayer.color, Colors.grey);
-    expect(physicalModelLayer.elevation, 1.0);
-    debugDisableShadows = true;
-  });
-
   testWidgets('PhysicalModel - clips when overflows and elevation is 0', (WidgetTester tester) async {
     const Key key = Key('test');
     await tester.pumpWidget(
diff --git a/packages/flutter/test/widgets/transform_test.dart b/packages/flutter/test/widgets/transform_test.dart
index 621a4ee..a21056e 100644
--- a/packages/flutter/test/widgets/transform_test.dart
+++ b/packages/flutter/test/widgets/transform_test.dart
@@ -240,8 +240,7 @@
           child: ClipRect(
             child: Transform(
               transform: Matrix4.diagonal3Values(0.5, 0.5, 1.0),
-              child: Opacity(
-                opacity: 0.9,
+              child: RepaintBoundary(
                 child: Container(
                   color: const Color(0xFF00FF00),
                 ),
@@ -265,7 +264,7 @@
     await tester.pumpWidget(
       Transform.rotate(
         angle: math.pi / 2.0,
-        child: Opacity(opacity: 0.5, child: Container()),
+        child: RepaintBoundary(child: Container()),
       ),
     );
 
@@ -305,7 +304,7 @@
     await tester.pumpWidget(
       Transform.translate(
         offset: const Offset(100.0, 50.0),
-        child: Opacity(opacity: 0.5, child: Container()),
+        child: RepaintBoundary(child: Container()),
       ),
     );
 
@@ -320,7 +319,7 @@
     await tester.pumpWidget(
       Transform.scale(
         scale: 2.0,
-        child: Opacity(opacity: 0.5, child: Container()),
+        child: RepaintBoundary(child: Container()),
       ),
     );