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

Commit f4cb4a00 authored by Lucas Dupin's avatar Lucas Dupin
Browse files

Port background blurs to SkiaRE

Bug: 164223050
Test: manual
Test: atest LayerTypeAndRenderTypeTransactionTest
Change-Id: I459780d9fd408865d1d9713f37bf308f07c4fdfc
parent 3f11e925
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ filegroup {
    srcs: [
        "skia/SkiaRenderEngine.cpp",
        "skia/SkiaGLRenderEngine.cpp",
        "skia/filters/BlurFilter.cpp",
    ],
}

+15 −4
Original line number Diff line number Diff line
@@ -29,12 +29,14 @@
#include <utils/Trace.h>
#include "../gl/GLExtensions.h"
#include "SkiaGLRenderEngine.h"
#include "filters/BlurFilter.h"

#include <GrContextOptions.h>
#include <gl/GrGLInterface.h>

#include <SkCanvas.h>
#include <SkImage.h>
#include <SkImageFilters.h>
#include <SkShadowUtils.h>
#include <SkSurface.h>

@@ -194,7 +196,7 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(

    // initialize the renderer while GL is current
    std::unique_ptr<SkiaGLRenderEngine> engine =
            std::make_unique<SkiaGLRenderEngine>(display, config, ctxt, placeholder,
            std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder,
                                                 protectedContext, protectedPlaceholder);

    ALOGI("OpenGL ES informations:");
@@ -247,9 +249,9 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo
    return config;
}

SkiaGLRenderEngine::SkiaGLRenderEngine(EGLDisplay display, EGLConfig config, EGLContext ctxt,
                                       EGLSurface placeholder, EGLContext protectedContext,
                                       EGLSurface protectedPlaceholder)
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
                                       EGLConfig config, EGLContext ctxt, EGLSurface placeholder,
                                       EGLContext protectedContext, EGLSurface protectedPlaceholder)
      : mEGLDisplay(display),
        mEGLConfig(config),
        mEGLContext(ctxt),
@@ -273,6 +275,10 @@ SkiaGLRenderEngine::SkiaGLRenderEngine(EGLDisplay display, EGLConfig config, EGL
    options.fPreferExternalImagesOverES3 = true;
    options.fDisableDistanceFieldPaths = true;
    mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options);

    if (args.supportsBackgroundBlur) {
        mBlurFilter = new BlurFilter();
    }
}

base::unique_fd SkiaGLRenderEngine::flush() {
@@ -408,6 +414,11 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
        const auto& bounds = layer->geometry.boundaries;
        const auto dest = getSkRect(bounds);

        if (layer->backgroundBlurRadius > 0) {
            ATRACE_NAME("BackgroundBlur");
            mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
        }

        if (layer->source.buffer.buffer) {
            ATRACE_NAME("DrawImage");
            const auto& item = layer->source.buffer;
+4 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <SkSurface.h>

#include "SkiaRenderEngine.h"
#include "filters/BlurFilter.h"

namespace android {
namespace renderengine {
@@ -36,8 +37,8 @@ namespace skia {
class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
public:
    static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
    SkiaGLRenderEngine(EGLDisplay display, EGLConfig config, EGLContext ctxt,
                       EGLSurface placeholder, EGLContext protectedContext,
    SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
                       EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext,
                       EGLSurface protectedPlaceholder);
    ~SkiaGLRenderEngine() override{};

@@ -78,6 +79,7 @@ private:
    EGLSurface mPlaceholderSurface;
    EGLContext mProtectedEGLContext;
    EGLSurface mProtectedPlaceholderSurface;
    BlurFilter* mBlurFilter = nullptr;

    // Cache of GL images that we'll store per GraphicBuffer ID
    std::unordered_map<uint64_t, sk_sp<SkImage>> mImageCache GUARDED_BY(mRenderingMutex);
+133 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "BlurFilter.h"
#include <SkCanvas.h>
#include <SkData.h>
#include <SkPaint.h>
#include <SkRuntimeEffect.h>
#include <SkSize.h>
#include <SkString.h>
#include <SkSurface.h>
#include <log/log.h>
#include <utils/Trace.h>

namespace android {
namespace renderengine {
namespace skia {

BlurFilter::BlurFilter() {
    SkString blurString(R"(
        in shader input;
        uniform float in_inverseScale;
        uniform float2 in_blurOffset;

        half4 main(float2 xy) {
            float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);

            float4 c = float4(sample(input, scaled_xy));
            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x,  in_blurOffset.y)));
            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)));
            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x,  in_blurOffset.y)));
            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)));

            return half4(c.rgb * 0.2, 1.0);
        }
    )");

    auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
    if (!blurEffect) {
        LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
    }
    mBlurEffect = std::move(blurEffect);
}

