OLD | NEW |
---|---|
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2014 Google Inc. | 3 * Copyright 2014 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 #include "GrAADistanceFieldPathRenderer.h" | 9 #include "GrAADistanceFieldPathRenderer.h" |
10 | 10 |
11 #include "GrAtlas.h" | 11 #include "GrBatch.h" |
12 #include "GrBatchTarget.h" | |
13 #include "GrBufferAllocPool.h" | |
12 #include "GrContext.h" | 14 #include "GrContext.h" |
13 #include "GrPipelineBuilder.h" | 15 #include "GrPipelineBuilder.h" |
14 #include "GrSurfacePriv.h" | 16 #include "GrSurfacePriv.h" |
15 #include "GrSWMaskHelper.h" | 17 #include "GrSWMaskHelper.h" |
16 #include "GrTexturePriv.h" | 18 #include "GrTexturePriv.h" |
17 #include "effects/GrDistanceFieldTextureEffect.h" | 19 #include "effects/GrDistanceFieldTextureEffect.h" |
18 | 20 |
19 #include "SkDistanceFieldGen.h" | 21 #include "SkDistanceFieldGen.h" |
20 #include "SkRTConf.h" | 22 #include "SkRTConf.h" |
21 | 23 |
22 #define ATLAS_TEXTURE_WIDTH 1024 | 24 #define ATLAS_TEXTURE_WIDTH 1024 |
23 #define ATLAS_TEXTURE_HEIGHT 2048 | 25 #define ATLAS_TEXTURE_HEIGHT 2048 |
24 #define PLOT_WIDTH 256 | 26 #define PLOT_WIDTH 256 |
25 #define PLOT_HEIGHT 256 | 27 #define PLOT_HEIGHT 256 |
26 | 28 |
27 #define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH) | 29 #define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH) |
28 #define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT) | 30 #define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT) |
29 | 31 |
30 SK_CONF_DECLARE(bool, c_DumpPathCache, "gpu.dumpPathCache", false, | |
31 "Dump the contents of the path cache before every purge."); | |
32 | |
33 #ifdef DF_PATH_TRACKING | 32 #ifdef DF_PATH_TRACKING |
34 static int g_NumCachedPaths = 0; | 33 static int g_NumCachedPaths = 0; |
35 static int g_NumFreedPaths = 0; | 34 static int g_NumFreedPaths = 0; |
36 #endif | 35 #endif |
37 | 36 |
38 // mip levels | 37 // mip levels |
39 static const int kSmallMIP = 32; | 38 static const int kSmallMIP = 32; |
40 static const int kMediumMIP = 78; | 39 static const int kMediumMIP = 78; |
41 static const int kLargeMIP = 192; | 40 static const int kLargeMIP = 192; |
42 | 41 |
43 //////////////////////////////////////////////////////////////////////////////// | 42 //////////////////////////////////////////////////////////////////////////////// |
44 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context) | 43 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context) |
45 : fContext(context) | 44 : fContext(context) |
46 , fAtlas(NULL) | 45 , fAtlas(NULL) { |
47 , fEffectFlags(kInvalid_DistanceFieldEffectFlag) { | |
48 } | 46 } |
49 | 47 |
50 GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { | 48 GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { |
51 PathDataList::Iter iter; | 49 PathDataList::Iter iter; |
52 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | 50 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); |
53 PathData* pathData; | 51 PathData* pathData; |
54 while ((pathData = iter.get())) { | 52 while ((pathData = iter.get())) { |
55 iter.next(); | 53 iter.next(); |
56 fPathList.remove(pathData); | 54 fPathList.remove(pathData); |
57 SkDELETE(pathData); | 55 SkDELETE(pathData); |
58 } | 56 } |
59 | 57 |
60 SkDELETE(fAtlas); | 58 if (fAtlas) { |
bsalomon
2015/03/05 22:11:08
delete is legal on NULL.
joshualitt
2015/03/09 19:45:14
Acknowledged.
| |
59 SkDELETE(fAtlas); | |
60 } | |
61 | 61 |
62 #ifdef DF_PATH_TRACKING | 62 #ifdef DF_PATH_TRACKING |
63 SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreed Paths); | 63 SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreed Paths); |
64 #endif | 64 #endif |
65 } | 65 } |
66 | 66 |
67 //////////////////////////////////////////////////////////////////////////////// | 67 //////////////////////////////////////////////////////////////////////////////// |
68 bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target, | 68 bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target, |
69 const GrPipelineBuilder* pipelin eBuilder, | 69 const GrPipelineBuilder* pipelin eBuilder, |
70 const SkMatrix& viewMatrix, | 70 const SkMatrix& viewMatrix, |
(...skipping 25 matching lines...) Expand all Loading... | |
96 GrPathRenderer::StencilSupport | 96 GrPathRenderer::StencilSupport |
97 GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*, | 97 GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*, |
98 const GrPipelineBuilder*, | 98 const GrPipelineBuilder*, |
99 const SkPath&, | 99 const SkPath&, |
100 const SkStrokeRec&) const { | 100 const SkStrokeRec&) const { |
101 return GrPathRenderer::kNoSupport_StencilSupport; | 101 return GrPathRenderer::kNoSupport_StencilSupport; |
102 } | 102 } |
103 | 103 |
104 //////////////////////////////////////////////////////////////////////////////// | 104 //////////////////////////////////////////////////////////////////////////////// |
105 | 105 |
106 | |
107 class AADistanceFieldPathBatch : public GrBatch { | |
108 public: | |
109 typedef GrAADistanceFieldPathRenderer::PathData PathData; | |
110 typedef SkTDynamicHash<PathData, PathData::Key> PathCache; | |
111 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList; | |
112 | |
113 struct Geometry { | |
114 Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {} | |
115 SkPath fPath; | |
116 SkStrokeRec fStroke; | |
117 bool fAntiAlias; | |
118 PathData* fPathData; | |
119 }; | |
120 | |
121 static GrBatch* Create(const Geometry& geometry, GrColor color, const SkMatr ix& viewMatrix, | |
122 GrBatchAtlas* atlas, PathCache* pathCache, PathDataLi st* pathList) { | |
123 return SkNEW_ARGS(AADistanceFieldPathBatch, (geometry, color, viewMatrix , | |
124 atlas, pathCache, pathList) ); | |
125 } | |
126 | |
127 const char* name() const SK_OVERRIDE { return "AADistanceFieldPathBatch"; } | |
128 | |
129 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { | |
130 out->setKnownFourComponents(fBatch.fColor); | |
131 } | |
132 | |
133 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E { | |
134 out->setUnknownSingleComponent(); | |
135 } | |
136 | |
137 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { | |
138 // Handle any color overrides | |
139 if (init.fColorIgnored) { | |
140 fBatch.fColor = GrColor_ILLEGAL; | |
141 } else if (GrColor_ILLEGAL != init.fOverrideColor) { | |
142 fBatch.fColor = init.fOverrideColor; | |
143 } | |
144 | |
145 // setup batch properties | |
146 fBatch.fColorIgnored = init.fColorIgnored; | |
147 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; | |
148 fBatch.fCoverageIgnored = init.fCoverageIgnored; | |
149 } | |
150 | |
151 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline ) SK_OVERRIDE { | |
152 int instanceCount = fGeoData.count(); | |
153 | |
154 SkMatrix invert; | |
155 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { | |
156 SkDebugf("Could not invert viewmatrix\n"); | |
157 return; | |
158 } | |
159 | |
160 uint32_t flags = 0; | |
161 flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEf fectFlag : 0; | |
162 | |
163 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBil erp_FilterMode); | |
164 | |
165 // Setup GrGeometryProcessor | |
166 GrBatchAtlas* atlas = fAtlas; | |
167 SkAutoTUnref<GrGeometryProcessor> dfProcessor( | |
168 GrDistanceFieldNoGammaTextureEffect::Create(this->color(), | |
169 this->viewMatrix(), | |
170 atlas->getTexture(), | |
171 params, | |
172 flags, | |
173 false)); | |
174 | |
175 this->initDraw(batchTarget, dfProcessor, pipeline); | |
176 | |
177 // allocate vertices | |
178 size_t vertexStride = dfProcessor->getVertexStride(); | |
179 SkASSERT(vertexStride == 2 * sizeof(SkPoint)); | |
180 | |
181 int vertexCount = GrBatchTarget::kVertsPerRect * instanceCount; | |
182 | |
183 const GrVertexBuffer* vertexBuffer; | |
184 int firstVertex; | |
185 | |
186 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, | |
187 vertexCount, | |
188 &vertexBuffer, | |
189 &firstVertex); | |
190 | |
191 // We may have to flush while uploading path data to the atlas, so we se t up the draw here | |
192 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer(); | |
193 int maxInstancesPerDraw = quadIndexBuffer->maxQuads(); | |
194 | |
195 GrDrawTarget::DrawInfo drawInfo; | |
196 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); | |
197 drawInfo.setStartVertex(0); | |
198 drawInfo.setStartIndex(0); | |
199 drawInfo.setVerticesPerInstance(GrBatchTarget::kVertsPerRect); | |
200 drawInfo.setIndicesPerInstance(GrBatchTarget::kIndicesPerRect); | |
201 drawInfo.adjustStartVertex(firstVertex); | |
202 drawInfo.setVertexBuffer(vertexBuffer); | |
203 drawInfo.setIndexBuffer(quadIndexBuffer); | |
204 | |
205 int instancesToFlush = 0; | |
206 for (int i = 0; i < instanceCount; i++) { | |
207 Geometry& args = fGeoData[i]; | |
208 | |
209 // get mip level | |
210 SkScalar maxScale = this->viewMatrix().getMaxScale(); | |
211 const SkRect& bounds = args.fPath.getBounds(); | |
212 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); | |
213 SkScalar size = maxScale * maxDim; | |
214 uint32_t desiredDimension; | |
215 if (size <= kSmallMIP) { | |
216 desiredDimension = kSmallMIP; | |
217 } else if (size <= kMediumMIP) { | |
218 desiredDimension = kMediumMIP; | |
219 } else { | |
220 desiredDimension = kLargeMIP; | |
221 } | |
222 | |
223 // check to see if path is cached | |
224 // TODO: handle stroked vs. filled version of same path | |
225 PathData::Key key = { args.fPath.getGenerationID(), desiredDimension }; | |
226 args.fPathData = fPathCache->find(key); | |
227 if (NULL == args.fPathData || !atlas->hasID(args.fPathData->fID)) { | |
228 // Remove the stale cache entry | |
229 if (args.fPathData) { | |
230 fPathCache->remove(args.fPathData->fKey); | |
231 fPathList->remove(args.fPathData); | |
232 SkDELETE(args.fPathData); | |
233 } | |
234 SkScalar scale = desiredDimension/maxDim; | |
235 args.fPathData = SkNEW(PathData); | |
236 if (!this->addPathToAtlas(batchTarget, | |
237 dfProcessor, | |
238 pipeline, | |
239 &drawInfo, | |
240 &instancesToFlush, | |
241 maxInstancesPerDraw, | |
242 atlas, | |
243 args.fPathData, | |
244 args.fPath, | |
245 args.fStroke, | |
246 args.fAntiAlias, | |
247 desiredDimension, | |
248 scale)) { | |
249 SkDebugf("Can't rasterize path\n"); | |
250 return; | |
251 } | |
252 } | |
253 | |
254 atlas->setLastRefToken(args.fPathData->fID, batchTarget->currentToke n()); | |
255 | |
256 // Now set vertices | |
257 intptr_t offset = reinterpret_cast<intptr_t>(vertices); | |
258 offset += i * GrBatchTarget::kVertsPerRect * vertexStride; | |
259 SkPoint* positions = reinterpret_cast<SkPoint*>(offset); | |
260 this->drawPath(batchTarget, | |
261 atlas, | |
262 pipeline, | |
263 dfProcessor, | |
264 positions, | |
265 vertexStride, | |
266 this->viewMatrix(), | |
267 args.fPath, | |
268 args.fPathData); | |
269 instancesToFlush++; | |
270 } | |
271 | |
272 this->flush(batchTarget, dfProcessor, pipeline, &drawInfo, instancesToFl ush, | |
273 maxInstancesPerDraw); | |
274 } | |
275 | |
276 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
277 | |
278 private: | |
279 AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMa trix& viewMatrix, | |
280 GrBatchAtlas* atlas, | |
281 PathCache* pathCache, PathDataList* pathList) { | |
282 this->initClassID<AADistanceFieldPathBatch>(); | |
283 fBatch.fColor = color; | |
284 fBatch.fViewMatrix = viewMatrix; | |
285 fGeoData.push_back(geometry); | |
286 fGeoData.back().fPathData = NULL; | |
287 | |
288 fAtlas = atlas; | |
289 fPathCache = pathCache; | |
290 fPathList = pathList; | |
291 } | |
292 | |
293 bool addPathToAtlas(GrBatchTarget* batchTarget, | |
294 const GrGeometryProcessor* dfProcessor, | |
295 const GrPipeline* pipeline, | |
296 GrDrawTarget::DrawInfo* drawInfo, | |
297 int* instancesToFlush, | |
298 int maxInstancesPerDraw, | |
299 GrBatchAtlas* atlas, | |
300 PathData* pathData, | |
301 const SkPath& path, | |
302 const SkStrokeRec& | |
303 stroke, bool antiAlias, | |
304 uint32_t dimension, | |
305 SkScalar scale) { | |
306 const SkRect& bounds = path.getBounds(); | |
307 | |
308 // generate bounding rect for bitmap draw | |
309 SkRect scaledBounds = bounds; | |
310 // scale to mip level size | |
311 scaledBounds.fLeft *= scale; | |
312 scaledBounds.fTop *= scale; | |
313 scaledBounds.fRight *= scale; | |
314 scaledBounds.fBottom *= scale; | |
315 // move the origin to an integer boundary (gives better results) | |
316 SkScalar dx = SkScalarFraction(scaledBounds.fLeft); | |
317 SkScalar dy = SkScalarFraction(scaledBounds.fTop); | |
318 scaledBounds.offset(-dx, -dy); | |
319 // get integer boundary | |
320 SkIRect devPathBounds; | |
321 scaledBounds.roundOut(&devPathBounds); | |
322 // pad to allow room for antialiasing | |
323 devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt (kAntiAliasPad)); | |
324 // move origin to upper left corner | |
325 devPathBounds.offsetTo(0,0); | |
326 | |
327 // draw path to bitmap | |
328 SkMatrix drawMatrix; | |
329 drawMatrix.setTranslate(-bounds.left(), -bounds.top()); | |
330 drawMatrix.postScale(scale, scale); | |
331 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); | |
332 | |
333 // setup bitmap backing | |
334 // Now translate so the bound's UL corner is at the origin | |
335 drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1, | |
336 -devPathBounds.fTop * SK_Scalar1); | |
337 SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(), | |
338 devPathBounds.height()); | |
339 | |
340 SkBitmap bmp; | |
341 const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(pathBounds.fRight, | |
342 pathBounds.fBottom); | |
343 if (!bmp.tryAllocPixels(bmImageInfo)) { | |
344 return false; | |
345 } | |
346 | |
347 sk_bzero(bmp.getPixels(), bmp.getSafeSize()); | |
348 | |
349 // rasterize path | |
350 SkPaint paint; | |
351 if (stroke.isHairlineStyle()) { | |
352 paint.setStyle(SkPaint::kStroke_Style); | |
353 paint.setStrokeWidth(SK_Scalar1); | |
354 } else { | |
355 if (stroke.isFillStyle()) { | |
356 paint.setStyle(SkPaint::kFill_Style); | |
357 } else { | |
358 paint.setStyle(SkPaint::kStroke_Style); | |
359 paint.setStrokeJoin(stroke.getJoin()); | |
360 paint.setStrokeCap(stroke.getCap()); | |
361 paint.setStrokeWidth(stroke.getWidth()); | |
362 } | |
363 } | |
364 paint.setAntiAlias(antiAlias); | |
365 | |
366 SkDraw draw; | |
367 sk_bzero(&draw, sizeof(draw)); | |
368 | |
369 SkRasterClip rasterClip; | |
370 rasterClip.setRect(pathBounds); | |
371 draw.fRC = &rasterClip; | |
372 draw.fClip = &rasterClip.bwRgn(); | |
373 draw.fMatrix = &drawMatrix; | |
374 draw.fBitmap = &bmp; | |
375 | |
376 draw.drawPathCoverage(path, paint); | |
377 | |
378 // generate signed distance field | |
379 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); | |
380 int width = devPathBounds.width(); | |
381 int height = devPathBounds.height(); | |
382 // TODO We should really generate this directly into the plot somehow | |
383 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); | |
384 | |
385 // Generate signed distance field | |
386 { | |
387 SkAutoLockPixels alp(bmp); | |
388 | |
389 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(), | |
390 (const unsigned char*)bmp.getPixe ls(), | |
391 bmp.width(), bmp.height(), bmp.ro wBytes()); | |
392 } | |
393 | |
394 // add to atlas | |
395 SkIPoint16 atlasLocation; | |
396 GrBatchAtlas::AtlasID id; | |
397 bool success = atlas->addToAtlas(&id, batchTarget, width, height, dfStor age.get(), | |
398 &atlasLocation); | |
399 if (!success) { | |
400 this->flush(batchTarget, dfProcessor, pipeline, drawInfo, *instances ToFlush, | |
401 maxInstancesPerDraw); | |
402 this->initDraw(batchTarget, dfProcessor, pipeline); | |
403 *instancesToFlush = 0; | |
404 | |
405 SkDEBUGCODE(success =) atlas->addToAtlas(&id, batchTarget, width, he ight, | |
406 dfStorage.get(), &atlasLoca tion); | |
407 SkASSERT(success); | |
408 | |
409 } | |
410 | |
411 // add to cache | |
412 pathData->fKey.fGenID = path.getGenerationID(); | |
413 pathData->fKey.fDimension = dimension; | |
414 pathData->fScale = scale; | |
415 pathData->fID = id; | |
416 // change the scaled rect to match the size of the inset distance field | |
417 scaledBounds.fRight = scaledBounds.fLeft + | |
418 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); | |
419 scaledBounds.fBottom = scaledBounds.fTop + | |
420 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); | |
421 // shift the origin to the correct place relative to the distance field | |
422 // need to also restore the fractional translation | |
423 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa d + dx, | |
424 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa d + dy); | |
425 pathData->fBounds = scaledBounds; | |
426 // origin we render from is inset from distance field edge | |
427 atlasLocation.fX += SK_DistanceFieldInset; | |
428 atlasLocation.fY += SK_DistanceFieldInset; | |
429 pathData->fAtlasLocation = atlasLocation; | |
430 | |
431 fPathCache->add(pathData); | |
432 fPathList->addToTail(pathData); | |
433 #ifdef DF_PATH_TRACKING | |
434 ++g_NumCachedPaths; | |
435 #endif | |
436 return true; | |
437 } | |
438 | |
439 void drawPath(GrBatchTarget* target, | |
440 GrBatchAtlas* atlas, | |
441 const GrPipeline* pipeline, | |
442 const GrGeometryProcessor* gp, | |
443 SkPoint* positions, | |
444 size_t vertexStride, | |
445 const SkMatrix& viewMatrix, | |
446 const SkPath& path, | |
447 const PathData* pathData) { | |
448 GrTexture* texture = atlas->getTexture(); | |
449 | |
450 SkScalar dx = pathData->fBounds.fLeft; | |
451 SkScalar dy = pathData->fBounds.fTop; | |
452 SkScalar width = pathData->fBounds.width(); | |
453 SkScalar height = pathData->fBounds.height(); | |
454 | |
455 SkScalar invScale = 1.0f / pathData->fScale; | |
456 dx *= invScale; | |
457 dy *= invScale; | |
458 width *= invScale; | |
459 height *= invScale; | |
460 | |
461 SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX); | |
462 SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY); | |
463 SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); | |
464 SkFixed th = SkScalarToFixed(pathData->fBounds.height()); | |
465 | |
466 // vertex positions | |
467 // TODO make the vertex attributes a struct | |
468 SkRect r = SkRect::MakeXYWH(dx, dy, width, height); | |
469 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexSt ride); | |
470 | |
471 // vertex texture coords | |
472 SkPoint* textureCoords = positions + 1; | |
473 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normaliz eFixedX(tx)), | |
474 SkFixedToFloat(texture->texturePriv().normaliz eFixedY(ty)), | |
475 SkFixedToFloat(texture->texturePriv().normaliz eFixedX(tx + tw)), | |
476 SkFixedToFloat(texture->texturePriv().normaliz eFixedY(ty + th)), | |
477 vertexStride); | |
478 } | |
479 | |
480 void initDraw(GrBatchTarget* batchTarget, | |
481 const GrGeometryProcessor* dfProcessor, | |
482 const GrPipeline* pipeline) { | |
483 batchTarget->initDraw(dfProcessor, pipeline); | |
484 | |
485 // TODO remove this when batch is everywhere | |
486 GrPipelineInfo init; | |
487 init.fColorIgnored = fBatch.fColorIgnored; | |
488 init.fOverrideColor = GrColor_ILLEGAL; | |
489 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
490 init.fUsesLocalCoords = this->usesLocalCoords(); | |
491 dfProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init); | |
492 } | |
493 | |
494 void flush(GrBatchTarget* batchTarget, | |
495 const GrGeometryProcessor* dfProcessor, | |
496 const GrPipeline* pipeline, | |
497 GrDrawTarget::DrawInfo* drawInfo, | |
498 int instanceCount, | |
499 int maxInstancesPerDraw) { | |
500 while (instanceCount) { | |
501 drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw )); | |
502 drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verti cesPerInstance()); | |
503 drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indice sPerInstance()); | |
504 | |
505 batchTarget->draw(*drawInfo); | |
506 | |
507 drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexC ount()); | |
508 instanceCount -= drawInfo->instanceCount(); | |
509 } | |
510 } | |
511 | |
512 GrColor color() const { return fBatch.fColor; } | |
513 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } | |
514 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } | |
515 | |
516 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { | |
517 AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>(); | |
518 | |
519 // TODO we could actually probably do a bunch of this work on the CPU, i e map viewMatrix, | |
520 // maybe upload color via attribute | |
521 if (this->color() != that->color()) { | |
522 return false; | |
523 } | |
524 | |
525 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { | |
526 return false; | |
527 } | |
528 | |
529 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()) ; | |
bsalomon
2015/03/05 22:11:08
seems like this could lead to unfortunate combinat
bsalomon
2015/03/06 16:34:59
Retracted... realized this doesn't lead to caching
| |
530 return true; | |
531 } | |
532 | |
533 struct BatchTracker { | |
534 GrColor fColor; | |
535 SkMatrix fViewMatrix; | |
536 bool fUsesLocalCoords; | |
537 bool fColorIgnored; | |
538 bool fCoverageIgnored; | |
539 }; | |
540 | |
541 BatchTracker fBatch; | |
542 SkSTArray<1, Geometry, true> fGeoData; | |
543 | |
544 // padding around path bounds to allow for antialiased pixels | |
545 const SkScalar kAntiAliasPad = 1.0f; | |
bsalomon
2015/03/05 22:11:08
does this require c++11? (do we allow anything els
joshualitt
2015/03/09 19:45:14
Acknowledged.
| |
546 GrBatchAtlas* fAtlas; | |
547 PathCache* fPathCache; | |
548 PathDataList* fPathList; | |
549 }; | |
550 | |
106 bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target, | 551 bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target, |
107 GrPipelineBuilder* pipelineBuilde r, | 552 GrPipelineBuilder* pipelineBuilde r, |
108 GrColor color, | 553 GrColor color, |
109 const SkMatrix& viewMatrix, | 554 const SkMatrix& viewMatrix, |
110 const SkPath& path, | 555 const SkPath& path, |
111 const SkStrokeRec& stroke, | 556 const SkStrokeRec& stroke, |
112 bool antiAlias) { | 557 bool antiAlias) { |
113 // we've already bailed on inverse filled paths, so this is safe | 558 // we've already bailed on inverse filled paths, so this is safe |
114 if (path.isEmpty()) { | 559 if (path.isEmpty()) { |
115 return true; | 560 return true; |
116 } | 561 } |
117 | 562 |
118 SkASSERT(fContext); | 563 SkASSERT(fContext); |
119 | 564 |
120 // get mip level | 565 if (!fAtlas) { |
121 SkScalar maxScale = viewMatrix.getMaxScale(); | 566 // Create a new atlas |
122 const SkRect& bounds = path.getBounds(); | 567 GrSurfaceDesc desc; |
123 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); | 568 desc.fFlags = kNone_GrSurfaceFlags; |
124 SkScalar size = maxScale*maxDim; | 569 desc.fWidth = ATLAS_TEXTURE_WIDTH; |
125 uint32_t desiredDimension; | 570 desc.fHeight = ATLAS_TEXTURE_HEIGHT; |
126 if (size <= kSmallMIP) { | 571 desc.fConfig = kAlpha_8_GrPixelConfig; |
127 desiredDimension = kSmallMIP; | 572 |
128 } else if (size <= kMediumMIP) { | 573 GrGpu* gpu = fContext->getGpu(); |
129 desiredDimension = kMediumMIP; | 574 GrTexture* texture = gpu->createTexture(desc, true, NULL, 0); |
bsalomon
2015/03/05 22:11:08
not using the cache?
joshualitt
2015/03/09 19:45:14
Acknowledged.
| |
130 } else { | 575 if (texture) { |
131 desiredDimension = kLargeMIP; | 576 fAtlas = SkNEW_ARGS(GrBatchAtlas, (gpu, texture, NUM_PLOTS_X, NUM_PL OTS_Y)); |
132 } | 577 } |
133 | 578 } |
134 // check to see if path is cached | 579 |
135 // TODO: handle stroked vs. filled version of same path | 580 AADistanceFieldPathBatch::Geometry geometry(stroke); |
136 PathData::Key key = { path.getGenerationID(), desiredDimension }; | 581 geometry.fPath = path; |
137 PathData* pathData = fPathCache.find(key); | 582 geometry.fAntiAlias = antiAlias; |
138 if (NULL == pathData) { | 583 |
139 SkScalar scale = desiredDimension/maxDim; | 584 SkAutoTUnref<GrBatch> batch(AADistanceFieldPathBatch::Create(geometry, color , viewMatrix, |
140 pathData = this->addPathToAtlas(path, stroke, antiAlias, desiredDimensio n, scale); | 585 fAtlas, &fPathC ache, &fPathList)); |
141 if (NULL == pathData) { | 586 |
142 return false; | 587 SkRect bounds = path.getBounds(); |
143 } | 588 viewMatrix.mapRect(&bounds); |
144 } | 589 target->drawBatch(pipelineBuilder, batch, &bounds); |
145 | 590 |
146 // use signed distance field to render | |
147 return this->internalDrawPath(target, pipelineBuilder, color, viewMatrix, pa th, pathData); | |
148 } | |
149 | |
150 // padding around path bounds to allow for antialiased pixels | |
151 const SkScalar kAntiAliasPad = 1.0f; | |
152 | |
153 inline bool GrAADistanceFieldPathRenderer::uploadPath(GrPlot** plot, SkIPoint16* atlasLocation, | |
154 int width, int height, voi d* dfStorage) { | |
155 *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocat ion); | |
156 | |
157 // if atlas full | |
158 if (NULL == *plot) { | |
159 if (this->freeUnusedPlot()) { | |
160 *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, at lasLocation); | |
161 if (*plot) { | |
162 return true; | |
163 } | |
164 } | |
165 | |
166 if (c_DumpPathCache) { | |
167 #ifdef SK_DEVELOPER | |
168 GrTexture* texture = fAtlas->getTexture(); | |
169 texture->surfacePriv().savePixels("pathcache.png"); | |
170 #endif | |
171 } | |
172 | |
173 // before we purge the cache, we must flush any accumulated draws | |
174 fContext->flush(); | |
175 | |
176 if (this->freeUnusedPlot()) { | |
177 *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, at lasLocation); | |
178 if (*plot) { | |
179 return true; | |
180 } | |
181 } | |
182 return false; | |
183 } | |
184 return true; | 591 return true; |
185 } | 592 } |
186 | 593 |
187 GrAADistanceFieldPathRenderer::PathData* GrAADistanceFieldPathRenderer::addPathT oAtlas( | |
188 const Sk Path& path, | |
189 const Sk StrokeRec& stroke, | |
190 bool ant iAlias, | |
191 uint32_t dimension, | |
192 SkScalar scale) { | |
193 | |
194 // generate distance field and add to atlas | |
195 if (NULL == fAtlas) { | |
196 SkISize textureSize = SkISize::Make(ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_H EIGHT); | |
197 fAtlas = SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kAlpha_8_GrPixelConfig , | |
198 kNone_GrSurfaceFlags, textureSize, | |
199 NUM_PLOTS_X, NUM_PLOTS_Y, false)); | |
200 if (NULL == fAtlas) { | |
201 return NULL; | |
202 } | |
203 } | |
204 | |
205 const SkRect& bounds = path.getBounds(); | |
206 | |
207 // generate bounding rect for bitmap draw | |
208 SkRect scaledBounds = bounds; | |
209 // scale to mip level size | |
210 scaledBounds.fLeft *= scale; | |
211 scaledBounds.fTop *= scale; | |
212 scaledBounds.fRight *= scale; | |
213 scaledBounds.fBottom *= scale; | |
214 // move the origin to an integer boundary (gives better results) | |
215 SkScalar dx = SkScalarFraction(scaledBounds.fLeft); | |
216 SkScalar dy = SkScalarFraction(scaledBounds.fTop); | |
217 scaledBounds.offset(-dx, -dy); | |
218 // get integer boundary | |
219 SkIRect devPathBounds; | |
220 scaledBounds.roundOut(&devPathBounds); | |
221 // pad to allow room for antialiasing | |
222 devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAn tiAliasPad)); | |
223 // move origin to upper left corner | |
224 devPathBounds.offsetTo(0,0); | |
225 | |
226 // draw path to bitmap | |
227 SkMatrix drawMatrix; | |
228 drawMatrix.setTranslate(-bounds.left(), -bounds.top()); | |
229 drawMatrix.postScale(scale, scale); | |
230 drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); | |
231 GrSWMaskHelper helper(fContext); | |
232 | |
233 if (!helper.init(devPathBounds, &drawMatrix)) { | |
234 return NULL; | |
235 } | |
236 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF); | |
237 | |
238 // generate signed distance field | |
239 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); | |
240 int width = devPathBounds.width(); | |
241 int height = devPathBounds.height(); | |
242 SkAutoSMalloc<1024> dfStorage(width*height*sizeof(unsigned char)); | |
243 helper.toSDF((unsigned char*) dfStorage.get()); | |
244 | |
245 // add to atlas | |
246 GrPlot* plot; | |
247 SkIPoint16 atlasLocation; | |
248 if (!this->uploadPath(&plot, &atlasLocation, width, height, dfStorage.get()) ) { | |
249 return NULL; | |
250 } | |
251 | |
252 // add to cache | |
253 PathData* pathData = SkNEW(PathData); | |
254 pathData->fKey.fGenID = path.getGenerationID(); | |
255 pathData->fKey.fDimension = dimension; | |
256 pathData->fScale = scale; | |
257 pathData->fPlot = plot; | |
258 // change the scaled rect to match the size of the inset distance field | |
259 scaledBounds.fRight = scaledBounds.fLeft + | |
260 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); | |
261 scaledBounds.fBottom = scaledBounds.fTop + | |
262 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); | |
263 // shift the origin to the correct place relative to the distance field | |
264 // need to also restore the fractional translation | |
265 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx, | |
266 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy); | |
267 pathData->fBounds = scaledBounds; | |
268 // origin we render from is inset from distance field edge | |
269 atlasLocation.fX += SK_DistanceFieldInset; | |
270 atlasLocation.fY += SK_DistanceFieldInset; | |
271 pathData->fAtlasLocation = atlasLocation; | |
272 | |
273 fPathCache.add(pathData); | |
274 fPathList.addToTail(pathData); | |
275 #ifdef DF_PATH_TRACKING | |
276 ++g_NumCachedPaths; | |
277 #endif | |
278 | |
279 return pathData; | |
280 } | |
281 | |
282 bool GrAADistanceFieldPathRenderer::freeUnusedPlot() { | |
283 // find an unused plot | |
284 GrPlot* plot = fAtlas->getUnusedPlot(); | |
285 if (NULL == plot) { | |
286 return false; | |
287 } | |
288 plot->resetRects(); | |
289 | |
290 // remove any paths that use this plot | |
291 PathDataList::Iter iter; | |
292 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | |
293 PathData* pathData; | |
294 while ((pathData = iter.get())) { | |
295 iter.next(); | |
296 if (plot == pathData->fPlot) { | |
297 fPathCache.remove(pathData->fKey); | |
298 fPathList.remove(pathData); | |
299 SkDELETE(pathData); | |
300 #ifdef DF_PATH_TRACKING | |
301 ++g_NumFreedPaths; | |
302 #endif | |
303 } | |
304 } | |
305 | |
306 // tell the atlas to free the plot | |
307 GrAtlas::RemovePlot(&fPlotUsage, plot); | |
308 | |
309 return true; | |
310 } | |
311 | |
312 bool GrAADistanceFieldPathRenderer::internalDrawPath(GrDrawTarget* target, | |
313 GrPipelineBuilder* pipeline Builder, | |
314 GrColor color, | |
315 const SkMatrix& viewMatrix, | |
316 const SkPath& path, | |
317 const PathData* pathData) { | |
318 GrTexture* texture = fAtlas->getTexture(); | |
319 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp(pipelineBuilder); | |
320 | |
321 SkASSERT(pathData->fPlot); | |
322 GrDrawTarget::DrawToken drawToken = target->getCurrentDrawToken(); | |
323 pathData->fPlot->setDrawToken(drawToken); | |
324 | |
325 // set up any flags | |
326 uint32_t flags = 0; | |
327 flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0 ; | |
328 | |
329 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_ FilterMode); | |
330 if (flags != fEffectFlags || fCachedGeometryProcessor->color() != color || | |
331 !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(viewMatrix)) { | |
332 fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Crea te(color, | |
333 viewMatrix, | |
334 texture, | |
335 params, | |
336 flags, | |
337 false)); | |
338 fEffectFlags = flags; | |
339 } | |
340 | |
341 void* vertices = NULL; | |
342 bool success = target->reserveVertexAndIndexSpace(4, | |
343 fCachedGeometryProcessor-> getVertexStride(), | |
344 0, &vertices, NULL); | |
345 SkASSERT(fCachedGeometryProcessor->getVertexStride() == 2 * sizeof(SkPoint)) ; | |
346 GrAlwaysAssert(success); | |
347 | |
348 SkScalar dx = pathData->fBounds.fLeft; | |
349 SkScalar dy = pathData->fBounds.fTop; | |
350 SkScalar width = pathData->fBounds.width(); | |
351 SkScalar height = pathData->fBounds.height(); | |
352 | |
353 SkScalar invScale = 1.0f/pathData->fScale; | |
354 dx *= invScale; | |
355 dy *= invScale; | |
356 width *= invScale; | |
357 height *= invScale; | |
358 | |
359 SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX); | |
360 SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY); | |
361 SkFixed tw = SkScalarToFixed(pathData->fBounds.width()); | |
362 SkFixed th = SkScalarToFixed(pathData->fBounds.height()); | |
363 | |
364 // vertex positions | |
365 SkRect r = SkRect::MakeXYWH(dx, dy, width, height); | |
366 size_t vertSize = 2 * sizeof(SkPoint); | |
367 SkPoint* positions = reinterpret_cast<SkPoint*>(vertices); | |
368 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertSize); | |
369 | |
370 // vertex texture coords | |
371 intptr_t intPtr = reinterpret_cast<intptr_t>(positions); | |
372 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(intPtr + vertSize - size of(SkPoint)); | |
373 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFix edX(tx)), | |
374 SkFixedToFloat(texture->texturePriv().normalizeFix edY(ty)), | |
375 SkFixedToFloat(texture->texturePriv().normalizeFix edX(tx + tw)), | |
376 SkFixedToFloat(texture->texturePriv().normalizeFix edY(ty + th)), | |
377 vertSize); | |
378 | |
379 viewMatrix.mapRect(&r); | |
380 target->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); | |
381 target->drawIndexedInstances(pipelineBuilder, fCachedGeometryProcessor.get() , | |
382 kTriangles_GrPrimitiveType, 1, 4, 6, &r); | |
383 target->resetVertexSource(); | |
384 | |
385 return true; | |
386 } | |
387 | |
OLD | NEW |