diff options
4 files changed, 74 insertions, 16 deletions
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index e523836c59..e11b59ff24 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -984,18 +984,19 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, } 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, .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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, 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 f8b0a7a891..b1a84bd0c2 100644 --- a/libs/renderengine/gl/filters/BlurFilter.cpp +++ b/libs/renderengine/gl/filters/BlurFilter.cpp @@ -96,13 +96,17 @@ void BlurFilter::drawMesh(GLuint uv, GLuint position) { 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 67b3895262..52dc8aab3c 100644 --- a/libs/renderengine/gl/filters/BlurFilter.h +++ b/libs/renderengine/gl/filters/BlurFilter.h @@ -45,7 +45,7 @@ public: // 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 3bbd12a242..9e619ca8ff 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -279,6 +279,48 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadius) { 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)); |