Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93c3ef10 authored by Lucas Dupin's avatar Lucas Dupin
Browse files

Have a single blur implementation

After experimenting with both Gaussian and Kawase, we concluded
that Kawase has similar visuals and better performance.

This CL removes GaussianBlurFilter and promotes KawaseBlurFilter
to just BlurFilter.

Bug: 149792636
Test: librenderengine_test
Test: visual
Change-Id: I6d5790b2735754b5a39dd7367280871ab43f723c
parent 219643ee
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -59,8 +59,6 @@ filegroup {
        "gl/Program.cpp",
        "gl/ProgramCache.cpp",
        "gl/filters/BlurFilter.cpp",
        "gl/filters/KawaseBlurFilter.cpp",
        "gl/filters/GaussianBlurFilter.cpp",
        "gl/filters/GenericProgram.cpp",
    ],
}
+1 −9
Original line number Diff line number Diff line
@@ -50,8 +50,6 @@
#include "Program.h"
#include "ProgramCache.h"
#include "filters/BlurFilter.h"
#include "filters/GaussianBlurFilter.h"
#include "filters/KawaseBlurFilter.h"

extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);

@@ -430,13 +428,7 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp
    }

    if (args.supportsBackgroundBlur) {
        char isGaussian[PROPERTY_VALUE_MAX];
        property_get("debug.sf.gaussianBlur", isGaussian, "0");
        if (atoi(isGaussian)) {
            mBlurFilter = new GaussianBlurFilter(*this);
        } else {
            mBlurFilter = new KawaseBlurFilter(*this);
        }
        mBlurFilter = new BlurFilter(*this);
        checkErrors("BlurFilter creation");
    }

+0 −2
Original line number Diff line number Diff line
@@ -261,8 +261,6 @@ private:
    friend class ImageManager;
    friend class GLFramebuffer;
    friend class BlurFilter;
    friend class GaussianBlurFilter;
    friend class KawaseBlurFilter;
    friend class GenericProgram;
    std::unique_ptr<FlushTracer> mFlushTracer;
    std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
+103 −2
Original line number Diff line number Diff line
@@ -31,13 +31,24 @@ namespace renderengine {
namespace gl {

BlurFilter::BlurFilter(GLESRenderEngine& engine)
      : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
      : mEngine(engine),
        mCompositionFbo(engine),
        mBlurredFbo(engine),
        mPingPongFbo(engine),
        mMixProgram(engine),
        mBlurProgram(engine) {
    mMixProgram.compile(getVertexShader(), getMixFragShader());
    mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
    mMUvLoc = mMixProgram.getAttributeLocation("aUV");
    mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
    mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
    mMMixLoc = mMixProgram.getUniformLocation("uMix");

    mBlurProgram.compile(getVertexShader(), getFragmentShader());
    mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
    mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
    mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
    mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
}

status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
@@ -52,7 +63,7 @@ status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t ra
        const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
        const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
        mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
        allocateTextures();
        mPingPongFbo.allocateBuffers(fboWidth, fboHeight);
        mTexturesAllocated = true;
    }

@@ -96,6 +107,65 @@ void BlurFilter::drawMesh(GLuint uv, GLuint position) {
    mEngine.checkErrors("Drawing blur mesh");
}

status_t BlurFilter::prepare() {
    ATRACE_NAME("BlurFilter::prepare");

    if (mPingPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
        ALOGE("Invalid FBO");
        return mPingPongFbo.getStatus();
    }
    if (!mBlurProgram.isValid()) {
        ALOGE("Invalid shader");
        return GL_INVALID_OPERATION;
    }

    blit(mCompositionFbo, mBlurredFbo);

    // Kawase is an approximation of Gaussian, but it behaves differently from it.
    // A radius transformation is required for approximating them, and also to introduce
    // non-integer steps, necessary to smoothly interpolate large radii.
    auto radius = mRadius / 6.0f;

    // Calculate how many passes we'll do, based on the radius.
    // Too many passes will make the operation expensive.
    auto passes = min(kMaxPasses, (uint32_t)ceil(radius));

    // We'll ping pong between our textures, to accumulate the result of various offsets.
    mBlurProgram.useProgram();
    GLFramebuffer* draw = &mPingPongFbo;
    GLFramebuffer* read = &mBlurredFbo;
    float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
    float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(mBTextureLoc, 0);
    for (auto i = 0; i < passes; i++) {
        ATRACE_NAME("BlurFilter::renderPass");
        draw->bind();

        glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
        glBindTexture(GL_TEXTURE_2D, read->getTextureName());
        glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
        mEngine.checkErrors("Setting uniforms");

        drawMesh(mBUvLoc, mBPosLoc);

        // Swap buffers for next iteration
        auto tmp = draw;
        draw = read;
        read = tmp;
    }

    // Copy texture, given that we're expected to end on mBlurredFbo.
    if (draw == &mBlurredFbo) {
        blit(mPingPongFbo, mBlurredFbo);
    }

    // Cleanup
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    return NO_ERROR;
}

status_t BlurFilter::render(bool multiPass) {
    ATRACE_NAME("BlurFilter::render");

@@ -145,6 +215,28 @@ string BlurFilter::getVertexShader() const {
    )SHADER";
}

string BlurFilter::getFragmentShader() const {
    return R"SHADER(#version 310 es
        precision mediump float;

        uniform sampler2D uTexture;
        uniform vec2 uOffset;

        highp in vec2 vUV;
        out vec4 fragColor;

        void main() {
            fragColor  = texture(uTexture, vUV, 0.0);
            fragColor += texture(uTexture, vUV + vec2( uOffset.x,  uOffset.y), 0.0);
            fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
            fragColor += texture(uTexture, vUV + vec2(-uOffset.x,  uOffset.y), 0.0);
            fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);

            fragColor = vec4(fragColor.rgb * 0.2, 1.0);
        }
    )SHADER";
}

