Chromium Code Reviews| Index: src/gpu/GrSoftwarePathRenderer.cpp |
| diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp |
| index 05b3ef8853bc42ded0f52972466e16c7462be75d..74cb0976531259c18505b01c7cdb3fc7d4008e63 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,47 +21,45 @@ 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. |
| +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. |
| bool get_shape_and_clip_bounds(int width, int height, |
|
robertphillips
2016/09/21 15:42:39
static here too ?
bsalomon
2016/09/21 16:01:09
Done.
|
| const GrClip& clip, |
| const GrShape& shape, |
| const SkMatrix& matrix, |
| - SkIRect* devShapeBounds, |
| + 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; |
| } |
| -//////////////////////////////////////////////////////////////////////////////// |
| -} |
| +//////////////////////////////////////////////////////////////////////////////// |
| void GrSoftwarePathRenderer::DrawNonAARect(GrDrawContext* drawContext, |
| const GrPaint& paint, |
| @@ -133,35 +132,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; |