diff options
41 files changed, 1377 insertions, 18 deletions
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index 7f2f94912e..b57409867f 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto @@ -51,6 +51,7 @@ message SurfaceChange { RelativeParentChange relative_parent = 18; DetachChildrenChange detach_children = 19; ReparentChildrenChange reparent_children = 20; + BackgroundBlurRadiusChange background_blur_radius = 21; ShadowRadiusChange shadow_radius = 22; } } @@ -73,6 +74,10 @@ message CornerRadiusChange { required float corner_radius = 1; } +message BackgroundBlurRadiusChange { + required float background_blur_radius = 1; +} + message LayerChange { required uint32 layer = 1; } diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 675aad6f81..2b5667d8fe 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -510,6 +510,14 @@ void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t, t.setCornerRadius(mLayers[id], cc.corner_radius()); } +void Replayer::setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t, + layer_id id, const BackgroundBlurRadiusChange& cc) { + ALOGV("Layer %d: Setting Background Blur Radius -- backgroundBlurRadius=%d", id, + cc.background_blur_radius()); + + t.setBackgroundBlurRadius(mLayers[id], cc.background_blur_radius()); +} + void Replayer::setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc) { ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(), diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index b5478344cb..95857e1e5b 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h @@ -94,6 +94,8 @@ class Replayer { layer_id id, const CropChange& cc); void setCornerRadius(SurfaceComposerClient::Transaction& t, layer_id id, const CornerRadiusChange& cc); + void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t, + layer_id id, const BackgroundBlurRadiusChange& cc); void setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc); void setOverrideScalingMode(SurfaceComposerClient::Transaction& t, diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 39b4d4bbb6..5547efc3ad 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -86,6 +86,7 @@ status_t layer_state_t::write(Parcel& output) const memcpy(output.writeInplace(16 * sizeof(float)), colorTransform.asArray(), 16 * sizeof(float)); output.writeFloat(cornerRadius); + output.writeUint32(backgroundBlurRadius); output.writeStrongBinder(cachedBuffer.token.promote()); output.writeUint64(cachedBuffer.id); output.writeParcelable(metadata); @@ -173,6 +174,7 @@ status_t layer_state_t::read(const Parcel& input) colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); cornerRadius = input.readFloat(); + backgroundBlurRadius = input.readUint32(); cachedBuffer.token = input.readStrongBinder(); cachedBuffer.id = input.readUint64(); input.readParcelable(&metadata); @@ -307,6 +309,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eCornerRadiusChanged; cornerRadius = other.cornerRadius; } + if (other.what & eBackgroundBlurRadiusChanged) { + what |= eBackgroundBlurRadiusChanged; + backgroundBlurRadius = other.backgroundBlurRadius; + } if (other.what & eDeferTransaction_legacy) { what |= eDeferTransaction_legacy; barrierHandle_legacy = other.barrierHandle_legacy; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 20614f8d85..69d4269fa6 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -918,6 +918,18 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCorne return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius( + const sp<SurfaceControl>& sc, int backgroundBlurRadius) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eBackgroundBlurRadiusChanged; + s->backgroundBlurRadius = backgroundBlurRadius; + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, const sp<IBinder>& handle, diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index cf641939e8..c256a09c40 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -101,6 +101,7 @@ struct layer_state_t { eColorSpaceAgnosticChanged = 0x10'00000000, eFrameRateSelectionPriority = 0x20'00000000, eFrameRateChanged = 0x40'00000000, + eBackgroundBlurRadiusChanged = 0x80'00000000, }; layer_state_t() @@ -117,6 +118,7 @@ struct layer_state_t { reserved(0), crop_legacy(Rect::INVALID_RECT), cornerRadius(0.0f), + backgroundBlurRadius(0), frameNumber_legacy(0), overrideScalingMode(-1), transform(0), @@ -163,6 +165,7 @@ struct layer_state_t { matrix22_t matrix; Rect crop_legacy; float cornerRadius; + uint32_t backgroundBlurRadius; sp<IBinder> barrierHandle_legacy; sp<IBinder> reparentHandle; uint64_t frameNumber_legacy; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 71ee82a72f..228bf33b9a 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -435,6 +435,8 @@ public: float dsdx, float dtdx, float dtdy, float dsdy); Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop); Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); + Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc, + int backgroundBlurRadius); 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/Android.bp b/libs/renderengine/Android.bp index 348377e4e3..2e3ab4c9f6 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -57,6 +57,10 @@ filegroup { "gl/ImageManager.cpp", "gl/Program.cpp", "gl/ProgramCache.cpp", + "gl/filters/BlurFilter.cpp", + "gl/filters/LensBlurFilter.cpp", + "gl/filters/GaussianBlurFilter.cpp", + "gl/filters/GenericProgram.cpp", ], } diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 0748dfb848..09659fe209 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -49,6 +49,9 @@ #include "GLShadowVertexGenerator.h" #include "Program.h" #include "ProgramCache.h" +#include "filters/BlurFilter.h" +#include "filters/GaussianBlurFilter.h" +#include "filters/LensBlurFilter.h" extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); @@ -422,6 +425,18 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp mTraceGpuCompletion = true; mFlushTracer = std::make_unique<FlushTracer>(this); } + + if (args.supportsBackgroundBlur) { + char isGaussian[PROPERTY_VALUE_MAX]; + property_get("debug.sf.gaussianBlur", isGaussian, "1"); + if (atoi(isGaussian)) { + mBlurFilter = new GaussianBlurFilter(*this); + } else { + mBlurFilter = new LensBlurFilter(*this); + } + checkErrors("BlurFilter creation"); + } + mImageManager = std::make_unique<ImageManager>(this); mDrawingBuffer = createFramebuffer(); } @@ -871,11 +886,19 @@ void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) { } void GLESRenderEngine::checkErrors() const { + checkErrors(nullptr); +} + +void GLESRenderEngine::checkErrors(const char* tag) const { do { // there could be more than one error flag GLenum error = glGetError(); if (error == GL_NO_ERROR) break; - ALOGE("GL error 0x%04x", int(error)); + if (tag == nullptr) { + ALOGE("GL error 0x%04x", int(error)); + } else { + ALOGE("GL error: %s -> 0x%04x", tag, int(error)); + } } while (true); } @@ -957,13 +980,36 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, return BAD_VALUE; } - BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache); + 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; + if (CC_LIKELY(mBlurFilter != nullptr)) { + for (auto const& layer : layers) { + if (layer.backgroundBlurRadius > 0) { + blurLayer = &layer; + } + } + } - if (fbo.getStatus() != NO_ERROR) { - ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", - buffer->handle); - checkErrors(); - return fbo.getStatus(); + if (blurLayer == nullptr) { + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache); + if (fbo->getStatus() != NO_ERROR) { + ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return fbo->getStatus(); + } + setViewportAndProjection(display.physicalDisplay, display.clip); + } else { + setViewportAndProjection(display.physicalDisplay, display.clip); + auto status = mBlurFilter->setAsDrawTarget(display); + if (status != NO_ERROR) { + ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return status; + } } // clear the entire buffer, sometimes when we reuse buffers we'd persist @@ -973,8 +1019,6 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, // opaque layers. clearWithColor(0.0, 0.0, 0.0, 0.0); - setViewportAndProjection(display.physicalDisplay, display.clip); - setOutputDataSpace(display.outputDataspace); setDisplayMaxLuminance(display.maxLuminance); @@ -991,7 +1035,36 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, .setTexCoords(2 /* size */) .setCropCoords(2 /* size */) .build(); - for (auto layer : layers) { + for (auto const& layer : layers) { + if (blurLayer == &layer) { + auto status = mBlurFilter->prepare(layer.backgroundBlurRadius); + if (status != NO_ERROR) { + ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors("Can't render first blur pass"); + return status; + } + + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, + useFramebufferCache); + status = fbo->getStatus(); + 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(); + if (status != NO_ERROR) { + ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors("Can't render blur filter"); + return status; + } + } + mState.maxMasteringLuminance = layer.source.buffer.maxMasteringLuminance; mState.maxContentLuminance = layer.source.buffer.maxContentLuminance; mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index f41eda2b63..547235a90d 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -46,6 +46,7 @@ class Texture; namespace gl { class GLImage; +class BlurFilter; class GLESRenderEngine : public impl::RenderEngine { public: @@ -117,6 +118,7 @@ private: std::unique_ptr<Framebuffer> createFramebuffer(); std::unique_ptr<Image> createImage(); void checkErrors() const; + void checkErrors(const char* tag) const; void setScissor(const Rect& region); void disableScissor(); bool waitSync(EGLSyncKHR sync, EGLint flags); @@ -228,6 +230,9 @@ private: std::unique_ptr<Framebuffer> mDrawingBuffer; + // Blur effect processor, only instantiated when a layer requests it. + BlurFilter* mBlurFilter = nullptr; + class FlushTracer { public: FlushTracer(GLESRenderEngine* engine); @@ -251,6 +256,11 @@ private: }; friend class FlushTracer; friend class ImageManager; + friend class GLFramebuffer; + friend class BlurFilter; + friend class GaussianBlurFilter; + friend class LensBlurFilter; + friend class GenericProgram; std::unique_ptr<FlushTracer> mFlushTracer; std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this); }; diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp index 5fbb5ba7d7..091eac90b2 100644 --- a/libs/renderengine/gl/GLFramebuffer.cpp +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -20,8 +20,8 @@ #include <GLES/gl.h> #include <GLES/glext.h> -#include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> #include <gui/DebugEGLImageTracker.h> #include <nativebase/nativebase.h> #include <utils/Trace.h> @@ -32,14 +32,23 @@ namespace renderengine { namespace gl { GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine) + : GLFramebuffer(engine, false /* multiTarget */) {} + +GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine, bool multiTarget) : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { glGenTextures(1, &mTextureName); + if (multiTarget) { + glGenTextures(1, &mSecondaryTextureName); + } glGenFramebuffers(1, &mFramebufferName); } GLFramebuffer::~GLFramebuffer() { glDeleteFramebuffers(1, &mFramebufferName); glDeleteTextures(1, &mTextureName); + if (mSecondaryTextureName != -1) { + glDeleteTextures(1, &mSecondaryTextureName); + } } bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, @@ -68,6 +77,55 @@ bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, boo return true; } +void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height) { + ATRACE_CALL(); + + glBindTexture(GL_TEXTURE_2D, mTextureName); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + + const bool multiTarget = mSecondaryTextureName != -1; + if (multiTarget) { + glBindTexture(GL_TEXTURE_2D, mSecondaryTextureName); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + } + + mBufferHeight = height; + mBufferWidth = width; + mEngine.checkErrors("Allocating Fbo texture"); + + bind(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0); + if (multiTarget) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, + mSecondaryTextureName, 0); + GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT}; + glDrawBuffers(2, buffers); + } + mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + unbind(); + glBindTexture(GL_TEXTURE_2D, 0); + + if (mStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE("Frame buffer is not complete. Error %d", mStatus); + } +} + +void GLFramebuffer::bind() const { + glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName); +} + +void GLFramebuffer::unbind() const { + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + } // namespace gl } // namespace renderengine } // namespace android diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h index b7650bbfd9..668685afd1 100644 --- a/libs/renderengine/gl/GLFramebuffer.h +++ b/libs/renderengine/gl/GLFramebuffer.h @@ -20,6 +20,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <GLES2/gl2.h> #include <renderengine/Framebuffer.h> struct ANativeWindowBuffer; @@ -33,22 +34,30 @@ class GLESRenderEngine; class GLFramebuffer : public renderengine::Framebuffer { public: explicit GLFramebuffer(GLESRenderEngine& engine); + explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget); ~GLFramebuffer() override; bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, const bool useFramebufferCache) override; + void allocateBuffers(uint32_t width, uint32_t height); EGLImageKHR getEGLImage() const { return mEGLImage; } uint32_t getTextureName() const { return mTextureName; } + uint32_t getSecondaryTextureName() const { return mSecondaryTextureName; } uint32_t getFramebufferName() const { return mFramebufferName; } int32_t getBufferHeight() const { return mBufferHeight; } int32_t getBufferWidth() const { return mBufferWidth; } + GLenum getStatus() const { return mStatus; } + void bind() const; + void unbind() const; private: GLESRenderEngine& mEngine; EGLDisplay mEGLDisplay; EGLImageKHR mEGLImage; bool usingFramebufferCache = false; + GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED; uint32_t mTextureName, mFramebufferName; + uint32_t mSecondaryTextureName = -1; int32_t mBufferHeight = 0; int32_t mBufferWidth = 0; diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp new file mode 100644 index 0000000000..a55468741d --- /dev/null +++ b/libs/renderengine/gl/filters/BlurFilter.cpp @@ -0,0 +1,134 @@ +/* + * Copyright 2019 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. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "BlurFilter.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#include <ui/GraphicTypes.h> +#include <cstdint> + +#include <utils/Trace.h> + +namespace android { +namespace renderengine { +namespace gl { + +BlurFilter::BlurFilter(GLESRenderEngine& engine) + : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mSimpleProgram(engine) { + mSimpleProgram.compile(getVertexShader(), getSimpleFragShader()); + mSPosLoc = mSimpleProgram.getAttributeLocation("aPosition"); + mSUvLoc = mSimpleProgram.getAttributeLocation("aUV"); + mSTextureLoc = mSimpleProgram.getUniformLocation("uTexture"); +} + +status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display) { + ATRACE_NAME("BlurFilter::setAsDrawTarget"); + + if (!mTexturesAllocated) { + const uint32_t fboWidth = floorf(display.physicalDisplay.width() * kFboScale); + const uint32_t fboHeight = floorf(display.physicalDisplay.height() * kFboScale); + mCompositionFbo.allocateBuffers(fboWidth, fboHeight); + mBlurredFbo.allocateBuffers(fboWidth, fboHeight); + allocateTextures(); + mTexturesAllocated = true; + } + + if (mBlurredFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) { + ALOGE("Invalid blur buffer"); + return mBlurredFbo.getStatus(); + } + if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) { + ALOGE("Invalid composition buffer"); + return mCompositionFbo.getStatus(); + } + + mCompositionFbo.bind(); + glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight()); + return NO_ERROR; +} + +void BlurFilter::drawMesh(GLuint uv, GLuint position) { + GLfloat positions[] = {-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f}; + GLfloat texCoords[] = {0.0, 0.0, 0.0, 1.0f, 1.0f, 1.0f, 1.0f, 0}; + + // set attributes + glEnableVertexAttribArray(uv); + glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0, texCoords); + glEnableVertexAttribArray(position); + glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), + positions); + + // draw mesh + glDrawArrays(GL_TRIANGLE_FAN, 0 /* first */, 4 /* count */); + mEngine.checkErrors("Drawing blur mesh"); +} + +status_t BlurFilter::render() { + ATRACE_NAME("BlurFilter::render"); + + // Now let's scale our blur up + mSimpleProgram.useProgram(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName()); + glUniform1i(mSTextureLoc, 0); + mEngine.checkErrors("Setting final pass uniforms"); + + drawMesh(mSUvLoc, mSPosLoc); + + glUseProgram(0); + return NO_ERROR; +} + +string BlurFilter::getVertexShader() const { + return R"SHADER( + #version 310 es + precision lowp float; + + in vec2 aPosition; + in mediump vec2 aUV; + out mediump vec2 vUV; + + void main() { + vUV = aUV; + gl_Position = vec4(aPosition, 0.0, 1.0); + } + )SHADER"; +} + +string BlurFilter::getSimpleFragShader() const { + string shader = R"SHADER( + #version 310 es + precision lowp float; + + in mediump vec2 vUV; + out vec4 fragColor; + + uniform sampler2D uTexture; + + void main() { + fragColor = texture(uTexture, vUV); + } + )SHADER"; + return shader; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h new file mode 100644 index 0000000000..2b5ea5830b --- /dev/null +++ b/libs/renderengine/gl/filters/BlurFilter.h @@ -0,0 +1,69 @@ +/* + * Copyright 2019 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 <ui/GraphicTypes.h> +#include "../GLESRenderEngine.h" +#include "../GLFramebuffer.h" +#include "GenericProgram.h" + +using namespace std; + +namespace android { +namespace renderengine { +namespace gl { + +class BlurFilter { +public: + // Downsample FBO to improve performance + static constexpr float kFboScale = 0.35f; + + explicit BlurFilter(GLESRenderEngine& engine); + virtual ~BlurFilter(){}; + + // Set up render targets, redirecting output to offscreen texture. + status_t setAsDrawTarget(const DisplaySettings&); + // Allocate any textures needed for the filter. + virtual void allocateTextures() = 0; + // Execute blur passes, rendering to offscreen texture. + virtual status_t prepare(uint32_t radius) = 0; + // Render blur to the bound framebuffer (screen). + status_t render(); + +protected: + void drawMesh(GLuint uv, GLuint position); + string getSimpleFragShader() const; + string getVertexShader() const; + + GLESRenderEngine& mEngine; + // Frame buffer holding the composited background. + GLFramebuffer mCompositionFbo; + // Frame buffer holding the blur result. + GLFramebuffer mBlurredFbo; + +private: + bool mTexturesAllocated = false; + + GenericProgram mSimpleProgram; + GLuint mSPosLoc; + GLuint mSUvLoc; + GLuint mSTextureLoc; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp new file mode 100644 index 0000000000..b1ad72ccef --- /dev/null +++ b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2019 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. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "GaussianBlurFilter.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#include <ui/GraphicTypes.h> +#include <cstdint> + +#include <utils/Trace.h> + +namespace android { +namespace renderengine { +namespace gl { + +GaussianBlurFilter::GaussianBlurFilter(GLESRenderEngine& engine) + : BlurFilter(engine), + mVerticalPassFbo(engine), + mVerticalProgram(engine), + mHorizontalProgram(engine) { + mVerticalProgram.compile(getVertexShader(), getFragmentShader(false)); + mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition"); + mVUvLoc = mVerticalProgram.getAttributeLocation("aUV"); + mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture"); + mVSizeLoc = mVerticalProgram.getUniformLocation("uSize"); + mVRadiusLoc = mVerticalProgram.getUniformLocation("uRadius"); + + mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true)); + mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition"); + mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV"); + mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture"); + mHSizeLoc = mHorizontalProgram.getUniformLocation("uSize"); + mHRadiusLoc = mHorizontalProgram.getUniformLocation("uRadius"); +} + +void GaussianBlurFilter::allocateTextures() { + mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight()); +} + +status_t GaussianBlurFilter::prepare(uint32_t radius) { + ATRACE_NAME("GaussianBlurFilter::prepare"); + + if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) { + ALOGE("Invalid vertical FBO"); + return mVerticalPassFbo.getStatus(); + } + if (!mVerticalProgram.isValid()) { + ALOGE("Invalid vertical shader"); + return GL_INVALID_OPERATION; + } + if (!mHorizontalProgram.isValid()) { + ALOGE("Invalid horizontal shader"); + return GL_INVALID_OPERATION; + } + + // First, we'll apply the vertical pass, that receives the flattened background layers. + mVerticalPassFbo.bind(); + mVerticalProgram.useProgram(); + + // set uniforms + auto width = mVerticalPassFbo.getBufferWidth(); + auto height = mVerticalPassFbo.getBufferHeight(); + glViewport(0, 0, width, height); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName()); + glUniform1i(mVTextureLoc, 0); + glUniform2f(mVSizeLoc, width, height); + glUniform1f(mVRadiusLoc, radius * kFboScale); + mEngine.checkErrors("Setting vertical-diagonal pass uniforms"); + + drawMesh(mVUvLoc, mVPosLoc); + + // Blur vertically on a secondary pass + mBlurredFbo.bind(); + mHorizontalProgram.useProgram(); + + // set uniforms + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName()); + glUniform1i(mHTextureLoc, 0); + glUniform2f(mHSizeLoc, width, height); + glUniform1f(mHRadiusLoc, radius * kFboScale); + mEngine.checkErrors("Setting vertical pass uniforms"); + + drawMesh(mHUvLoc, mHPosLoc); + + // reset active texture + mBlurredFbo.unbind(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + + // unbind program + glUseProgram(0); + + return NO_ERROR; +} + +string GaussianBlurFilter::getFragmentShader(bool horizontal) const { + string shader = "#version 310 es\n#define DIRECTION "; + shader += (horizontal ? "1" : "0"); + shader += R"SHADER( + precision lowp float; + + uniform sampler2D uTexture; + uniform vec2 uSize; + uniform float uRadius; + + in mediump vec2 vUV; + + out vec4 fragColor; + + #define PI 3.14159265359 + #define THETA 0.352 + #define MU 0.0 + #define A 1.0 / (THETA * sqrt(2.0 * PI)) + #define K 1.0 / (2.0 * THETA * THETA) + #define MAX_SAMPLES 12 + + float gaussianBellCurve(float x) { + float tmp = (x - MU); + return exp(-K * tmp * tmp); + } + + vec3 gaussianBlur(sampler2D texture, mediump vec2 uv, float size, + vec2 direction, float radius) { + float totalWeight = 0.0; + vec3 blurred = vec3(0.); + int samples = min(int(floor(radius / 2.0)), MAX_SAMPLES); + float inc = radius / (size * 2.0); + + for (int i = -samples; i <= samples; i++) { + float normalized = (float(i) / float(samples)); + float weight = gaussianBellCurve(normalized); + float radInc = inc * normalized; + blurred += weight * (texture(texture, uv + radInc * direction)).rgb;; + totalWeight += weight; + } + + return blurred / totalWeight; + } + + void main() { + #if DIRECTION == 1 + vec3 color = gaussianBlur(uTexture, vUV, uSize.x, vec2(1.0, 0.0), uRadius); + #else + vec3 color = gaussianBlur(uTexture, vUV, uSize.y, vec2(0.0, 1.0), uRadius); + #endif + fragColor = vec4(color.r, color.g, color.b, texture(uTexture, vUV).a); + } + + )SHADER"; + return shader; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h new file mode 100644 index 0000000000..acf0f07d36 --- /dev/null +++ b/libs/renderengine/gl/filters/GaussianBlurFilter.h @@ -0,0 +1,62 @@ +/* + * Copyright 2019 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 <ui/GraphicTypes.h> +#include "../GLESRenderEngine.h" +#include "../GLFramebuffer.h" +#include "BlurFilter.h" +#include "GenericProgram.h" + +using namespace std; + +namespace android { +namespace renderengine { +namespace gl { + +class GaussianBlurFilter : public BlurFilter { +public: + explicit GaussianBlurFilter(GLESRenderEngine& engine); + status_t prepare(uint32_t radius) override; + void allocateTextures() override; + +private: + string getFragmentShader(bool horizontal) const; + + // Initial, vertical render pass + GLFramebuffer mVerticalPassFbo; + + // Vertical pass and its uniforms + GenericProgram mVerticalProgram; + GLuint mVPosLoc; + GLuint mVUvLoc; + GLuint mVTextureLoc; + GLuint mVSizeLoc; + GLuint mVRadiusLoc; + + // Horizontal pass and its uniforms + GenericProgram mHorizontalProgram; + GLuint mHPosLoc; + GLuint mHUvLoc; + GLuint mHTextureLoc; + GLuint mHSizeLoc; + GLuint mHRadiusLoc; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/gl/filters/GenericProgram.cpp b/libs/renderengine/gl/filters/GenericProgram.cpp new file mode 100644 index 0000000000..bb35889665 --- /dev/null +++ b/libs/renderengine/gl/filters/GenericProgram.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2019 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 "GenericProgram.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +namespace android { +namespace renderengine { +namespace gl { + +GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {} + +GenericProgram::~GenericProgram() { + if (mVertexShaderHandle != 0) { + if (mProgramHandle != 0) { + glDetachShader(mProgramHandle, mVertexShaderHandle); + } + glDeleteShader(mVertexShaderHandle); + } + + if (mFragmentShaderHandle != 0) { + if (mProgramHandle != 0) { + glDetachShader(mProgramHandle, mFragmentShaderHandle); + } + glDeleteShader(mFragmentShaderHandle); + } + + if (mProgramHandle != 0) { + glDeleteProgram(mProgramHandle); + } +} + +void GenericProgram::compile(string vertexShader, string fragmentShader) { + mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader); + mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader); + if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) { + ALOGE("Aborting program creation."); + return; + } + mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle); + mEngine.checkErrors("Linking program"); +} + +void GenericProgram::useProgram() const { + glUseProgram(mProgramHandle); +} + +GLuint GenericProgram::compileShader(GLuint type, string src) const { + const GLuint shader = glCreateShader(type); + if (shader == 0) { + mEngine.checkErrors("Creating shader"); + return 0; + } + const GLchar* charSrc = (const GLchar*)src.c_str(); + glShaderSource(shader, 1, &charSrc, nullptr); + glCompileShader(shader); + + GLint isCompiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); + if (isCompiled == GL_FALSE) { + GLint maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + string errorLog; + errorLog.reserve(maxLength); + glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data()); + glDeleteShader(shader); + ALOGE("Error compiling shader: %s", errorLog.c_str()); + return 0; + } + return shader; +} +GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const { + const GLuint program = glCreateProgram(); + mEngine.checkErrors("Creating program"); + + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + mEngine.checkErrors("Linking program"); + return program; +} + +GLuint GenericProgram::getUniformLocation(const string name) const { + if (mProgramHandle == 0) { + ALOGE("Can't get location of %s on an invalid program.", name.c_str()); + return -1; + } + return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str()); +} + +GLuint GenericProgram::getAttributeLocation(const string name) const { + if (mProgramHandle == 0) { + ALOGE("Can't get location of %s on an invalid program.", name.c_str()); + return -1; + } + return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str()); +} + +bool GenericProgram::isValid() const { + return mProgramHandle != 0; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/filters/GenericProgram.h b/libs/renderengine/gl/filters/GenericProgram.h new file mode 100644 index 0000000000..6da2a5af58 --- /dev/null +++ b/libs/renderengine/gl/filters/GenericProgram.h @@ -0,0 +1,51 @@ +/* + * Copyright 2019 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 <ui/GraphicTypes.h> +#include "../GLESRenderEngine.h" +#include "../GLFramebuffer.h" + +using namespace std; + +namespace android { +namespace renderengine { +namespace gl { + +class GenericProgram { +public: + explicit GenericProgram(GLESRenderEngine& renderEngine); + ~GenericProgram(); + void compile(string vertexShader, string fragmentShader); + bool isValid() const; + void useProgram() const; + GLuint getAttributeLocation(const string name) const; + GLuint getUniformLocation(const string name) const; + +private: + GLuint compileShader(GLuint type, const string src) const; + GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const; + + GLESRenderEngine& mEngine; + GLuint mVertexShaderHandle = 0; + GLuint mFragmentShaderHandle = 0; + GLuint mProgramHandle = 0; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/filters/LensBlurFilter.cpp b/libs/renderengine/gl/filters/LensBlurFilter.cpp new file mode 100644 index 0000000000..386bd91322 --- /dev/null +++ b/libs/renderengine/gl/filters/LensBlurFilter.cpp @@ -0,0 +1,232 @@ +/* + * Copyright 2019 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. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "LensBlurFilter.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#include <ui/GraphicTypes.h> +#include <cstdint> + +#include <utils/Trace.h> + +namespace android { +namespace renderengine { +namespace gl { + +// Number of blur samples in shader (for loop) +static constexpr auto kNumSamples = 12; + +LensBlurFilter::LensBlurFilter(GLESRenderEngine& engine) + : BlurFilter(engine), + mVerticalDiagonalPassFbo(engine, true /* multiTarget */), + mVerticalDiagonalProgram(engine), + mCombinedProgram(engine) { + mVerticalDiagonalProgram.compile(getVertexShader(), getFragmentShader(false)); + mCombinedProgram.compile(getVertexShader(), getFragmentShader(true)); + + mVDPosLoc = mVerticalDiagonalProgram.getAttributeLocation("aPosition"); + mVDUvLoc = mVerticalDiagonalProgram.getAttributeLocation("aUV"); + mVDTexture0Loc = mVerticalDiagonalProgram.getUniformLocation("uTexture0"); + mVDSizeLoc = mVerticalDiagonalProgram.getUniformLocation("uSize"); + mVDRadiusLoc = mVerticalDiagonalProgram.getUniformLocation("uRadius"); + mVDNumSamplesLoc = mVerticalDiagonalProgram.getUniformLocation("uNumSamples"); + + mCPosLoc = mCombinedProgram.getAttributeLocation("aPosition"); + mCUvLoc = mCombinedProgram.getAttributeLocation("aUV"); + mCTexture0Loc = mCombinedProgram.getUniformLocation("uTexture0"); + mCTexture1Loc = mCombinedProgram.getUniformLocation("uTexture1"); + mCSizeLoc = mCombinedProgram.getUniformLocation("uSize"); + mCRadiusLoc = mCombinedProgram.getUniformLocation("uRadius"); + mCNumSamplesLoc = mCombinedProgram.getUniformLocation("uNumSamples"); +} + +void LensBlurFilter::allocateTextures() { + mVerticalDiagonalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), + mBlurredFbo.getBufferHeight()); +} + +status_t LensBlurFilter::prepare(uint32_t radius) { + ATRACE_NAME("LensBlurFilter::prepare"); + + if (mVerticalDiagonalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) { + ALOGE("Invalid vertical-diagonal FBO"); + return mVerticalDiagonalPassFbo.getStatus(); + } + if (!mVerticalDiagonalProgram.isValid()) { + ALOGE("Invalid vertical-diagonal shader"); + return GL_INVALID_OPERATION; + } + if (!mCombinedProgram.isValid()) { + ALOGE("Invalid blur shader"); + return GL_INVALID_OPERATION; + } + + // First, we'll apply the vertical/diagonal pass, that receives the flattened background layers, + // and writes the output to two textures (vertical and diagonal.) + mVerticalDiagonalPassFbo.bind(); + mVerticalDiagonalProgram.useProgram(); + + // set uniforms + auto width = mVerticalDiagonalPassFbo.getBufferWidth(); + auto height = mVerticalDiagonalPassFbo.getBufferHeight(); + glViewport(0, 0, width, height); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName()); + glUniform1i(mVDTexture0Loc, 0); + glUniform2f(mVDSizeLoc, width, height); + glUniform1f(mVDRadiusLoc, radius * kFboScale); + glUniform1i(mVDNumSamplesLoc, kNumSamples); + mEngine.checkErrors("Setting vertical-diagonal pass uniforms"); + + drawMesh(mVDUvLoc, mVDPosLoc); + + // Now we'll combine the multi render pass into a blurred image + mBlurredFbo.bind(); + mCombinedProgram.useProgram(); + + // set uniforms + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getTextureName()); + glUniform1i(mCTexture0Loc, 0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getSecondaryTextureName()); + glUniform1i(mCTexture1Loc, 1); + glUniform2f(mCSizeLoc, width, height); + glUniform1f(mCRadiusLoc, radius * kFboScale); + glUniform1i(mCNumSamplesLoc, kNumSamples); + mEngine.checkErrors("Setting vertical pass uniforms"); + + drawMesh(mCUvLoc, mCPosLoc); + + // reset active texture + mBlurredFbo.unbind(); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + + // unbind program + glUseProgram(0); + + return NO_ERROR; +} + +string LensBlurFilter::getFragmentShader(bool forComposition) const { + string shader = "#version 310 es\n#define DIRECTION "; + shader += (forComposition ? "1" : "0"); + shader += R"SHADER( + precision lowp float; + + #define BOKEH_ANGLE 0.0 + #define PI 3.14159265359 + + uniform sampler2D uTexture0; + uniform vec2 uSize; + uniform float uRadius; + uniform int uNumSamples; + + in mediump vec2 vUV; + + #if DIRECTION == 0 + layout(location = 0) out vec4 fragColor0; + layout(location = 1) out vec4 fragColor1; + #else + uniform sampler2D uTexture1; + out vec4 fragColor; + #endif + + vec4 blur(const sampler2D tex, in vec2 uv, const vec2 direction, float radius, + in int samples, float intensity) { + vec4 finalColor = vec4(vec3(0.0), 1.0); + float blurAmount = 0.0; + uv += direction * 0.5; + + for (int i = 0; i < samples; i++){ + float delta = radius * float(i) / float(samples); + vec4 color = texture(tex, uv + direction * delta); + color.rgb *= intensity; + color *= color.a; + blurAmount += color.a; + finalColor += color; + } + + return finalColor / blurAmount; + } + + vec4 blur(const sampler2D tex, in vec2 uv, const vec2 direction, float radius, + in int samples) { + return blur(tex, uv, direction, radius, samples, 1.0); + } + + vec4[2] verticalDiagonalLensBlur (vec2 uv, sampler2D texture, vec2 resolution, + float radius, int samples) { + float coc = texture(texture, uv).a; + + // Vertical Blur + vec2 blurDirV = (coc / resolution.xy) * vec2(cos(BOKEH_ANGLE + PI / 2.0), + sin(BOKEH_ANGLE + PI / 2.0)); + vec3 colorV = blur(texture, uv, blurDirV, radius, samples).rgb * coc; + + // Diagonal Blur + vec2 blurDirD = (coc / resolution.xy) * vec2(cos(BOKEH_ANGLE - PI / 6.0), + sin(BOKEH_ANGLE - PI / 6.0)); + vec3 colorD = blur(texture, uv, blurDirD, radius, samples).rgb * coc; + + vec4 composed[2]; + composed[0] = vec4(colorV, coc); + // added * 0.5, to remap + composed[1] = vec4((colorD + colorV) * 0.5, coc); + + return composed; + } + + vec4 rhombiLensBlur (vec2 uv, sampler2D texture0, sampler2D texture1, vec2 resolution, + float radius, int samples) { + float coc1 = texture(texture0, uv).a; + float coc2 = texture(texture1, uv).a; + + vec2 blurDirection1 = coc1 / resolution.xy * vec2(cos(BOKEH_ANGLE - PI / 6.0), sin(BOKEH_ANGLE - PI / 6.0)); + vec3 color1 = blur(texture0, uv, blurDirection1, radius, samples).rgb * coc1; + + vec2 blurDirection2 = coc2 / resolution.xy * vec2(cos(BOKEH_ANGLE - 5.0 * PI / 6.0), sin(BOKEH_ANGLE - 5.0 * PI / 6.0)); + vec3 color2 = blur(texture1, uv, blurDirection2, radius, samples, 2.0).rgb * coc2; + + return vec4((color1 + color2) * 0.33, 1.0); + } + + void main() { + #if DIRECTION == 0 + // First pass: outputs two textures + vec4 colorOut[] = verticalDiagonalLensBlur(vUV, uTexture0, uSize, uRadius, uNumSamples); + fragColor0 = colorOut[0]; + fragColor1 = colorOut[1]; + #else + // Second pass: combines both textures into a blurred one. + fragColor = rhombiLensBlur(vUV, uTexture0, uTexture1, uSize, uRadius, uNumSamples); + #endif + } + + )SHADER"; + return shader; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/filters/LensBlurFilter.h b/libs/renderengine/gl/filters/LensBlurFilter.h new file mode 100644 index 0000000000..8543f0db4f --- /dev/null +++ b/libs/renderengine/gl/filters/LensBlurFilter.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 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 <ui/GraphicTypes.h> +#include "../GLESRenderEngine.h" +#include "../GLFramebuffer.h" +#include "BlurFilter.h" +#include "GenericProgram.h" + +using namespace std; + +namespace android { +namespace renderengine { +namespace gl { + +class LensBlurFilter : public BlurFilter { +public: + explicit LensBlurFilter(GLESRenderEngine& engine); + status_t prepare(uint32_t radius) override; + void allocateTextures() override; + +private: + string getFragmentShader(bool forComposition) const; + + // Intermediate render pass + GLFramebuffer mVerticalDiagonalPassFbo; + + // Vertical/diagonal pass and its uniforms + GenericProgram mVerticalDiagonalProgram; + GLuint mVDPosLoc; + GLuint mVDUvLoc; + GLuint mVDTexture0Loc; + GLuint mVDSizeLoc; + GLuint mVDRadiusLoc; + GLuint mVDNumSamplesLoc; + + // Blur composition pass and its uniforms + GenericProgram mCombinedProgram; + GLuint mCPosLoc; + GLuint mCUvLoc; + GLuint mCTexture0Loc; + GLuint mCTexture1Loc; + GLuint mCSizeLoc; + GLuint mCRadiusLoc; + GLuint mCNumSamplesLoc; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 5aa3f3bd69..3dc198f398 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -149,6 +149,8 @@ struct LayerSettings { bool disableBlending = false; ShadowSettings shadow; + + int backgroundBlurRadius = 0; }; static inline bool operator==(const Buffer& lhs, const Buffer& rhs) { @@ -182,7 +184,8 @@ static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs 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.shadow == rhs.shadow && + lhs.backgroundBlurRadius == rhs.backgroundBlurRadius; } // Defining PrintTo helps with Google Tests. @@ -243,6 +246,7 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { PrintTo(settings.sourceDataspace, os); *os << "\n .colorTransform = " << settings.colorTransform; *os << "\n .disableBlending = " << settings.disableBlending; + *os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius; *os << "\n .shadow = "; PrintTo(settings.shadow, os); *os << "\n}"; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 4db5c57d6d..e3c2d84e86 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -174,6 +174,7 @@ struct RenderEngineCreationArgs { bool useColorManagement; bool enableProtectedContext; bool precacheToneMapperShaderOnly; + bool supportsBackgroundBlur; RenderEngine::ContextPriority contextPriority; struct Builder; @@ -186,12 +187,14 @@ private: bool _useColorManagement, bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, + bool _supportsBackgroundBlur, RenderEngine::ContextPriority _contextPriority) : pixelFormat(_pixelFormat) , imageCacheSize(_imageCacheSize) , useColorManagement(_useColorManagement) , enableProtectedContext(_enableProtectedContext) , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly) + , supportsBackgroundBlur(_supportsBackgroundBlur) , contextPriority(_contextPriority) {} RenderEngineCreationArgs() = delete; }; @@ -219,13 +222,18 @@ struct RenderEngineCreationArgs::Builder { this->precacheToneMapperShaderOnly = precacheToneMapperShaderOnly; return *this; } + Builder& setSupportsBackgroundBlur(bool supportsBackgroundBlur) { + this->supportsBackgroundBlur = supportsBackgroundBlur; + return *this; + } Builder& setContextPriority(RenderEngine::ContextPriority contextPriority) { this->contextPriority = contextPriority; return *this; } RenderEngineCreationArgs build() const { return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement, - enableProtectedContext, precacheToneMapperShaderOnly, contextPriority); + enableProtectedContext, precacheToneMapperShaderOnly, + supportsBackgroundBlur, contextPriority); } private: @@ -235,6 +243,7 @@ private: bool useColorManagement = true; bool enableProtectedContext = false; bool precacheToneMapperShaderOnly = false; + bool supportsBackgroundBlur = false; RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM; }; diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 7700b2e01f..e676740c6a 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -23,6 +23,7 @@ #include <fstream> #include <gtest/gtest.h> +#include <cutils/properties.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <ui/PixelFormat.h> @@ -44,6 +45,7 @@ struct RenderEngineTest : public ::testing::Test { .setUseColorManagerment(false) .setEnableProtectedContext(false) .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) .build()); } @@ -328,6 +330,9 @@ struct RenderEngineTest : public ::testing::Test { void fillBufferWithRoundedCorners(); template <typename SourceVariant> + void fillBufferAndBlurBackground(); + + template <typename SourceVariant> void overlayCorners(); void fillRedBufferTextureTransform(); @@ -706,6 +711,51 @@ void RenderEngineTest::fillBufferWithRoundedCorners() { } template <typename SourceVariant> +void RenderEngineTest::fillBufferAndBlurBackground() { + 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 blurRadius = 50; + auto center = DEFAULT_DISPLAY_WIDTH / 2; + + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings backgroundLayer; + backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this); + backgroundLayer.alpha = 1.0f; + layers.push_back(backgroundLayer); + + renderengine::LayerSettings leftLayer; + leftLayer.geometry.boundaries = + Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect(); + SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this); + leftLayer.alpha = 1.0f; + layers.push_back(leftLayer); + + renderengine::LayerSettings blurLayer; + blurLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + blurLayer.backgroundBlurRadius = blurRadius; + blurLayer.alpha = 0; + layers.push_back(blurLayer); + + invokeDraw(settings, layers, mBuffer); + + expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255, + 50 /* tolerance */); + expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255, + 50 /* tolerance */); +} + +template <typename SourceVariant> void RenderEngineTest::overlayCorners() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); @@ -1032,6 +1082,10 @@ TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { fillBufferWithRoundedCorners<ColorSourceVariant>(); } +TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { + fillBufferAndBlurBackground<ColorSourceVariant>(); +} + TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) { overlayCorners<ColorSourceVariant>(); } @@ -1084,6 +1138,10 @@ TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } +TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { + fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } @@ -1136,6 +1194,10 @@ TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } +TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { + fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index a64fdbf88a..3dbd25e173 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -63,6 +63,9 @@ struct LayerFECompositionState { // The alpha value for this layer float alpha{1.f}; + // Background blur in pixels + int backgroundBlurRadius{0}; + // The transform from layer local coordinates to composition coordinates ui::Transform geomLayerTransform; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 159e928761..3a9776d57a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -116,6 +116,7 @@ protected: private: void dirtyEntireOutput(); + compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const; ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const; compositionengine::Output::ColorProfile pickColorProfile( const compositionengine::CompositionRefreshArgs&) const; @@ -126,6 +127,7 @@ private: std::unique_ptr<compositionengine::RenderSurface> mRenderSurface; ReleasedLayers mReleasedLayers; + OutputLayer* mLayerRequestingBackgroundBlur = nullptr; }; // This template factory function standardizes the implementation details of the diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp index 085e83838e..8065e658d5 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -61,6 +61,7 @@ void LayerFECompositionState::dump(std::string& out) const { out.append("\n "); dumpVal(out, "blend", toString(blendMode), blendMode); dumpVal(out, "alpha", alpha); + dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius); out.append("\n "); dumpVal(out, "type", type); diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 01413b9478..650b5c1d29 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -578,15 +578,33 @@ void Output::updateAndWriteCompositionState( return; } + mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition(); + bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr; + for (auto* layer : getOutputLayersOrderedByZ()) { layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame, - refreshArgs.devOptForceClientComposition); + refreshArgs.devOptForceClientComposition || + forceClientComposition); + + if (mLayerRequestingBackgroundBlur == layer) { + forceClientComposition = false; + } // Send the updated state to the HWC, if appropriate. layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame); } } +compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const { + compositionengine::OutputLayer* layerRequestingBgComposition = nullptr; + for (auto* layer : getOutputLayersOrderedByZ()) { + if (layer->getLayer().getFEState().backgroundBlurRadius > 0) { + layerRequestingBgComposition = layer; + } + } + return layerRequestingBgComposition; +} + void Output::updateColorProfile(const compositionengine::CompositionRefreshArgs& refreshArgs) { setColorProfile(pickColorProfile(refreshArgs)); } @@ -854,11 +872,12 @@ std::optional<base::unique_fd> Output::composeSurfaces(const Region& debugRegion } // We boost GPU frequency here because there will be color spaces conversion - // and it's expensive. We boost the GPU frequency so that GPU composition can - // finish in time. We must reset GPU frequency afterwards, because high frequency - // consumes extra battery. + // or complex GPU shaders and it's expensive. We boost the GPU frequency so that + // GPU composition can finish in time. We must reset GPU frequency afterwards, + // because high frequency consumes extra battery. const bool expensiveRenderingExpected = - clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3; + clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 || + mLayerRequestingBackgroundBlur != nullptr; if (expensiveRenderingExpected) { setExpensiveRenderingExpected(true); } diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 24311c7e5b..6761b866c4 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -138,6 +138,10 @@ struct OutputLatchFEStateTest : public OutputTest { EXPECT_CALL(mLayer1, editFEState()).WillRepeatedly(ReturnRef(mLayer1FEState)); EXPECT_CALL(mLayer2, editFEState()).WillRepeatedly(ReturnRef(mLayer2FEState)); EXPECT_CALL(mLayer3, editFEState()).WillRepeatedly(ReturnRef(mLayer3FEState)); + + EXPECT_CALL(mLayer1, getFEState()).WillRepeatedly(ReturnRef(mLayer1FEState)); + EXPECT_CALL(mLayer2, getFEState()).WillRepeatedly(ReturnRef(mLayer2FEState)); + EXPECT_CALL(mLayer3, getFEState()).WillRepeatedly(ReturnRef(mLayer3FEState)); } void injectLayer(std::unique_ptr<mock::OutputLayer> layer) { @@ -3637,6 +3641,29 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, kDisplayDataspace)); } +TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) { + // Layer requesting blur, or below, should request client composition. + EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, true)); + EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false)); + EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, true)); + EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false)); + EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, false)); + EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false)); + + mLayer2FEState.backgroundBlurRadius = 10; + + injectLayer(std::move(mOutputLayer1)); + injectLayer(std::move(mOutputLayer2)); + injectLayer(std::move(mOutputLayer3)); + + mOutput->editState().isEnabled = true; + + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateAndWriteCompositionState(args); +} + TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) { // In split-screen landscape mode, the screen is rotated 90 degrees, with // one layer on the left covering the left side of the output, and one layer diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 9c4a784883..f4d4329f6d 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -109,6 +109,7 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.hdrMetadata.validTypes = 0; mCurrentState.surfaceDamageRegion = Region::INVALID_REGION; mCurrentState.cornerRadius = 0.0f; + mCurrentState.backgroundBlurRadius = 0; mCurrentState.api = -1; mCurrentState.hasColorTransform = false; mCurrentState.colorSpaceAgnostic = false; @@ -448,6 +449,7 @@ void Layer::latchBasicGeometry(compositionengine::LayerFECompositionState& compo compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); compositionState.alpha = alpha; + compositionState.backgroundBlurRadius = drawingState.backgroundBlurRadius; } void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const { @@ -575,6 +577,7 @@ std::optional<renderengine::LayerSettings> Layer::prepareClientComposition( layerSettings.alpha = alpha; layerSettings.sourceDataspace = getDataSpace(); + layerSettings.backgroundBlurRadius = getBackgroundBlurRadius(); return layerSettings; } @@ -1103,6 +1106,16 @@ bool Layer::setCornerRadius(float cornerRadius) { return true; } +bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) { + if (mCurrentState.backgroundBlurRadius == backgroundBlurRadius) return false; + + mCurrentState.sequence++; + mCurrentState.backgroundBlurRadius = backgroundBlurRadius; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms) { ui::Transform t; @@ -1873,6 +1886,10 @@ half4 Layer::getColor() const { return half4(color.r, color.g, color.b, getAlpha()); } +int32_t Layer::getBackgroundBlurRadius() const { + return getDrawingState().backgroundBlurRadius; +} + Layer::RoundedCornerState Layer::getRoundedCornerState() const { const auto& p = mDrawingParent.promote(); if (p != nullptr) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 025309894a..ffe004f9e9 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -179,6 +179,7 @@ public: half4 color; float cornerRadius; + int backgroundBlurRadius; bool inputInfoChanged; InputWindowInfo inputInfo; @@ -299,6 +300,9 @@ public: // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer // from which we inferred the rounded corner radius. virtual bool setCornerRadius(float cornerRadius); + // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which + // is specified in pixels. + virtual bool setBackgroundBlurRadius(int backgroundBlurRadius); virtual bool setTransparentRegionHint(const Region& transparent); virtual bool setFlags(uint8_t flags, uint8_t mask); virtual bool setLayerStack(uint32_t layerStack); @@ -663,6 +667,7 @@ public: // down the hierarchy). half getAlpha() const; half4 getColor() const; + int32_t getBackgroundBlurRadius() const; // Returns how rounded corners should be drawn for this layer. // This will traverse the hierarchy until it reaches its root, finding topmost rounded diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 0a33830937..ba57d3763f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -338,6 +338,14 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mLayerTripleBufferingDisabled = atoi(value); ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering"); + property_get("ro.surface_flinger.supports_background_blur", value, "0"); + bool supportsBlurs = atoi(value); + property_get("debug.sf.disableBlurs", value, "0"); + bool disableBlurs = atoi(value); + mEnableBlurs = supportsBlurs && !disableBlurs; + ALOGI_IF(!mEnableBlurs, "Disabling blur effects. supported: %d, disabled: %d", supportsBlurs, + disableBlurs); + const size_t defaultListSize = MAX_LAYERS; auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize)); mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize; @@ -580,6 +588,7 @@ void SurfaceFlinger::init() { .setUseColorManagerment(useColorManagement) .setEnableProtectedContext(enable_protected_contents(false)) .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(mEnableBlurs) .setContextPriority(useContextPriority ? renderengine::RenderEngine::ContextPriority::HIGH : renderengine::RenderEngine::ContextPriority::MEDIUM) @@ -3379,6 +3388,9 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (layer->setCornerRadius(s.cornerRadius)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eBackgroundBlurRadiusChanged) { + if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded; + } if (what & layer_state_t::eLayerStackChanged) { ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); // We only allow setting layer stacks for top level layers, diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 723e332ad1..b9f230d982 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1025,6 +1025,7 @@ private: const std::shared_ptr<TimeStats> mTimeStats; const std::unique_ptr<FrameTracer> mFrameTracer; bool mUseHwcVirtualDisplays = false; + bool mEnableBlurs = false; std::atomic<uint32_t> mFrameMissedCount = 0; std::atomic<uint32_t> mHwcFrameMissedCount = 0; std::atomic<uint32_t> mGpuFrameMissedCount = 0; diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 79123f979b..6884b4c42e 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -114,6 +114,7 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack); addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy); addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius); + addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius); if (layer->mCurrentState.barrierLayer_legacy != nullptr) { addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer_legacy.promote(), @@ -322,6 +323,13 @@ void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t cornerRadiusChange->set_corner_radius(cornerRadius); } +void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId, + int32_t backgroundBlurRadius) { + SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); + BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius()); + blurRadiusChange->set_background_blur_radius(backgroundBlurRadius); +} + void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber) { @@ -422,6 +430,9 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, if (state.what & layer_state_t::eCornerRadiusChanged) { addCornerRadiusLocked(transaction, layerId, state.cornerRadius); } + if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) { + addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius); + } if (state.what & layer_state_t::eDeferTransaction_legacy) { sp<Layer> otherLayer = nullptr; if (state.barrierHandle_legacy != nullptr) { diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index c6f9e8a9a1..a665f62ad7 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -152,6 +152,8 @@ private: void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack); void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect); void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius); + void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId, + int32_t backgroundBlurRadius); void addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber); void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId, diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp index ef27847e55..8fce0c9bf3 100644 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp @@ -106,6 +106,7 @@ LayerProtoParser::Layer LayerProtoParser::generateLayer(const LayerProto& layerP layer.refreshPending = layerProto.refresh_pending(); layer.isProtected = layerProto.is_protected(); layer.cornerRadius = layerProto.corner_radius(); + layer.backgroundBlurRadius = layerProto.background_blur_radius(); for (const auto& entry : layerProto.metadata()) { const std::string& dataStr = entry.second; std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first]; @@ -290,6 +291,7 @@ std::string LayerProtoParser::Layer::to_string() const { StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate); StringAppendF(&result, "dataspace=%s, ", dataspace.c_str()); StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str()); + StringAppendF(&result, "backgroundBlurRadius=%1d, ", backgroundBlurRadius); StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", static_cast<double>(color.r), static_cast<double>(color.g), static_cast<double>(color.b), static_cast<double>(color.a), flags); diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h index 774b0e15f0..52b916555f 100644 --- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h +++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h @@ -110,6 +110,7 @@ public: bool refreshPending; bool isProtected; float cornerRadius; + int backgroundBlurRadius; LayerMetadata metadata; LayerProtoParser::FloatRect cornerRadiusCrop; float shadowRadius; diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index 41ecafadd2..8afe5039f8 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -104,6 +104,8 @@ message LayerProto { ColorTransformProto color_transform = 50; bool is_relative_of = 51; + // Layer's background blur radius in pixels. + int32 background_blur_radius = 52; } message PositionProto { diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 049c872f07..ed2b220e86 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -371,3 +371,12 @@ prop { access: Readonly prop_name: "ro.surface_flinger.support_kernel_idle_timer" } + +# Indicates whether background blurs are supported. +prop { + api_name: "supports_background_blur" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.supports_background_blur" +} diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 2d525073b9..d24ad18eaf 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -104,6 +104,10 @@ props { prop_name: "ro.surface_flinger.support_kernel_idle_timer" } prop { + api_name: "supports_background_blur" + prop_name: "ro.surface_flinger.supports_background_blur" + } + prop { api_name: "use_color_management" prop_name: "ro.surface_flinger.use_color_management" } diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index 71f01b0c1d..3bbd12a242 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -18,6 +18,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#include <cutils/properties.h> #include <gui/BufferItemConsumer.h> #include "TransactionTestHarnesses.h" @@ -243,6 +244,41 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) { shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK); } } + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadius) { + 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> blurLayer; + ASSERT_NO_FATAL_FAILURE(blurLayer = createLayer("blur", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(blurLayer, Color::TRANSPARENT, size, size)); + + Transaction().setBackgroundBlurRadius(blurLayer, blurRadius).apply(); + + auto shot = getScreenCapture(); + // Edges are mixed + shot->expectColor(Rect(center - 1, center - 5, center, center + 5), Color{150, 150, 0, 255}, + 50 /* tolerance */); + shot->expectColor(Rect(center, center - 5, center + 1, center + 5), Color{150, 150, 0, 255}, + 50 /* tolerance */); +} + TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) { sp<SurfaceControl> bufferLayer; ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32)); diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index 75d0761907..4a2ab7c080 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -53,6 +53,7 @@ constexpr uint64_t DEFERRED_UPDATE = 0; constexpr int32_t RELATIVE_Z = 42; constexpr float ALPHA_UPDATE = 0.29f; constexpr float CORNER_RADIUS_UPDATE = 0.2f; +constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24; constexpr float POSITION_UPDATE = 121; const Rect CROP_UPDATE(16, 16, 32, 32); const float SHADOW_RADIUS_UPDATE = 35.0f; @@ -183,6 +184,8 @@ public: bool layerUpdateFound(const SurfaceChange& change, bool foundLayer); bool cropUpdateFound(const SurfaceChange& change, bool foundCrop); bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius); + bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change, + bool foundBackgroundBlurRadius); bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix); bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode); bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion); @@ -220,6 +223,7 @@ public: void layerUpdate(Transaction&); void cropUpdate(Transaction&); void cornerRadiusUpdate(Transaction&); + void backgroundBlurRadiusUpdate(Transaction&); void matrixUpdate(Transaction&); void overrideScalingModeUpdate(Transaction&); void transparentRegionHintUpdate(Transaction&); @@ -355,6 +359,10 @@ void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) { t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE); } +void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) { + t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE); +} + void SurfaceInterceptorTest::layerUpdate(Transaction& t) { t.setLayer(mBGSurfaceControl, LAYER_UPDATE); } @@ -432,6 +440,7 @@ void SurfaceInterceptorTest::runAllUpdates() { runInTransaction(&SurfaceInterceptorTest::sizeUpdate); runInTransaction(&SurfaceInterceptorTest::alphaUpdate); runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate); + runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate); runInTransaction(&SurfaceInterceptorTest::layerUpdate); runInTransaction(&SurfaceInterceptorTest::cropUpdate); runInTransaction(&SurfaceInterceptorTest::matrixUpdate); @@ -509,6 +518,18 @@ bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change return foundCornerRadius; } +bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change, + bool foundBackgroundBlur) { + bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() == + BACKGROUND_BLUR_RADIUS_UPDATE); + if (hasBackgroundBlur && !foundBackgroundBlur) { + foundBackgroundBlur = true; + } else if (hasBackgroundBlur && foundBackgroundBlur) { + []() { FAIL(); }(); + } + return foundBackgroundBlur; +} + bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) { bool hasLayer(change.layer().layer() == LAYER_UPDATE); if (hasLayer && !foundLayer) { @@ -705,6 +726,9 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace, case SurfaceChange::SurfaceChangeCase::kCornerRadius: foundUpdate = cornerRadiusUpdateFound(change, foundUpdate); break; + case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius: + foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate); + break; case SurfaceChange::SurfaceChangeCase::kMatrix: foundUpdate = matrixUpdateFound(change, foundUpdate); break; @@ -887,6 +911,11 @@ TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) { SurfaceChange::SurfaceChangeCase::kCornerRadius); } +TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) { + captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate, + SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius); +} + TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) { captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix); } |