blob: 5e0410fd751994459bf392561b962413f0d05652 [file] [log] [blame]
jvanverthfa38a302014-10-06 05:59:05 -07001
2/*
3 * Copyright 2014 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrAADistanceFieldPathRenderer.h"
10
11#include "GrAtlas.h"
12#include "GrContext.h"
13#include "GrDrawState.h"
14#include "GrSurfacePriv.h"
15#include "GrSWMaskHelper.h"
16#include "GrTexturePriv.h"
17#include "effects/GrDistanceFieldTextureEffect.h"
18
19#include "SkDistanceFieldGen.h"
20#include "SkRTConf.h"
21
22#define ATLAS_TEXTURE_WIDTH 1024
jvanverthb61283f2014-10-30 05:57:21 -070023#define ATLAS_TEXTURE_HEIGHT 2048
jvanverthfa38a302014-10-06 05:59:05 -070024#define PLOT_WIDTH 256
25#define PLOT_HEIGHT 256
26
27#define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
28#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
29
30SK_CONF_DECLARE(bool, c_DumpPathCache, "gpu.dumpPathCache", false,
31 "Dump the contents of the path cache before every purge.");
32
jvanverthb3eb6872014-10-24 07:12:51 -070033#ifdef DF_PATH_TRACKING
34static int g_NumCachedPaths = 0;
35static int g_NumFreedPaths = 0;
36#endif
37
jvanverthb61283f2014-10-30 05:57:21 -070038// mip levels
39static const int kSmallMIP = 32;
jvanverthada68ef2014-11-03 14:00:24 -080040static const int kMediumMIP = 78;
41static const int kLargeMIP = 192;
jvanverthb61283f2014-10-30 05:57:21 -070042
jvanverthfa38a302014-10-06 05:59:05 -070043////////////////////////////////////////////////////////////////////////////////
jvanverth6d22eca2014-10-28 11:10:48 -070044GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context)
45 : fContext(context)
46 , fAtlas(NULL)
47 , fEffectFlags(kInvalid_DistanceFieldEffectFlag) {
48}
49
jvanverthfa38a302014-10-06 05:59:05 -070050GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
51 PathDataList::Iter iter;
52 iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
53 PathData* pathData;
54 while ((pathData = iter.get())) {
55 iter.next();
56 fPathList.remove(pathData);
57 SkDELETE(pathData);
58 }
59
60 SkDELETE(fAtlas);
jvanverthb3eb6872014-10-24 07:12:51 -070061
62#ifdef DF_PATH_TRACKING
63 SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths);
64#endif
jvanverthfa38a302014-10-06 05:59:05 -070065}
66
67////////////////////////////////////////////////////////////////////////////////
joshualitt9853cce2014-11-17 14:22:48 -080068bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target,
69 const GrDrawState* drawState,
70 const SkPath& path,
jvanverthfa38a302014-10-06 05:59:05 -070071 const SkStrokeRec& stroke,
jvanverthfa38a302014-10-06 05:59:05 -070072 bool antiAlias) const {
jvanverthb3eb6872014-10-24 07:12:51 -070073
jvanverthfa38a302014-10-06 05:59:05 -070074 // TODO: Support inverse fill
75 // TODO: Support strokes
76 if (!target->caps()->shaderDerivativeSupport() || !antiAlias || path.isInverseFillType()
jvanverthb3eb6872014-10-24 07:12:51 -070077 || path.isVolatile() || SkStrokeRec::kFill_Style != stroke.getStyle()) {
jvanverthfa38a302014-10-06 05:59:05 -070078 return false;
79 }
80
jvanverthb61283f2014-10-30 05:57:21 -070081 // currently don't support perspective
joshualitt9853cce2014-11-17 14:22:48 -080082 const SkMatrix& vm = drawState->getViewMatrix();
jvanverthb61283f2014-10-30 05:57:21 -070083 if (vm.hasPerspective()) {
jvanverthfa38a302014-10-06 05:59:05 -070084 return false;
85 }
86
jvanverthb61283f2014-10-30 05:57:21 -070087 // only support paths smaller than 64x64, scaled to less than 256x256
88 // the goal is to accelerate rendering of lots of small paths that may be scaling
89 SkScalar maxScale = vm.getMaxScale();
jvanverthfa38a302014-10-06 05:59:05 -070090 const SkRect& bounds = path.getBounds();
jvanverthb61283f2014-10-30 05:57:21 -070091 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
92 return maxDim < 64.f && maxDim*maxScale < 256.f;
jvanverthfa38a302014-10-06 05:59:05 -070093}
94
95
joshualitt9853cce2014-11-17 14:22:48 -080096GrPathRenderer::StencilSupport
97GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*,
98 const GrDrawState*,
99 const SkPath&,
100 const SkStrokeRec&) const {
jvanverthfa38a302014-10-06 05:59:05 -0700101 return GrPathRenderer::kNoSupport_StencilSupport;
102}
103
104////////////////////////////////////////////////////////////////////////////////
105
106// position + texture coord
107extern const GrVertexAttrib gSDFPathVertexAttribs[] = {
108 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
109 { kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding }
110};
111static const size_t kSDFPathVASize = 2 * sizeof(SkPoint);
112
joshualitt9853cce2014-11-17 14:22:48 -0800113bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target,
114 GrDrawState* drawState,
115 const SkPath& path,
jvanverthfa38a302014-10-06 05:59:05 -0700116 const SkStrokeRec& stroke,
jvanverthfa38a302014-10-06 05:59:05 -0700117 bool antiAlias) {
118 // we've already bailed on inverse filled paths, so this is safe
119 if (path.isEmpty()) {
120 return true;
121 }
122
123 SkASSERT(fContext);
124
jvanverthb61283f2014-10-30 05:57:21 -0700125 // get mip level
joshualitt9853cce2014-11-17 14:22:48 -0800126 const SkMatrix& vm = drawState->getViewMatrix();
jvanverthb61283f2014-10-30 05:57:21 -0700127 SkScalar maxScale = vm.getMaxScale();
128 const SkRect& bounds = path.getBounds();
129 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
130 SkScalar size = maxScale*maxDim;
131 uint32_t desiredDimension;
132 if (size <= kSmallMIP) {
133 desiredDimension = kSmallMIP;
134 } else if (size <= kMediumMIP) {
135 desiredDimension = kMediumMIP;
136 } else {
137 desiredDimension = kLargeMIP;
138 }
139
jvanverthfa38a302014-10-06 05:59:05 -0700140 // check to see if path is cached
141 // TODO: handle stroked vs. filled version of same path
jvanverthb61283f2014-10-30 05:57:21 -0700142 PathData::Key key = { path.getGenerationID(), desiredDimension };
143 PathData* pathData = fPathCache.find(key);
jvanverthfa38a302014-10-06 05:59:05 -0700144 if (NULL == pathData) {
jvanverthb61283f2014-10-30 05:57:21 -0700145 SkScalar scale = desiredDimension/maxDim;
146 pathData = this->addPathToAtlas(path, stroke, antiAlias, desiredDimension, scale);
jvanverthfa38a302014-10-06 05:59:05 -0700147 if (NULL == pathData) {
148 return false;
149 }
150 }
151
152 // use signed distance field to render
joshualitt9853cce2014-11-17 14:22:48 -0800153 return this->internalDrawPath(target, drawState, path, pathData);
jvanverthfa38a302014-10-06 05:59:05 -0700154}
155
jvanverthfa38a302014-10-06 05:59:05 -0700156// padding around path bounds to allow for antialiased pixels
157const SkScalar kAntiAliasPad = 1.0f;
158
159GrAADistanceFieldPathRenderer::PathData* GrAADistanceFieldPathRenderer::addPathToAtlas(
160 const SkPath& path,
161 const SkStrokeRec& stroke,
jvanverthb61283f2014-10-30 05:57:21 -0700162 bool antiAlias,
163 uint32_t dimension,
164 SkScalar scale) {
jvanverthfa38a302014-10-06 05:59:05 -0700165
166 // generate distance field and add to atlas
167 if (NULL == fAtlas) {
168 SkISize textureSize = SkISize::Make(ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT);
169 fAtlas = SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kAlpha_8_GrPixelConfig,
bsalomonf2703d82014-10-28 14:33:06 -0700170 kNone_GrSurfaceFlags, textureSize,
jvanverthfa38a302014-10-06 05:59:05 -0700171 NUM_PLOTS_X, NUM_PLOTS_Y, false));
172 if (NULL == fAtlas) {
173 return NULL;
174 }
175 }
176
177 const SkRect& bounds = path.getBounds();
178
179 // generate bounding rect for bitmap draw
180 SkRect scaledBounds = bounds;
jvanverthb61283f2014-10-30 05:57:21 -0700181 // scale to mip level size
182 scaledBounds.fLeft *= scale;
183 scaledBounds.fTop *= scale;
184 scaledBounds.fRight *= scale;
185 scaledBounds.fBottom *= scale;
jvanverthfa38a302014-10-06 05:59:05 -0700186 // move the origin to an integer boundary (gives better results)
187 SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
188 SkScalar dy = SkScalarFraction(scaledBounds.fTop);
189 scaledBounds.offset(-dx, -dy);
190 // get integer boundary
191 SkIRect devPathBounds;
192 scaledBounds.roundOut(&devPathBounds);
193 // pad to allow room for antialiasing
194 devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad));
195 // move origin to upper left corner
196 devPathBounds.offsetTo(0,0);
197
198 // draw path to bitmap
199 SkMatrix drawMatrix;
200 drawMatrix.setTranslate(-bounds.left(), -bounds.top());
jvanverthb61283f2014-10-30 05:57:21 -0700201 drawMatrix.postScale(scale, scale);
jvanverthfa38a302014-10-06 05:59:05 -0700202 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
203 GrSWMaskHelper helper(fContext);
204
205 if (!helper.init(devPathBounds, &drawMatrix)) {
206 return NULL;
207 }
208 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
209
210 // generate signed distance field
211 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
212 int width = devPathBounds.width();
213 int height = devPathBounds.height();
214 SkAutoSMalloc<1024> dfStorage(width*height*sizeof(unsigned char));
215 helper.toSDF((unsigned char*) dfStorage.get());
216
217 // add to atlas
218 SkIPoint16 atlasLocation;
219 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(),
220 &atlasLocation);
221
222 // if atlas full
223 if (NULL == plot) {
224 if (this->freeUnusedPlot()) {
225 plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(),
226 &atlasLocation);
227 if (plot) {
228 goto HAS_ATLAS;
229 }
230 }
231
232 if (c_DumpPathCache) {
233#ifdef SK_DEVELOPER
234 GrTexture* texture = fAtlas->getTexture();
235 texture->surfacePriv().savePixels("pathcache.png");
236#endif
237 }
238
239 // before we purge the cache, we must flush any accumulated draws
240 fContext->flush();
241
242 if (this->freeUnusedPlot()) {
243 plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage.get(),
244 &atlasLocation);
245 if (plot) {
246 goto HAS_ATLAS;
247 }
248 }
249
250 return NULL;
251 }
252
253HAS_ATLAS:
254 // add to cache
255 PathData* pathData = SkNEW(PathData);
jvanverthb61283f2014-10-30 05:57:21 -0700256 pathData->fKey.fGenID = path.getGenerationID();
257 pathData->fKey.fDimension = dimension;
258 pathData->fScale = scale;
jvanverthfa38a302014-10-06 05:59:05 -0700259 pathData->fPlot = plot;
260 // change the scaled rect to match the size of the inset distance field
261 scaledBounds.fRight = scaledBounds.fLeft +
262 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
263 scaledBounds.fBottom = scaledBounds.fTop +
264 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
265 // shift the origin to the correct place relative to the distance field
266 // need to also restore the fractional translation
267 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
268 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
269 pathData->fBounds = scaledBounds;
270 // origin we render from is inset from distance field edge
271 atlasLocation.fX += SK_DistanceFieldInset;
272 atlasLocation.fY += SK_DistanceFieldInset;
273 pathData->fAtlasLocation = atlasLocation;
274
275 fPathCache.add(pathData);
276 fPathList.addToTail(pathData);
jvanverthb3eb6872014-10-24 07:12:51 -0700277#ifdef DF_PATH_TRACKING
278 ++g_NumCachedPaths;
279#endif
280
jvanverthfa38a302014-10-06 05:59:05 -0700281 return pathData;
282}
283
284bool GrAADistanceFieldPathRenderer::freeUnusedPlot() {
285 // find an unused plot
286 GrPlot* plot = fAtlas->getUnusedPlot();
287 if (NULL == plot) {
288 return false;
289 }
290 plot->resetRects();
291
292 // remove any paths that use this plot
293 PathDataList::Iter iter;
294 iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
295 PathData* pathData;
296 while ((pathData = iter.get())) {
297 iter.next();
298 if (plot == pathData->fPlot) {
jvanverthb61283f2014-10-30 05:57:21 -0700299 fPathCache.remove(pathData->fKey);
jvanverthfa38a302014-10-06 05:59:05 -0700300 fPathList.remove(pathData);
301 SkDELETE(pathData);
jvanverthb3eb6872014-10-24 07:12:51 -0700302#ifdef DF_PATH_TRACKING
303 ++g_NumFreedPaths;
304#endif
jvanverthfa38a302014-10-06 05:59:05 -0700305 }
306 }
307
308 // tell the atlas to free the plot
309 GrAtlas::RemovePlot(&fPlotUsage, plot);
310
311 return true;
312}
313
joshualitt9853cce2014-11-17 14:22:48 -0800314bool GrAADistanceFieldPathRenderer::internalDrawPath(GrDrawTarget* target,
315 GrDrawState* drawState,
316 const SkPath& path,
317 const PathData* pathData) {
jvanverthfa38a302014-10-06 05:59:05 -0700318 GrTexture* texture = fAtlas->getTexture();
jvanverthf19657f2014-10-07 08:04:58 -0700319 GrDrawState::AutoRestoreEffects are(drawState);
jvanverthfa38a302014-10-06 05:59:05 -0700320
321 SkASSERT(pathData->fPlot);
322 GrDrawTarget::DrawToken drawToken = target->getCurrentDrawToken();
323 pathData->fPlot->setDrawToken(drawToken);
324
325 // make me some vertices
326 drawState->setVertexAttribs<gSDFPathVertexAttribs>(SK_ARRAY_COUNT(gSDFPathVertexAttribs),
327 kSDFPathVASize);
328 void* vertices = NULL;
joshualitt9853cce2014-11-17 14:22:48 -0800329 bool success = target->reserveVertexAndIndexSpace(4, drawState->getVertexStride(), 0, &vertices,
330 NULL);
jvanverthfa38a302014-10-06 05:59:05 -0700331 GrAlwaysAssert(success);
332
333 SkScalar dx = pathData->fBounds.fLeft;
334 SkScalar dy = pathData->fBounds.fTop;
335 SkScalar width = pathData->fBounds.width();
336 SkScalar height = pathData->fBounds.height();
337
jvanverthb61283f2014-10-30 05:57:21 -0700338 SkScalar invScale = 1.0f/pathData->fScale;
jvanverthfa38a302014-10-06 05:59:05 -0700339 dx *= invScale;
340 dy *= invScale;
341 width *= invScale;
342 height *= invScale;
343
344 SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX);
345 SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY);
346 SkFixed tw = SkScalarToFixed(pathData->fBounds.width());
347 SkFixed th = SkScalarToFixed(pathData->fBounds.height());
348
349 // vertex positions
350 SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
351 size_t vertSize = 2 * sizeof(SkPoint);
352 SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
353 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertSize);
354
355 // vertex texture coords
356 intptr_t intPtr = reinterpret_cast<intptr_t>(positions);
357 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(intPtr + vertSize - sizeof(SkPoint));
358 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
359 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
360 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
361 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
362 vertSize);
363
jvanverthfa38a302014-10-06 05:59:05 -0700364 // set up any flags
365 uint32_t flags = 0;
366 const SkMatrix& vm = drawState->getViewMatrix();
367 flags |= vm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
368
369 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
jvanverth6d22eca2014-10-28 11:10:48 -0700370 if (flags != fEffectFlags) {
371 fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Create(texture,
372 params,
373 flags));
374 fEffectFlags = flags;
375 }
376 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
jvanverthfa38a302014-10-06 05:59:05 -0700377
378 vm.mapRect(&r);
jvanverth6d22eca2014-10-28 11:10:48 -0700379 target->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
joshualitt9853cce2014-11-17 14:22:48 -0800380 target->drawIndexedInstances(drawState, kTriangles_GrPrimitiveType, 1, 4, 6, &r);
jvanverthfa38a302014-10-06 05:59:05 -0700381 target->resetVertexSource();
jvanverthfa38a302014-10-06 05:59:05 -0700382
383 return true;
384}
385