Chromium Code Reviews| Index: src/gpu/GrAADistanceFieldPathRenderer.cpp |
| diff --git a/src/gpu/GrAADistanceFieldPathRenderer.cpp b/src/gpu/GrAADistanceFieldPathRenderer.cpp |
| index d50af9fbd85829e0b2c9235301f96da65cf7d036..62d6e702626983ec057e88261d5a96465cf9bbb3 100755 |
| --- a/src/gpu/GrAADistanceFieldPathRenderer.cpp |
| +++ b/src/gpu/GrAADistanceFieldPathRenderer.cpp |
| @@ -8,7 +8,9 @@ |
| #include "GrAADistanceFieldPathRenderer.h" |
| -#include "GrAtlas.h" |
| +#include "GrBatch.h" |
| +#include "GrBatchTarget.h" |
| +#include "GrBufferAllocPool.h" |
| #include "GrContext.h" |
| #include "GrPipelineBuilder.h" |
| #include "GrSurfacePriv.h" |
| @@ -27,9 +29,6 @@ |
| #define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH) |
| #define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT) |
| -SK_CONF_DECLARE(bool, c_DumpPathCache, "gpu.dumpPathCache", false, |
| - "Dump the contents of the path cache before every purge."); |
| - |
| #ifdef DF_PATH_TRACKING |
| static int g_NumCachedPaths = 0; |
| static int g_NumFreedPaths = 0; |
| @@ -43,8 +42,7 @@ static const int kLargeMIP = 192; |
| //////////////////////////////////////////////////////////////////////////////// |
| GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context) |
| : fContext(context) |
| - , fAtlas(NULL) |
| - , fEffectFlags(kInvalid_DistanceFieldEffectFlag) { |
| + , fAtlas(NULL) { |
| } |
| GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { |
| @@ -57,7 +55,9 @@ GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { |
| SkDELETE(pathData); |
| } |
| - SkDELETE(fAtlas); |
| + if (fAtlas) { |
|
bsalomon
2015/03/05 22:11:08
delete is legal on NULL.
joshualitt
2015/03/09 19:45:14
Acknowledged.
|
| + SkDELETE(fAtlas); |
| + } |
| #ifdef DF_PATH_TRACKING |
| SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths); |
| @@ -103,285 +103,491 @@ GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*, |
| //////////////////////////////////////////////////////////////////////////////// |
| -bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target, |
| - GrPipelineBuilder* pipelineBuilder, |
| - GrColor color, |
| - const SkMatrix& viewMatrix, |
| - const SkPath& path, |
| - const SkStrokeRec& stroke, |
| - bool antiAlias) { |
| - // we've already bailed on inverse filled paths, so this is safe |
| - if (path.isEmpty()) { |
| - return true; |
| + |
| +class AADistanceFieldPathBatch : public GrBatch { |
| +public: |
| + typedef GrAADistanceFieldPathRenderer::PathData PathData; |
| + typedef SkTDynamicHash<PathData, PathData::Key> PathCache; |
| + typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList; |
| + |
| + struct Geometry { |
| + Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {} |
| + SkPath fPath; |
| + SkStrokeRec fStroke; |
| + bool fAntiAlias; |
| + PathData* fPathData; |
| + }; |
| + |
| + static GrBatch* Create(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix, |
| + GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) { |
| + return SkNEW_ARGS(AADistanceFieldPathBatch, (geometry, color, viewMatrix, |
| + atlas, pathCache, pathList)); |
| } |
| - SkASSERT(fContext); |
| + const char* name() const SK_OVERRIDE { return "AADistanceFieldPathBatch"; } |
| - // get mip level |
| - SkScalar maxScale = viewMatrix.getMaxScale(); |
| - const SkRect& bounds = path.getBounds(); |
| - SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); |
| - SkScalar size = maxScale*maxDim; |
| - uint32_t desiredDimension; |
| - if (size <= kSmallMIP) { |
| - desiredDimension = kSmallMIP; |
| - } else if (size <= kMediumMIP) { |
| - desiredDimension = kMediumMIP; |
| - } else { |
| - desiredDimension = kLargeMIP; |
| + void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { |
| + out->setKnownFourComponents(fBatch.fColor); |
| } |
| - // check to see if path is cached |
| - // TODO: handle stroked vs. filled version of same path |
| - PathData::Key key = { path.getGenerationID(), desiredDimension }; |
| - PathData* pathData = fPathCache.find(key); |
| - if (NULL == pathData) { |
| - SkScalar scale = desiredDimension/maxDim; |
| - pathData = this->addPathToAtlas(path, stroke, antiAlias, desiredDimension, scale); |
| - if (NULL == pathData) { |
| - return false; |
| + void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE { |
| + out->setUnknownSingleComponent(); |
| + } |
| + |
| + void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { |
| + // Handle any color overrides |
| + if (init.fColorIgnored) { |
| + fBatch.fColor = GrColor_ILLEGAL; |
| + } else if (GrColor_ILLEGAL != init.fOverrideColor) { |
| + fBatch.fColor = init.fOverrideColor; |
| } |
| + |
| + // setup batch properties |
| + fBatch.fColorIgnored = init.fColorIgnored; |
| + fBatch.fUsesLocalCoords = init.fUsesLocalCoords; |
| + fBatch.fCoverageIgnored = init.fCoverageIgnored; |
| } |
| - // use signed distance field to render |
| - return this->internalDrawPath(target, pipelineBuilder, color, viewMatrix, path, pathData); |
| -} |
| + void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE { |
| + int instanceCount = fGeoData.count(); |
| -// padding around path bounds to allow for antialiased pixels |
| -const SkScalar kAntiAliasPad = 1.0f; |
| + SkMatrix invert; |
| + if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { |
| + SkDebugf("Could not invert viewmatrix\n"); |
| + return; |
| + } |
| -inline bool GrAADistanceFieldPathRenderer::uploadPath(GrPlot** plot, SkIPoint16* atlasLocation, |
| - int width, int height, void* dfStorage) { |
| - *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation); |
| + uint32_t flags = 0; |
| + flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; |
| + |
| + GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); |
| + |
| + // Setup GrGeometryProcessor |
| + GrBatchAtlas* atlas = fAtlas; |
| + SkAutoTUnref<GrGeometryProcessor> dfProcessor( |
| + GrDistanceFieldNoGammaTextureEffect::Create(this->color(), |
| + this->viewMatrix(), |
| + atlas->getTexture(), |
| + params, |
| + flags, |
| + false)); |
| + |
| + this->initDraw(batchTarget, dfProcessor, pipeline); |
| + |
| + // allocate vertices |
| + size_t vertexStride = dfProcessor->getVertexStride(); |
| + SkASSERT(vertexStride == 2 * sizeof(SkPoint)); |
| + |
| + int vertexCount = GrBatchTarget::kVertsPerRect * instanceCount; |
| + |
| + const GrVertexBuffer* vertexBuffer; |
| + int firstVertex; |
| + |
| + void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, |
| + vertexCount, |
| + &vertexBuffer, |
| + &firstVertex); |
| + |
| + // We may have to flush while uploading path data to the atlas, so we set up the draw here |
| + const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer(); |
| + int maxInstancesPerDraw = quadIndexBuffer->maxQuads(); |
| + |
| + GrDrawTarget::DrawInfo drawInfo; |
| + drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); |
| + drawInfo.setStartVertex(0); |
| + drawInfo.setStartIndex(0); |
| + drawInfo.setVerticesPerInstance(GrBatchTarget::kVertsPerRect); |
| + drawInfo.setIndicesPerInstance(GrBatchTarget::kIndicesPerRect); |
| + drawInfo.adjustStartVertex(firstVertex); |
| + drawInfo.setVertexBuffer(vertexBuffer); |
| + drawInfo.setIndexBuffer(quadIndexBuffer); |
| + |
| + int instancesToFlush = 0; |
| + for (int i = 0; i < instanceCount; i++) { |
| + Geometry& args = fGeoData[i]; |
| + |
| + // get mip level |
| + SkScalar maxScale = this->viewMatrix().getMaxScale(); |
| + const SkRect& bounds = args.fPath.getBounds(); |
| + SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); |
| + SkScalar size = maxScale * maxDim; |
| + uint32_t desiredDimension; |
| + if (size <= kSmallMIP) { |
| + desiredDimension = kSmallMIP; |
| + } else if (size <= kMediumMIP) { |
| + desiredDimension = kMediumMIP; |
| + } else { |
| + desiredDimension = kLargeMIP; |
| + } |
| - // if atlas full |
| - if (NULL == *plot) { |
| - if (this->freeUnusedPlot()) { |
| - *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation); |
| - if (*plot) { |
| - return true; |
| + // check to see if path is cached |
| + // TODO: handle stroked vs. filled version of same path |
| + PathData::Key key = { args.fPath.getGenerationID(), desiredDimension }; |
| + args.fPathData = fPathCache->find(key); |
| + if (NULL == args.fPathData || !atlas->hasID(args.fPathData->fID)) { |
| + // Remove the stale cache entry |
| + if (args.fPathData) { |
| + fPathCache->remove(args.fPathData->fKey); |
| + fPathList->remove(args.fPathData); |
| + SkDELETE(args.fPathData); |
| + } |
| + SkScalar scale = desiredDimension/maxDim; |
| + args.fPathData = SkNEW(PathData); |
| + if (!this->addPathToAtlas(batchTarget, |
| + dfProcessor, |
| + pipeline, |
| + &drawInfo, |
| + &instancesToFlush, |
| + maxInstancesPerDraw, |
| + atlas, |
| + args.fPathData, |
| + args.fPath, |
| + args.fStroke, |
| + args.fAntiAlias, |
| + desiredDimension, |
| + scale)) { |
| + SkDebugf("Can't rasterize path\n"); |
| + return; |
| + } |
| } |
| - } |
| - if (c_DumpPathCache) { |
| -#ifdef SK_DEVELOPER |
| - GrTexture* texture = fAtlas->getTexture(); |
| - texture->surfacePriv().savePixels("pathcache.png"); |
| -#endif |
| + atlas->setLastRefToken(args.fPathData->fID, batchTarget->currentToken()); |
| + |
| + // Now set vertices |
| + intptr_t offset = reinterpret_cast<intptr_t>(vertices); |
| + offset += i * GrBatchTarget::kVertsPerRect * vertexStride; |
| + SkPoint* positions = reinterpret_cast<SkPoint*>(offset); |
| + this->drawPath(batchTarget, |
| + atlas, |
| + pipeline, |
| + dfProcessor, |
| + positions, |
| + vertexStride, |
| + this->viewMatrix(), |
| + args.fPath, |
| + args.fPathData); |
| + instancesToFlush++; |
| } |
| - // before we purge the cache, we must flush any accumulated draws |
| - fContext->flush(); |
| + this->flush(batchTarget, dfProcessor, pipeline, &drawInfo, instancesToFlush, |
| + maxInstancesPerDraw); |
| + } |
| + |
| + SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } |
| + |
| +private: |
| + AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix, |
| + GrBatchAtlas* atlas, |
| + PathCache* pathCache, PathDataList* pathList) { |
| + this->initClassID<AADistanceFieldPathBatch>(); |
| + fBatch.fColor = color; |
| + fBatch.fViewMatrix = viewMatrix; |
| + fGeoData.push_back(geometry); |
| + fGeoData.back().fPathData = NULL; |
| + |
| + fAtlas = atlas; |
| + fPathCache = pathCache; |
| + fPathList = pathList; |
| + } |
| - if (this->freeUnusedPlot()) { |
| - *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation); |
| - if (*plot) { |
| - return true; |
| + bool addPathToAtlas(GrBatchTarget* batchTarget, |
| + const GrGeometryProcessor* dfProcessor, |
| + const GrPipeline* pipeline, |
| + GrDrawTarget::DrawInfo* drawInfo, |
| + int* instancesToFlush, |
| + int maxInstancesPerDraw, |
| + GrBatchAtlas* atlas, |
| + PathData* pathData, |
| + const SkPath& path, |
| + const SkStrokeRec& |
| + stroke, bool antiAlias, |
| + uint32_t dimension, |
| + SkScalar scale) { |
| + const SkRect& bounds = path.getBounds(); |
| + |
| + // generate bounding rect for bitmap draw |
| + SkRect scaledBounds = bounds; |
| + // scale to mip level size |
| + scaledBounds.fLeft *= scale; |
| + scaledBounds.fTop *= scale; |
| + scaledBounds.fRight *= scale; |
| + scaledBounds.fBottom *= scale; |
| + // move the origin to an integer boundary (gives better results) |
| + SkScalar dx = SkScalarFraction(scaledBounds.fLeft); |
| + SkScalar dy = SkScalarFraction(scaledBounds.fTop); |
| + scaledBounds.offset(-dx, -dy); |
| + // get integer boundary |
| + SkIRect devPathBounds; |
| + scaledBounds.roundOut(&devPathBounds); |
| + // pad to allow room for antialiasing |
| + devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad)); |
| + // move origin to upper left corner |
| + devPathBounds.offsetTo(0,0); |
| + |
| + // draw path to bitmap |
| + SkMatrix drawMatrix; |
| + drawMatrix.setTranslate(-bounds.left(), -bounds.top()); |
| + drawMatrix.postScale(scale, scale); |
| + drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); |
| + |
| + // setup bitmap backing |
| + // Now translate so the bound's UL corner is at the origin |
| + drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1, |
| + -devPathBounds.fTop * SK_Scalar1); |
| + SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(), |
| + devPathBounds.height()); |
| + |
| + SkBitmap bmp; |
| + const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(pathBounds.fRight, |
| + pathBounds.fBottom); |
| + if (!bmp.tryAllocPixels(bmImageInfo)) { |
| + return false; |
| + } |
| + |
| + sk_bzero(bmp.getPixels(), bmp.getSafeSize()); |
| + |
| + // rasterize path |
| + SkPaint paint; |
| + if (stroke.isHairlineStyle()) { |
| + paint.setStyle(SkPaint::kStroke_Style); |
| + paint.setStrokeWidth(SK_Scalar1); |
| + } else { |
| + if (stroke.isFillStyle()) { |
| + paint.setStyle(SkPaint::kFill_Style); |
| + } else { |
| + paint.setStyle(SkPaint::kStroke_Style); |
| + paint.setStrokeJoin(stroke.getJoin()); |
| + paint.setStrokeCap(stroke.getCap()); |
| + paint.setStrokeWidth(stroke.getWidth()); |
| } |
| } |
| - return false; |
| - } |
| - return true; |
| -} |
| + paint.setAntiAlias(antiAlias); |
| + |
| + SkDraw draw; |
| + sk_bzero(&draw, sizeof(draw)); |
| + |
| + SkRasterClip rasterClip; |
| + rasterClip.setRect(pathBounds); |
| + draw.fRC = &rasterClip; |
| + draw.fClip = &rasterClip.bwRgn(); |
| + draw.fMatrix = &drawMatrix; |
| + draw.fBitmap = &bmp; |
| + |
| + draw.drawPathCoverage(path, paint); |
| + |
| + // generate signed distance field |
| + devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); |
| + int width = devPathBounds.width(); |
| + int height = devPathBounds.height(); |
| + // TODO We should really generate this directly into the plot somehow |
| + SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); |
| + |
| + // Generate signed distance field |
| + { |
| + SkAutoLockPixels alp(bmp); |
| + |
| + SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(), |
| + (const unsigned char*)bmp.getPixels(), |
| + bmp.width(), bmp.height(), bmp.rowBytes()); |
| + } |
| + |
| + // add to atlas |
| + SkIPoint16 atlasLocation; |
| + GrBatchAtlas::AtlasID id; |
| + bool success = atlas->addToAtlas(&id, batchTarget, width, height, dfStorage.get(), |
| + &atlasLocation); |
| + if (!success) { |
| + this->flush(batchTarget, dfProcessor, pipeline, drawInfo, *instancesToFlush, |
| + maxInstancesPerDraw); |
| + this->initDraw(batchTarget, dfProcessor, pipeline); |
| + *instancesToFlush = 0; |
| + |
| + SkDEBUGCODE(success =) atlas->addToAtlas(&id, batchTarget, width, height, |
| + dfStorage.get(), &atlasLocation); |
| + SkASSERT(success); |
| -GrAADistanceFieldPathRenderer::PathData* GrAADistanceFieldPathRenderer::addPathToAtlas( |
| - const SkPath& path, |
| - const SkStrokeRec& stroke, |
| - bool antiAlias, |
| - uint32_t dimension, |
| - SkScalar scale) { |
| - |
| - // generate distance field and add to atlas |
| - if (NULL == fAtlas) { |
| - SkISize textureSize = SkISize::Make(ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT); |
| - fAtlas = SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kAlpha_8_GrPixelConfig, |
| - kNone_GrSurfaceFlags, textureSize, |
| - NUM_PLOTS_X, NUM_PLOTS_Y, false)); |
| - if (NULL == fAtlas) { |
| - return NULL; |
| } |
| - } |
| - |
| - const SkRect& bounds = path.getBounds(); |
| - |
| - // generate bounding rect for bitmap draw |
| - SkRect scaledBounds = bounds; |
| - // scale to mip level size |
| - scaledBounds.fLeft *= scale; |
| - scaledBounds.fTop *= scale; |
| - scaledBounds.fRight *= scale; |
| - scaledBounds.fBottom *= scale; |
| - // move the origin to an integer boundary (gives better results) |
| - SkScalar dx = SkScalarFraction(scaledBounds.fLeft); |
| - SkScalar dy = SkScalarFraction(scaledBounds.fTop); |
| - scaledBounds.offset(-dx, -dy); |
| - // get integer boundary |
| - SkIRect devPathBounds; |
| - scaledBounds.roundOut(&devPathBounds); |
| - // pad to allow room for antialiasing |
| - devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad)); |
| - // move origin to upper left corner |
| - devPathBounds.offsetTo(0,0); |
| - |
| - // draw path to bitmap |
| - SkMatrix drawMatrix; |
| - drawMatrix.setTranslate(-bounds.left(), -bounds.top()); |
| - drawMatrix.postScale(scale, scale); |
| - drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); |
| - GrSWMaskHelper helper(fContext); |
| - |
| - if (!helper.init(devPathBounds, &drawMatrix)) { |
| - return NULL; |
| - } |
| - helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF); |
| - |
| - // generate signed distance field |
| - devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); |
| - int width = devPathBounds.width(); |
| - int height = devPathBounds.height(); |
| - SkAutoSMalloc<1024> dfStorage(width*height*sizeof(unsigned char)); |
| - helper.toSDF((unsigned char*) dfStorage.get()); |
| - |
| - // add to atlas |
| - GrPlot* plot; |
| - SkIPoint16 atlasLocation; |
| - if (!this->uploadPath(&plot, &atlasLocation, width, height, dfStorage.get())) { |
| - return NULL; |
| - } |
| - // add to cache |
| - PathData* pathData = SkNEW(PathData); |
| - pathData->fKey.fGenID = path.getGenerationID(); |
| - pathData->fKey.fDimension = dimension; |
| - pathData->fScale = scale; |
| - pathData->fPlot = plot; |
| - // change the scaled rect to match the size of the inset distance field |
| - scaledBounds.fRight = scaledBounds.fLeft + |
| - SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); |
| - scaledBounds.fBottom = scaledBounds.fTop + |
| - SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); |
| - // shift the origin to the correct place relative to the distance field |
| - // need to also restore the fractional translation |
| - scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx, |
| - -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy); |
| - pathData->fBounds = scaledBounds; |
| - // origin we render from is inset from distance field edge |
| - atlasLocation.fX += SK_DistanceFieldInset; |
| - atlasLocation.fY += SK_DistanceFieldInset; |
| - pathData->fAtlasLocation = atlasLocation; |
| - |
| - fPathCache.add(pathData); |
| - fPathList.addToTail(pathData); |
| + // add to cache |
| + pathData->fKey.fGenID = path.getGenerationID(); |
| + pathData->fKey.fDimension = dimension; |
| + pathData->fScale = scale; |
| + pathData->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); |
| + scaledBounds.fBottom = scaledBounds.fTop + |
| + SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); |
| + // shift the origin to the correct place relative to the distance field |
| + // need to also restore the fractional translation |
| + scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx, |
| + -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy); |
| + pathData->fBounds = scaledBounds; |
| + // origin we render from is inset from distance field edge |
| + atlasLocation.fX += SK_DistanceFieldInset; |
| + atlasLocation.fY += SK_DistanceFieldInset; |
| + pathData->fAtlasLocation = atlasLocation; |
| + |
| + fPathCache->add(pathData); |
| + fPathList->addToTail(pathData); |
| #ifdef DF_PATH_TRACKING |
| - ++g_NumCachedPaths; |
| + ++g_NumCachedPaths; |
| #endif |
| + return true; |
| + } |
| - return pathData; |
| -} |
| + void drawPath(GrBatchTarget* target, |
| + GrBatchAtlas* atlas, |
| + const GrPipeline* pipeline, |
| + const GrGeometryProcessor* gp, |
| + SkPoint* positions, |
| + size_t vertexStride, |
| + const SkMatrix& viewMatrix, |
| + const SkPath& path, |
| + const PathData* pathData) { |
| + 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 invScale = 1.0f / pathData->fScale; |
| + dx *= invScale; |
| + dy *= invScale; |
| + width *= invScale; |
| + height *= invScale; |
| + |
| + SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX); |
| + SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY); |
| + SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); |
| + SkFixed th = SkScalarToFixed(pathData->fBounds.height()); |
| + |
| + // vertex positions |
| + // TODO make the vertex attributes a struct |
| + SkRect r = SkRect::MakeXYWH(dx, dy, width, height); |
| + positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride); |
| + |
| + // vertex texture coords |
| + SkPoint* textureCoords = positions + 1; |
| + textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)), |
| + SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)), |
| + SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)), |
| + SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)), |
| + vertexStride); |
| + } |
| -bool GrAADistanceFieldPathRenderer::freeUnusedPlot() { |
| - // find an unused plot |
| - GrPlot* plot = fAtlas->getUnusedPlot(); |
| - if (NULL == plot) { |
| - return false; |
| + void initDraw(GrBatchTarget* batchTarget, |
| + const GrGeometryProcessor* dfProcessor, |
| + const GrPipeline* pipeline) { |
| + batchTarget->initDraw(dfProcessor, pipeline); |
| + |
| + // TODO remove this when batch is everywhere |
| + GrPipelineInfo init; |
| + init.fColorIgnored = fBatch.fColorIgnored; |
| + init.fOverrideColor = GrColor_ILLEGAL; |
| + init.fCoverageIgnored = fBatch.fCoverageIgnored; |
| + init.fUsesLocalCoords = this->usesLocalCoords(); |
| + dfProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init); |
| } |
| - plot->resetRects(); |
| - // remove any paths that use this plot |
| - PathDataList::Iter iter; |
| - iter.init(fPathList, PathDataList::Iter::kHead_IterStart); |
| - PathData* pathData; |
| - while ((pathData = iter.get())) { |
| - iter.next(); |
| - if (plot == pathData->fPlot) { |
| - fPathCache.remove(pathData->fKey); |
| - fPathList.remove(pathData); |
| - SkDELETE(pathData); |
| -#ifdef DF_PATH_TRACKING |
| - ++g_NumFreedPaths; |
| -#endif |
| + void flush(GrBatchTarget* batchTarget, |
| + const GrGeometryProcessor* dfProcessor, |
| + const GrPipeline* pipeline, |
| + GrDrawTarget::DrawInfo* drawInfo, |
| + int instanceCount, |
| + int maxInstancesPerDraw) { |
| + while (instanceCount) { |
| + drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw)); |
| + drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verticesPerInstance()); |
| + drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indicesPerInstance()); |
| + |
| + batchTarget->draw(*drawInfo); |
| + |
| + drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexCount()); |
| + instanceCount -= drawInfo->instanceCount(); |
| + } |
| + } |
| + |
| + GrColor color() const { return fBatch.fColor; } |
| + const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } |
| + bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } |
| + |
| + bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { |
| + AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>(); |
| + |
| + // TODO we could actually probably do a bunch of this work on the CPU, ie map viewMatrix, |
| + // maybe upload color via attribute |
| + if (this->color() != that->color()) { |
| + return false; |
| + } |
| + |
| + if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
| + return false; |
| } |
| + |
| + fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); |
|
bsalomon
2015/03/05 22:11:08
seems like this could lead to unfortunate combinat
bsalomon
2015/03/06 16:34:59
Retracted... realized this doesn't lead to caching
|
| + return true; |
| } |
| - |
| - // tell the atlas to free the plot |
| - GrAtlas::RemovePlot(&fPlotUsage, plot); |
| - |
| - return true; |
| -} |
| -bool GrAADistanceFieldPathRenderer::internalDrawPath(GrDrawTarget* target, |
| - GrPipelineBuilder* pipelineBuilder, |
| - GrColor color, |
| - const SkMatrix& viewMatrix, |
| - const SkPath& path, |
| - const PathData* pathData) { |
| - GrTexture* texture = fAtlas->getTexture(); |
| - GrPipelineBuilder::AutoRestoreFragmentProcessors arfp(pipelineBuilder); |
| - |
| - SkASSERT(pathData->fPlot); |
| - GrDrawTarget::DrawToken drawToken = target->getCurrentDrawToken(); |
| - pathData->fPlot->setDrawToken(drawToken); |
| - |
| - // set up any flags |
| - uint32_t flags = 0; |
| - flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; |
| - |
| - GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); |
| - if (flags != fEffectFlags || fCachedGeometryProcessor->color() != color || |
| - !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(viewMatrix)) { |
| - fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Create(color, |
| - viewMatrix, |
| - texture, |
| - params, |
| - flags, |
| - false)); |
| - fEffectFlags = flags; |
| + struct BatchTracker { |
| + GrColor fColor; |
| + SkMatrix fViewMatrix; |
| + bool fUsesLocalCoords; |
| + bool fColorIgnored; |
| + bool fCoverageIgnored; |
| + }; |
| + |
| + BatchTracker fBatch; |
| + SkSTArray<1, Geometry, true> fGeoData; |
| + |
| + // padding around path bounds to allow for antialiased pixels |
| + const SkScalar kAntiAliasPad = 1.0f; |
|
bsalomon
2015/03/05 22:11:08
does this require c++11? (do we allow anything els
joshualitt
2015/03/09 19:45:14
Acknowledged.
|
| + GrBatchAtlas* fAtlas; |
| + PathCache* fPathCache; |
| + PathDataList* fPathList; |
| +}; |
| + |
| +bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target, |
| + GrPipelineBuilder* pipelineBuilder, |
| + GrColor color, |
| + const SkMatrix& viewMatrix, |
| + const SkPath& path, |
| + const SkStrokeRec& stroke, |
| + bool antiAlias) { |
| + // we've already bailed on inverse filled paths, so this is safe |
| + if (path.isEmpty()) { |
| + return true; |
| } |
| - void* vertices = NULL; |
| - bool success = target->reserveVertexAndIndexSpace(4, |
| - fCachedGeometryProcessor->getVertexStride(), |
| - 0, &vertices, NULL); |
| - SkASSERT(fCachedGeometryProcessor->getVertexStride() == 2 * sizeof(SkPoint)); |
| - GrAlwaysAssert(success); |
| - |
| - SkScalar dx = pathData->fBounds.fLeft; |
| - SkScalar dy = pathData->fBounds.fTop; |
| - SkScalar width = pathData->fBounds.width(); |
| - SkScalar height = pathData->fBounds.height(); |
| - |
| - SkScalar invScale = 1.0f/pathData->fScale; |
| - dx *= invScale; |
| - dy *= invScale; |
| - width *= invScale; |
| - height *= invScale; |
| - |
| - SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX); |
| - SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY); |
| - SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); |
| - SkFixed th = SkScalarToFixed(pathData->fBounds.height()); |
| - |
| - // vertex positions |
| - SkRect r = SkRect::MakeXYWH(dx, dy, width, height); |
| - size_t vertSize = 2 * sizeof(SkPoint); |
| - SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); |
| - positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertSize); |
| - |
| - // vertex texture coords |
| - intptr_t intPtr = reinterpret_cast<intptr_t>(positions); |
| - SkPoint* textureCoords = reinterpret_cast<SkPoint*>(intPtr + vertSize - sizeof(SkPoint)); |
| - textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)), |
| - SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)), |
| - SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)), |
| - SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)), |
| - vertSize); |
| - |
| - viewMatrix.mapRect(&r); |
| - target->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); |
| - target->drawIndexedInstances(pipelineBuilder, fCachedGeometryProcessor.get(), |
| - kTriangles_GrPrimitiveType, 1, 4, 6, &r); |
| - target->resetVertexSource(); |
| - |
| + SkASSERT(fContext); |
| + |
| + if (!fAtlas) { |
| + // Create a new atlas |
| + GrSurfaceDesc desc; |
| + desc.fFlags = kNone_GrSurfaceFlags; |
| + desc.fWidth = ATLAS_TEXTURE_WIDTH; |
| + desc.fHeight = ATLAS_TEXTURE_HEIGHT; |
| + desc.fConfig = kAlpha_8_GrPixelConfig; |
| + |
| + GrGpu* gpu = fContext->getGpu(); |
| + GrTexture* texture = gpu->createTexture(desc, true, NULL, 0); |
|
bsalomon
2015/03/05 22:11:08
not using the cache?
joshualitt
2015/03/09 19:45:14
Acknowledged.
|
| + if (texture) { |
| + fAtlas = SkNEW_ARGS(GrBatchAtlas, (gpu, texture, NUM_PLOTS_X, NUM_PLOTS_Y)); |
| + } |
| + } |
| + |
| + AADistanceFieldPathBatch::Geometry geometry(stroke); |
| + geometry.fPath = path; |
| + geometry.fAntiAlias = antiAlias; |
| + |
| + SkAutoTUnref<GrBatch> batch(AADistanceFieldPathBatch::Create(geometry, color, viewMatrix, |
| + fAtlas, &fPathCache, &fPathList)); |
| + |
| + SkRect bounds = path.getBounds(); |
| + viewMatrix.mapRect(&bounds); |
| + target->drawBatch(pipelineBuilder, batch, &bounds); |
| + |
| return true; |
| } |