blob: 3e054d144ae98ccde4d7e0c79c00834e5468bbbd [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"
joshualitt21279c72015-05-11 07:21:37 -070013#include "GrBatchTest.h"
jvanverthfa38a302014-10-06 05:59:05 -070014#include "GrContext.h"
egdaniel8dd688b2015-01-22 10:16:09 -080015#include "GrPipelineBuilder.h"
bsalomonb5238a72015-05-05 07:49:49 -070016#include "GrResourceProvider.h"
jvanverthfa38a302014-10-06 05:59:05 -070017#include "GrSurfacePriv.h"
18#include "GrSWMaskHelper.h"
19#include "GrTexturePriv.h"
bsalomon72e3ae42015-04-28 08:08:46 -070020#include "GrVertexBuffer.h"
jvanverth8ed3b9a2015-04-09 08:00:49 -070021#include "effects/GrDistanceFieldGeoProc.h"
jvanverthfa38a302014-10-06 05:59:05 -070022
23#include "SkDistanceFieldGen.h"
24#include "SkRTConf.h"
25
reede4ef1ca2015-02-17 18:38:38 -080026#define ATLAS_TEXTURE_WIDTH 1024
jvanverthb61283f2014-10-30 05:57:21 -070027#define ATLAS_TEXTURE_HEIGHT 2048
reede4ef1ca2015-02-17 18:38:38 -080028#define PLOT_WIDTH 256
29#define PLOT_HEIGHT 256
jvanverthfa38a302014-10-06 05:59:05 -070030
31#define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
32#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
33
jvanverthb3eb6872014-10-24 07:12:51 -070034#ifdef DF_PATH_TRACKING
35static int g_NumCachedPaths = 0;
36static int g_NumFreedPaths = 0;
37#endif
38
jvanverthb61283f2014-10-30 05:57:21 -070039// mip levels
40static const int kSmallMIP = 32;
jvanverthada68ef2014-11-03 14:00:24 -080041static const int kMediumMIP = 78;
42static const int kLargeMIP = 192;
jvanverthb61283f2014-10-30 05:57:21 -070043
joshualitt5bf99f12015-03-13 11:47:42 -070044// Callback to clear out internal path cache when eviction occurs
45void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
46 GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr;
47 // remove any paths that use this plot
48 PathDataList::Iter iter;
49 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
50 PathData* pathData;
51 while ((pathData = iter.get())) {
52 iter.next();
53 if (id == pathData->fID) {
54 dfpr->fPathCache.remove(pathData->fKey);
55 dfpr->fPathList.remove(pathData);
56 SkDELETE(pathData);
57#ifdef DF_PATH_TRACKING
58 ++g_NumFreedPaths;
59#endif
60 }
61 }
62}
63
jvanverthfa38a302014-10-06 05:59:05 -070064////////////////////////////////////////////////////////////////////////////////
jvanverth6d22eca2014-10-28 11:10:48 -070065GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context)
66 : fContext(context)
joshualitt5bf99f12015-03-13 11:47:42 -070067 , fAtlas(NULL) {
jvanverth6d22eca2014-10-28 11:10:48 -070068}
69
jvanverthfa38a302014-10-06 05:59:05 -070070GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
71 PathDataList::Iter iter;
72 iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
73 PathData* pathData;
74 while ((pathData = iter.get())) {
75 iter.next();
76 fPathList.remove(pathData);
77 SkDELETE(pathData);
78 }
jvanverthfa38a302014-10-06 05:59:05 -070079 SkDELETE(fAtlas);
jvanverthb3eb6872014-10-24 07:12:51 -070080
81#ifdef DF_PATH_TRACKING
82 SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreedPaths);
83#endif
jvanverthfa38a302014-10-06 05:59:05 -070084}
85
86////////////////////////////////////////////////////////////////////////////////
joshualitt9853cce2014-11-17 14:22:48 -080087bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -080088 const GrPipelineBuilder* pipelineBuilder,
joshualitt8059eb92014-12-29 15:10:07 -080089 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -080090 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -070091 const GrStrokeInfo& stroke,
jvanverthfa38a302014-10-06 05:59:05 -070092 bool antiAlias) const {
jvanverthb3eb6872014-10-24 07:12:51 -070093
jvanverthfa38a302014-10-06 05:59:05 -070094 // TODO: Support inverse fill
95 // TODO: Support strokes
jvanverthe9c0fc62015-04-29 11:18:05 -070096 if (!target->caps()->shaderCaps()->shaderDerivativeSupport() || !antiAlias
97 || path.isInverseFillType() || path.isVolatile() || !stroke.isFillStyle()) {
jvanverthfa38a302014-10-06 05:59:05 -070098 return false;
99 }
100
jvanverthb61283f2014-10-30 05:57:21 -0700101 // currently don't support perspective
joshualitt8059eb92014-12-29 15:10:07 -0800102 if (viewMatrix.hasPerspective()) {
jvanverthfa38a302014-10-06 05:59:05 -0700103 return false;
104 }
105
jvanverthb61283f2014-10-30 05:57:21 -0700106 // only support paths smaller than 64x64, scaled to less than 256x256
107 // the goal is to accelerate rendering of lots of small paths that may be scaling
joshualitt8059eb92014-12-29 15:10:07 -0800108 SkScalar maxScale = viewMatrix.getMaxScale();
jvanverthfa38a302014-10-06 05:59:05 -0700109 const SkRect& bounds = path.getBounds();
jvanverthb61283f2014-10-30 05:57:21 -0700110 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
joshualitt5bf99f12015-03-13 11:47:42 -0700111 return maxDim < 64.f && maxDim * maxScale < 256.f;
jvanverthfa38a302014-10-06 05:59:05 -0700112}
113
114
joshualitt9853cce2014-11-17 14:22:48 -0800115GrPathRenderer::StencilSupport
116GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*,
egdaniel8dd688b2015-01-22 10:16:09 -0800117 const GrPipelineBuilder*,
joshualitt9853cce2014-11-17 14:22:48 -0800118 const SkPath&,
kkinnunen18996512015-04-26 23:18:49 -0700119 const GrStrokeInfo&) const {
jvanverthfa38a302014-10-06 05:59:05 -0700120 return GrPathRenderer::kNoSupport_StencilSupport;
121}
122
123////////////////////////////////////////////////////////////////////////////////
124
joshualitt5bf99f12015-03-13 11:47:42 -0700125// padding around path bounds to allow for antialiased pixels
126static const SkScalar kAntiAliasPad = 1.0f;
127
128class AADistanceFieldPathBatch : public GrBatch {
129public:
130 typedef GrAADistanceFieldPathRenderer::PathData PathData;
131 typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
132 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
133
134 struct Geometry {
135 Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {}
136 SkPath fPath;
137 SkStrokeRec fStroke;
138 bool fAntiAlias;
139 PathData* fPathData;
140 };
141
142 static GrBatch* Create(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
143 GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) {
144 return SkNEW_ARGS(AADistanceFieldPathBatch, (geometry, color, viewMatrix,
145 atlas, pathCache, pathList));
146 }
147
mtklein36352bf2015-03-25 18:17:31 -0700148 const char* name() const override { return "AADistanceFieldPathBatch"; }
joshualitt5bf99f12015-03-13 11:47:42 -0700149
mtklein36352bf2015-03-25 18:17:31 -0700150 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt5bf99f12015-03-13 11:47:42 -0700151 out->setKnownFourComponents(fBatch.fColor);
152 }
153
mtklein36352bf2015-03-25 18:17:31 -0700154 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt5bf99f12015-03-13 11:47:42 -0700155 out->setUnknownSingleComponent();
156 }
157
mtklein36352bf2015-03-25 18:17:31 -0700158 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700159 // Handle any color overrides
160 if (init.fColorIgnored) {
161 fBatch.fColor = GrColor_ILLEGAL;
162 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
163 fBatch.fColor = init.fOverrideColor;
164 }
165
166 // setup batch properties
167 fBatch.fColorIgnored = init.fColorIgnored;
168 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
169 fBatch.fCoverageIgnored = init.fCoverageIgnored;
170 }
171
bsalomonb5238a72015-05-05 07:49:49 -0700172 struct FlushInfo {
173 SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
174 SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
175 int fVertexOffset;
176 int fInstancesToFlush;
177 };
178
mtklein36352bf2015-03-25 18:17:31 -0700179 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700180 int instanceCount = fGeoData.count();
181
182 SkMatrix invert;
183 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
184 SkDebugf("Could not invert viewmatrix\n");
185 return;
186 }
187
188 uint32_t flags = 0;
189 flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
190
191 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
192
193 // Setup GrGeometryProcessor
194 GrBatchAtlas* atlas = fAtlas;
195 SkAutoTUnref<GrGeometryProcessor> dfProcessor(
jvanverth502286d2015-04-08 12:37:51 -0700196 GrDistanceFieldPathGeoProc::Create(this->color(),
197 this->viewMatrix(),
198 atlas->getTexture(),
199 params,
joshualitt50cb76b2015-04-28 09:17:05 -0700200 flags,
201 false));
joshualitt5bf99f12015-03-13 11:47:42 -0700202
203 this->initDraw(batchTarget, dfProcessor, pipeline);
204
bsalomonb5238a72015-05-05 07:49:49 -0700205 FlushInfo flushInfo;
bsalomoned0bcad2015-05-04 10:36:42 -0700206
joshualitt5bf99f12015-03-13 11:47:42 -0700207 // allocate vertices
208 size_t vertexStride = dfProcessor->getVertexStride();
209 SkASSERT(vertexStride == 2 * sizeof(SkPoint));
bsalomonb5238a72015-05-05 07:49:49 -0700210
joshualitt5bf99f12015-03-13 11:47:42 -0700211 const GrVertexBuffer* vertexBuffer;
robertphillipse40d3972015-05-07 09:51:43 -0700212 void* vertices = batchTarget->makeVertSpace(vertexStride,
213 kVerticesPerQuad * instanceCount,
214 &vertexBuffer,
215 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -0700216 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
217 flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
218 if (!vertices || !flushInfo.fIndexBuffer) {
joshualitt5bf99f12015-03-13 11:47:42 -0700219 SkDebugf("Could not allocate vertices\n");
220 return;
221 }
222
bsalomonb5238a72015-05-05 07:49:49 -0700223 flushInfo.fInstancesToFlush = 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700224 for (int i = 0; i < instanceCount; i++) {
225 Geometry& args = fGeoData[i];
226
227 // get mip level
228 SkScalar maxScale = this->viewMatrix().getMaxScale();
229 const SkRect& bounds = args.fPath.getBounds();
230 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
231 SkScalar size = maxScale * maxDim;
232 uint32_t desiredDimension;
233 if (size <= kSmallMIP) {
234 desiredDimension = kSmallMIP;
235 } else if (size <= kMediumMIP) {
236 desiredDimension = kMediumMIP;
237 } else {
238 desiredDimension = kLargeMIP;
239 }
240
241 // check to see if path is cached
242 // TODO: handle stroked vs. filled version of same path
243 PathData::Key key = { args.fPath.getGenerationID(), desiredDimension };
244 args.fPathData = fPathCache->find(key);
245 if (NULL == args.fPathData || !atlas->hasID(args.fPathData->fID)) {
246 // Remove the stale cache entry
247 if (args.fPathData) {
248 fPathCache->remove(args.fPathData->fKey);
249 fPathList->remove(args.fPathData);
250 SkDELETE(args.fPathData);
251 }
252 SkScalar scale = desiredDimension/maxDim;
253 args.fPathData = SkNEW(PathData);
254 if (!this->addPathToAtlas(batchTarget,
255 dfProcessor,
256 pipeline,
bsalomonb5238a72015-05-05 07:49:49 -0700257 &flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700258 atlas,
259 args.fPathData,
260 args.fPath,
261 args.fStroke,
262 args.fAntiAlias,
263 desiredDimension,
264 scale)) {
265 SkDebugf("Can't rasterize path\n");
266 return;
267 }
268 }
269
joshualittb4c507e2015-04-08 08:07:59 -0700270 atlas->setLastUseToken(args.fPathData->fID, batchTarget->currentToken());
joshualitt5bf99f12015-03-13 11:47:42 -0700271
272 // Now set vertices
273 intptr_t offset = reinterpret_cast<intptr_t>(vertices);
bsalomonb5238a72015-05-05 07:49:49 -0700274 offset += i * kVerticesPerQuad * vertexStride;
joshualitt5bf99f12015-03-13 11:47:42 -0700275 SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
bsalomonb5238a72015-05-05 07:49:49 -0700276 this->writePathVertices(batchTarget,
277 atlas,
278 pipeline,
279 dfProcessor,
280 positions,
281 vertexStride,
282 this->viewMatrix(),
283 args.fPath,
284 args.fPathData);
285 flushInfo.fInstancesToFlush++;
joshualitt5bf99f12015-03-13 11:47:42 -0700286 }
287
bsalomonb5238a72015-05-05 07:49:49 -0700288 this->flush(batchTarget, &flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700289 }
290
291 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
292
293private:
294 AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
295 GrBatchAtlas* atlas,
296 PathCache* pathCache, PathDataList* pathList) {
297 this->initClassID<AADistanceFieldPathBatch>();
298 fBatch.fColor = color;
299 fBatch.fViewMatrix = viewMatrix;
300 fGeoData.push_back(geometry);
301 fGeoData.back().fPathData = NULL;
302
303 fAtlas = atlas;
304 fPathCache = pathCache;
305 fPathList = pathList;
joshualitt99c7c072015-05-01 13:43:30 -0700306
307 // Compute bounds
308 fBounds = geometry.fPath.getBounds();
309 viewMatrix.mapRect(&fBounds);
joshualitt5bf99f12015-03-13 11:47:42 -0700310 }
311
312 bool addPathToAtlas(GrBatchTarget* batchTarget,
313 const GrGeometryProcessor* dfProcessor,
314 const GrPipeline* pipeline,
bsalomonb5238a72015-05-05 07:49:49 -0700315 FlushInfo* flushInfo,
joshualitt5bf99f12015-03-13 11:47:42 -0700316 GrBatchAtlas* atlas,
317 PathData* pathData,
318 const SkPath& path,
319 const SkStrokeRec&
320 stroke, bool antiAlias,
321 uint32_t dimension,
322 SkScalar scale) {
323 const SkRect& bounds = path.getBounds();
324
325 // generate bounding rect for bitmap draw
326 SkRect scaledBounds = bounds;
327 // scale to mip level size
328 scaledBounds.fLeft *= scale;
329 scaledBounds.fTop *= scale;
330 scaledBounds.fRight *= scale;
331 scaledBounds.fBottom *= scale;
332 // move the origin to an integer boundary (gives better results)
333 SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
334 SkScalar dy = SkScalarFraction(scaledBounds.fTop);
335 scaledBounds.offset(-dx, -dy);
336 // get integer boundary
337 SkIRect devPathBounds;
338 scaledBounds.roundOut(&devPathBounds);
339 // pad to allow room for antialiasing
340 devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad));
341 // move origin to upper left corner
342 devPathBounds.offsetTo(0,0);
343
344 // draw path to bitmap
345 SkMatrix drawMatrix;
346 drawMatrix.setTranslate(-bounds.left(), -bounds.top());
347 drawMatrix.postScale(scale, scale);
348 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
349
350 // setup bitmap backing
351 // Now translate so the bound's UL corner is at the origin
352 drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1,
353 -devPathBounds.fTop * SK_Scalar1);
354 SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(),
355 devPathBounds.height());
356
357 SkBitmap bmp;
358 const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(pathBounds.fRight,
359 pathBounds.fBottom);
360 if (!bmp.tryAllocPixels(bmImageInfo)) {
361 return false;
362 }
363
364 sk_bzero(bmp.getPixels(), bmp.getSafeSize());
365
366 // rasterize path
367 SkPaint paint;
368 if (stroke.isHairlineStyle()) {
369 paint.setStyle(SkPaint::kStroke_Style);
370 paint.setStrokeWidth(SK_Scalar1);
371 } else {
372 if (stroke.isFillStyle()) {
373 paint.setStyle(SkPaint::kFill_Style);
374 } else {
375 paint.setStyle(SkPaint::kStroke_Style);
376 paint.setStrokeJoin(stroke.getJoin());
377 paint.setStrokeCap(stroke.getCap());
378 paint.setStrokeWidth(stroke.getWidth());
379 }
380 }
381 paint.setAntiAlias(antiAlias);
382
383 SkDraw draw;
384 sk_bzero(&draw, sizeof(draw));
385
386 SkRasterClip rasterClip;
387 rasterClip.setRect(pathBounds);
388 draw.fRC = &rasterClip;
389 draw.fClip = &rasterClip.bwRgn();
390 draw.fMatrix = &drawMatrix;
391 draw.fBitmap = &bmp;
392
393 draw.drawPathCoverage(path, paint);
394
395 // generate signed distance field
396 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
397 int width = devPathBounds.width();
398 int height = devPathBounds.height();
399 // TODO We should really generate this directly into the plot somehow
400 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
401
402 // Generate signed distance field
403 {
404 SkAutoLockPixels alp(bmp);
405
406 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
407 (const unsigned char*)bmp.getPixels(),
408 bmp.width(), bmp.height(), bmp.rowBytes());
409 }
410
411 // add to atlas
412 SkIPoint16 atlasLocation;
413 GrBatchAtlas::AtlasID id;
414 bool success = atlas->addToAtlas(&id, batchTarget, width, height, dfStorage.get(),
415 &atlasLocation);
416 if (!success) {
bsalomonb5238a72015-05-05 07:49:49 -0700417 this->flush(batchTarget, flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700418 this->initDraw(batchTarget, dfProcessor, pipeline);
joshualitt5bf99f12015-03-13 11:47:42 -0700419
420 SkDEBUGCODE(success =) atlas->addToAtlas(&id, batchTarget, width, height,
421 dfStorage.get(), &atlasLocation);
422 SkASSERT(success);
423
424 }
425
426 // add to cache
427 pathData->fKey.fGenID = path.getGenerationID();
428 pathData->fKey.fDimension = dimension;
429 pathData->fScale = scale;
430 pathData->fID = id;
431 // change the scaled rect to match the size of the inset distance field
432 scaledBounds.fRight = scaledBounds.fLeft +
433 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
434 scaledBounds.fBottom = scaledBounds.fTop +
435 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
436 // shift the origin to the correct place relative to the distance field
437 // need to also restore the fractional translation
438 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
439 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
440 pathData->fBounds = scaledBounds;
441 // origin we render from is inset from distance field edge
442 atlasLocation.fX += SK_DistanceFieldInset;
443 atlasLocation.fY += SK_DistanceFieldInset;
444 pathData->fAtlasLocation = atlasLocation;
445
446 fPathCache->add(pathData);
447 fPathList->addToTail(pathData);
448#ifdef DF_PATH_TRACKING
449 ++g_NumCachedPaths;
450#endif
451 return true;
452 }
453
bsalomonb5238a72015-05-05 07:49:49 -0700454 void writePathVertices(GrBatchTarget* target,
455 GrBatchAtlas* atlas,
456 const GrPipeline* pipeline,
457 const GrGeometryProcessor* gp,
458 SkPoint* positions,
459 size_t vertexStride,
460 const SkMatrix& viewMatrix,
461 const SkPath& path,
462 const PathData* pathData) {
joshualitt5bf99f12015-03-13 11:47:42 -0700463 GrTexture* texture = atlas->getTexture();
464
465 SkScalar dx = pathData->fBounds.fLeft;
466 SkScalar dy = pathData->fBounds.fTop;
467 SkScalar width = pathData->fBounds.width();
468 SkScalar height = pathData->fBounds.height();
469
470 SkScalar invScale = 1.0f / pathData->fScale;
471 dx *= invScale;
472 dy *= invScale;
473 width *= invScale;
474 height *= invScale;
475
476 SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX);
477 SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY);
478 SkFixed tw = SkScalarToFixed(pathData->fBounds.width());
479 SkFixed th = SkScalarToFixed(pathData->fBounds.height());
480
481 // vertex positions
482 // TODO make the vertex attributes a struct
483 SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
484 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
485
486 // vertex texture coords
487 SkPoint* textureCoords = positions + 1;
488 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
489 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
490 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
491 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
492 vertexStride);
493 }
494
495 void initDraw(GrBatchTarget* batchTarget,
496 const GrGeometryProcessor* dfProcessor,
497 const GrPipeline* pipeline) {
498 batchTarget->initDraw(dfProcessor, pipeline);
499
500 // TODO remove this when batch is everywhere
501 GrPipelineInfo init;
502 init.fColorIgnored = fBatch.fColorIgnored;
503 init.fOverrideColor = GrColor_ILLEGAL;
504 init.fCoverageIgnored = fBatch.fCoverageIgnored;
505 init.fUsesLocalCoords = this->usesLocalCoords();
506 dfProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
507 }
508
bsalomonb5238a72015-05-05 07:49:49 -0700509 void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
bsalomoncb8979d2015-05-05 09:51:38 -0700510 GrVertices vertices;
bsalomonb5238a72015-05-05 07:49:49 -0700511 int maxInstancesPerDraw = flushInfo->fIndexBuffer->maxQuads();
bsalomoncb8979d2015-05-05 09:51:38 -0700512 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -0700513 flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad,
bsalomone64eb572015-05-07 11:35:55 -0700514 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
515 batchTarget->draw(vertices);
bsalomonb5238a72015-05-05 07:49:49 -0700516 flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
517 flushInfo->fInstancesToFlush = 0;
joshualitt5bf99f12015-03-13 11:47:42 -0700518 }
519
520 GrColor color() const { return fBatch.fColor; }
521 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
522 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
523
mtklein36352bf2015-03-25 18:17:31 -0700524 bool onCombineIfPossible(GrBatch* t) override {
joshualitt5bf99f12015-03-13 11:47:42 -0700525 AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
526
527 // TODO we could actually probably do a bunch of this work on the CPU, ie map viewMatrix,
528 // maybe upload color via attribute
529 if (this->color() != that->color()) {
530 return false;
531 }
532
533 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
534 return false;
535 }
536
537 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700538 this->joinBounds(that->bounds());
joshualitt5bf99f12015-03-13 11:47:42 -0700539 return true;
540 }
541
542 struct BatchTracker {
543 GrColor fColor;
544 SkMatrix fViewMatrix;
545 bool fUsesLocalCoords;
546 bool fColorIgnored;
547 bool fCoverageIgnored;
548 };
549
550 BatchTracker fBatch;
551 SkSTArray<1, Geometry, true> fGeoData;
552 GrBatchAtlas* fAtlas;
553 PathCache* fPathCache;
554 PathDataList* fPathList;
555};
556
joshualitt21279c72015-05-11 07:21:37 -0700557static GrBatchAtlas* create_atlas(GrContext* context, GrBatchAtlas::EvictionFunc func, void* data) {
558 GrBatchAtlas* atlas;
559 // Create a new atlas
560 GrSurfaceDesc desc;
561 desc.fFlags = kNone_GrSurfaceFlags;
562 desc.fWidth = ATLAS_TEXTURE_WIDTH;
563 desc.fHeight = ATLAS_TEXTURE_HEIGHT;
564 desc.fConfig = kAlpha_8_GrPixelConfig;
565
566 // We don't want to flush the context so we claim we're in the middle of flushing so as to
567 // guarantee we do not recieve a texture with pending IO
568 GrTexture* texture = context->textureProvider()->refScratchTexture(
569 desc, GrTextureProvider::kApprox_ScratchTexMatch, true);
570 if (texture) {
571 atlas = SkNEW_ARGS(GrBatchAtlas, (texture, NUM_PLOTS_X, NUM_PLOTS_Y));
572 } else {
573 return NULL;
574 }
575 atlas->registerEvictionCallback(func, data);
576 return atlas;
577}
578
joshualitt9853cce2014-11-17 14:22:48 -0800579bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800580 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800581 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800582 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800583 const SkPath& path,
kkinnunen18996512015-04-26 23:18:49 -0700584 const GrStrokeInfo& stroke,
jvanverthfa38a302014-10-06 05:59:05 -0700585 bool antiAlias) {
586 // we've already bailed on inverse filled paths, so this is safe
587 if (path.isEmpty()) {
588 return true;
589 }
590
591 SkASSERT(fContext);
592
joshualitt5bf99f12015-03-13 11:47:42 -0700593 if (!fAtlas) {
joshualitt21279c72015-05-11 07:21:37 -0700594 fAtlas = create_atlas(fContext, &GrAADistanceFieldPathRenderer::HandleEviction,
595 (void*)this);
596 if (!fAtlas) {
jvanverthfa38a302014-10-06 05:59:05 -0700597 return false;
598 }
599 }
600
kkinnunen18996512015-04-26 23:18:49 -0700601 AADistanceFieldPathBatch::Geometry geometry(stroke.getStrokeRec());
joshualitt5bf99f12015-03-13 11:47:42 -0700602 geometry.fPath = path;
603 geometry.fAntiAlias = antiAlias;
jvanverthfa38a302014-10-06 05:59:05 -0700604
joshualitt5bf99f12015-03-13 11:47:42 -0700605 SkAutoTUnref<GrBatch> batch(AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
606 fAtlas, &fPathCache, &fPathList));
joshualitt99c7c072015-05-01 13:43:30 -0700607 target->drawBatch(pipelineBuilder, batch);
joshualitt9491f7f2015-02-11 11:33:38 -0800608
jvanverthfa38a302014-10-06 05:59:05 -0700609 return true;
610}
611
joshualitt21279c72015-05-11 07:21:37 -0700612///////////////////////////////////////////////////////////////////////////////////////////////////
613
614#ifdef GR_TEST_UTILS
615
616struct PathTestStruct {
617 typedef GrAADistanceFieldPathRenderer::PathCache PathCache;
618 typedef GrAADistanceFieldPathRenderer::PathData PathData;
619 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
620 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(NULL) {}
621 ~PathTestStruct() { this->reset(); }
622
623 void reset() {
624 PathDataList::Iter iter;
625 iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
626 PathData* pathData;
627 while ((pathData = iter.get())) {
628 iter.next();
629 fPathList.remove(pathData);
630 SkDELETE(pathData);
631 }
632 SkDELETE(fAtlas);
633 }
634
635 static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
636 PathTestStruct* dfpr = (PathTestStruct*)pr;
637 // remove any paths that use this plot
638 PathDataList::Iter iter;
639 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
640 PathData* pathData;
641 while ((pathData = iter.get())) {
642 iter.next();
643 if (id == pathData->fID) {
644 dfpr->fPathCache.remove(pathData->fKey);
645 dfpr->fPathList.remove(pathData);
646 SkDELETE(pathData);
647 }
648 }
649 }
650
651 uint32_t fContextID;
652 GrBatchAtlas* fAtlas;
653 PathCache fPathCache;
654 PathDataList fPathList;
655};
656
657BATCH_TEST_DEFINE(AADistanceFieldPathRenderer) {
658 static PathTestStruct gTestStruct;
659
660 if (context->uniqueID() != gTestStruct.fContextID) {
661 gTestStruct.fContextID = context->uniqueID();
662 gTestStruct.reset();
663 gTestStruct.fAtlas = create_atlas(context, &PathTestStruct::HandleEviction,
664 (void*)&gTestStruct);
665 }
666
667 SkMatrix viewMatrix = GrTest::TestMatrix(random);
668 GrColor color = GrRandomColor(random);
669
670 AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
671 geometry.fPath = GrTest::TestPath(random);
672 geometry.fAntiAlias = random->nextBool();
673
674 return AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
675 gTestStruct.fAtlas,
676 &gTestStruct.fPathCache,
677 &gTestStruct.fPathList);
678}
679
680#endif