Additional clipping logic for vk webview
Bug: 172857687
Bug: 176834621
Test: WebView test app
Change-Id: I6885e0078f6ff6a90a5a2a9e616adb41deee12db
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 6bf2e99..11a9086 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -16,12 +16,14 @@
#include "RecordingCanvas.h"
-#include "pipeline/skia/FunctorDrawable.h"
-#include "VectorDrawable.h"
+#include <GrRecordingContext.h>
+
+#include <experimental/type_traits>
#include "SkAndroidFrameworkUtils.h"
#include "SkCanvas.h"
#include "SkCanvasPriv.h"
+#include "SkColor.h"
#include "SkData.h"
#include "SkDrawShadowInfo.h"
#include "SkImage.h"
@@ -33,8 +35,8 @@
#include "SkRegion.h"
#include "SkTextBlob.h"
#include "SkVertices.h"
-
-#include <experimental/type_traits>
+#include "VectorDrawable.h"
+#include "pipeline/skia/FunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -500,7 +502,68 @@
// SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw.
// SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke
// onSnapGpuDrawHandler.
- void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get()); }
+private:
+ // Unfortunately WebView does not have complex clip information serialized, and we only perform
+ // best-effort stencil fill for GLES. So for Vulkan we create an intermediate layer if the
+ // canvas clip is complex.
+ static bool needsCompositedLayer(SkCanvas* c) {
+ if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
+ return false;
+ }
+ SkRegion clipRegion;
+ // WebView's rasterizer has access to simple clips, so for Vulkan we only need to check if
+ // the clip is more complex than a rectangle.
+ c->temporary_internal_getRgnClip(&clipRegion);
+ return clipRegion.isComplex();
+ }
+
+ mutable SkImageInfo mLayerImageInfo;
+ mutable sk_sp<SkSurface> mLayerSurface = nullptr;
+
+public:
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ if (needsCompositedLayer(c)) {
+ // What we do now is create an offscreen surface, sized by the clip bounds.
+ // We won't apply a clip while drawing - clipping will be performed when compositing the
+ // surface back onto the original canvas. Note also that we're not using saveLayer
+ // because the webview functor still doesn't respect the canvas clip stack.
+ const SkIRect deviceBounds = c->getDeviceClipBounds();
+ if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) {
+ GrRecordingContext* directContext = c->recordingContext();
+ mLayerImageInfo =
+ c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height());
+ mLayerSurface = SkSurface::MakeRenderTarget(directContext, SkBudgeted::kYes,
+ mLayerImageInfo, 0,
+ kTopLeft_GrSurfaceOrigin, nullptr);
+ }
+
+ SkCanvas* layerCanvas = mLayerSurface->getCanvas();
+
+ SkAutoCanvasRestore(layerCanvas, true);
+ layerCanvas->clear(SK_ColorTRANSPARENT);
+
+ // Preserve the transform from the original canvas, but now the clip rectangle is
+ // anchored at the origin so we need to transform the clipped content to the origin.
+ SkM44 mat4(c->getLocalToDevice());
+ mat4.postTranslate(-deviceBounds.fLeft, -deviceBounds.fTop);
+ layerCanvas->concat(mat4);
+ layerCanvas->drawDrawable(drawable.get());
+
+ SkAutoCanvasRestore acr(c, true);
+
+ // Temporarily use an identity transform, because this is just blitting to the parent
+ // canvas with an offset.
+ SkMatrix invertedMatrix;
+ if (!c->getTotalMatrix().invert(&invertedMatrix)) {
+ ALOGW("Unable to extract invert canvas matrix; aborting VkFunctor draw");
+ return;
+ }
+ c->concat(invertedMatrix);
+ mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop, nullptr);
+ } else {
+ c->drawDrawable(drawable.get());
+ }
+ }
};
}