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

Commit ecb21465 authored by Derek Sollenberger's avatar Derek Sollenberger
Browse files

Update RenderEngine blur to snap an image of the full surface.

Snapping a subregion of a surface causes Skia's GPU surfaces to make
a copy of the pixels into a new buffer so that the dimensions of the
buffer match the requested size. By requesting a basic snapshot this
CL avoids any uncessary framebuffer copies.

Also refactor the code to start consolidating where blurs are used.

Test: tracing SysUI blurs
Bug: 176903027
Change-Id: I72a9e442e50bc6d945c601efca1fdd2ce7cdfcd1
parent 019f359d
Loading
Loading
Loading
Loading
+29 −25
Original line number Diff line number Diff line
@@ -661,30 +661,33 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
        // Layers have a local transform that should be applied to them
        canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());

        SkPaint paint;
        const auto& bounds = layer->geometry.boundaries;
        const auto dest = getSkRect(bounds);
        const auto layerRect = canvas->getTotalMatrix().mapRect(dest);
        const auto bounds = getSkRect(layer->geometry.boundaries);
        if (mBlurFilter && layerHasBlur(layer)) {
            std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
        if (mBlurFilter) {

            // image to be blurred
            sk_sp<SkImage> blurInput = surface->makeImageSnapshot();
            // rect to be blurred in the coordinate space of blurInput
            const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);

            if (layer->backgroundBlurRadius > 0) {
                ATRACE_NAME("BackgroundBlur");
                auto blurredSurface = mBlurFilter->generate(canvas, surface,
                                                            layer->backgroundBlurRadius, layerRect);
                cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
                auto blurredImage =
                        mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
                                              blurInput, blurRect);

                cachedBlurs[layer->backgroundBlurRadius] = blurredImage;

                drawBlurRegion(canvas, getBlurRegion(layer), layerRect, blurredSurface);
                drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage);
            }
            if (layer->blurRegions.size() > 0) {
            for (auto region : layer->blurRegions) {
                    if (cachedBlurs[region.blurRadius]) {
                        continue;
                    }
                if (cachedBlurs[region.blurRadius] != nullptr) {
                    ATRACE_NAME("BlurRegion");
                    auto blurredSurface =
                            mBlurFilter->generate(canvas, surface, region.blurRadius, layerRect);
                    cachedBlurs[region.blurRadius] = blurredSurface;
                    cachedBlurs[region.blurRadius] =
                            mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
                                                  blurRect);
                }
                drawBlurRegion(canvas, region, blurRect, cachedBlurs[region.blurRadius]);
            }
        }

@@ -698,6 +701,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
                           : layer->sourceDataspace)
                : ui::Dataspace::UNKNOWN;

        SkPaint paint;
        if (layer->source.buffer.buffer) {
            ATRACE_NAME("DrawImage");
            const auto& item = layer->source.buffer;
@@ -724,7 +728,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
            // textureTansform was intended to be passed directly into a shader, so when
            // building the total matrix with the textureTransform we need to first
            // normalize it, then apply the textureTransform, then scale back up.
            texMatrix.preScale(1.0f / bounds.getWidth(), 1.0f / bounds.getHeight());
            texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
            texMatrix.postScale(image->width(), image->height());

            SkMatrix matrix;
@@ -794,14 +798,10 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,

        paint.setColorFilter(filter);

        for (const auto effectRegion : layer->blurRegions) {
            drawBlurRegion(canvas, effectRegion, layerRect, cachedBlurs[effectRegion.blurRadius]);
        }

        if (layer->shadow.length > 0) {
            const auto rect = layer->geometry.roundedCornersRadius > 0
                    ? getSkRect(layer->geometry.roundedCornersCrop)
                    : dest;
                    : bounds;
            drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
        } else {
            // Shadows are assumed to live only on their own layer - it's not valid
@@ -812,7 +812,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
            if (layer->geometry.roundedCornersRadius > 0) {
                canvas->clipRRect(getRoundedRect(layer), true);
            }
            canvas->drawRect(dest, paint);
            canvas->drawRect(bounds, paint);
        }
        canvas->restore();
    }
@@ -877,6 +877,10 @@ inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer)
                      .bottom = static_cast<int>(rect.fBottom)};
}

inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
    return layer->backgroundBlurRadius > 0 || layer->blurRegions.size();
}

inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
    return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
}
+1 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ private:
    inline SkRect getSkRect(const Rect& layer);
    inline SkRRect getRoundedRect(const LayerSettings* layer);
    inline BlurRegion getBlurRegion(const LayerSettings* layer);
    inline bool layerHasBlur(const LayerSettings* layer);
    inline SkColor getSkColor(const vec4& color);
    inline SkM44 getSkM44(const mat4& matrix);
    inline SkPoint3 getSkPoint3(const vec3& vector);
+21 −22
Original line number Diff line number Diff line
@@ -34,17 +34,14 @@ 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);

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

            return half4(c.rgb * 0.2, 1.0);
        }
@@ -57,8 +54,8 @@ BlurFilter::BlurFilter() {
    mBlurEffect = std::move(blurEffect);
}

sk_sp<SkImage> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
                                    const uint32_t blurRadius, SkRect rect) const {
sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
                                    const sk_sp<SkImage> input, const SkRect& blurRect) const {
    // 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.
@@ -66,33 +63,35 @@ sk_sp<SkImage> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> inp
    float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
    float radiusByPasses = tmpRadius / (float)numberOfPasses;

    SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)rect.width() * kInputScale,
                                                        (float)rect.height() * kInputScale);
    // create blur surface with the bit depth and colorspace of the original surface
    SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale,
                                                       blurRect.height() * kInputScale);

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

    // start by drawing and downscaling and doing the first blur pass
    // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
    // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
    // but instead we must do the inverse.
    SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
    blurMatrix.postScale(kInputScale, kInputScale);

    // start by downscaling and doing the first blur pass
    SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
    SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
    blurBuilder.child("input") =
            input->makeImageSnapshot(rect.round())
                    ->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
    blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
    blurBuilder.uniform("in_blurOffset") =
            SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
            input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
    blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};

    sk_sp<SkImage> tmpBlur(
            blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false));
    sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));

    // And now we'll build our chain of scaled blur stages
    blurBuilder.uniform("in_inverseScale") = 1.0f;
    for (auto i = 1; i < numberOfPasses; i++) {
        const float stepScale = (float)i * kInputScale;
        blurBuilder.child("input") =
                tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
        blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
        tmpBlur = blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false);
        tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
    }

    return tmpBlur;
+2 −2
Original line number Diff line number Diff line
@@ -48,8 +48,8 @@ public:
    virtual ~BlurFilter(){};

    // Execute blur, saving it to a texture
    sk_sp<SkImage> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
                            SkRect rect) const;
    sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
                            const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
    // Returns a matrix that should be applied to the blur shader
    SkMatrix getShaderMatrix() const;

+7 −4
Original line number Diff line number Diff line
@@ -950,15 +950,18 @@ void RenderEngineTest::fillBufferAndBlurBackground() {
    blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
    blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
    blurLayer.backgroundBlurRadius = blurRadius;
    SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
    blurLayer.alpha = 0;
    layers.push_back(&blurLayer);

    invokeDraw(settings, layers);

    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 */);
    // solid color
    expectBufferColor(Rect(0, 0, 1, 1), 255, 0, 0, 255, 0 /* tolerance */);

    // blurred color (downsampling should result in the center color being close to 128)
    expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255,
                      10 /* tolerance */);
}

template <typename SourceVariant>