From 579317d4eb9648845f6f45bfbea2a98fa7813de4 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Tue, 29 Aug 2017 16:33:49 -0400 Subject: [PATCH] Avoid performing the pathOp for clipped shadows if possible. Bug: 64487466 Test: SystemUiJankTests#testRecentAppsFling Change-Id: I2ca96bd6adba299cd31e12f005b2529c559740d2 --- libs/hwui/RenderNode.cpp | 19 ++++++++++++++++ libs/hwui/RenderNode.h | 21 ++++++++++++++++++ .../pipeline/skia/ReorderBarrierDrawables.cpp | 22 +++++++------------ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 61b3876cd095e..36a747519c373 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -32,6 +32,7 @@ #include "protos/hwui.pb.h" #include "protos/ProtoHelpers.h" +#include #include #include #include @@ -555,5 +556,23 @@ void RenderNode::computeOrderingImpl( } } +const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const { + const SkPath* outlinePath = properties().getOutline().getPath(); + const uint32_t outlineID = outlinePath->getGenerationID(); + + if (outlineID != mClippedOutlineCache.outlineID || clipRect != mClippedOutlineCache.clipRect) { + // update the cache keys + mClippedOutlineCache.outlineID = outlineID; + mClippedOutlineCache.clipRect = clipRect; + + // update the cache value by recomputing a new path + SkPath clipPath; + clipPath.addRect(clipRect); + Op(*outlinePath, clipPath, kIntersect_SkPathOp, &mClippedOutlineCache.clippedOutline); + + } + return &mClippedOutlineCache.clippedOutline; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c4ae82af430c1..89e022f5e68d3 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -365,6 +365,17 @@ public: return mSkiaLayer.get(); } + /** + * Returns the path that represents the outline of RenderNode intersected with + * the provided rect. This call will internally cache the resulting path in + * order to potentially return that path for subsequent calls to this method. + * By reusing the same path we get better performance on the GPU backends since + * those resources are cached in the hardware based on the path's genID. + * + * The returned path is only guaranteed to be valid until this function is called + * again or the RenderNode's outline is mutated. + */ + const SkPath* getClippedOutline(const SkRect& clipRect) const; private: /** * If this RenderNode has been used in a previous frame then the SkiaDisplayList @@ -380,6 +391,16 @@ private: * when it has been set to draw as a LayerType::RenderLayer. */ std::unique_ptr mSkiaLayer; + + struct ClippedOutlineCache { + // keys + uint32_t outlineID = 0; + SkRect clipRect; + + // value + SkPath clippedOutline; + }; + mutable ClippedOutlineCache mClippedOutlineCache; }; // class RenderNode class MarkAndSweepRemoved : public TreeObserver { diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 374d364787de8..c8207bc70dd49 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -163,28 +163,22 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* hwuiMatrix.copyTo(shadowMatrix); canvas->concat(shadowMatrix); - const SkPath* casterOutlinePath = casterProperties.getOutline().getPath(); - // holds temporary SkPath to store the result of intersections - SkPath tmpPath; - const SkPath* casterPath = casterOutlinePath; + // default the shadow-casting path to the outline of the caster + const SkPath* casterPath = casterProperties.getOutline().getPath(); + + // intersect the shadow-casting path with the clipBounds, if present + if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) { + casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect); + } - // TODO: In to following course of code that calculates the final shape, is there an optimal - // of doing the Op calculations? // intersect the shadow-casting path with the reveal, if present + SkPath tmpPath; // holds temporary SkPath to store the result of intersections if (revealClipPath) { Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath); tmpPath.setIsVolatile(true); casterPath = &tmpPath; } - // intersect the shadow-casting path with the clipBounds, if present - if (clippedToBounds) { - SkPath clipBoundsPath; - clipBoundsPath.addRect(casterClipRect); - Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath); - tmpPath.setIsVolatile(true); - casterPath = &tmpPath; - } const Vector3 lightPos = SkiaPipeline::getLightCenter(); SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); SkPoint3 zParams;