Avoid performing the pathOp for clipped shadows if possible.

Bug: 64487466
Test: SystemUiJankTests#testRecentAppsFling
Change-Id: I2ca96bd6adba299cd31e12f005b2529c559740d2
This commit is contained in:
Derek Sollenberger
2017-08-29 16:33:49 -04:00
parent fbedb22745
commit 579317d4eb
3 changed files with 48 additions and 14 deletions

View File

@@ -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 @@ 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 */

View File

@@ -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<skiapipeline::SkiaLayer> mSkiaLayer;
struct ClippedOutlineCache {
// keys
uint32_t outlineID = 0;
SkRect clipRect;
// value
SkPath clippedOutline;
};
mutable ClippedOutlineCache mClippedOutlineCache;
}; // class RenderNode
class MarkAndSweepRemoved : public TreeObserver {

View File

@@ -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;