string BlurFilter::getMixFragShader() const {
    string shader = R"SHADER(#version 310 es
        precision mediump float;
@@ -165,6 +257,15 @@ string BlurFilter::getMixFragShader() const {
    return shader;
}

void BlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
    read.bindAsReadBuffer();
    draw.bindAsDrawBuffer();
    glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
                      draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
                      GL_LINEAR);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

} // namespace gl
} // namespace renderengine
} // namespace android
+19 −7
Original line number Diff line number Diff line
@@ -27,10 +27,17 @@ namespace android {
namespace renderengine {
namespace gl {

/**
 * This is an implementation of a Kawase blur, as described in here:
 * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
 * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
 */
class BlurFilter {
public:
    // Downsample FBO to improve performance
    static constexpr float kFboScale = 0.25f;
    // Maximum number of render passes
    static constexpr uint32_t kMaxPasses = 6;
    // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
    // image, up to this radius.
    static constexpr float kMaxCrossFadeRadius = 30.0f;
@@ -40,17 +47,18 @@ public:

    // Set up render targets, redirecting output to offscreen texture.
    status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
    // Allocate any textures needed for the filter.
    virtual void allocateTextures() = 0;
    // Execute blur passes, rendering to offscreen texture.
    virtual status_t prepare() = 0;
    status_t prepare();
    // Render blur to the bound framebuffer (screen).
    status_t render(bool multiPass);

protected:
private:
    uint32_t mRadius;
    void drawMesh(GLuint uv, GLuint position);
    void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
    string getVertexShader() const;
    string getFragmentShader() const;
    string getMixFragShader() const;

    GLESRenderEngine& mEngine;
    // Frame buffer holding the composited background.
@@ -59,9 +67,7 @@ protected:
    GLFramebuffer mBlurredFbo;
    uint32_t mDisplayWidth;
    uint32_t mDisplayHeight;

private:
    string getMixFragShader() const;
    GLFramebuffer mPingPongFbo;
    bool mTexturesAllocated = false;

    GenericProgram mMixProgram;
@@ -70,6 +76,12 @@ private:
    GLuint mMMixLoc;
    GLuint mMTextureLoc;
    GLuint mMCompositionTextureLoc;

    GenericProgram mBlurProgram;
    GLuint mBPosLoc;
    GLuint mBUvLoc;
    GLuint mBTextureLoc;
    GLuint mBOffsetLoc;
};

} // namespace gl
Loading