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