Remove unnecessary drawing commands when drawing shadows.

When blurring behind a shadow, this will ensure that it
is only blurred once. It does so by updating
Layer::prepareShadowClientComposition to add ShadowSettings
to the input layer, rather than adding an extra layer.

Collapsing the shadow layer into the same layer as the caster will
result in undefined behavior in the old GLESRenderEngine.

Bug: 188050128
Bug: 175915334
Test: atest SurfaceFlinger_test and atest librenderengine_test
Change-Id: I5b74ef9659b519fcc6b89dfd873c35fc579fddd7
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 63ed1ba..715f3f8 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -107,6 +107,9 @@
  * material design guidelines.
  */
 struct ShadowSettings {
+    // Boundaries of the shadow.
+    FloatRect boundaries = FloatRect();
+
     // Color to the ambient shadow. The alpha is premultiplied.
     vec4 ambientColor = vec4();
 
@@ -150,6 +153,10 @@
     // True if blending will be forced to be disabled.
     bool disableBlending = false;
 
+    // If true, then this layer casts a shadow and/or blurs behind it, but it does
+    // not otherwise draw any of the layer's other contents.
+    bool skipContentDraw = false;
+
     ShadowSettings shadow;
 
     int backgroundBlurRadius = 0;
@@ -189,9 +196,10 @@
 }
 
 static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
-    return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
-            lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
-            lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+    return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
+            lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
+            lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
+            lhs.casterIsTranslucent == rhs.casterIsTranslucent;
 }
 
 static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
@@ -208,7 +216,8 @@
     return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
-            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+            lhs.disableBlending == rhs.disableBlending &&
+            lhs.skipContentDraw == rhs.skipContentDraw && lhs.shadow == rhs.shadow &&
             lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
             lhs.blurRegionTransform == rhs.blurRegionTransform &&
             lhs.stretchEffect == rhs.stretchEffect;
@@ -251,6 +260,8 @@
 
 static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
     *os << "ShadowSettings {";
+    *os << "\n    .boundaries = ";
+    PrintTo(settings.boundaries, os);
     *os << "\n    .ambientColor = " << settings.ambientColor;
     *os << "\n    .spotColor = " << settings.spotColor;
     *os << "\n    .lightPos = " << settings.lightPos;
@@ -286,6 +297,7 @@
     PrintTo(settings.sourceDataspace, os);
     *os << "\n    .colorTransform = " << settings.colorTransform;
     *os << "\n    .disableBlending = " << settings.disableBlending;
+    *os << "\n    .skipContentDraw = " << settings.skipContentDraw;
     *os << "\n    .backgroundBlurRadius = " << settings.backgroundBlurRadius;
     for (auto blurRegion : settings.blurRegions) {
         *os << "\n";
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 6cc3d37..6d749ed 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -846,7 +846,9 @@
         // Layers have a local transform that should be applied to them
         canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
 
-        const auto [bounds, roundRectClip] = getBoundsAndClip(layer);
+        const auto [bounds, roundRectClip] =
+                getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop,
+                                 layer->geometry.roundedCornersRadius);
         if (mBlurFilter && layerHasBlur(layer)) {
             std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
 
@@ -895,14 +897,21 @@
             }
         }
 
-        // Shadows are assumed to live only on their own layer - it's not valid
-        // to draw the boundary rectangles when there is already a caster shadow
-        // TODO(b/175915334): consider relaxing this restriction to enable more flexible
-        // composition - using a well-defined invalid color is long-term less error-prone.
         if (layer->shadow.length > 0) {
             // This would require a new parameter/flag to SkShadowUtils::DrawShadow
             LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
 
+            SkRRect shadowBounds, shadowClip;
+            if (layer->geometry.boundaries == layer->shadow.boundaries) {
+                shadowBounds = bounds;
+                shadowClip = roundRectClip;
+            } else {
+                std::tie(shadowBounds, shadowClip) =
+                        getBoundsAndClip(layer->shadow.boundaries,
+                                         layer->geometry.roundedCornersCrop,
+                                         layer->geometry.roundedCornersRadius);
+            }
+
             // Technically, if bounds is a rect and roundRectClip is not empty,
             // it means that the bounds and roundedCornersCrop were different
             // enough that we should intersect them to find the proper shadow.
@@ -910,9 +919,8 @@
             // not match due to rounding errors. Draw the rounded version, which
             // looks more like the intent.
             const auto& rrect =
-                    bounds.isRect() && !roundRectClip.isEmpty() ? roundRectClip : bounds;
+                    shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
             drawShadow(canvas, rrect, layer->shadow);
-            continue;
         }
 
         const bool requiresLinearEffect = layer->colorTransform != mat4() ||
@@ -922,8 +930,9 @@
                  display.sdrWhitePointNits != display.maxLuminance);
 
         // quick abort from drawing the remaining portion of the layer
-        if (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
-            (!displayColorTransform || displayColorTransform->isAlphaUnchanged())) {
+        if (layer->skipContentDraw ||
+            (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
+             (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
             continue;
         }
 
@@ -1093,11 +1102,11 @@
     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
 }
 
-inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(
-        const LayerSettings* layer) {
-    const auto bounds = getSkRect(layer->geometry.boundaries);
-    const auto crop = getSkRect(layer->geometry.roundedCornersCrop);
-    const auto cornerRadius = layer->geometry.roundedCornersRadius;
+inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
+                                                                        const FloatRect& cropRect,
+                                                                        const float cornerRadius) {
+    const SkRect bounds = getSkRect(boundsRect);
+    const SkRect crop = getSkRect(cropRect);
 
     SkRRect clip;
     if (cornerRadius > 0) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 1f528b7..4265c08 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -88,7 +88,8 @@
                                                          int hwcFormat, Protection protection);
     inline SkRect getSkRect(const FloatRect& layer);
     inline SkRect getSkRect(const Rect& layer);
-    inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const LayerSettings* layer);
+    inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
+                                                        const FloatRect& crop, float cornerRadius);
     inline bool layerHasBlur(const LayerSettings* layer);
     inline SkColor getSkColor(const vec4& color);
     inline SkM44 getSkM44(const mat4& matrix);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 72e32ed..1ca7a16 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -1265,6 +1265,7 @@
     renderengine::LayerSettings shadowLayer;
     shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     shadowLayer.geometry.boundaries = castingBounds;
+    shadowLayer.skipContentDraw = true;
     shadowLayer.alpha = 1.0f;
     ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
     shadowLayer.shadow = shadow;