| 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;
|
| }
|
|
|
|
|