Index: src/gpu/GrAADistanceFieldPathRenderer.cpp |
diff --git a/src/gpu/GrAADistanceFieldPathRenderer.cpp b/src/gpu/GrAADistanceFieldPathRenderer.cpp |
index d50af9fbd85829e0b2c9235301f96da65cf7d036..b9ca1ec26b6fc77fcae1b585e97842abc07a1869 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; |
@@ -40,11 +39,30 @@ static const int kSmallMIP = 32; |
static const int kMediumMIP = 78; |
static const int kLargeMIP = 192; |
+// Callback to clear out internal path cache when eviction occurs |
+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())) { |
+ iter.next(); |
+ if (id == pathData->fID) { |
+ dfpr->fPathCache.remove(pathData->fKey); |
+ dfpr->fPathList.remove(pathData); |
+ SkDELETE(pathData); |
+#ifdef DF_PATH_TRACKING |
+ ++g_NumFreedPaths; |
+#endif |
+ } |
+ } |
+} |
+ |
//////////////////////////////////////////////////////////////////////////////// |
GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context) |
: fContext(context) |
- , fAtlas(NULL) |
- , fEffectFlags(kInvalid_DistanceFieldEffectFlag) { |
+ , fAtlas(NULL) { |
} |
GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { |
@@ -56,7 +74,6 @@ GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { |
fPathList.remove(pathData); |
SkDELETE(pathData); |
} |
- |
SkDELETE(fAtlas); |
#ifdef DF_PATH_TRACKING |
@@ -89,7 +106,7 @@ bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target, |
SkScalar maxScale = viewMatrix.getMaxScale(); |
const SkRect& bounds = path.getBounds(); |
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); |
- return maxDim < 64.f && maxDim*maxScale < 256.f; |
+ return maxDim < 64.f && maxDim * maxScale < 256.f; |
} |
@@ -103,285 +120,501 @@ 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; |
+// padding around path bounds to allow for antialiased pixels |
+static const SkScalar kAntiAliasPad = 1.0f; |
+ |
+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; |
- |
-inline bool GrAADistanceFieldPathRenderer::uploadPath(GrPlot** plot, SkIPoint16* atlasLocation, |
- int width, int height, void* dfStorage) { |
- *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation); |
- |
- // if atlas full |
- if (NULL == *plot) { |
- if (this->freeUnusedPlot()) { |
- *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation); |
- if (*plot) { |
- return true; |
- } |
+ SkMatrix invert; |
+ if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { |
+ SkDebugf("Could not invert viewmatrix\n"); |
+ return; |
} |
- if (c_DumpPathCache) { |
-#ifdef SK_DEVELOPER |
- GrTexture* texture = fAtlas->getTexture(); |
- texture->surfacePriv().savePixels("pathcache.png"); |
-#endif |
+ 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); |
+ |
+ if (!vertices) { |
+ SkDebugf("Could not allocate vertices\n"); |
+ return; |
} |
- // before we purge the cache, we must flush any accumulated draws |
- fContext->flush(); |
+ // 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 (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; |
+ } |
} |
- } |
- return false; |
- } |
- return true; |
-} |
-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; |
+ 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++; |
} |
+ |
+ this->flush(batchTarget, dfProcessor, pipeline, &drawInfo, instancesToFlush, |
+ maxInstancesPerDraw); |
} |
- |
- 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; |
+ |
+ 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; |
} |
- // 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); |
+ 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()); |
+ } |
+ } |
+ 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); |
+ |
+ } |
+ |
+ // 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()); |
+ 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; |
+ 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; |
+ |
+ // We don't want to flush the context so we claim we're in the middle of flushing so as to |
+ // guarantee we do not recieve a texture with pending IO |
+ GrTexture* texture = fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch, |
+ true); |
+ if (texture) { |
+ fAtlas = SkNEW_ARGS(GrBatchAtlas, (texture, NUM_PLOTS_X, NUM_PLOTS_Y)); |
+ } else { |
+ return false; |
+ } |
+ fAtlas->registerEvictionCallback(&GrAADistanceFieldPathRenderer::HandleEviction, |
+ (void*)this); |
+ } |
+ |
+ 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; |
} |