aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu
diff options
context:
space:
mode:
authorGravatar bsalomon <bsalomon@google.com>2016-09-21 11:16:05 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-09-21 11:16:05 -0700
commit39ef7fb885d7be648b9f0ecd027bc400d1213cec (patch)
treedadf2a165d64b90cce680395b1c013e19c01bee7 /src/gpu
parent669983856d99b9312be3166b7dd1f8483a90c315 (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.cpp2
-rw-r--r--src/gpu/GrDrawingManager.cpp4
-rw-r--r--src/gpu/GrPathRendererChain.cpp46
-rw-r--r--src/gpu/GrPathRendererChain.h2
-rw-r--r--src/gpu/GrSWMaskHelper.cpp22
-rw-r--r--src/gpu/GrSWMaskHelper.h28
-rw-r--r--src/gpu/GrSoftwarePathRenderer.cpp138
-rw-r--r--src/gpu/GrSoftwarePathRenderer.h5
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;
};