From f09ee58eb7edf7efb0cf8e24ad763a582dac0228 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 6 Nov 2018 17:35:50 -0500 Subject: [PATCH] Put Vulkan WebViews on a HW layer if stencil clip Put WebViews in a HW layer, if the clip is a non-rect. This CL reuses logic implemented by ag/705975. This used to be the way GL WebViews were drawn in Android M. Implement complex clip detection at recording time, which was not previously supported by the canvas. Vulkan WebViews using GL interop are already drawn in a layer, but this CL will be useful when WebView supports new Vulkan interop. Test: WebView CTS pass for Vulkan and GL Bug: 115613038 Change-Id: I9b02c6f4de8efd504a7507633f3d849004215a16 --- libs/hwui/RecordingCanvas.cpp | 18 ++++++++++++++ libs/hwui/RecordingCanvas.h | 31 +++++++++++++++++++++++++ libs/hwui/RenderProperties.h | 8 +++++++ libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 1 - libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 4 ++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 6dc9d34cc3fd..85947665839a 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -759,6 +759,8 @@ RecordingCanvas::RecordingCanvas() : INHERITED(1, 1), fDL(nullptr) {} void RecordingCanvas::reset(DisplayListData* dl, const SkIRect& bounds) { this->resetCanvas(bounds.right(), bounds.bottom()); fDL = dl; + mClipMayBeComplex = false; + mSaveCount = mComplexSaveCount = 0; } sk_sp RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { @@ -770,6 +772,7 @@ void RecordingCanvas::onFlush() { } void RecordingCanvas::willSave() { + mSaveCount++; fDL->save(); } SkCanvas::SaveLayerStrategy RecordingCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { @@ -778,6 +781,11 @@ SkCanvas::SaveLayerStrategy RecordingCanvas::getSaveLayerStrategy(const SaveLaye return SkCanvas::kNoLayer_SaveLayerStrategy; } void RecordingCanvas::willRestore() { + mSaveCount--; + if (mSaveCount < mComplexSaveCount) { + mClipMayBeComplex = false; + mComplexSaveCount = 0; + } fDL->restore(); } @@ -798,17 +806,27 @@ void RecordingCanvas::didTranslate(SkScalar dx, SkScalar dy) { void RecordingCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle style) { fDL->clipRect(rect, op, style == kSoft_ClipEdgeStyle); + if (!getTotalMatrix().isScaleTranslate()) { + setClipMayBeComplex(); + } this->INHERITED::onClipRect(rect, op, style); } void RecordingCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle style) { + if (rrect.getType() > SkRRect::kRect_Type || !getTotalMatrix().isScaleTranslate()) { + setClipMayBeComplex(); + } fDL->clipRRect(rrect, op, style == kSoft_ClipEdgeStyle); this->INHERITED::onClipRRect(rrect, op, style); } void RecordingCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle style) { + setClipMayBeComplex(); fDL->clipPath(path, op, style == kSoft_ClipEdgeStyle); this->INHERITED::onClipPath(path, op, style); } void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) { + if (region.isComplex() || !getTotalMatrix().isScaleTranslate()) { + setClipMayBeComplex(); + } fDL->clipRegion(region, op); this->INHERITED::onClipRegion(region, op); } diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index caaef67f724f..3a76ca1137a5 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -203,10 +203,41 @@ public: void drawVectorDrawable(VectorDrawableRoot* tree); + /** + * If "isClipMayBeComplex" returns false, it is guaranteed the current clip is a rectangle. + * If the return value is true, then clip may or may not be complex (there is no guarantee). + */ + inline bool isClipMayBeComplex() { return mClipMayBeComplex; } + private: typedef SkCanvasVirtualEnforcer INHERITED; + inline void setClipMayBeComplex() { + if (!mClipMayBeComplex) { + mComplexSaveCount = mSaveCount; + mClipMayBeComplex = true; + } + } + DisplayListData* fDL; + + /** + * mClipMayBeComplex tracks if the current clip is a rectangle. This flag is used to promote + * FunctorDrawable to a layer, if it is clipped by a non-rect. + */ + bool mClipMayBeComplex = false; + + /** + * mSaveCount is the current level of our save tree. + */ + int mSaveCount = 0; + + /** + * mComplexSaveCount is the first save level, which has a complex clip. Every level below + * mComplexSaveCount is assumed to have a complex clip and every level above mComplexSaveCount + * is guaranteed to not be complex. + */ + int mComplexSaveCount = 0; }; } // namespace uirenderer diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index ddb7e4e4ce74..e6710cc8f950 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -151,6 +151,7 @@ public: // parent may have already dictated that a descendant layer is needed bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer + || CC_UNLIKELY(isClipMayBeComplex()) // Round rect clipping forces layer for functors || CC_UNLIKELY(getOutline().willRoundRectClip()) || @@ -193,6 +194,12 @@ public: bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; } + bool setClipMayBeComplex(bool isClipMayBeComplex) { + return RP_SET(mPrimitiveFields.mClipMayBeComplex, isClipMayBeComplex); + } + + bool isClipMayBeComplex() const { return mPrimitiveFields.mClipMayBeComplex; } + bool setStaticMatrix(const SkMatrix* matrix) { delete mStaticMatrix; if (matrix) { @@ -563,6 +570,7 @@ private: bool mProjectBackwards = false; bool mProjectionReceiver = false; bool mAllowForceDark = true; + bool mClipMayBeComplex = false; Rect mClipBounds; Outline mOutline; RevealClip mRevealClip; diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 230065c222a9..29d5ef233338 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -73,7 +73,6 @@ bool SkiaDisplayList::prepareListAndChildren( RenderNode* childNode = child.getRenderNode(); Matrix4 mat4(child.getRecordedMatrix()); info.damageAccumulator->pushTransform(&mat4); - // TODO: a layer is needed if the canvas is rotated or has a non-rect clip info.hasBackwardProjectedNodes = false; childFn(childNode, observer, info, functorsNeedLayer); hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index d54275fe7e19..b67aea224055 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -113,6 +113,10 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared. mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier); auto& renderNodeDrawable = mDisplayList->mChildNodes.back(); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + // Put Vulkan WebViews with non-rectangular clips in a HW layer + renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex()); + } drawDrawable(&renderNodeDrawable); // use staging property, since recording on UI thread -- 2.11.0