Index: src/gpu/GrSoftwarePathRenderer.cpp |
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp |
index 05b3ef8853bc42ded0f52972466e16c7462be75d..c22ce66005478ae7cce4f2665af99f8209ceee96 100644 |
--- a/src/gpu/GrSoftwarePathRenderer.cpp |
+++ b/src/gpu/GrSoftwarePathRenderer.cpp |
@@ -9,6 +9,7 @@ |
#include "GrAuditTrail.h" |
#include "GrClip.h" |
#include "GrPipelineBuilder.h" |
+#include "GrGpuResourcePriv.h" |
#include "GrSWMaskHelper.h" |
#include "GrTextureProvider.h" |
#include "batches/GrRectBatchFactory.h" |
@@ -20,39 +21,38 @@ bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { |
return !args.fShape->style().applies() && SkToBool(fTexProvider); |
} |
-namespace { |
- |
//////////////////////////////////////////////////////////////////////////////// |
-// gets device coord bounds of path (not considering the fill) and clip. The |
-// path bounds will be a subset of the clip bounds. returns false if |
-// path bounds would be empty. |
-bool get_shape_and_clip_bounds(int width, int height, |
- const GrClip& clip, |
- const GrShape& shape, |
- const SkMatrix& matrix, |
- SkIRect* devShapeBounds, |
- SkIRect* devClipBounds) { |
+static bool get_unclipped_shape_dev_bounds(const GrShape& shape, const SkMatrix& matrix, |
+ SkIRect* devBounds) { |
+ SkRect shapeBounds = shape.styledBounds(); |
+ if (shapeBounds.isEmpty()) { |
+ return false; |
+ } |
+ SkRect shapeDevBounds; |
+ matrix.mapRect(&shapeDevBounds, shapeBounds); |
+ shapeDevBounds.roundOut(devBounds); |
+ return true; |
+} |
+ |
+// Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there |
+// is no intersection. |
+static bool get_shape_and_clip_bounds(int width, int height, |
+ const GrClip& clip, |
+ const GrShape& shape, |
+ const SkMatrix& matrix, |
+ SkIRect* unclippedDevShapeBounds, |
+ SkIRect* clippedDevShapeBounds, |
+ SkIRect* devClipBounds) { |
// compute bounds as intersection of rt size, clip, and path |
clip.getConservativeBounds(width, height, devClipBounds); |
- if (devClipBounds->isEmpty()) { |
- *devShapeBounds = SkIRect::MakeWH(width, height); |
+ if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) { |
+ *unclippedDevShapeBounds = SkIRect::EmptyIRect(); |
+ *clippedDevShapeBounds = SkIRect::EmptyIRect(); |
return false; |
} |
- SkRect shapeBounds = shape.styledBounds(); |
- if (!shapeBounds.isEmpty()) { |
- SkRect shapeSBounds; |
- matrix.mapRect(&shapeSBounds, shapeBounds); |
- SkIRect shapeIBounds; |
- shapeSBounds.roundOut(&shapeIBounds); |
- *devShapeBounds = *devClipBounds; |
- if (!devShapeBounds->intersect(shapeIBounds)) { |
- // set the correct path bounds, as this would be used later. |
- *devShapeBounds = shapeIBounds; |
- return false; |
- } |
- } else { |
- *devShapeBounds = SkIRect::EmptyIRect(); |
+ if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) { |
+ *clippedDevShapeBounds = SkIRect::EmptyIRect(); |
return false; |
} |
return true; |
@@ -60,8 +60,6 @@ bool get_shape_and_clip_bounds(int width, int height, |
//////////////////////////////////////////////////////////////////////////////// |
-} |
- |
void GrSoftwarePathRenderer::DrawNonAARect(GrDrawContext* drawContext, |
const GrPaint& paint, |
const GrUserStencilSettings& userStencilSettings, |
@@ -133,35 +131,97 @@ bool GrSoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) { |
SkASSERT(!args.fShape->style().applies()); |
inverseFilled = args.fShape->inverseFilled(); |
- SkIRect devShapeBounds, devClipBounds; |
+ SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds; |
+ // To prevent overloading the cache with entries during animations we limit the cache of masks |
+ // to cases where the matrix preserves axis alignment. |
+ bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() && |
+ args.fShape->hasUnstyledKey() && args.fAntiAlias; |
+ |
if (!get_shape_and_clip_bounds(args.fDrawContext->width(), args.fDrawContext->height(), |
*args.fClip, *args.fShape, |
- *args.fViewMatrix, &devShapeBounds, &devClipBounds)) { |
+ *args.fViewMatrix, &unclippedDevShapeBounds, |
+ &clippedDevShapeBounds, |
+ &devClipBounds)) { |
if (inverseFilled) { |
DrawAroundInvPath(args.fDrawContext, *args.fPaint, *args.fUserStencilSettings, |
*args.fClip, |
- *args.fViewMatrix, devClipBounds, devShapeBounds); |
+ *args.fViewMatrix, devClipBounds, unclippedDevShapeBounds); |
} |
return true; |
} |
- SkAutoTUnref<GrTexture> texture( |
- GrSWMaskHelper::DrawShapeMaskToTexture(fTexProvider, *args.fShape, devShapeBounds, |
- args.fAntiAlias, args.fViewMatrix)); |
+ const SkIRect* boundsForMask = &clippedDevShapeBounds; |
+ if (useCache) { |
+ // Use the cache only if >50% of the path is visible. |
+ int unclippedWidth = unclippedDevShapeBounds.width(); |
+ int unclippedHeight = unclippedDevShapeBounds.height(); |
+ int unclippedArea = unclippedWidth * unclippedHeight; |
+ int clippedArea = clippedDevShapeBounds.width() * clippedDevShapeBounds.height(); |
+ int maxTextureSize = args.fDrawContext->caps()->maxTextureSize(); |
+ if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize || |
+ unclippedHeight > maxTextureSize) { |
+ useCache = false; |
+ } else { |
+ boundsForMask = &unclippedDevShapeBounds; |
+ } |
+ } |
+ |
+ GrUniqueKey maskKey; |
+ struct KeyData { |
+ SkScalar fFractionalTranslateX; |
+ SkScalar fFractionalTranslateY; |
+ }; |
+ |
+ if (useCache) { |
+ // We require the upper left 2x2 of the matrix to match exactly for a cache hit. |
+ SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX); |
+ SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY); |
+ SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX); |
+ SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY); |
+ SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX); |
+ SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY); |
+ // Allow 8 bits each in x and y of subpixel positioning. |
+ SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00; |
+ SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00; |
+ static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
+ GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize()); |
+ builder[0] = SkFloat2Bits(sx); |
+ builder[1] = SkFloat2Bits(sy); |
+ builder[2] = SkFloat2Bits(kx); |
+ builder[3] = SkFloat2Bits(ky); |
+ builder[4] = fracX | (fracY >> 8); |
+ args.fShape->writeUnstyledKey(&builder[5]); |
+ } |
+ |
+ sk_sp<GrTexture> texture; |
+ if (useCache) { |
+ texture.reset(args.fResourceProvider->findAndRefTextureByUniqueKey(maskKey)); |
+ } |
if (!texture) { |
- return false; |
+ GrSWMaskHelper::TextureType type = useCache ? GrSWMaskHelper::TextureType::kExactFit |
+ : GrSWMaskHelper::TextureType::kApproximateFit; |
+ texture.reset(GrSWMaskHelper::DrawShapeMaskToTexture(fTexProvider, *args.fShape, |
+ *boundsForMask, args.fAntiAlias, |
+ type, args.fViewMatrix)); |
+ if (!texture) { |
+ return false; |
+ } |
+ if (useCache) { |
+ texture->resourcePriv().setUniqueKey(maskKey); |
+ } |
} |
- GrSWMaskHelper::DrawToTargetWithShapeMask(texture, args.fDrawContext, *args.fPaint, |
+ GrSWMaskHelper::DrawToTargetWithShapeMask(texture.get(), args.fDrawContext, *args.fPaint, |
*args.fUserStencilSettings, |
*args.fClip, *args.fViewMatrix, |
- devShapeBounds); |
+ SkIPoint {boundsForMask->fLeft, boundsForMask->fTop}, |
+ *boundsForMask); |
if (inverseFilled) { |
DrawAroundInvPath(args.fDrawContext, *args.fPaint, *args.fUserStencilSettings, |
*args.fClip, |
- *args.fViewMatrix, devClipBounds, devShapeBounds); |
+ *args.fViewMatrix, devClipBounds, unclippedDevShapeBounds); |
} |
return true; |