void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t blurRadius) const {
    ATRACE_CALL();
    // Kawase is an approximation of Gaussian, but it behaves differently from it.
    // A radius transformation is required for approximating them, and also to introduce
    // non-integer steps, necessary to smoothly interpolate large radii.
    float tmpRadius = (float)blurRadius / 6.0f;
    float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
    float radiusByPasses = tmpRadius / (float)numberOfPasses;

    SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)input->width() * kInputScale,
                                                        (float)input->height() * kInputScale);
    auto drawSurface = canvas->makeSurface(scaledInfo);

    const float stepX = radiusByPasses;
    const float stepY = radiusByPasses;

    // start by drawing and downscaling and doing the first blur pass
    SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
    blurBuilder.child("input") = input->makeImageSnapshot()->makeShader();
    blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
    blurBuilder.uniform("in_blurOffset") =
            SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};

    {
        // limit the lifetime of the input surface's snapshot to ensure that it goes out of
        // scope before the surface is written into to avoid any copy-on-write behavior.
        SkPaint paint;
        paint.setShader(blurBuilder.makeShader(nullptr, false));
        paint.setFilterQuality(kLow_SkFilterQuality);
        drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
        blurBuilder.child("input") = nullptr;
    }

    // And now we'll ping pong between our surfaces, to accumulate the result of various offsets.
    auto lastDrawTarget = drawSurface;
    if (numberOfPasses > 1) {
        auto readSurface = drawSurface;
        drawSurface = canvas->makeSurface(scaledInfo);

        for (auto i = 1; i < numberOfPasses; i++) {
            const float stepScale = (float)i * kInputScale;

            blurBuilder.child("input") = readSurface->makeImageSnapshot()->makeShader();
            blurBuilder.uniform("in_inverseScale") = 1.0f;
            blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};

            SkPaint paint;
            paint.setShader(blurBuilder.makeShader(nullptr, false));
            paint.setFilterQuality(kLow_SkFilterQuality);
            drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);

            // Swap buffers for next iteration
            auto tmp = drawSurface;
            drawSurface = readSurface;
            readSurface = tmp;
            blurBuilder.child("input") = nullptr;
        }
        lastDrawTarget = readSurface;
    }

    drawSurface->flushAndSubmit();

    // do the final composition, with alpha blending to hide downscaling artifacts.
    {
        SkPaint paint;
        paint.setShader(lastDrawTarget->makeImageSnapshot()->makeShader(
                SkMatrix::MakeScale(kInverseInputScale)));
        paint.setFilterQuality(kLow_SkFilterQuality);
        paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
        canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
    }
}

} // namespace skia
} // namespace renderengine
} // namespace android
 No newline at end of file
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <SkCanvas.h>
#include <SkImage.h>
#include <SkRuntimeEffect.h>
#include <SkSurface.h>

using namespace std;

namespace android {
namespace renderengine {
namespace skia {

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

    explicit BlurFilter();
    virtual ~BlurFilter(){};

    // Execute blur passes, rendering to a canvas.
    void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const;

private:
    sk_sp<SkRuntimeEffect> mBlurEffect;
};

} // namespace skia
} // namespace renderengine
} // namespace android