Chromium Code Reviews| Index: src/gpu/GrAADistanceFieldPathRenderer.cpp | 
| diff --git a/src/gpu/GrAADistanceFieldPathRenderer.cpp b/src/gpu/GrAADistanceFieldPathRenderer.cpp | 
| new file mode 100755 | 
| index 0000000000000000000000000000000000000000..8cc2480bf60759f9a62a7782b5670276c3c31576 | 
| --- /dev/null | 
| +++ b/src/gpu/GrAADistanceFieldPathRenderer.cpp | 
| @@ -0,0 +1,351 @@ | 
| + | 
| +/* | 
| + * Copyright 2014 Google Inc. | 
| + * | 
| + * Use of this source code is governed by a BSD-style license that can be | 
| + * found in the LICENSE file. | 
| + */ | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:30
Is it kosher to include a gl header in gpu?
 
bsalomon
2014/09/26 17:32:51
We do this for processors all over the place, othe
 
jvanverth1
2014/10/03 17:28:21
Removed anyway, it's not needed.
 
 | 
| +#include "gl/builders/GrGLProgramBuilder.h" | 
| +#include "GrAADistanceFieldPathRenderer.h" | 
| + | 
| +#include "GrAtlas.h" | 
| +#include "GrContext.h" | 
| +#include "GrDrawState.h" | 
| +#include "GrSWMaskHelper.h" | 
| +#include "effects/GrDistanceFieldTextureEffect.h" | 
| + | 
| +#include "SkDistanceFieldGen.h" | 
| +#include "SkRTConf.h" | 
| + | 
| +#define GR_ATLAS_TEXTURE_WIDTH 1024 | 
| 
 
bsalomon
2014/09/26 17:32:51
Let's drop the GR_ prefix on these macros. They do
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| +#define GR_ATLAS_TEXTURE_HEIGHT 1024 | 
| + | 
| +#define GR_PLOT_WIDTH 256 | 
| +#define GR_PLOT_HEIGHT 256 | 
| + | 
| +#define GR_NUM_PLOTS_X (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH) | 
| +#define GR_NUM_PLOTS_Y (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT) | 
| + | 
| +SK_CONF_DECLARE(bool, c_DumpPathCache, "gpu.dumpPathCache", false, | 
| + "Dump the contents of the path cache before every purge."); | 
| + | 
| +//////////////////////////////////////////////////////////////////////////////// | 
| +GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { | 
| + PathDataList::Iter iter; | 
| + iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | 
| + PathData* pathData; | 
| + while ((pathData = iter.get())) { | 
| + iter.next(); | 
| + fPathList.remove(pathData); | 
| 
 
robertphillips
2014/09/26 14:00:29
SkDELETE
 
jvanverth1
2014/10/03 17:28:23
Done.
 
 | 
| + delete pathData; | 
| + } | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:30
SkDELETE
 
jvanverth1
2014/10/03 17:28:23
Done.
 
 | 
| + delete fAtlas; | 
| +} | 
| + | 
| +//////////////////////////////////////////////////////////////////////////////// | 
| +bool GrAADistanceFieldPathRenderer::canDrawPath(const SkPath& path, | 
| 
 
robertphillips
2014/09/26 14:00:29
make these line up?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + const SkStrokeRec& stroke, | 
| + const GrDrawTarget* target, | 
| + bool antiAlias) const { | 
| + // TODO: Support inverse fill | 
| 
 
bsalomon
2014/09/26 17:32:51
You might want to check out the GrGLPath stuff...
 
jvanverth1
2014/10/03 17:28:22
Acknowledged.
 
 | 
| + // TODO: Support strokes | 
| + if (!target->caps()->shaderDerivativeSupport() || !antiAlias || path.isInverseFillType() | 
| + || SkStrokeRec::kFill_Style != stroke.getStyle()) { | 
| + return false; | 
| + } | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:30
3x ?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + // currently don't support scaling more than 4x | 
| 
 
egdaniel
2014/09/26 13:30:25
But check is on for max scale of 3?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + const GrDrawState& drawState = target->getDrawState(); | 
| + const SkMatrix& vm = drawState.getViewMatrix(); | 
| + if (vm.getMaxScale() > 3.0f) { | 
| + return false; | 
| + } | 
| + | 
| + // only support paths smaller than 64 x 64 | 
| + const SkRect& bounds = path.getBounds(); | 
| + return bounds.width() < 64.f && bounds.height() < 64.f; | 
| +} | 
| + | 
| + | 
| +//*** not sure if this is needed | 
| 
 
bsalomon
2014/09/26 17:32:51
I think it's not, but we should have a GM that cli
 
jvanverth1
2014/10/03 17:28:22
I think it is needed -- GrSoftwarePathRenderer has
 
 | 
