diff options
author | 2016-06-27 07:18:18 -0700 | |
---|---|---|
committer | 2016-06-27 07:18:18 -0700 | |
commit | ee43241d72148545e4e3b75aa4338c491b39cc0c (patch) | |
tree | 45db61f18014bd7c5a7a376576aa8065cf2fb65b /src | |
parent | b05df0ff261d7819da55509df03fb4525af49b40 (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.h | 7 | ||||
-rw-r--r-- | src/gpu/batches/GrAADistanceFieldPathRenderer.cpp | 247 | ||||
-rwxr-xr-x | src/gpu/batches/GrAADistanceFieldPathRenderer.h | 66 | ||||
-rw-r--r-- | src/gpu/batches/GrDefaultPathRenderer.cpp | 11 | ||||
-rw-r--r-- | src/gpu/batches/GrMSAAPathRenderer.cpp | 9 | ||||
-rw-r--r-- | src/gpu/batches/GrStencilAndCoverPathRenderer.cpp | 6 | ||||
-rw-r--r-- | src/gpu/batches/GrTessellatingPathRenderer.cpp | 117 |
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 |