diff options
author | bsalomon <bsalomon@google.com> | 2016-09-21 11:16:05 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-09-21 11:16:05 -0700 |
commit | 39ef7fb885d7be648b9f0ecd027bc400d1213cec (patch) | |
tree | dadf2a165d64b90cce680395b1c013e19c01bee7 /src/gpu | |
parent | 669983856d99b9312be3166b7dd1f8483a90c315 (diff) |
Add optional sw generated path coverage mask caching
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2335343008
All the bots except the painfully slow windows compiler have finished so,
NOTRY=true
Review-Url: https://codereview.chromium.org/2335343008
Diffstat (limited to 'src/gpu')
-rw-r--r-- | src/gpu/GrContext.cpp | 2 | ||||
-rw-r--r-- | src/gpu/GrDrawingManager.cpp | 4 | ||||
-rw-r--r-- | src/gpu/GrPathRendererChain.cpp | 46 | ||||
-rw-r--r-- | src/gpu/GrPathRendererChain.h | 2 | ||||
-rw-r--r-- | src/gpu/GrSWMaskHelper.cpp | 22 | ||||
-rw-r--r-- | src/gpu/GrSWMaskHelper.h | 28 | ||||
-rw-r--r-- | src/gpu/GrSoftwarePathRenderer.cpp | 138 | ||||
-rw-r--r-- | src/gpu/GrSoftwarePathRenderer.h | 5 |
8 files changed, 162 insertions, 85 deletions
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index fbb59a3e9a..6cec1da86b 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -97,6 +97,8 @@ void GrContext::initCommon(const GrContextOptions& options) { dtOptions.fMaxBatchLookahead = options.fMaxBatchLookahead; GrPathRendererChain::Options prcOptions; prcOptions.fDisableDistanceFieldRenderer = options.fDisableDistanceFieldPaths; + prcOptions.fAllowPathMaskCaching = options.fAllowPathMaskCaching; + prcOptions.fDisableAllPathRenderers = options.fForceSWPathMasks; fDrawingManager.reset(new GrDrawingManager(this, dtOptions, prcOptions, options.fImmediateMode, &fSingleOwner)); diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp index 9dd5613f21..f4ac150830 100644 --- a/src/gpu/GrDrawingManager.cpp +++ b/src/gpu/GrDrawingManager.cpp @@ -203,7 +203,9 @@ GrPathRenderer* GrDrawingManager::getPathRenderer(const GrPathRenderer::CanDrawP GrPathRenderer* pr = fPathRendererChain->getPathRenderer(args, drawType, stencilSupport); if (!pr && allowSW) { if (!fSoftwarePathRenderer) { - fSoftwarePathRenderer = new GrSoftwarePathRenderer(fContext->textureProvider()); + fSoftwarePathRenderer = + new GrSoftwarePathRenderer(fContext->textureProvider(), + fOptionsForPathRendererChain.fAllowPathMaskCaching); } pr = fSoftwarePathRenderer; } diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp index 12ad51e7f3..95105ba079 100644 --- a/src/gpu/GrPathRendererChain.cpp +++ b/src/gpu/GrPathRendererChain.cpp @@ -26,30 +26,32 @@ #include "batches/GrTessellatingPathRenderer.h" GrPathRendererChain::GrPathRendererChain(GrContext* context, const Options& options) { - const GrCaps& caps = *context->caps(); - this->addPathRenderer(new GrDashLinePathRenderer)->unref(); + if (!options.fDisableAllPathRenderers) { + const GrCaps& caps = *context->caps(); + this->addPathRenderer(new GrDashLinePathRenderer)->unref(); - if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(context->resourceProvider(), - caps)) { - this->addPathRenderer(pr)->unref(); - } -#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK - if (caps.sampleShadingSupport()) { - this->addPathRenderer(new GrMSAAPathRenderer)->unref(); - } -#endif - this->addPathRenderer(new GrAAHairLinePathRenderer)->unref(); - this->addPathRenderer(new GrAAConvexPathRenderer)->unref(); - this->addPathRenderer(new GrAALinearizingConvexPathRenderer)->unref(); - if (caps.shaderCaps()->plsPathRenderingSupport()) { - this->addPathRenderer(new GrPLSPathRenderer)->unref(); - } - if (!options.fDisableDistanceFieldRenderer) { - this->addPathRenderer(new GrAADistanceFieldPathRenderer)->unref(); + if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(context->resourceProvider(), + caps)) { + this->addPathRenderer(pr)->unref(); + } + #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK + if (caps.sampleShadingSupport()) { + this->addPathRenderer(new GrMSAAPathRenderer)->unref(); + } + #endif + this->addPathRenderer(new GrAAHairLinePathRenderer)->unref(); + this->addPathRenderer(new GrAAConvexPathRenderer)->unref(); + this->addPathRenderer(new GrAALinearizingConvexPathRenderer)->unref(); + if (caps.shaderCaps()->plsPathRenderingSupport()) { + this->addPathRenderer(new GrPLSPathRenderer)->unref(); + } + if (!options.fDisableDistanceFieldRenderer) { + this->addPathRenderer(new GrAADistanceFieldPathRenderer)->unref(); + } + this->addPathRenderer(new GrTessellatingPathRenderer)->unref(); + this->addPathRenderer(new GrDefaultPathRenderer(caps.twoSidedStencilSupport(), + caps.stencilWrapOpsSupport()))->unref(); } - this->addPathRenderer(new GrTessellatingPathRenderer)->unref(); - this->addPathRenderer(new GrDefaultPathRenderer(caps.twoSidedStencilSupport(), - caps.stencilWrapOpsSupport()))->unref(); } GrPathRendererChain::~GrPathRendererChain() { diff --git a/src/gpu/GrPathRendererChain.h b/src/gpu/GrPathRendererChain.h index 2a11a19cda..8788374d71 100644 --- a/src/gpu/GrPathRendererChain.h +++ b/src/gpu/GrPathRendererChain.h @@ -25,6 +25,8 @@ class GrPathRendererChain : public SkNoncopyable { public: struct Options { bool fDisableDistanceFieldRenderer = false; + bool fAllowPathMaskCaching = false; + bool fDisableAllPathRenderers = false; }; GrPathRendererChain(GrContext* context, const Options&); diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp index 66d60ab956..a20eacb75c 100644 --- a/src/gpu/GrSWMaskHelper.cpp +++ b/src/gpu/GrSWMaskHelper.cpp @@ -99,13 +99,17 @@ bool GrSWMaskHelper::init(const SkIRect& resultBounds, const SkMatrix* matrix) { /** * Get a texture (from the texture cache) of the correct size & format. */ -GrTexture* GrSWMaskHelper::createTexture() { +GrTexture* GrSWMaskHelper::createTexture(TextureType textureType) { GrSurfaceDesc desc; desc.fWidth = fPixels.width(); desc.fHeight = fPixels.height(); desc.fConfig = kAlpha_8_GrPixelConfig; - return fTexProvider->createApproxTexture(desc); + if (TextureType::kApproximateFit == textureType) { + return fTexProvider->createApproxTexture(desc); + } else { + return fTexProvider->createTexture(desc, SkBudgeted::kYes); + } } /** @@ -138,6 +142,7 @@ GrTexture* GrSWMaskHelper::DrawShapeMaskToTexture(GrTextureProvider* texProvider const GrShape& shape, const SkIRect& resultBounds, bool antiAlias, + TextureType textureType, const SkMatrix* matrix) { GrSWMaskHelper helper(texProvider); @@ -147,7 +152,7 @@ GrTexture* GrSWMaskHelper::DrawShapeMaskToTexture(GrTextureProvider* texProvider helper.drawShape(shape, SkRegion::kReplace_Op, antiAlias, 0xFF); - GrTexture* texture(helper.createTexture()); + GrTexture* texture(helper.createTexture(textureType)); if (!texture) { return nullptr; } @@ -163,23 +168,22 @@ void GrSWMaskHelper::DrawToTargetWithShapeMask(GrTexture* texture, const GrUserStencilSettings& userStencilSettings, const GrClip& clip, const SkMatrix& viewMatrix, - const SkIRect& rect) { + const SkIPoint& textureOriginInDeviceSpace, + const SkIRect& deviceSpaceRectToDraw) { SkMatrix invert; if (!viewMatrix.invert(&invert)) { return; } - SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft, - SK_Scalar1 * rect.fTop, - SK_Scalar1 * rect.fRight, - SK_Scalar1 * rect.fBottom); + SkRect dstRect = SkRect::Make(deviceSpaceRectToDraw); // We use device coords to compute the texture coordinates. We take the device coords and apply // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling // matrix to normalized coords. SkMatrix maskMatrix; maskMatrix.setIDiv(texture->width(), texture->height()); - maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop)); + maskMatrix.preTranslate(SkIntToScalar(-textureOriginInDeviceSpace.fX), + SkIntToScalar(-textureOriginInDeviceSpace.fY)); GrPipelineBuilder pipelineBuilder(paint, drawContext->mustUseHWAA(paint)); pipelineBuilder.setUserStencil(&userStencilSettings); diff --git a/src/gpu/GrSWMaskHelper.h b/src/gpu/GrSWMaskHelper.h index f2bb34b30c..46520a91be 100644 --- a/src/gpu/GrSWMaskHelper.h +++ b/src/gpu/GrSWMaskHelper.h @@ -8,8 +8,9 @@ #ifndef GrSWMaskHelper_DEFINED #define GrSWMaskHelper_DEFINED -#include "SkAutoPixmapStorage.h" #include "GrColor.h" +#include "GrTextureProvider.h" +#include "SkAutoPixmapStorage.h" #include "SkBitmap.h" #include "SkDraw.h" #include "SkMatrix.h" @@ -66,36 +67,37 @@ public: fPixels.erase(SkColorSetARGB(alpha, 0xFF, 0xFF, 0xFF)); } + + enum class TextureType { + kExactFit, + kApproximateFit + }; + // Canonical usage utility that draws a single path and uploads it // to the GPU. The result is returned. static GrTexture* DrawShapeMaskToTexture(GrTextureProvider*, const GrShape&, const SkIRect& resultBounds, bool antiAlias, + TextureType, const SkMatrix* matrix); - // This utility routine is used to add a shape's mask to some other draw. - // The GrClipStackClip uses it to accumulate clip masks while the - // GrSoftwarePathRenderer uses it to fulfill a drawPath call. - // It draws with "texture" as a path mask into "target" using "rect" as - // geometry and the current drawState. The current drawState is altered to - // accommodate the mask. - // Note that this method assumes that the GrPaint::kTotalStages slot in - // the draw state can be used to hold the mask texture stage. - // This method is really only intended to be used with the - // output of DrawPathMaskToTexture. + // This utility draws a path mask generated by DrawShapeMaskToTexture using a provided paint. + // The rectangle is drawn in device space. The 'viewMatrix' will be used to ensure the correct + // local coords are provided to any fragment processors in the paint. static void DrawToTargetWithShapeMask(GrTexture* texture, GrDrawContext*, const GrPaint& paint, const GrUserStencilSettings& userStencilSettings, const GrClip&, const SkMatrix& viewMatrix, - const SkIRect& rect); + const SkIPoint& textureOriginInDeviceSpace, + const SkIRect& deviceSpaceRectToDraw); private: // Helper function to get a scratch texture suitable for capturing the // result (i.e., right size & format) - GrTexture* createTexture(); + GrTexture* createTexture(TextureType); GrTextureProvider* fTexProvider; SkMatrix fMatrix; diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp index 05b3ef8853..c22ce66005 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; diff --git a/src/gpu/GrSoftwarePathRenderer.h b/src/gpu/GrSoftwarePathRenderer.h index 1548fa69c0..72d967323e 100644 --- a/src/gpu/GrSoftwarePathRenderer.h +++ b/src/gpu/GrSoftwarePathRenderer.h @@ -18,7 +18,9 @@ class GrTextureProvider; */ class GrSoftwarePathRenderer : public GrPathRenderer { public: - GrSoftwarePathRenderer(GrTextureProvider* texProvider) : fTexProvider(texProvider) { } + GrSoftwarePathRenderer(GrTextureProvider* texProvider, bool allowCaching) + : fTexProvider(texProvider) + , fAllowCaching(allowCaching) {} private: static void DrawNonAARect(GrDrawContext* drawContext, const GrPaint& paint, @@ -45,6 +47,7 @@ private: private: GrTextureProvider* fTexProvider; + bool fAllowCaching; typedef GrPathRenderer INHERITED; }; |