Implement blur region in RenderEngine

Blur regions should be be piped through SurfaceFlinger
all the way down to the compositor, in order to render
blurs.

It's also necessary to cache them if multiple regions require
the same blur radius.

Test: manual
Test: atest SurfaceInterceptor_test
Test: atest OutputUpdateAndWriteCompositionStateTest
Bug: 159712515
Change-Id: Id4759f65eb2522a80e9328062fe641fe29786a30
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 4333126..9722f36 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -147,6 +147,20 @@
     SAFE_PARCEL(output.writeUint32, fixedTransformHint);
     SAFE_PARCEL(output.writeUint64, frameNumber);
     SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId);
+
+    SAFE_PARCEL(output.writeUint32, blurRegions.size());
+    for (auto region : blurRegions) {
+        SAFE_PARCEL(output.writeUint32, region.blurRadius);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR);
+        SAFE_PARCEL(output.writeFloat, region.alpha);
+        SAFE_PARCEL(output.writeInt32, region.left);
+        SAFE_PARCEL(output.writeInt32, region.top);
+        SAFE_PARCEL(output.writeInt32, region.right);
+        SAFE_PARCEL(output.writeInt32, region.bottom);
+    }
     return NO_ERROR;
 }
 
@@ -252,6 +266,24 @@
     fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
     SAFE_PARCEL(input.readUint64, &frameNumber);
     SAFE_PARCEL(input.readInt64, &frameTimelineVsyncId);
+
+    uint32_t numRegions = 0;
+    SAFE_PARCEL(input.readUint32, &numRegions);
+    blurRegions.clear();
+    for (uint32_t i = 0; i < numRegions; i++) {
+        BlurRegion region;
+        SAFE_PARCEL(input.readUint32, &region.blurRadius);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusTL);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusTR);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusBL);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusBR);
+        SAFE_PARCEL(input.readFloat, &region.alpha);
+        SAFE_PARCEL(input.readInt32, &region.left);
+        SAFE_PARCEL(input.readInt32, &region.top);
+        SAFE_PARCEL(input.readInt32, &region.right);
+        SAFE_PARCEL(input.readInt32, &region.bottom);
+        blurRegions.push_back(region);
+    }
     return NO_ERROR;
 }
 
@@ -375,6 +407,10 @@
         what |= eBackgroundBlurRadiusChanged;
         backgroundBlurRadius = other.backgroundBlurRadius;
     }
+    if (other.what & eBlurRegionsChanged) {
+        what |= eBlurRegionsChanged;
+        blurRegions = other.blurRegions;
+    }
     if (other.what & eDeferTransaction_legacy) {
         what |= eDeferTransaction_legacy;
         barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 32f9695..039e900 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1017,6 +1017,18 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions(
+        const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eBlurRegionsChanged;
+    s->blurRegions = blurRegions;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(
         const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index ff3a87d..a73d9a6 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -51,6 +51,7 @@
 #include <gui/LayerMetadata.h>
 #include <gui/SurfaceControl.h>
 #include <math/vec3.h>
+#include <ui/BlurRegion.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -128,6 +129,7 @@
         eFixedTransformHintChanged = 0x200'00000000,
         eFrameNumberChanged = 0x400'00000000,
         eFrameTimelineVsyncChanged = 0x800'00000000,
+        eBlurRegionsChanged = 0x1000'00000000,
     };
 
     layer_state_t();
@@ -186,6 +188,7 @@
     int32_t api;
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
+    std::vector<BlurRegion> blurRegions;
 
 #ifndef NO_INPUT
     sp<InputWindowHandle> inputHandle = new InputWindowHandle();
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 13abed2..73909a3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -29,6 +29,7 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
+#include <ui/BlurRegion.h>
 #include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
@@ -439,6 +440,8 @@
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
                                              int backgroundBlurRadius);
+        Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
+                                    const std::vector<BlurRegion>& regions);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
         Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
         // Defers applying any changes made in this transaction until the Layer
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 95e9367..d8d989e 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -21,6 +21,7 @@
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <renderengine/Texture.h>
+#include <ui/BlurRegion.h>
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
@@ -151,6 +152,8 @@
     ShadowSettings shadow;
 
     int backgroundBlurRadius = 0;
+
+    std::vector<BlurRegion> blurRegions;
 };
 
 // Keep in sync with custom comparison function in
@@ -182,7 +185,29 @@
             lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
 }
 
+static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
+    return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
+            lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
+            lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
+            lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
+            lhs.bottom == rhs.bottom;
+}
+
+static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
+    return !(lhs == rhs);
+}
+
 static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+    if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
+        return false;
+    }
+    const auto size = lhs.blurRegions.size();
+    for (size_t i = 0; i < size; i++) {
+        if (lhs.blurRegions[i] != rhs.blurRegions[i]) {
+            return false;
+        }
+    }
+
     return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 69ad189..03c8e80 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -23,6 +23,14 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <GLES2/gl2.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
+#include "filters/BlurFilter.h"
+
 #include <GrContextOptions.h>
 #include <SkCanvas.h>
 #include <SkColorFilter.h>
@@ -500,10 +508,25 @@
         SkPaint paint;
         const auto& bounds = layer->geometry.boundaries;
         const auto dest = getSkRect(bounds);
+        std::unordered_map<uint32_t, sk_sp<SkSurface>> cachedBlurs;
 
