aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar bsalomon <bsalomon@google.com>2016-06-27 07:18:18 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-06-27 07:18:18 -0700
commitee43241d72148545e4e3b75aa4338c491b39cc0c (patch)
tree45db61f18014bd7c5a7a376576aa8065cf2fb65b /src
parentb05df0ff261d7819da55509df03fb4525af49b40 (diff)
Remove style application from GrPathRenderer subclasses
Now that GrPathRenderer is using GrShape it is possible to get a key for a path that was computed by applying style to an original path. This improves path renderer subclass selection, particularly when a post-styled path happens to work with a simpler path renderer (e.g. it is convex). Previously a more expensive path renderer may have applied the style and rendered it (e.g. the distance field PR) GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2064753003 Review-Url: https://codereview.chromium.org/2064753003
Diffstat (limited to 'src')
-rw-r--r--src/gpu/GrStyle.h7
-rw-r--r--src/gpu/batches/GrAADistanceFieldPathRenderer.cpp247
-rwxr-xr-xsrc/gpu/batches/GrAADistanceFieldPathRenderer.h66
-rw-r--r--src/gpu/batches/GrDefaultPathRenderer.cpp11
-rw-r--r--src/gpu/batches/GrMSAAPathRenderer.cpp9
-rw-r--r--src/gpu/batches/GrStencilAndCoverPathRenderer.cpp6
-rw-r--r--src/gpu/batches/GrTessellatingPathRenderer.cpp117
7 files changed, 218 insertions, 245 deletions
diff --git a/src/gpu/GrStyle.h b/src/gpu/GrStyle.h
index 478564f3f9..326f800442 100644
--- a/src/gpu/GrStyle.h
+++ b/src/gpu/GrStyle.h
@@ -113,13 +113,6 @@ public:
/** Is this style a hairline with no path effect? */
bool isSimpleHairline() const { return fStrokeRec.isHairlineStyle() && !fPathEffect; }
- bool couldBeHairline() const {
- // If the original stroke rec has hairline style then either a null or dash patheffect
- // would preserve the hairline status. An arbitrary path effect could introduce hairline
- // style.
- return this->strokeRec().isHairlineStyle() || this->hasNonDashPathEffect();
- }
-
SkPathEffect* pathEffect() const { return fPathEffect.get(); }
bool hasPathEffect() const { return SkToBool(fPathEffect.get()); }
diff --git a/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp b/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
index 6cf3e4b82c..8aaabbc229 100644
--- a/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
+++ b/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
@@ -31,8 +31,8 @@
#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
#ifdef DF_PATH_TRACKING
-static int g_NumCachedPaths = 0;
-static int g_NumFreedPaths = 0;
+static int g_NumCachedShapes = 0;
+static int g_NumFreedShapes = 0;
#endif
// mip levels
@@ -44,15 +44,15 @@ static const int kLargeMIP = 162;
void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr;
// remove any paths that use this plot
- PathDataList::Iter iter;
- iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
- PathData* pathData;
- while ((pathData = iter.get())) {
+ ShapeDataList::Iter iter;
+ iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
+ ShapeData* shapeData;
+ while ((shapeData = iter.get())) {
iter.next();
- if (id == pathData->fID) {
- dfpr->fPathCache.remove(pathData->fKey);
- dfpr->fPathList.remove(pathData);
- delete pathData;
+ if (id == shapeData->fID) {
+ dfpr->fShapeCache.remove(shapeData->fKey);
+ dfpr->fShapeList.remove(shapeData);
+ delete shapeData;
#ifdef DF_PATH_TRACKING
++g_NumFreedPaths;
#endif
@@ -64,34 +64,42 @@ void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, voi
GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer() : fAtlas(nullptr) {}
GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
- PathDataList::Iter iter;
- iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
- PathData* pathData;
- while ((pathData = iter.get())) {
+ ShapeDataList::Iter iter;
+ iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
+ ShapeData* shapeData;
+ while ((shapeData = iter.get())) {
iter.next();
- fPathList.remove(pathData);
- delete pathData;
+ delete shapeData;
}
delete fAtlas;
#ifdef DF_PATH_TRACKING
- SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths);
+ SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFreedShapes);
#endif
}
////////////////////////////////////////////////////////////////////////////////
bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
- // We don't currently apply the dash or factor it into the DF key. (skbug.com/5082)
- if (args.fShape->style().pathEffect()) {
+ if (!args.fShaderCaps->shaderDerivativeSupport()) {
+ return false;
+ }
+ // If the shape has no key then we won't get any reuse.
+ if (!args.fShape->hasUnstyledKey()) {
+ return false;
+ }
+ // This only supports filled paths, however, the caller may apply the style to make a filled
+ // path and try again.
+ if (!args.fShape->style().isSimpleFill()) {
+ return false;
+ }
+ // This does non-inverse antialiased fills.
+ if (!args.fAntiAlias) {
return false;
}
// TODO: Support inverse fill
- if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias ||
- args.fShape->style().isSimpleHairline() || args.fShape->mayBeInverseFilledAfterStyling() ||
- !args.fShape->hasUnstyledKey()) {
+ if (!args.fShape->inverseFilled()) {
return false;
}
-
// currently don't support perspective
if (args.fViewMatrix->hasPerspective()) {
return false;
@@ -117,34 +125,20 @@ class AADistanceFieldPathBatch : public GrVertexBatch {
public:
DEFINE_BATCH_CLASS_ID
- typedef GrAADistanceFieldPathRenderer::PathData PathData;
- typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
- typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
+ typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData;
+ typedef SkTDynamicHash<ShapeData, ShapeData::Key> ShapeCache;
+ typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList;
struct Geometry {
- Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {
- if (!stroke.needToApply()) {
- // purify unused values to ensure binary equality
- fStroke.setStrokeParams(SkPaint::kDefault_Cap, SkPaint::kDefault_Join,
- SkIntToScalar(4));
- if (fStroke.getWidth() < 0) {
- fStroke.setStrokeStyle(-1.0f);
- }
- }
- }
- SkPath fPath;
- // The unique ID of the path involved in this draw. This may be different than the ID
- // in fPath since that path may have resulted from a SkStrokeRec::applyToPath call.
- uint32_t fGenID;
- SkStrokeRec fStroke;
+ GrShape fShape;
GrColor fColor;
bool fAntiAlias;
};
static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
- GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList,
- bool gammaCorrect) {
- return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, pathCache, pathList,
+ GrBatchAtlas* atlas, ShapeCache* shapeCache,
+ ShapeDataList* shapeList, bool gammaCorrect) {
+ return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, shapeCache, shapeList,
gammaCorrect);
}
@@ -229,7 +223,7 @@ private:
// get mip level
SkScalar maxScale = this->viewMatrix().getMaxScale();
- const SkRect& bounds = args.fPath.getBounds();
+ const SkRect& bounds = args.fShape.bounds();
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
SkScalar size = maxScale * maxDim;
uint32_t desiredDimension;
@@ -242,24 +236,22 @@ private:
}
// check to see if path is cached
- PathData::Key key(args.fGenID, desiredDimension, args.fStroke);
- PathData* pathData = fPathCache->find(key);
- if (nullptr == pathData || !atlas->hasID(pathData->fID)) {
+ ShapeData::Key key(args.fShape, desiredDimension);
+ ShapeData* shapeData = fShapeCache->find(key);
+ if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
// Remove the stale cache entry
- if (pathData) {
- fPathCache->remove(pathData->fKey);
- fPathList->remove(pathData);
- delete pathData;
+ if (shapeData) {
+ fShapeCache->remove(shapeData->fKey);
+ fShapeList->remove(shapeData);
+ delete shapeData;
}
SkScalar scale = desiredDimension/maxDim;
- pathData = new PathData;
+ shapeData = new ShapeData;
if (!this->addPathToAtlas(target,
&flushInfo,
atlas,
- pathData,
- args.fPath,
- args.fGenID,
- args.fStroke,
+ shapeData,
+ args.fShape,
args.fAntiAlias,
desiredDimension,
scale)) {
@@ -268,7 +260,7 @@ private:
}
}
- atlas->setLastUseToken(pathData->fID, target->nextDrawToken());
+ atlas->setLastUseToken(shapeData->fID, target->nextDrawToken());
// Now set vertices
intptr_t offset = reinterpret_cast<intptr_t>(vertices);
@@ -279,46 +271,44 @@ private:
args.fColor,
vertexStride,
this->viewMatrix(),
- args.fPath,
- pathData);
+ args.fShape,
+ shapeData);
flushInfo.fInstancesToFlush++;
}
this->flush(target, &flushInfo);
}
- SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
-
AADistanceFieldPathBatch(const Geometry& geometry,
const SkMatrix& viewMatrix,
GrBatchAtlas* atlas,
- PathCache* pathCache, PathDataList* pathList,
+ ShapeCache* shapeCache, ShapeDataList* shapeList,
bool gammaCorrect)
: INHERITED(ClassID()) {
+ SkASSERT(geometry.fShape.hasUnstyledKey());
fBatch.fViewMatrix = viewMatrix;
fGeoData.push_back(geometry);
+ SkASSERT(fGeoData[0].fShape.hasUnstyledKey());
fAtlas = atlas;
- fPathCache = pathCache;
- fPathList = pathList;
+ fShapeCache = shapeCache;
+ fShapeList = shapeList;
fGammaCorrect = gammaCorrect;
// Compute bounds
- fBounds = geometry.fPath.getBounds();
+ fBounds = geometry.fShape.bounds();
viewMatrix.mapRect(&fBounds);
}
bool addPathToAtlas(GrVertexBatch::Target* target,
FlushInfo* flushInfo,
GrBatchAtlas* atlas,
- PathData* pathData,
- const SkPath& path,
- uint32_t genID,
- const SkStrokeRec& stroke,
+ ShapeData* shapeData,
+ const GrShape& shape,
bool antiAlias,
uint32_t dimension,
SkScalar scale) const {
- const SkRect& bounds = path.getBounds();
+ const SkRect& bounds = shape.bounds();
// generate bounding rect for bitmap draw
SkRect scaledBounds = bounds;
@@ -375,6 +365,8 @@ private:
draw.fMatrix = &drawMatrix;
draw.fDst = dst;
+ SkPath path;
+ shape.asPath(&path);
draw.drawPathCoverage(path, paint);
// generate signed distance field
@@ -404,9 +396,9 @@ private:
}
// add to cache
- pathData->fKey = PathData::Key(genID, dimension, stroke);
- pathData->fScale = scale;
- pathData->fID = id;
+ shapeData->fKey.set(shape, dimension);
+ shapeData->fScale = scale;
+ shapeData->fID = id;
// change the scaled rect to match the size of the inset distance field
scaledBounds.fRight = scaledBounds.fLeft +
SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
@@ -416,14 +408,14 @@ private:
// need to also restore the fractional translation
scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
- pathData->fBounds = scaledBounds;
+ shapeData->fBounds = scaledBounds;
// origin we render from is inset from distance field edge
atlasLocation.fX += SK_DistanceFieldInset;
atlasLocation.fY += SK_DistanceFieldInset;
- pathData->fAtlasLocation = atlasLocation;
+ shapeData->fAtlasLocation = atlasLocation;
- fPathCache->add(pathData);
- fPathList->addToTail(pathData);
+ fShapeCache->add(shapeData);
+ fShapeList->addToTail(shapeData);
#ifdef DF_PATH_TRACKING
++g_NumCachedPaths;
#endif
@@ -436,16 +428,16 @@ private:
GrColor color,
size_t vertexStride,
const SkMatrix& viewMatrix,
- const SkPath& path,
- const PathData* pathData) const {
+ const GrShape& shape,
+ const ShapeData* shapeData) const {
GrTexture* texture = atlas->getTexture();
- SkScalar dx = pathData->fBounds.fLeft;
- SkScalar dy = pathData->fBounds.fTop;
- SkScalar width = pathData->fBounds.width();
- SkScalar height = pathData->fBounds.height();
+ SkScalar dx = shapeData->fBounds.fLeft;
+ SkScalar dy = shapeData->fBounds.fTop;
+ SkScalar width = shapeData->fBounds.width();
+ SkScalar height = shapeData->fBounds.height();
- SkScalar invScale = 1.0f / pathData->fScale;
+ SkScalar invScale = 1.0f / shapeData->fScale;
dx *= invScale;
dy *= invScale;
width *= invScale;
@@ -464,15 +456,15 @@ private:
*colorPtr = color;
}
- const SkScalar tx = SkIntToScalar(pathData->fAtlasLocation.fX);
- const SkScalar ty = SkIntToScalar(pathData->fAtlasLocation.fY);
+ const SkScalar tx = SkIntToScalar(shapeData->fAtlasLocation.fX);
+ const SkScalar ty = SkIntToScalar(shapeData->fAtlasLocation.fY);
// vertex texture coords
SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
textureCoords->setRectFan(tx / texture->width(),
ty / texture->height(),
- (tx + pathData->fBounds.width()) / texture->width(),
- (ty + pathData->fBounds.height()) / texture->height(),
+ (tx + shapeData->fBounds.width()) / texture->width(),
+ (ty + shapeData->fBounds.height()) / texture->height(),
vertexStride);
}
@@ -504,7 +496,7 @@ private:
return false;
}
- fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+ fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
this->joinBounds(that->bounds());
return true;
}
@@ -517,10 +509,10 @@ private:
};
BatchTracker fBatch;
- SkSTArray<1, Geometry, true> fGeoData;
+ SkSTArray<1, Geometry> fGeoData;
GrBatchAtlas* fAtlas;
- PathCache* fPathCache;
- PathDataList* fPathList;
+ ShapeCache* fShapeCache;
+ ShapeDataList* fShapeList;
bool fGammaCorrect;
typedef GrVertexBatch INHERITED;
@@ -530,10 +522,11 @@ bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(),
"GrAADistanceFieldPathRenderer::onDrawPath");
SkASSERT(!args.fDrawContext->isUnifiedMultisampled());
+ SkASSERT(args.fShape->style().isSimpleFill());
// we've already bailed on inverse filled paths, so this is safe
SkASSERT(!args.fShape->isEmpty());
-
+ SkASSERT(args.fShape->hasUnstyledKey());
if (!fAtlas) {
fAtlas = args.fResourceProvider->createAtlas(kAlpha_8_GrPixelConfig,
ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
@@ -545,23 +538,14 @@ bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
}
}
- const GrStyle& style = args.fShape->style();
- // It's ok to ignore style's path effect because canDrawPath filtered out path effects.
- AADistanceFieldPathBatch::Geometry geometry(style.strokeRec());
- args.fShape->asPath(&geometry.fPath);
- // Note: this is the generation ID of the _original_ path. When a new path is
- // generated due to stroking it is important that the original path's id is used
- // for caching.
- geometry.fGenID = geometry.fPath.getGenerationID();
- if (!style.isSimpleFill()) {
- style.strokeRec().applyToPath(&geometry.fPath, geometry.fPath);
- }
+ AADistanceFieldPathBatch::Geometry geometry;
+ geometry.fShape = *args.fShape;
geometry.fColor = args.fColor;
geometry.fAntiAlias = args.fAntiAlias;
SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry,
*args.fViewMatrix, fAtlas,
- &fPathCache, &fPathList,
+ &fShapeCache, &fShapeList,
args.fGammaCorrect));
GrPipelineBuilder pipelineBuilder(*args.fPaint);
@@ -577,45 +561,45 @@ bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) {
#ifdef GR_TEST_UTILS
struct PathTestStruct {
- typedef GrAADistanceFieldPathRenderer::PathCache PathCache;
- typedef GrAADistanceFieldPathRenderer::PathData PathData;
- typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
+ typedef GrAADistanceFieldPathRenderer::ShapeCache ShapeCache;
+ typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData;
+ typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList;
PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
~PathTestStruct() { this->reset(); }
void reset() {
- PathDataList::Iter iter;
- iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
- PathData* pathData;
- while ((pathData = iter.get())) {
+ ShapeDataList::Iter iter;
+ iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
+ ShapeData* shapeData;
+ while ((shapeData = iter.get())) {
iter.next();
- fPathList.remove(pathData);
- delete pathData;
+ fShapeList.remove(shapeData);
+ delete shapeData;
}
delete fAtlas;
- fPathCache.reset();
+ fShapeCache.reset();
}
static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
PathTestStruct* dfpr = (PathTestStruct*)pr;
// remove any paths that use this plot
- PathDataList::Iter iter;
- iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
- PathData* pathData;
- while ((pathData = iter.get())) {
+ ShapeDataList::Iter iter;
+ iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
+ ShapeData* shapeData;
+ while ((shapeData = iter.get())) {
iter.next();
- if (id == pathData->fID) {
- dfpr->fPathCache.remove(pathData->fKey);
- dfpr->fPathList.remove(pathData);
- delete pathData;
+ if (id == shapeData->fID) {
+ dfpr->fShapeCache.remove(shapeData->fKey);
+ dfpr->fShapeList.remove(shapeData);
+ delete shapeData;
}
}
}
uint32_t fContextID;
GrBatchAtlas* fAtlas;
- PathCache fPathCache;
- PathDataList fPathList;
+ ShapeCache fShapeCache;
+ ShapeDataList fShapeList;
};
DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
@@ -636,16 +620,17 @@ DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
GrColor color = GrRandomColor(random);
bool gammaCorrect = random->nextBool();
- AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
+ AADistanceFieldPathBatch::Geometry geometry;
+ // This path renderer only allows fill styles.
+ GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
+ geometry.fShape = shape;
geometry.fColor = color;
- geometry.fPath = GrTest::TestPath(random);
geometry.fAntiAlias = random->nextBool();
- geometry.fGenID = random->nextU();
return AADistanceFieldPathBatch::Create(geometry, viewMatrix,
gTestStruct.fAtlas,
- &gTestStruct.fPathCache,
- &gTestStruct.fPathList,
+ &gTestStruct.fShapeCache,
+ &gTestStruct.fShapeList,
gammaCorrect);
}
diff --git a/src/gpu/batches/GrAADistanceFieldPathRenderer.h b/src/gpu/batches/GrAADistanceFieldPathRenderer.h
index e5c7dceb12..db17b07d36 100755
--- a/src/gpu/batches/GrAADistanceFieldPathRenderer.h
+++ b/src/gpu/batches/GrAADistanceFieldPathRenderer.h
@@ -11,6 +11,7 @@
#include "GrBatchAtlas.h"
#include "GrPathRenderer.h"
#include "GrRect.h"
+#include "GrShape.h"
#include "SkChecksum.h"
#include "SkTDynamicHash.h"
@@ -31,56 +32,67 @@ private:
bool onDrawPath(const DrawPathArgs&) override;
- struct PathData {
+ struct ShapeData {
class Key {
public:
- // default ctor needed for new of uninitialized PathData
- // since fStroke has no default ctor
- Key()
- : fGenID(0)
- , fDimension(0)
- , fStroke(SkStrokeRec::kFill_InitStyle) {}
- Key(uint32_t genID, uint32_t dim, const SkStrokeRec& stroke)
- : fGenID(genID)
- , fDimension(dim)
- , fStroke(stroke) {}
-
- bool operator==(const Key& other) const {
- return other.fGenID == fGenID && other.fDimension == fDimension &&
- fStroke.hasEqualEffect(other.fStroke);
+ Key() {}
+ Key(const Key& that) { *this = that; }
+ Key(const GrShape& shape, uint32_t dim) { this->set(shape, dim); }
+
+ Key& operator=(const Key& that) {
+ fKey.reset(that.fKey.count());
+ memcpy(fKey.get(), that.fKey.get(), fKey.count() * sizeof(uint32_t));
+ return *this;
}
+ void set(const GrShape& shape, uint32_t dim) {
+ // Shapes' keys are for their pre-style geometry, but by now we shouldn't have any
+ // relevant styling information.
+ SkASSERT(shape.style().isSimpleFill());
+ SkASSERT(shape.hasUnstyledKey());
+ int shapeKeySize = shape.unstyledKeySize();
+ fKey.reset(1 + shapeKeySize);
+ fKey[0] = dim;
+ shape.writeUnstyledKey(&fKey[1]);
+ }
+
+ bool operator==(const Key& that) const {
+ return fKey.count() == that.fKey.count() &&
+ 0 == memcmp(fKey.get(), that.fKey.get(), sizeof(uint32_t) * fKey.count());
+ }
+
+ int count32() const { return fKey.count(); }
+ const uint32_t* data() const { return fKey.get(); }
+
private:
- uint32_t fGenID;
- // rendered size for stored path (32x32 max, 64x64 max, 128x128 max)
- uint32_t fDimension;
- // stroking information
- SkStrokeRec fStroke;
+ // The key is composed of the dimensions of the DF generated for the path (32x32 max,
+ // 64x64 max, 128x128 max) and the GrShape's key.
+ SkAutoSTArray<24, uint32_t> fKey;
};
Key fKey;
SkScalar fScale;
GrBatchAtlas::AtlasID fID;
SkRect fBounds;
SkIPoint16 fAtlasLocation;
- SK_DECLARE_INTERNAL_LLIST_INTERFACE(PathData);
+ SK_DECLARE_INTERNAL_LLIST_INTERFACE(ShapeData);
- static inline const Key& GetKey(const PathData& data) {
+ static inline const Key& GetKey(const ShapeData& data) {
return data.fKey;
}
static inline uint32_t Hash(Key key) {
- return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(key));
+ return SkChecksum::Murmur3(key.data(), sizeof(uint32_t) * key.count32());
}
};
static void HandleEviction(GrBatchAtlas::AtlasID, void*);
- typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
- typedef SkTInternalLList<PathData> PathDataList;
+ typedef SkTDynamicHash<ShapeData, ShapeData::Key> ShapeCache;
+ typedef SkTInternalLList<ShapeData> ShapeDataList;
GrBatchAtlas* fAtlas;
- PathCache fPathCache;
- PathDataList fPathList;
+ ShapeCache fShapeCache;
+ ShapeDataList fShapeList;
typedef GrPathRenderer INHERITED;
diff --git a/src/gpu/batches/GrDefaultPathRenderer.cpp b/src/gpu/batches/GrDefaultPathRenderer.cpp
index 1e807f8e3a..97200a1981 100644
--- a/src/gpu/batches/GrDefaultPathRenderer.cpp
+++ b/src/gpu/batches/GrDefaultPathRenderer.cpp
@@ -38,10 +38,17 @@ static inline bool single_pass_shape(const GrShape& shape) {
#if STENCIL_OFF
return true;
#else
- if (!shape.style().couldBeHairline() && !shape.inverseFilled()) {
+ // Inverse fill is always two pass.
+ if (shape.inverseFilled()) {
+ return false;
+ }
+ // This path renderer only accepts simple fill paths or stroke paths that are either hairline
+ // or have a stroke width small enough to treat as hairline. Hairline paths are always single
+ // pass. Filled paths are single pass if they're convex.
+ if (shape.style().isSimpleFill()) {
return shape.knownToBeConvex();
}
- return false;
+ return true;
#endif
}
diff --git a/src/gpu/batches/GrMSAAPathRenderer.cpp b/src/gpu/batches/GrMSAAPathRenderer.cpp
index 31b9ebf48c..c4b8835f6e 100644
--- a/src/gpu/batches/GrMSAAPathRenderer.cpp
+++ b/src/gpu/batches/GrMSAAPathRenderer.cpp
@@ -709,11 +709,10 @@ bool GrMSAAPathRenderer::internalDrawPath(GrDrawContext* drawContext,
}
bool GrMSAAPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
- // This path renderer does not support hairlines. We defer on anything that could be handled
- // as a hairline by another path renderer. Also, arbitrary path effects could produce
- // a hairline result.
- return !IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr) &&
- !args.fShape->style().couldBeHairline() && !args.fAntiAlias;
+ // This path renderer only fills and relies on MSAA for antialiasing. Stroked shapes are
+ // handled by passing on the original shape and letting the caller compute the stroked shape
+ // which will have a fill style.
+ return args.fShape->style().isSimpleFill() && !args.fAntiAlias;
}
bool GrMSAAPathRenderer::onDrawPath(const DrawPathArgs& args) {
diff --git a/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp b/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp
index 0995310c13..4139629d14 100644
--- a/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp
@@ -33,8 +33,10 @@ GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrResourceProvider*
}
bool GrStencilAndCoverPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
- // GrPath doesn't support hairline paths.
- if (args.fShape->style().couldBeHairline()) {
+ // GrPath doesn't support hairline paths. An arbitrary path effect could produce a hairline
+ // path.
+ if (args.fShape->style().strokeRec().isHairlineStyle() ||
+ args.fShape->style().hasNonDashPathEffect()) {
return false;
}
if (args.fHasUserStencilSettings) {
diff --git a/src/gpu/batches/GrTessellatingPathRenderer.cpp b/src/gpu/batches/GrTessellatingPathRenderer.cpp
index 060e3f0adf..b17bf1a4d5 100644
--- a/src/gpu/batches/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/batches/GrTessellatingPathRenderer.cpp
@@ -105,14 +105,13 @@ GrTessellatingPathRenderer::GrTessellatingPathRenderer() {
}
bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
- // This path renderer can draw all fill styles, all stroke styles except hairlines, but does
- // not do antialiasing. It can do convex and concave paths, but we'll leave the convex ones to
- // simpler algorithms. Similary, we skip the non-hairlines that can be treated as hairline.
- // An arbitrary path effect could produce a hairline result so we pass on those.
- return !IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr) &&
- !args.fShape->style().strokeRec().isHairlineStyle() &&
- !args.fShape->style().hasNonDashPathEffect() && !args.fAntiAlias &&
- !args.fShape->knownToBeConvex();
+ // This path renderer can draw fill styles but does not do antialiasing. It can do convex and
+ // concave paths, but we'll leave the convex ones to simpler algorithms. We pass on paths that
+ // have styles, though they may come back around after applying the styling information to the
+ // geometry to create a filled path. We also skip paths that don't have a key since the real
+ // advantage of this path renderer comes from caching the tessellated geometry.
+ return !args.fShape->style().applies() && args.fShape->style().isSimpleFill() &&
+ !args.fAntiAlias && args.fShape->hasUnstyledKey() && !args.fShape->knownToBeConvex();
}
class TessellatingPathBatch : public GrVertexBatch {
@@ -120,11 +119,10 @@ public:
DEFINE_BATCH_CLASS_ID
static GrDrawBatch* Create(const GrColor& color,
- const SkPath& path,
- const GrStyle& style,
+ const GrShape& shape,
const SkMatrix& viewMatrix,
SkRect clipBounds) {
- return new TessellatingPathBatch(color, path, style, viewMatrix, clipBounds);
+ return new TessellatingPathBatch(color, shape, viewMatrix, clipBounds);
}
const char* name() const override { return "TessellatingPathBatch"; }
@@ -150,48 +148,33 @@ private:
GrResourceProvider* rp = target->resourceProvider();
SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix,
- fPath.getBounds());
-
- SkScalar styleScale = SK_Scalar1;
- if (fStyle.applies()) {
- styleScale = GrStyle::MatrixToScaleFactor(fViewMatrix);
- }
+ fShape.bounds());
+ SkPath path;
+ fShape.asPath(&path);
+ bool inverseFill = path.isInverseFillType();
// construct a cache key from the path's genID and the view matrix
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey key;
- int clipBoundsCnt =
- fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) : 0;
- int styleDataCnt = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
- if (styleDataCnt >= 0) {
- GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsCnt + styleDataCnt);
- builder[0] = fPath.getGenerationID();
- builder[1] = fPath.getFillType();
- // For inverse fills, the tessellation is dependent on clip bounds.
- if (fPath.isInverseFillType()) {
- memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds));
- }
- if (styleDataCnt) {
- GrStyle::WriteKey(&builder[2 + clipBoundsCnt], fStyle,
- GrStyle::Apply::kPathEffectAndStrokeRec, styleScale);
- }
- builder.finish();
- SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
- int actualCount;
- if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
- this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
- return;
- }
- }
-
- SkPath path;
- if (fStyle.applies()) {
- SkStrokeRec::InitStyle fill;
- SkAssertResult(fStyle.applyToPath(&path, &fill, fPath, styleScale));
- SkASSERT(SkStrokeRec::kFill_InitStyle == fill);
+ static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / sizeof(uint32_t);
+ int shapeKeyDataCnt = fShape.unstyledKeySize();
+ SkASSERT(shapeKeyDataCnt >= 0);
+ GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt);
+ fShape.writeUnstyledKey(&builder[0]);
+ // For inverse fills, the tessellation is dependent on clip bounds.
+ if (inverseFill) {
+ memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds));
} else {
- path = fPath;
+ memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds));
+ }
+ builder.finish();
+ SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key));
+ int actualCount;
+ if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
+ this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount);
+ return;
}
+
bool isLinear;
bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
StaticVertexAllocator allocator(rp, canMapVB);
@@ -200,15 +183,12 @@ private:
return;
}
this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count);
- if (!fPath.isVolatile() && styleDataCnt >= 0) {
- TessInfo info;
- info.fTolerance = isLinear ? 0 : tol;
- info.fCount = count;
- SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
- key.setCustomData(data.get());
- rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
- SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(key));
- }
+ TessInfo info;
+ info.fTolerance = isLinear ? 0 : tol;
+ info.fCount = count;
+ SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info)));
+ key.setCustomData(data.get());
+ rp->assignUniqueKeyToResource(key, allocator.vertexBuffer());
}
void onPrepareDraws(Target* target) const override {
@@ -246,32 +226,28 @@ private:
bool onCombineIfPossible(GrBatch*, const GrCaps&) override { return false; }
TessellatingPathBatch(const GrColor& color,
- const SkPath& path,
- const GrStyle& style,
+ const GrShape& shape,
const SkMatrix& viewMatrix,
const SkRect& clipBounds)
: INHERITED(ClassID())
, fColor(color)
- , fPath(path)
- , fStyle(style)
+ , fShape(shape)
, fViewMatrix(viewMatrix) {
- const SkRect& pathBounds = path.getBounds();
+ const SkRect& pathBounds = shape.bounds();
fClipBounds = clipBounds;
// Because the clip bounds are used to add a contour for inverse fills, they must also
// include the path bounds.
fClipBounds.join(pathBounds);
- if (path.isInverseFillType()) {
+ if (shape.inverseFilled()) {
fBounds = fClipBounds;
} else {
- fBounds = path.getBounds();
+ fBounds = pathBounds;
}
- style.adjustBounds(&fBounds, fBounds);
viewMatrix.mapRect(&fBounds);
}
GrColor fColor;
- SkPath fPath;
- GrStyle fStyle;
+ GrShape fShape;
SkMatrix fViewMatrix;
SkRect fClipBounds; // in source space
GrXPOverridesForBatch fPipelineInfo;
@@ -295,10 +271,8 @@ bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
vmi.mapRect(&clipBounds);
SkPath path;
args.fShape->asPath(&path);
- SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fColor, path,
- args.fShape->style(),
- *args.fViewMatrix,
- clipBounds));
+ SkAutoTUnref<GrDrawBatch> batch(TessellatingPathBatch::Create(args.fColor, *args.fShape,
+ *args.fViewMatrix, clipBounds));
GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fDrawContext->mustUseHWAA(*args.fPaint));
pipelineBuilder.setUserStencil(args.fUserStencilSettings);
@@ -327,7 +301,8 @@ DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) {
do {
GrTest::TestStyle(random, &style);
} while (style.strokeRec().isHairlineStyle());
- return TessellatingPathBatch::Create(color, path, style, viewMatrix, clipBounds);
+ GrShape shape(path, style);
+ return TessellatingPathBatch::Create(color, shape, viewMatrix, clipBounds);
}
#endif