Multi-layer blur support

Test: open power menu on top of notification shade
Test: LayerTypeAndRenderTypeTransaction_test
Change-Id: I967a5385199a3ff76928a073471d052f7d6c87dd
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e523836..e11b59f 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -984,18 +984,19 @@
     }
 
     std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
-    // Let's find the topmost layer requesting background blur (if any.)
-    // Blurs in multiple layers are not supported, given the cost of the shader.
-    const LayerSettings* blurLayer = nullptr;
+    // Gathering layers that requested blur, we'll need them to decide when to render to an
+    // offscreen buffer, and when to render to the native buffer.
+    std::deque<const LayerSettings*> blurLayers;
     if (CC_LIKELY(mBlurFilter != nullptr)) {
-        for (auto const layer : layers) {
+        for (auto layer : layers) {
             if (layer->backgroundBlurRadius > 0) {
-                blurLayer = layer;
+                blurLayers.push_back(layer);
             }
         }
     }
+    const auto blurLayersSize = blurLayers.size();
 
-    if (blurLayer == nullptr) {
+    if (blurLayersSize == 0) {
         fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
         if (fbo->getStatus() != NO_ERROR) {
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
@@ -1006,7 +1007,8 @@
         setViewportAndProjection(display.physicalDisplay, display.clip);
     } else {
         setViewportAndProjection(display.physicalDisplay, display.clip);
-        auto status = mBlurFilter->setAsDrawTarget(display, blurLayer->backgroundBlurRadius);
+        auto status =
+                mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
         if (status != NO_ERROR) {
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
                   buffer->handle);
@@ -1039,7 +1041,9 @@
                         .setCropCoords(2 /* size */)
                         .build();
     for (auto const layer : layers) {
-        if (blurLayer == layer) {
+        if (blurLayers.size() > 0 && blurLayers.front() == layer) {
+            blurLayers.pop_front();
+
             auto status = mBlurFilter->prepare();
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
@@ -1048,18 +1052,26 @@
                 return status;
             }
 
-            fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
-                                                                  useFramebufferCache);
-            status = fbo->getStatus();
+            if (blurLayers.size() == 0) {
+                // Done blurring, time to bind the native FBO and render our blur onto it.
+                fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+                                                                      useFramebufferCache);
+                status = fbo->getStatus();
+                setViewportAndProjection(display.physicalDisplay, display.clip);
+            } else {
+                // There's still something else to blur, so let's keep rendering to our FBO
+                // instead of to the display.
+                status = mBlurFilter->setAsDrawTarget(display,
+                                                      blurLayers.front()->backgroundBlurRadius);
+            }
             if (status != NO_ERROR) {
                 ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                       buffer->handle);
                 checkErrors("Can't bind native framebuffer");
                 return status;
             }
-            setViewportAndProjection(display.physicalDisplay, display.clip);
 
-            status = mBlurFilter->render();
+            status = mBlurFilter->render(blurLayersSize > 1);
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->handle);
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index f8b0a7a..b1a84bd 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -96,13 +96,17 @@
     mEngine.checkErrors("Drawing blur mesh");
 }
 
-status_t BlurFilter::render() {
+status_t BlurFilter::render(bool multiPass) {
     ATRACE_NAME("BlurFilter::render");
 
     // Now let's scale our blur up. It will be interpolated with the larger composited
     // texture for the first frames, to hide downscaling artifacts.
     GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
-    if (mix >= 1) {
+
+    // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
+    // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
+    // as large as the screen size.
+    if (mix >= 1 || multiPass) {
         mBlurredFbo.bindAsReadBuffer();
         glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
                           mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index 67b3895..52dc8aa 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -45,7 +45,7 @@
     // Execute blur passes, rendering to offscreen texture.
     virtual status_t prepare() = 0;
     // Render blur to the bound framebuffer (screen).
-    status_t render();
+    status_t render(bool multiPass);
 
 protected:
     uint32_t mRadius;
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 3bbd12a..9e619ca 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -279,6 +279,48 @@
                       50 /* tolerance */);
 }
 
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusOnMultipleLayers) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    auto size = 256;
+    auto center = size / 2;
+    auto blurRadius = 50;
+
+    sp<SurfaceControl> backgroundLayer;
+    ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+    sp<SurfaceControl> leftLayer;
+    ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+    sp<SurfaceControl> blurLayer1;
+    auto centralSquareSize = size / 2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer1 =
+                                    createLayer("blur1", centralSquareSize, centralSquareSize));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer1, Color::BLUE, centralSquareSize, centralSquareSize));
+
+    sp<SurfaceControl> blurLayer2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer2 = createLayer("blur2", size, size));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer2, Color::TRANSPARENT, centralSquareSize, centralSquareSize));
+
+    Transaction()
+            .setBackgroundBlurRadius(blurLayer1, blurRadius)
+            .setBackgroundBlurRadius(blurLayer2, blurRadius)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(center - 5, center - 5, center, center), Color{100, 100, 100, 255},
+                      40 /* tolerance */);
+}
+
 TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
     sp<SurfaceControl> bufferLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));