| +GrPathRenderer::StencilSupport GrAADistanceFieldPathRenderer::onGetStencilSupport( | 
| + const SkPath&, | 
| + const SkStrokeRec&, | 
| + const GrDrawTarget*) const { | 
| + return GrPathRenderer::kNoSupport_StencilSupport; | 
| +} | 
| + | 
| +//////////////////////////////////////////////////////////////////////////////// | 
| + | 
| +// position + texture coord | 
| +extern const GrVertexAttrib gSDFPathVertexAttribs[] = { | 
| + { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, | 
| + { kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding } | 
| +}; | 
| +static const size_t kSDFPathVASize = 2 * sizeof(SkPoint); | 
| + | 
| +bool GrAADistanceFieldPathRenderer::onDrawPath(const SkPath& path, | 
| 
 
robertphillips
2014/09/26 14:00:29
line these up?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + const SkStrokeRec& stroke, | 
| + GrDrawTarget* target, | 
| + bool antiAlias) { | 
| + if (path.isEmpty()) { | 
| 
 
bsalomon
2014/09/26 17:32:51
maybe a comment that we already bailed on inverse
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + return true; | 
| + } | 
| + | 
| + if (NULL == fContext) { | 
| 
 
bsalomon
2014/09/26 17:32:51
Do we need this?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + return false; | 
| + } | 
| + | 
| + // check to see if path is cached | 
| + // TODO: handle stroked vs. filled version of same path | 
| + PathData* pathData = fPathCache.find(path.getGenerationID()); | 
| + if (NULL == pathData) { | 
| 
 
robertphillips
2014/09/26 14:00:29
this-> ?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + pathData = addPathToAtlas(path, stroke, antiAlias); | 
| + if (NULL == pathData) { | 
| + return false; | 
| + } | 
| + } | 
| + | 
| + // use signed distance field to render | 
| 
 
robertphillips
2014/09/26 14:00:29
this-> ?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + return internalDrawPath(path, pathData, target); | 
| +} | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:30
// This is the factor by which we scale the path p
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| +const SkScalar kScaleFactor = 2.0f; | 
| +const SkScalar kAntiAliasPad = 1.0f; | 
| + | 
| +GrAADistanceFieldPathRenderer::PathData* GrAADistanceFieldPathRenderer::addPathToAtlas( | 
| + const SkPath& path, | 
| + const SkStrokeRec& stroke, | 
| + bool antiAlias) { | 
| + // generate distance field and add to atlas | 
| + if (NULL == fAtlas) { | 
| + SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, | 
| + GR_ATLAS_TEXTURE_HEIGHT); | 
| + fAtlas = SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kAlpha_8_GrPixelConfig, | 
| + kNone_GrTextureFlags, textureSize, | 
| + GR_NUM_PLOTS_X, GR_NUM_PLOTS_Y, false)); | 
| + if (NULL == fAtlas) { | 
| + return NULL; | 
| + } | 
| + } | 
| + | 
| + const SkRect& bounds = path.getBounds(); | 
| + | 
| + // generate bounding rect for bitmap draw | 
| + SkRect scaledBounds = bounds; | 
| + // scale up by 2x | 
| + scaledBounds.fLeft *= kScaleFactor; | 
| + scaledBounds.fTop *= kScaleFactor; | 
| + scaledBounds.fRight *= kScaleFactor; | 
| + scaledBounds.fBottom *= kScaleFactor; | 
| + // 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 by 1 to allow room for antialiasing | 
| 
 
robertphillips
2014/09/26 14:00:30
Not CeilToInt ?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + devPathBounds.outset(SkScalarFloorToInt(kAntiAliasPad), SkScalarFloorToInt(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(kScaleFactor, kScaleFactor); | 
| + 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(dfStorage.get()); | 
| + | 
| + // add to atlas | 
| + SkIPoint16 atlasLocation; | 
| + GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(), | 
| + &atlasLocation); | 
| + | 
| + // if atlas full | 
| + if (NULL == plot) { | 
| 
 
robertphillips
2014/09/26 14:00:29
this-> ?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + if (freeUnusedPlot()) { | 
| + plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(), | 
| + &atlasLocation); | 
| + if (plot) { | 
| + goto HAS_ATLAS; | 
| + } | 
| + } | 
| + | 
| + if (c_DumpPathCache) { | 
| +#ifdef SK_DEVELOPER | 
| + GrTexture* texture = fAtlas->getTexture(); | 
| + texture->savePixels("pathcache.png"); | 
| +#endif | 
| + } | 
| + | 
| + // before we purge the cache, we must flush any accumulated draws | 
| + fContext->flush(); | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:29
this-> ?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + if (freeUnusedPlot()) { | 
| + plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(), | 
| + &atlasLocation); | 
| + if (plot) { | 
| + goto HAS_ATLAS; | 
| + } | 
| + } | 
| + | 
| + return NULL; | 
| + } | 
| + | 
| +HAS_ATLAS: | 
| 
 
robertphillips
2014/09/26 14:00:29
Guard this ?!
 
jvanverth1
2014/10/03 17:28:22
Whoops, removed.
 
 | 
| + GrTexture* texture = fAtlas->getTexture(); | 
| + texture->savePixels("pathcache.png"); | 
| + | 
| + // add to cache | 
| 
 