-        if (layer->backgroundBlurRadius > 0) {
-            ATRACE_NAME("BackgroundBlur");
-            mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+        if (mBlurFilter) {
+            if (layer->backgroundBlurRadius > 0) {
+                ATRACE_NAME("BackgroundBlur");
+                auto blurredSurface =
+                        mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+                cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
+            }
+            if (layer->blurRegions.size() > 0) {
+                for (auto region : layer->blurRegions) {
+                    if (cachedBlurs[region.blurRadius]) {
+                        continue;
+                    }
+                    ATRACE_NAME("BlurRegion");
+                    auto blurredSurface = mBlurFilter->generate(canvas, surface, region.blurRadius);
+                    cachedBlurs[region.blurRadius] = blurredSurface;
+                }
+            }
         }
 
         if (layer->source.buffer.buffer) {
@@ -571,7 +594,11 @@
         } else {
             ATRACE_NAME("DrawColor");
             const auto color = layer->source.solidColor;
-            paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
+            paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r,
+                                                       .fG = color.g,
+                                                       .fB = color.b,
+                                                       layer->alpha},
+                                             nullptr));
         }
 
         paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
@@ -580,6 +607,10 @@
         canvas->save();
         canvas->concat(getSkM44(layer->geometry.positionTransform));
 
+        for (const auto effectRegion : layer->blurRegions) {
+            drawBlurRegion(canvas, effectRegion, dest, cachedBlurs[effectRegion.blurRadius]);
+        }
+
         if (layer->shadow.length > 0) {
             const auto rect = layer->geometry.roundedCornersRadius > 0
                     ? getSkRect(layer->geometry.roundedCornersCrop)
@@ -592,6 +623,7 @@
         } else {
             canvas->drawRect(dest, paint);
         }
+
         canvas->restore();
     }
     {
@@ -678,6 +710,34 @@
                               flags);
 }
 
+void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+                                        const SkRect& layerBoundaries,
+                                        sk_sp<SkSurface> blurredSurface) {
+    ATRACE_CALL();
+    SkPaint paint;
+    paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
+    const auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+                                       effectRegion.bottom);
+
+    const auto matrix = mBlurFilter->getShaderMatrix(
+            SkMatrix::MakeTrans(layerBoundaries.left(), layerBoundaries.top()));
+    paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(matrix));
+
+    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
+        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
+        const SkVector radii[4] =
+                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
+                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
+                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
+                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
+        SkRRect roundedRect;
+        roundedRect.setRectRadii(rect, radii);
+        canvas->drawRRect(roundedRect, paint);
+    } else {
+        canvas->drawRect(rect, paint);
+    }
+}
+
 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
                                                 EGLContext shareContext, bool useContextPriority,
                                                 Protection protection) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index ed4ba11..0143445 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -75,6 +75,8 @@
     bool waitFence(base::unique_fd fenceFd);
     void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
                     const ShadowSettings& shadowSettings);
+    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerBounds,
+                        sk_sp<SkSurface> blurrendSurface);
 
     EGLDisplay mEGLDisplay;
     EGLConfig mEGLConfig;
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 8704b3a..f6a316f 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -55,8 +55,10 @@
     mBlurEffect = std::move(blurEffect);
 }
 
-void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t blurRadius) const {
+sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                                      const uint32_t blurRadius) const {
     ATRACE_CALL();
+
     // Kawase is an approximation of Gaussian, but it behaves differently from it.
     // A radius transformation is required for approximating them, and also to introduce
     // non-integer steps, necessary to smoothly interpolate large radii.
@@ -111,25 +113,35 @@
             drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
 
             // Swap buffers for next iteration
-            auto tmp = drawSurface;
+            const auto tmp = drawSurface;
             drawSurface = readSurface;
             readSurface = tmp;
             blurBuilder.child("input") = nullptr;
         }
         lastDrawTarget = readSurface;
     }
+    lastDrawTarget->flushAndSubmit();
+    return lastDrawTarget;
+}
 
-    drawSurface->flushAndSubmit();
+sk_sp<SkSurface> BlurFilter::draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                                  const uint32_t blurRadius) const {
+    ATRACE_CALL();
+    auto surface = generate(canvas, input, blurRadius);
 
-    // do the final composition, with alpha blending to hide downscaling artifacts.
-    {
-        SkPaint paint;
-        paint.setShader(lastDrawTarget->makeImageSnapshot()->makeShader(
-                SkMatrix::MakeScale(kInverseInputScale)));
-        paint.setFilterQuality(kLow_SkFilterQuality);
-        paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
-        canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
-    }
+    SkPaint paint;
+    const auto image = surface->makeImageSnapshot();
+    paint.setShader(image->makeShader(SkMatrix::MakeScale(kInverseInputScale)));
+    paint.setFilterQuality(kLow_SkFilterQuality);
+    paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
+    canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
+    return surface;
+}
+
+SkMatrix BlurFilter::getShaderMatrix(const SkMatrix& transformMatrix) const {
+    SkMatrix matrix;
+    matrix.setConcat(transformMatrix, SkMatrix::MakeScale(kInverseInputScale));
+    return matrix;
 }
 
 } // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 94b3673..6f973d7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -47,8 +47,14 @@
     explicit BlurFilter();
     virtual ~BlurFilter(){};
 
-    // Execute blur passes, rendering to a canvas.
-    void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const;
+    // Execute blur, saving it to a texture
+    sk_sp<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                              const uint32_t radius) const;
+    // Same as generate but also drawing to the screen
+    sk_sp<SkSurface> draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                          const uint32_t radius) const;
+    // Returns a matrix that should be applied to the blur shader
+    SkMatrix getShaderMatrix(const SkMatrix& transformMatrix) const;
 
 private:
     sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h
new file mode 100644
index 0000000..c5a5d47
--- /dev/null
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+namespace android {
+
+struct BlurRegion {
+    uint32_t blurRadius;
+    float cornerRadiusTL;
+    float cornerRadiusTR;
+    float cornerRadiusBL;
+    float cornerRadiusBR;
+    float alpha;
+    int left;
+    int top;
+    int right;
+    int bottom;
+};
+
+} // namespace android
\ No newline at end of file