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
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 1eb8da9..fcb3196 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -661,30 +661,33 @@
// 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);
- std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
- if (mBlurFilter) {
+ const auto bounds = getSkRect(layer->geometry.boundaries);
+ if (mBlurFilter && layerHasBlur(layer)) {
+ std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
+
+ // 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);
- drawBlurRegion(canvas, getBlurRegion(layer), layerRect, blurredSurface);
+ cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+
+ drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage);
}
- if (layer->blurRegions.size() > 0) {
- for (auto region : layer->blurRegions) {
- if (cachedBlurs[region.blurRadius]) {
- continue;
- }
+ for (auto region : layer->blurRegions) {
+ 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 @@
: 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 @@
// 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 @@
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 @@
if (layer->geometry.roundedCornersRadius > 0) {
canvas->clipRRect(getRoundedRect(layer), true);
}
- canvas->drawRect(dest, paint);
+ canvas->drawRect(bounds, paint);
}
canvas->restore();
}
@@ -877,6 +877,10 @@
.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);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 810fc2a..4f4a9d4 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -81,6 +81,7 @@
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);
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 87b1a7c..0d123c7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -34,17 +34,14 @@
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 @@
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 @@
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;
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 08641c9..e9a5dee 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -48,8 +48,8 @@
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;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 58afe6e..886c9da 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -950,15 +950,18 @@
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>