robertphillips
2014/09/26 14:00:29
SkNEW ?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + PathData* pathData = new PathData(); | 
| + pathData->fGenID = path.getGenerationID(); | 
| + 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); | 
| + | 
| + return pathData; | 
| +} | 
| + | 
| +bool GrAADistanceFieldPathRenderer::freeUnusedPlot() { | 
| + // find an unused plot | 
| 
 
robertphillips
2014/09/26 14:00:30
I don't think this fAtlas alias buys us much.
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + GrAtlas* atlas = fAtlas; | 
| + GrPlot* plot = atlas->getUnusedPlot(); | 
| + if (NULL == plot) { | 
| + return false; | 
| + } | 
| + 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->fGenID); | 
| + fPathList.remove(pathData); | 
| 
 
robertphillips
2014/09/26 14:00:29
SkDELETE ?
 
jvanverth1
2014/10/03 17:28:23
Done.
 
 | 
| + delete pathData; | 
| + } | 
| + } | 
| + | 
| + // tell the atlas to free the plot | 
| + GrAtlas::RemovePlot(&fPlotUsage, plot); | 
| + | 
| + return true; | 
| +} | 
| + | 
| +bool GrAADistanceFieldPathRenderer::internalDrawPath(const SkPath& path, | 
| 
 
robertphillips
2014/09/26 14:00:29
line these up ?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + const PathData* pathData, | 
| + GrDrawTarget* target) { | 
| + | 
| + GrTexture* texture = fAtlas->getTexture(); | 
| + GrDrawState* drawState = target->drawState(); | 
| + | 
| + SkASSERT(pathData->fPlot); | 
| + GrDrawTarget::DrawToken drawToken = target->getCurrentDrawToken(); | 
| + pathData->fPlot->setDrawToken(drawToken); | 
| + | 
| + // make me some vertices | 
| + drawState->setVertexAttribs<gSDFPathVertexAttribs>(SK_ARRAY_COUNT(gSDFPathVertexAttribs), | 
| + kSDFPathVASize); | 
| + void* vertices = NULL; | 
| + void* indices = NULL; | 
| 
 
robertphillips
2014/09/26 14:00:28
Will this fit on one line ?
 
jvanverth1
2014/10/03 17:28:21
Done.
 
 | 
| + bool success = target->reserveVertexAndIndexSpace(4, | 
| + 6, | 
| + &vertices, | 
| + &indices); | 
| + GrAlwaysAssert(success); | 
| + | 
| + SkScalar dx = pathData->fBounds.fLeft; | 
| + SkScalar dy = pathData->fBounds.fTop; | 
| + SkScalar width = pathData->fBounds.width(); | 
| + SkScalar height = pathData->fBounds.height(); | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:29
invScale ?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + SkScalar scale = 1.0f/kScaleFactor; | 
| + dx *= scale; | 
| + dy *= scale; | 
| + width *= scale; | 
| + height *= scale; | 
| + | 
| + SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX); | 
| + SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY); | 
| + SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); | 
| + SkFixed th = SkScalarToFixed(pathData->fBounds.height()); | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:29
SkRect::MakeWH(dx, dy, width, height); ?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + SkRect r; | 
| + r.fLeft = dx; | 
| + r.fTop = dy; | 
| + r.fRight = dx + width; | 
| + r.fBottom = dy + height; | 
| + | 
| + size_t vertSize = 2 * sizeof(SkPoint); | 
| + | 
| + SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); | 
| + positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertSize); | 
| + | 
| + // The texture coords are last in both the with and without color vertex layouts. | 
| + SkPoint* textureCoords = reinterpret_cast<SkPoint*>( | 
| 
 
robertphillips
2014/09/26 14:00:29
overlength.
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + reinterpret_cast<intptr_t>(positions)+vertSize - sizeof(SkPoint)); | 
| 
 
egdaniel
2014/09/26 13:30:25
fix line wrap
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), | 
| + SkFixedToFloat(texture->normalizeFixedY(ty)), | 
| + SkFixedToFloat(texture->normalizeFixedX(tx + tw)), | 
| + SkFixedToFloat(texture->normalizeFixedY(ty + th)), | 
| + vertSize); | 
| + | 
| + uint16_t* indexPtr = reinterpret_cast<uint16_t*>(indices); | 
| + *indexPtr++ = 0; | 
| + *indexPtr++ = 1; | 
| + *indexPtr++ = 2; | 
| + *indexPtr++ = 0; | 
| + *indexPtr++ = 2; | 
| + *indexPtr++ = 3; | 
| + | 
| + // set up any flags | 
| + uint32_t flags = 0; | 
| + const SkMatrix& vm = drawState->getViewMatrix(); | 
| + flags |= vm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; | 
| + | 
| + GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); | 
| + drawState->setGeometryProcessor(GrDistanceFieldNoGammaTextureEffect::Create(texture, | 
| + params, | 
| + flags))->unref(); | 
| + | 
| 
 
robertphillips
2014/09/26 14:00:29
one line ?
 
jvanverth1
2014/10/03 17:28:22
Done.
 
 | 
| + target->drawIndexedInstances(kTriangles_GrPrimitiveType, | 
| + 1, | 
| + 4, 6, &r); | 
| + target->resetVertexSource(); | 
| + target->resetIndexSource(); | 
| + | 
| + return true; | 
| +} | 
| + |