Improve RenderEngine's blur performance.
This CL uses new Skia APIs to skip the unnecessary glClear issued when
creating the temporary surfaces. On a Pixel 5 device this showed an
~6% improvement in total GPU time as well as a similar improvement on
the CPU time taken by Skia to isssue the calls to the GPU.
Test: perfetto traces
Bug: 176903027
Change-Id: If04b795ce44107670e9e752b10ab5470393e1e32
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index dc04f69..2ab3c6c 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -593,6 +593,7 @@
// view is still on-screen. The clear region could be re-specified as a black color layer,
// however.
if (!display.clearRegion.isEmpty()) {
+ ATRACE_NAME("ClearRegion");
size_t numRects = 0;
Rect const* rects = display.clearRegion.getArray(&numRects);
SkIRect skRects[numRects];
@@ -612,6 +613,7 @@
}
for (const auto& layer : layers) {
+ ATRACE_NAME("DrawLayer");
canvas->save();
if (mCapture->isCaptureRunning()) {
@@ -630,7 +632,7 @@
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<SkSurface>> cachedBlurs;
+ std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
if (mBlurFilter) {
if (layer->backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
@@ -882,17 +884,15 @@
}
void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
- const SkRect& layerRect, sk_sp<SkSurface> blurredSurface) {
+ const SkRect& layerRect, sk_sp<SkImage> blurredImage) {
ATRACE_CALL();
SkPaint paint;
paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
const auto matrix = getBlurShaderTransform(canvas, layerRect);
- paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(
- SkTileMode::kClamp,
- SkTileMode::kClamp,
- SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
- &matrix));
+ SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ paint.setShader(blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
+ &matrix));
auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
effectRegion.bottom);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index ed62a2a..c094680 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -89,7 +89,7 @@
void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
const ShadowSettings& shadowSettings);
void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerRect,
- sk_sp<SkSurface> blurredSurface);
+ sk_sp<SkImage> blurredImage);
SkMatrix getBlurShaderTransform(const SkCanvas* canvas, const SkRect& layerRect);
// If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime
// shader. Otherwise it returns the input shader.
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 8927be8..87b1a7c 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -57,8 +57,8 @@
mBlurEffect = std::move(blurEffect);
}
-sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
- const uint32_t blurRadius, SkRect rect) const {
+sk_sp<SkImage> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+ const uint32_t blurRadius, SkRect rect) 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.
@@ -68,9 +68,6 @@
SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)rect.width() * kInputScale,
(float)rect.height() * kInputScale);
- SkRect scaledRect = SkRect::MakeWH(scaledInfo.width(), scaledInfo.height());
-
- auto drawSurface = canvas->makeSurface(scaledInfo);
const float stepX = radiusByPasses;
const float stepY = radiusByPasses;
@@ -85,49 +82,20 @@
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);
+ sk_sp<SkImage> tmpBlur(
+ blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false));
- drawSurface->getCanvas()->drawRect(scaledRect, paint);
-
- blurBuilder.child("input") = nullptr;
+ // 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);
}
- // 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(SkTileMode::kClamp,
- SkTileMode::kClamp, linear);
- 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()->drawRect(scaledRect, paint);
-
- // Swap buffers for next iteration
- const auto tmp = drawSurface;
- drawSurface = readSurface;
- readSurface = tmp;
- blurBuilder.child("input") = nullptr;
- }
- lastDrawTarget = readSurface;
- }
-
- return lastDrawTarget;
+ return tmpBlur;
}
SkMatrix BlurFilter::getShaderMatrix() const {
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 734bfcb..08641c9 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<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
- SkRect rect) const;
+ sk_sp<SkImage> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
+ SkRect rect) const;
// Returns a matrix that should be applied to the blur shader
SkMatrix getShaderMatrix() const;