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