blob: f0b6c7779b304e9072d62c370e95ea773eaae793 [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
joshualitt5bf99f12015-03-13 11:47:42 -070011#include "GrBatch.h"
12#include "GrBatchTarget.h"
13#include "GrBufferAllocPool.h"
jvanverthfa38a302014-10-06 05:59:05 -070014#include "GrContext.h"
egdaniel8dd688b2015-01-22 10:16:09 -080015#include "GrPipelineBuilder.h"
jvanverthfa38a302014-10-06 05:59:05 -070016#include "GrSurfacePriv.h"
17#include "GrSWMaskHelper.h"
18#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070019#include "GrVertexBuffer.h"
jvanverth8ed3b9a2015-04-09 08:00:49 -070020#include "effects/GrDistanceFieldGeoProc.h"
jvanverthfa38a302014-10-06 05:59:05 -070021
22#include "SkDistanceFieldGen.h"
23#include "SkRTConf.h"
24
reede4ef1ca2015-02-17 18:38:38 -080025#define ATLAS_TEXTURE_WIDTH 1024
jvanverthb61283f2014-10-30 05:57:21 -070026#define ATLAS_TEXTURE_HEIGHT 2048
reede4ef1ca2015-02-17 18:38:38 -080027#define PLOT_WIDTH 256
28#define PLOT_HEIGHT 256
jvanverthfa38a302014-10-06 05:59:05 -070029
30#define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
31#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
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
joshualitt5bf99f12015-03-13 11:47:42 -070043// Callback to clear out internal path cache when eviction occurs
44void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
45 GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr;
46 // remove any paths that use this plot
47 PathDataList::Iter iter;
48 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
49 PathData* pathData;
50 while ((pathData = iter.get())) {
51 iter.next();
52 if (id == pathData->fID) {
53 dfpr->fPathCache.remove(pathData->fKey);
54 dfpr->fPathList.remove(pathData);
55 SkDELETE(pathData);
56#ifdef DF_PATH_TRACKING
57 ++g_NumFreedPaths;
58#endif
59 }
60 }
61}
62
jvanverthfa38a302014-10-06 05:59:05 -070063////////////////////////////////////////////////////////////////////////////////
jvanverth6d22eca2014-10-28 11:10:48 -070064GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context)
65 : fContext(context)
joshualitt5bf99f12015-03-13 11:47:42 -070066 , fAtlas(NULL) {
jvanverth6d22eca2014-10-28 11:10:48 -070067}
68
jvanverthfa38a302014-10-06 05:59:05 -070069GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
70 PathDataList::Iter iter;
71 iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
72 PathData* pathData;
73 while ((pathData = iter.get())) {
74 iter.next();
75 fPathList.remove(pathData);
76 SkDELETE(pathData);
77 }
jvanverthfa38a302014-10-06 05:59:05 -070078 SkDELETE(fAtlas);
jvanverthb3eb6872014-10-24 07:12:51 -070079
80#ifdef DF_PATH_TRACKING
81 SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths);
82#endif
jvanverthfa38a302014-10-06 05:59:05 -070083}
84
85////////////////////////////////////////////////////////////////////////////////
joshualitt9853cce2014-11-17 14:22:48 -080086bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -080087 const GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -080088 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -080089 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -070090 const GrStrokeInfo& stroke,
jvanverthfa38a302014-10-06 05:59:05 -070091 bool antiAlias) const {
jvanverthb3eb6872014-10-24 07:12:51 -070092
jvanverthfa38a302014-10-06 05:59:05 -070093 // TODO: Support inverse fill
94 // TODO: Support strokes
jvanverthe9c0fc62015-04-29 11:18:05 -070095 if (!target->caps()->shaderCaps()->shaderDerivativeSupport() || !antiAlias
96 || path.isInverseFillType() || path.isVolatile() || !stroke.isFillStyle()) {
jvanverthfa38a302014-10-06 05:59:05 -070097 return false;
98 }
99
jvanverthb61283f2014-10-30 05:57:21 -0700100 // currently don't support perspective
joshualitt8059eb92014-12-29 15:10:07 -0800101 if (viewMatrix.hasPerspective()) {
jvanverthfa38a302014-10-06 05:59:05 -0700102 return false;
103 }
104
jvanverthb61283f2014-10-30 05:57:21 -0700105 // only support paths smaller than 64x64, scaled to less than 256x256
106 // the goal is to accelerate rendering of lots of small paths that may be scaling
joshualitt8059eb92014-12-29 15:10:07 -0800107 SkScalar maxScale = viewMatrix.getMaxScale();
jvanverthfa38a302014-10-06 05:59:05 -0700108 const SkRect& bounds = path.getBounds();
jvanverthb61283f2014-10-30 05:57:21 -0700109 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
joshualitt5bf99f12015-03-13 11:47:42 -0700110 return maxDim < 64.f && maxDim * maxScale < 256.f;
jvanverthfa38a302014-10-06 05:59:05 -0700111}
112
113
joshualitt9853cce2014-11-17 14:22:48 -0800114GrPathRenderer::StencilSupport
115GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*,
egdaniel8dd688b2015-01-22 10:16:09 -0800116 const GrPipelineBuilder*,
joshualitt9853cce2014-11-17 14:22:48 -0800117 const SkPath&,
kkinnunen18996512015-04-26 23:18:49 -0700118 const GrStrokeInfo&) const {
jvanverthfa38a302014-10-06 05:59:05 -0700119 return GrPathRenderer::kNoSupport_StencilSupport;
120}
121
122////////////////////////////////////////////////////////////////////////////////
123
joshualitt5bf99f12015-03-13 11:47:42 -0700124// padding around path bounds to allow for antialiased pixels
125static const SkScalar kAntiAliasPad = 1.0f;
126
127class AADistanceFieldPathBatch : public GrBatch {
128public:
129 typedef GrAADistanceFieldPathRenderer::PathData PathData;
130 typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
131 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
132
133 struct Geometry {
134 Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {}
135 SkPath fPath;
136 SkStrokeRec fStroke;
137 bool fAntiAlias;
138 PathData* fPathData;
139 };
140
141 static GrBatch* Create(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
142 GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) {
143 return SkNEW_ARGS(AADistanceFieldPathBatch, (geometry, color, viewMatrix,
144 atlas, pathCache, pathList));
145 }
146
mtklein36352bf2015-03-25 18:17:31 -0700147 const char* name() const override { return "AADistanceFieldPathBatch"; }
joshualitt5bf99f12015-03-13 11:47:42 -0700148
mtklein36352bf2015-03-25 18:17:31 -0700149 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt5bf99f12015-03-13 11:47:42 -0700150 out->setKnownFourComponents(fBatch.fColor);
151 }
152
mtklein36352bf2015-03-25 18:17:31 -0700153 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt5bf99f12015-03-13 11:47:42 -0700154 out->setUnknownSingleComponent();
155 }
156
mtklein36352bf2015-03-25 18:17:31 -0700157 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700158 // Handle any color overrides
159 if (init.fColorIgnored) {
160 fBatch.fColor = GrColor_ILLEGAL;
161 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
162 fBatch.fColor = init.fOverrideColor;
163 }
164
165 // setup batch properties
166 fBatch.fColorIgnored = init.fColorIgnored;
167 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
168 fBatch.fCoverageIgnored = init.fCoverageIgnored;
169 }
170
mtklein36352bf2015-03-25 18:17:31 -0700171 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700172 int instanceCount = fGeoData.count();
173
174 SkMatrix invert;
175 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
176 SkDebugf("Could not invert viewmatrix\n");
177 return;
178 }
179
180 uint32_t flags = 0;
181 flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
182
183 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
184
185 // Setup GrGeometryProcessor
186 GrBatchAtlas* atlas = fAtlas;
187 SkAutoTUnref<GrGeometryProcessor> dfProcessor(
jvanverth502286d2015-04-08 12:37:51 -0700188 GrDistanceFieldPathGeoProc::Create(this->color(),
189 this->viewMatrix(),
190 atlas->getTexture(),
191 params,
joshualitt50cb76b2015-04-28 09:17:05 -0700192 flags,
193 false));
joshualitt5bf99f12015-03-13 11:47:42 -0700194
195 this->initDraw(batchTarget, dfProcessor, pipeline);
196
197 // allocate vertices
198 size_t vertexStride = dfProcessor->getVertexStride();
199 SkASSERT(vertexStride == 2 * sizeof(SkPoint));
200
201 int vertexCount = GrBatchTarget::kVertsPerRect * instanceCount;
202
203 const GrVertexBuffer* vertexBuffer;
204 int firstVertex;
205
206 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
207 vertexCount,
208 &vertexBuffer,
209 &firstVertex);
210
211 if (!vertices) {
212 SkDebugf("Could not allocate vertices\n");
213 return;
214 }
215
216 // We may have to flush while uploading path data to the atlas, so we set up the draw here
217 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
218 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
219
220 GrDrawTarget::DrawInfo drawInfo;
221 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
222 drawInfo.setStartVertex(0);
223 drawInfo.setStartIndex(0);
224 drawInfo.setVerticesPerInstance(GrBatchTarget::kVertsPerRect);
225 drawInfo.setIndicesPerInstance(GrBatchTarget::kIndicesPerRect);
226 drawInfo.adjustStartVertex(firstVertex);
227 drawInfo.setVertexBuffer(vertexBuffer);
228 drawInfo.setIndexBuffer(quadIndexBuffer);
229
230 int instancesToFlush = 0;
231 for (int i = 0; i < instanceCount; i++) {
232 Geometry& args = fGeoData[i];
233
234 // get mip level
235 SkScalar maxScale = this->viewMatrix().getMaxScale();
236 const SkRect& bounds = args.fPath.getBounds();
237 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
238 SkScalar size = maxScale * maxDim;
239 uint32_t desiredDimension;
240 if (size <= kSmallMIP) {
241 desiredDimension = kSmallMIP;
242 } else if (size <= kMediumMIP) {
243 desiredDimension = kMediumMIP;
244 } else {
245 desiredDimension = kLargeMIP;
246 }
247
248 // check to see if path is cached
249 // TODO: handle stroked vs. filled version of same path
250 PathData::Key key = { args.fPath.getGenerationID(), desiredDimension };
251 args.fPathData = fPathCache->find(key);
252 if (NULL == args.fPathData || !atlas->hasID(args.fPathData->fID)) {
253 // Remove the stale cache entry
254 if (args.fPathData) {
255 fPathCache->remove(args.fPathData->fKey);
256 fPathList->remove(args.fPathData);
257 SkDELETE(args.fPathData);
258 }
259 SkScalar scale = desiredDimension/maxDim;
260 args.fPathData = SkNEW(PathData);
261 if (!this->addPathToAtlas(batchTarget,
262 dfProcessor,
263 pipeline,
264 &drawInfo,
265 &instancesToFlush,
266 maxInstancesPerDraw,
267 atlas,
268 args.fPathData,
269 args.fPath,
270 args.fStroke,
271 args.fAntiAlias,
272 desiredDimension,
273 scale)) {
274 SkDebugf("Can't rasterize path\n");
275 return;
276 }
277 }
278
joshualittb4c507e2015-04-08 08:07:59 -0700279 atlas->setLastUseToken(args.fPathData->fID, batchTarget->currentToken());
joshualitt5bf99f12015-03-13 11:47:42 -0700280
281 // Now set vertices
282 intptr_t offset = reinterpret_cast<intptr_t>(vertices);
283 offset += i * GrBatchTarget::kVertsPerRect * vertexStride;
284 SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
285 this->drawPath(batchTarget,
286 atlas,
287 pipeline,
288 dfProcessor,
289 positions,
290 vertexStride,
291 this->viewMatrix(),
292 args.fPath,
293 args.fPathData);
294 instancesToFlush++;
295 }
296
joshualitt7c3a2f82015-03-31 13:32:05 -0700297 this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDraw);
joshualitt5bf99f12015-03-13 11:47:42 -0700298 }
299
300 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
301
302private:
303 AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
304 GrBatchAtlas* atlas,
305 PathCache* pathCache, PathDataList* pathList) {
306 this->initClassID<AADistanceFieldPathBatch>();
307 fBatch.fColor = color;
308 fBatch.fViewMatrix = viewMatrix;
309 fGeoData.push_back(geometry);
310 fGeoData.back().fPathData = NULL;
311
312 fAtlas = atlas;
313 fPathCache = pathCache;
314 fPathList = pathList;
315 }
316
317 bool addPathToAtlas(GrBatchTarget* batchTarget,
318 const GrGeometryProcessor* dfProcessor,
319 const GrPipeline* pipeline,
320 GrDrawTarget::DrawInfo* drawInfo,
321 int* instancesToFlush,
322 int maxInstancesPerDraw,
323 GrBatchAtlas* atlas,
324 PathData* pathData,
325 const SkPath& path,
326 const SkStrokeRec&
327 stroke, bool antiAlias,
328 uint32_t dimension,
329 SkScalar scale) {
330 const SkRect& bounds = path.getBounds();
331
332 // generate bounding rect for bitmap draw
333 SkRect scaledBounds = bounds;
334 // scale to mip level size
335 scaledBounds.fLeft *= scale;
336 scaledBounds.fTop *= scale;
337 scaledBounds.fRight *= scale;
338 scaledBounds.fBottom *= scale;
339 // move the origin to an integer boundary (gives better results)
340 SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
341 SkScalar dy = SkScalarFraction(scaledBounds.fTop);
342 scaledBounds.offset(-dx, -dy);
343 // get integer boundary
344 SkIRect devPathBounds;
345 scaledBounds.roundOut(&devPathBounds);
346 // pad to allow room for antialiasing
347 devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad));
348 // move origin to upper left corner
349 devPathBounds.offsetTo(0,0);
350
351 // draw path to bitmap
352 SkMatrix drawMatrix;
353 drawMatrix.setTranslate(-bounds.left(), -bounds.top());
354 drawMatrix.postScale(scale, scale);
355 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
356
357 // setup bitmap backing
358 // Now translate so the bound's UL corner is at the origin
359 drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1,
360 -devPathBounds.fTop * SK_Scalar1);
361 SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(),
362 devPathBounds.height());
363
364 SkBitmap bmp;
365 const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(pathBounds.fRight,
366 pathBounds.fBottom);
367 if (!bmp.tryAllocPixels(bmImageInfo)) {
368 return false;
369 }
370
371 sk_bzero(bmp.getPixels(), bmp.getSafeSize());
372
373 // rasterize path
374 SkPaint paint;
375 if (stroke.isHairlineStyle()) {
376 paint.setStyle(SkPaint::kStroke_Style);
377 paint.setStrokeWidth(SK_Scalar1);
378 } else {
379 if (stroke.isFillStyle()) {
380 paint.setStyle(SkPaint::kFill_Style);
381 } else {
382 paint.setStyle(SkPaint::kStroke_Style);
383 paint.setStrokeJoin(stroke.getJoin());
384 paint.setStrokeCap(stroke.getCap());
385 paint.setStrokeWidth(stroke.getWidth());
386 }
387 }
388 paint.setAntiAlias(antiAlias);
389
390 SkDraw draw;
391 sk_bzero(&draw, sizeof(draw));
392
393 SkRasterClip rasterClip;
394 rasterClip.setRect(pathBounds);
395 draw.fRC = &rasterClip;
396 draw.fClip = &rasterClip.bwRgn();
397 draw.fMatrix = &drawMatrix;
398 draw.fBitmap = &bmp;
399
400 draw.drawPathCoverage(path, paint);
401
402 // generate signed distance field
403 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
404 int width = devPathBounds.width();
405 int height = devPathBounds.height();
406 // TODO We should really generate this directly into the plot somehow
407 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
408
409 // Generate signed distance field
410 {
411 SkAutoLockPixels alp(bmp);
412
413 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
414 (const unsigned char*)bmp.getPixels(),
415 bmp.width(), bmp.height(), bmp.rowBytes());
416 }
417
418 // add to atlas
419 SkIPoint16 atlasLocation;
420 GrBatchAtlas::AtlasID id;
421 bool success = atlas->addToAtlas(&id, batchTarget, width, height, dfStorage.get(),
422 &atlasLocation);
423 if (!success) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700424 this->flush(batchTarget, drawInfo, *instancesToFlush, maxInstancesPerDraw);
joshualitt5bf99f12015-03-13 11:47:42 -0700425 this->initDraw(batchTarget, dfProcessor, pipeline);
426 *instancesToFlush = 0;
427
428 SkDEBUGCODE(success =) atlas->addToAtlas(&id, batchTarget, width, height,
429 dfStorage.get(), &atlasLocation);
430 SkASSERT(success);
431
432 }
433
434 // add to cache
435 pathData->fKey.fGenID = path.getGenerationID();
436 pathData->fKey.fDimension = dimension;
437 pathData->fScale = scale;
438 pathData->fID = id;
439 // change the scaled rect to match the size of the inset distance field
440 scaledBounds.fRight = scaledBounds.fLeft +
441 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
442 scaledBounds.fBottom = scaledBounds.fTop +
443 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
444 // shift the origin to the correct place relative to the distance field
445 // need to also restore the fractional translation
446 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
447 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
448 pathData->fBounds = scaledBounds;
449 // origin we render from is inset from distance field edge
450 atlasLocation.fX += SK_DistanceFieldInset;
451 atlasLocation.fY += SK_DistanceFieldInset;
452 pathData->fAtlasLocation = atlasLocation;
453
454 fPathCache->add(pathData);
455 fPathList->addToTail(pathData);
456#ifdef DF_PATH_TRACKING
457 ++g_NumCachedPaths;
458#endif
459 return true;
460 }
461
462 void drawPath(GrBatchTarget* target,
463 GrBatchAtlas* atlas,
464 const GrPipeline* pipeline,
465 const GrGeometryProcessor* gp,
466 SkPoint* positions,
467 size_t vertexStride,
468 const SkMatrix& viewMatrix,
469 const SkPath& path,
470 const PathData* pathData) {
471 GrTexture* texture = atlas->getTexture();
472
473 SkScalar dx = pathData->fBounds.fLeft;
474 SkScalar dy = pathData->fBounds.fTop;
475 SkScalar width = pathData->fBounds.width();
476 SkScalar height = pathData->fBounds.height();
477
478 SkScalar invScale = 1.0f / pathData->fScale;
479 dx *= invScale;
480 dy *= invScale;
481 width *= invScale;
482 height *= invScale;
483
484 SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX);
485 SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY);
486 SkFixed tw = SkScalarToFixed(pathData->fBounds.width());
487 SkFixed th = SkScalarToFixed(pathData->fBounds.height());
488
489 // vertex positions
490 // TODO make the vertex attributes a struct
491 SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
492 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
493
494 // vertex texture coords
495 SkPoint* textureCoords = positions + 1;
496 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
497 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
498 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
499 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
500 vertexStride);
501 }
502
503 void initDraw(GrBatchTarget* batchTarget,
504 const GrGeometryProcessor* dfProcessor,
505 const GrPipeline* pipeline) {
506 batchTarget->initDraw(dfProcessor, pipeline);
507
508 // TODO remove this when batch is everywhere
509 GrPipelineInfo init;
510 init.fColorIgnored = fBatch.fColorIgnored;
511 init.fOverrideColor = GrColor_ILLEGAL;
512 init.fCoverageIgnored = fBatch.fCoverageIgnored;
513 init.fUsesLocalCoords = this->usesLocalCoords();
514 dfProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
515 }
516
517 void flush(GrBatchTarget* batchTarget,
joshualitt5bf99f12015-03-13 11:47:42 -0700518 GrDrawTarget::DrawInfo* drawInfo,
519 int instanceCount,
520 int maxInstancesPerDraw) {
521 while (instanceCount) {
522 drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
523 drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verticesPerInstance());
524 drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indicesPerInstance());
525
526 batchTarget->draw(*drawInfo);
527
528 drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexCount());
529 instanceCount -= drawInfo->instanceCount();
530 }
531 }
532
533 GrColor color() const { return fBatch.fColor; }
534 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
535 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
536
mtklein36352bf2015-03-25 18:17:31 -0700537 bool onCombineIfPossible(GrBatch* t) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700538 AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
539
540 // TODO we could actually probably do a bunch of this work on the CPU, ie map viewMatrix,
541 // maybe upload color via attribute
542 if (this->color() != that->color()) {
543 return false;
544 }
545
546 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
547 return false;
548 }
549
550 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
551 return true;
552 }
553
554 struct BatchTracker {
555 GrColor fColor;
556 SkMatrix fViewMatrix;
557 bool fUsesLocalCoords;
558 bool fColorIgnored;
559 bool fCoverageIgnored;
560 };
561
562 BatchTracker fBatch;
563 SkSTArray<1, Geometry, true> fGeoData;
564 GrBatchAtlas* fAtlas;
565 PathCache* fPathCache;
566 PathDataList* fPathList;
567};
568
joshualitt9853cce2014-11-17 14:22:48 -0800569bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800570 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800571 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800572 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800573 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700574 const GrStrokeInfo& stroke,
jvanverthfa38a302014-10-06 05:59:05 -0700575 bool antiAlias) {
576 // we've already bailed on inverse filled paths, so this is safe
577 if (path.isEmpty()) {
578 return true;
579 }
580
581 SkASSERT(fContext);
582
joshualitt5bf99f12015-03-13 11:47:42 -0700583 if (!fAtlas) {
584 // Create a new atlas
585 GrSurfaceDesc desc;
586 desc.fFlags = kNone_GrSurfaceFlags;
587 desc.fWidth = ATLAS_TEXTURE_WIDTH;
588 desc.fHeight = ATLAS_TEXTURE_HEIGHT;
589 desc.fConfig = kAlpha_8_GrPixelConfig;
jvanverthb61283f2014-10-30 05:57:21 -0700590
joshualitt5bf99f12015-03-13 11:47:42 -0700591 // We don't want to flush the context so we claim we're in the middle of flushing so as to
592 // guarantee we do not recieve a texture with pending IO
bsalomond309e7a2015-04-30 14:18:54 -0700593 GrTexture* texture = fContext->textureProvider()->refScratchTexture(
594 desc, GrTextureProvider::kApprox_ScratchTexMatch, true);
joshualitt5bf99f12015-03-13 11:47:42 -0700595 if (texture) {
596 fAtlas = SkNEW_ARGS(GrBatchAtlas, (texture, NUM_PLOTS_X, NUM_PLOTS_Y));
597 } else {
jvanverthfa38a302014-10-06 05:59:05 -0700598 return false;
599 }
joshualitt5bf99f12015-03-13 11:47:42 -0700600 fAtlas->registerEvictionCallback(&GrAADistanceFieldPathRenderer::HandleEviction,
601 (void*)this);
jvanverthfa38a302014-10-06 05:59:05 -0700602 }
603
kkinnunen18996512015-04-26 23:18:49 -0700604 AADistanceFieldPathBatch::Geometry geometry(stroke.getStrokeRec());
joshualitt5bf99f12015-03-13 11:47:42 -0700605 geometry.fPath = path;
606 geometry.fAntiAlias = antiAlias;
jvanverthfa38a302014-10-06 05:59:05 -0700607
joshualitt5bf99f12015-03-13 11:47:42 -0700608 SkAutoTUnref<GrBatch> batch(AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
609 fAtlas, &fPathCache, &fPathList));
jvanverthfa38a302014-10-06 05:59:05 -0700610
joshualitt5bf99f12015-03-13 11:47:42 -0700611 SkRect bounds = path.getBounds();
612 viewMatrix.mapRect(&bounds);
613 target->drawBatch(pipelineBuilder, batch, &bounds);
joshualitt9491f7f2015-02-11 11:33:38 -0800614
jvanverthfa38a302014-10-06 05:59:05 -0700615 return true;
616}
617