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
This commit is contained in:
parent
669983856d
commit
39ef7fb885
117
gm/pathmaskcache.cpp
Normal file
117
gm/pathmaskcache.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
#include "GrContext.h"
|
||||
#include "GrContextOptions.h"
|
||||
#include "SkPath.h"
|
||||
|
||||
/** This tests the GPU backend's caching of path coverage masks */
|
||||
class PathMaskCache : public skiagm::GM {
|
||||
public:
|
||||
PathMaskCache() {}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override { return SkString("path_mask_cache"); }
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(650, 950);
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
static constexpr SkScalar kPad = 5.f;
|
||||
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
auto drawPathSet = [canvas] (const SkPath& path, const SkMatrix& m) {
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
SkRect bounds = path.getBounds();
|
||||
m.mapRect(&bounds);
|
||||
bounds.roundOut();
|
||||
canvas->save();
|
||||
canvas->translate(-bounds.fLeft, -bounds.fTop);
|
||||
|
||||
canvas->save();
|
||||
canvas->concat(m);
|
||||
canvas->drawPath(path, paint);
|
||||
canvas->restore();
|
||||
|
||||
// translate by integer
|
||||
canvas->translate(bounds.width() + kPad, 0.f);
|
||||
canvas->save();
|
||||
canvas->concat(m);
|
||||
canvas->drawPath(path, paint);
|
||||
canvas->restore();
|
||||
|
||||
// translate by non-integer
|
||||
canvas->translate(bounds.width() + kPad + 0.15f, 0.f);
|
||||
canvas->save();
|
||||
canvas->concat(m);
|
||||
canvas->drawPath(path, paint);
|
||||
canvas->restore();
|
||||
|
||||
// translate again so total translate fraction is almost identical to previous.
|
||||
canvas->translate(bounds.width() + kPad + 0.002f, 0.f);
|
||||
canvas->save();
|
||||
canvas->concat(m);
|
||||
canvas->drawPath(path, paint);
|
||||
canvas->restore();
|
||||
canvas->restore();
|
||||
return bounds.fBottom + kPad;
|
||||
};
|
||||
|
||||
|
||||
SkTArray<SkPath> paths;
|
||||
paths.push_back();
|
||||
paths.back().moveTo(0.f, 0.f);
|
||||
paths.back().lineTo(98.f, 100.f);
|
||||
paths.back().lineTo(100.f, 100.f);
|
||||
paths.back().conicTo(150.f, 50.f, 100.f, 0.f, 0.6f);
|
||||
paths.back().conicTo(148.f, 50.f, 100.f, 100.f, 0.6f);
|
||||
paths.back().conicTo(50.f, 30.f, 0.f, 100.f, 0.9f);
|
||||
|
||||
paths.push_back();
|
||||
paths.back().addCircle(30.f, 30.f, 30.f);
|
||||
paths.back().addRect(SkRect::MakeXYWH(45.f, 45.f, 50.f, 60.f));
|
||||
paths.back().setFillType(SkPath::kEvenOdd_FillType);
|
||||
|
||||
canvas->translate(kPad, kPad);
|
||||
|
||||
for (const SkPath& path : paths) {
|
||||
SkScalar ty = drawPathSet(path, SkMatrix::I());
|
||||
canvas->translate(0, ty);
|
||||
|
||||
// Non-uniform scale.
|
||||
SkMatrix s;
|
||||
s.setScale(0.5f, 2.f);
|
||||
ty = drawPathSet(path, s);
|
||||
canvas->translate(0.f, ty);
|
||||
|
||||
// Rotation
|
||||
SkMatrix r;
|
||||
r.setRotate(60.f, path.getBounds().centerX(), path.getBounds().centerY());
|
||||
ty = drawPathSet(path, r);
|
||||
canvas->translate(0.f, ty);
|
||||
}
|
||||
}
|
||||
|
||||
void modifyGrContextOptions(GrContextOptions* options) override {
|
||||
options->fForceSWPathMasks = true;
|
||||
options->fAllowPathMaskCaching = true;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
DEF_GM( return new PathMaskCache(); )
|
||||
|
||||
#endif
|
@ -69,6 +69,19 @@ struct GrContextOptions {
|
||||
/** Disables distance field rendering for paths. Distance field computation can be expensive
|
||||
and yields no benefit if a path is not rendered multiple times with different transforms */
|
||||
bool fDisableDistanceFieldPaths = false;
|
||||
|
||||
/**
|
||||
* If true this allows path mask textures to be cached. This is only really useful if paths
|
||||
* are commonly rendered at the same scale and fractional translation.
|
||||
*/
|
||||
bool fAllowPathMaskCaching = false;
|
||||
|
||||
/**
|
||||
* Force all path draws to go through through the sw-rasterize-to-texture code path (assuming
|
||||
* the path is not recognized as a simpler shape (e.g. a rrect). This is intended for testing
|
||||
* purposes.
|
||||
*/
|
||||
bool fForceSWPathMasks = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
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();
|
||||
}
|
||||
#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();
|
||||
}
|
||||
|
||||
GrPathRendererChain::~GrPathRendererChain() {
|
||||
|
@ -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&);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
if (!texture) {
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
GrSWMaskHelper::DrawToTargetWithShapeMask(texture, args.fDrawContext, *args.fPaint,
|
||||
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) {
|
||||
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.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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user