Avoid performing the pathOp for clipped shadows if possible.
Bug: 64487466
Test: SystemUiJankTests#testRecentAppsFling
Change-Id: I2ca96bd6adba299cd31e12f005b2529c559740d2
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 61b3876..36a7475 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 <SkPathOps.h>
#include <algorithm>
#include <sstream>
#include <string>
@@ -555,5 +556,23 @@
}
}
+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 c4ae82a..89e022f 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -365,6 +365,17 @@
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 @@
* when it has been set to draw as a LayerType::RenderLayer.
*/
std::unique_ptr<skiapipeline::SkiaLayer> 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 374d364..c8207bc 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -163,28 +163,22 @@
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();
- // 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 clipBounds, if present
+ if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) {
+ casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect);
+ }
+
// 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;