Port background blurs to SkiaRE

Bug: 164223050
Test: manual
Test: atest LayerTypeAndRenderTypeTransactionTest
Change-Id: I459780d9fd408865d1d9713f37bf308f07c4fdfc
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 1ccbff1..eb967ce 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -77,6 +77,7 @@
     srcs: [
         "skia/SkiaRenderEngine.cpp",
         "skia/SkiaGLRenderEngine.cpp",
+        "skia/filters/BlurFilter.cpp",
     ],
 }
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index cb752b0..0fed08b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -29,12 +29,14 @@
 #include <utils/Trace.h>
 #include "../gl/GLExtensions.h"
 #include "SkiaGLRenderEngine.h"
+#include "filters/BlurFilter.h"
 
 #include <GrContextOptions.h>
 #include <gl/GrGLInterface.h>
 
 #include <SkCanvas.h>
 #include <SkImage.h>
+#include <SkImageFilters.h>
 #include <SkShadowUtils.h>
 #include <SkSurface.h>
 
@@ -194,7 +196,7 @@
 
     // initialize the renderer while GL is current
     std::unique_ptr<SkiaGLRenderEngine> engine =
-            std::make_unique<SkiaGLRenderEngine>(display, config, ctxt, placeholder,
+            std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder,
                                                  protectedContext, protectedPlaceholder);
 
     ALOGI("OpenGL ES informations:");
@@ -247,9 +249,9 @@
     return config;
 }
 
-SkiaGLRenderEngine::SkiaGLRenderEngine(EGLDisplay display, EGLConfig config, EGLContext ctxt,
-                                       EGLSurface placeholder, EGLContext protectedContext,
-                                       EGLSurface protectedPlaceholder)
+SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+                                       EGLConfig config, EGLContext ctxt, EGLSurface placeholder,
+                                       EGLContext protectedContext, EGLSurface protectedPlaceholder)
       : mEGLDisplay(display),
         mEGLConfig(config),
         mEGLContext(ctxt),
@@ -273,6 +275,10 @@
     options.fPreferExternalImagesOverES3 = true;
     options.fDisableDistanceFieldPaths = true;
     mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options);
+
+    if (args.supportsBackgroundBlur) {
+        mBlurFilter = new BlurFilter();
+    }
 }
 
 base::unique_fd SkiaGLRenderEngine::flush() {
@@ -408,6 +414,11 @@
         const auto& bounds = layer->geometry.boundaries;
         const auto dest = getSkRect(bounds);
 
+        if (layer->backgroundBlurRadius > 0) {
+            ATRACE_NAME("BackgroundBlur");
+            mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+        }
+
         if (layer->source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
             const auto& item = layer->source.buffer;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 3da7f25..898dc54 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -28,6 +28,7 @@
 #include <SkSurface.h>
 
 #include "SkiaRenderEngine.h"
+#include "filters/BlurFilter.h"
 
 namespace android {
 namespace renderengine {
@@ -36,8 +37,8 @@
 class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
 public:
     static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
-    SkiaGLRenderEngine(EGLDisplay display, EGLConfig config, EGLContext ctxt,
-                       EGLSurface placeholder, EGLContext protectedContext,
+    SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
+                       EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext,
                        EGLSurface protectedPlaceholder);
     ~SkiaGLRenderEngine() override{};
 
@@ -78,6 +79,7 @@
     EGLSurface mPlaceholderSurface;
     EGLContext mProtectedEGLContext;
     EGLSurface mProtectedPlaceholderSurface;
+    BlurFilter* mBlurFilter = nullptr;
 
     // Cache of GL images that we'll store per GraphicBuffer ID
     std::unordered_map<uint64_t, sk_sp<SkImage>> mImageCache GUARDED_BY(mRenderingMutex);
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
new file mode 100644
index 0000000..eb791a7
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#include "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+BlurFilter::BlurFilter() {
+    SkString blurString(R"(
+        in shader input;
+        uniform float in_inverseScale;
+        uniform float2 in_blurOffset;
+
+        half4 main(float2 xy) {
+            float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
+
+            float4 c = float4(sample(input, scaled_xy));
+            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x,  in_blurOffset.y)));
+            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)));
+            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x,  in_blurOffset.y)));
+            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)));
+
+            return half4(c.rgb * 0.2, 1.0);
+        }
+    )");
+
+    auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+    if (!blurEffect) {
+        LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+    }
+    mBlurEffect = std::move(blurEffect);
+}
+
+void BlurFilter::draw(SkCanvas* canvas, 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.
+    float tmpRadius = (float)blurRadius / 6.0f;
+    float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+    float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+    SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)input->width() * kInputScale,
+                                                        (float)input->height() * kInputScale);
+    auto drawSurface = canvas->makeSurface(scaledInfo);
+
+    const float stepX = radiusByPasses;
+    const float stepY = radiusByPasses;
+
+    // start by drawing and downscaling and doing the first blur pass
+    SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+    blurBuilder.child("input") = input->makeImageSnapshot()->makeShader();
+    blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
+    blurBuilder.uniform("in_blurOffset") =
+            SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
+
+    {
+        // limit the lifetime of the input surface's snapshot to ensure that it goes out of
+        // scope before the surface is written into to avoid any copy-on-write behavior.
+        SkPaint paint;
+        paint.setShader(blurBuilder.makeShader(nullptr, false));
+        paint.setFilterQuality(kLow_SkFilterQuality);
+        drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
+        blurBuilder.child("input") = nullptr;
+    }
+
+    // And now we'll ping pong between our surfaces, to accumulate the result of various offsets.
+    auto lastDrawTarget = drawSurface;
+    if (numberOfPasses > 1) {
+        auto readSurface = drawSurface;
+        drawSurface = canvas->makeSurface(scaledInfo);
+
+        for (auto i = 1; i < numberOfPasses; i++) {
+            const float stepScale = (float)i * kInputScale;
+
+            blurBuilder.child("input") = readSurface->makeImageSnapshot()->makeShader();
+            blurBuilder.uniform("in_inverseScale") = 1.0f;
+            blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
+
+            SkPaint paint;
+            paint.setShader(blurBuilder.makeShader(nullptr, false));
+            paint.setFilterQuality(kLow_SkFilterQuality);
+            drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
+
+            // Swap buffers for next iteration
+            auto tmp = drawSurface;
+            drawSurface = readSurface;
+            readSurface = tmp;
+            blurBuilder.child("input") = nullptr;
+        }
+        lastDrawTarget = readSurface;
+    }
+
+    drawSurface->flushAndSubmit();
+
+    // 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);
+    }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
new file mode 100644
index 0000000..94b3673
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class BlurFilter {
+public:
+    // Downsample FBO to improve performance
+    static constexpr float kInputScale = 0.25f;
+    // Downsample scale factor used to improve performance
+    static constexpr float kInverseInputScale = 1.0f / kInputScale;
+    // Maximum number of render passes
+    static constexpr uint32_t kMaxPasses = 4;
+    // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+    // image, up to this radius.
+    static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+    explicit BlurFilter();
+    virtual ~BlurFilter(){};
+
+    // Execute blur passes, rendering to a canvas.
+    void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const;
+
+private:
+    sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android