Loading libs/renderengine/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ filegroup { srcs: [ "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", "skia/filters/BlurFilter.cpp", ], } Loading libs/renderengine/skia/SkiaGLRenderEngine.cpp +15 −4 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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:"); Loading Loading @@ -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), Loading @@ -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() { Loading Loading @@ -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; Loading libs/renderengine/skia/SkiaGLRenderEngine.h +4 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <SkSurface.h> #include "SkiaRenderEngine.h" #include "filters/BlurFilter.h" namespace android { namespace renderengine { Loading @@ -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{}; Loading Loading @@ -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); Loading libs/renderengine/skia/filters/BlurFilter.cpp 0 → 100644 +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 libs/renderengine/skia/filters/BlurFilter.h 0 → 100644 +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 Loading
libs/renderengine/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ filegroup { srcs: [ "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", "skia/filters/BlurFilter.cpp", ], } Loading
libs/renderengine/skia/SkiaGLRenderEngine.cpp +15 −4 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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:"); Loading Loading @@ -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), Loading @@ -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() { Loading Loading @@ -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; Loading
libs/renderengine/skia/SkiaGLRenderEngine.h +4 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <SkSurface.h> #include "SkiaRenderEngine.h" #include "filters/BlurFilter.h" namespace android { namespace renderengine { Loading @@ -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{}; Loading Loading @@ -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); Loading
libs/renderengine/skia/filters/BlurFilter.cpp 0 → 100644 +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
libs/renderengine/skia/filters/BlurFilter.h 0 → 100644 +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