OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrAADistanceFieldPathRenderer.h" | 8 #include "GrAADistanceFieldPathRenderer.h" |
9 | 9 |
10 #include "GrBatchFlushState.h" | 10 #include "GrBatchFlushState.h" |
(...skipping 13 matching lines...) Expand all Loading... |
24 | 24 |
25 #define ATLAS_TEXTURE_WIDTH 2048 | 25 #define ATLAS_TEXTURE_WIDTH 2048 |
26 #define ATLAS_TEXTURE_HEIGHT 2048 | 26 #define ATLAS_TEXTURE_HEIGHT 2048 |
27 #define PLOT_WIDTH 512 | 27 #define PLOT_WIDTH 512 |
28 #define PLOT_HEIGHT 256 | 28 #define PLOT_HEIGHT 256 |
29 | 29 |
30 #define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH) | 30 #define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH) |
31 #define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT) | 31 #define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT) |
32 | 32 |
33 #ifdef DF_PATH_TRACKING | 33 #ifdef DF_PATH_TRACKING |
34 static int g_NumCachedPaths = 0; | 34 static int g_NumCachedShapes = 0; |
35 static int g_NumFreedPaths = 0; | 35 static int g_NumFreedShapes = 0; |
36 #endif | 36 #endif |
37 | 37 |
38 // mip levels | 38 // mip levels |
39 static const int kSmallMIP = 32; | 39 static const int kSmallMIP = 32; |
40 static const int kMediumMIP = 73; | 40 static const int kMediumMIP = 73; |
41 static const int kLargeMIP = 162; | 41 static const int kLargeMIP = 162; |
42 | 42 |
43 // Callback to clear out internal path cache when eviction occurs | 43 // Callback to clear out internal path cache when eviction occurs |
44 void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, voi
d* pr) { | 44 void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, voi
d* pr) { |
45 GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr; | 45 GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr; |
46 // remove any paths that use this plot | 46 // remove any paths that use this plot |
47 PathDataList::Iter iter; | 47 ShapeDataList::Iter iter; |
48 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart); | 48 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart); |
49 PathData* pathData; | 49 ShapeData* shapeData; |
50 while ((pathData = iter.get())) { | 50 while ((shapeData = iter.get())) { |
51 iter.next(); | 51 iter.next(); |
52 if (id == pathData->fID) { | 52 if (id == shapeData->fID) { |
53 dfpr->fPathCache.remove(pathData->fKey); | 53 dfpr->fShapeCache.remove(shapeData->fKey); |
54 dfpr->fPathList.remove(pathData); | 54 dfpr->fShapeList.remove(shapeData); |
55 delete pathData; | 55 delete shapeData; |
56 #ifdef DF_PATH_TRACKING | 56 #ifdef DF_PATH_TRACKING |
57 ++g_NumFreedPaths; | 57 ++g_NumFreedPaths; |
58 #endif | 58 #endif |
59 } | 59 } |
60 } | 60 } |
61 } | 61 } |
62 | 62 |
63 //////////////////////////////////////////////////////////////////////////////// | 63 //////////////////////////////////////////////////////////////////////////////// |
64 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer() : fAtlas(nullptr)
{} | 64 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer() : fAtlas(nullptr)
{} |
65 | 65 |
66 GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { | 66 GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() { |
67 PathDataList::Iter iter; | 67 ShapeDataList::Iter iter; |
68 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | 68 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart); |
69 PathData* pathData; | 69 ShapeData* shapeData; |
70 while ((pathData = iter.get())) { | 70 while ((shapeData = iter.get())) { |
71 iter.next(); | 71 iter.next(); |
72 fPathList.remove(pathData); | 72 delete shapeData; |
73 delete pathData; | |
74 } | 73 } |
75 delete fAtlas; | 74 delete fAtlas; |
76 | 75 |
77 #ifdef DF_PATH_TRACKING | 76 #ifdef DF_PATH_TRACKING |
78 SkDebugf("Cached paths: %d, freed paths: %d\n", g_NumCachedPaths, g_NumFreed
Paths); | 77 SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFr
eedShapes); |
79 #endif | 78 #endif |
80 } | 79 } |
81 | 80 |
82 //////////////////////////////////////////////////////////////////////////////// | 81 //////////////////////////////////////////////////////////////////////////////// |
83 bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c
onst { | 82 bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c
onst { |
84 // We don't currently apply the dash or factor it into the DF key. (skbug.co
m/5082) | 83 if (!args.fShaderCaps->shaderDerivativeSupport()) { |
85 if (args.fShape->style().pathEffect()) { | 84 return false; |
| 85 } |
| 86 // If the shape has no key then we won't get any reuse. |
| 87 if (!args.fShape->hasUnstyledKey()) { |
| 88 return false; |
| 89 } |
| 90 // This only supports filled paths, however, the caller may apply the style
to make a filled |
| 91 // path and try again. |
| 92 if (!args.fShape->style().isSimpleFill()) { |
| 93 return false; |
| 94 } |
| 95 // This does non-inverse antialiased fills. |
| 96 if (!args.fAntiAlias) { |
86 return false; | 97 return false; |
87 } | 98 } |
88 // TODO: Support inverse fill | 99 // TODO: Support inverse fill |
89 if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias || | 100 if (!args.fShape->inverseFilled()) { |
90 args.fShape->style().isSimpleHairline() || args.fShape->mayBeInverseFill
edAfterStyling() || | |
91 !args.fShape->hasUnstyledKey()) { | |
92 return false; | 101 return false; |
93 } | 102 } |
94 | |
95 // currently don't support perspective | 103 // currently don't support perspective |
96 if (args.fViewMatrix->hasPerspective()) { | 104 if (args.fViewMatrix->hasPerspective()) { |
97 return false; | 105 return false; |
98 } | 106 } |
99 | 107 |
100 // only support paths with bounds within kMediumMIP by kMediumMIP, | 108 // only support paths with bounds within kMediumMIP by kMediumMIP, |
101 // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP | 109 // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP |
102 // the goal is to accelerate rendering of lots of small paths that may be sc
aling | 110 // the goal is to accelerate rendering of lots of small paths that may be sc
aling |
103 SkScalar maxScale = args.fViewMatrix->getMaxScale(); | 111 SkScalar maxScale = args.fViewMatrix->getMaxScale(); |
104 SkRect bounds; | 112 SkRect bounds; |
105 args.fShape->styledBounds(&bounds); | 113 args.fShape->styledBounds(&bounds); |
106 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); | 114 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); |
107 | 115 |
108 return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP; | 116 return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP; |
109 } | 117 } |
110 | 118 |
111 //////////////////////////////////////////////////////////////////////////////// | 119 //////////////////////////////////////////////////////////////////////////////// |
112 | 120 |
113 // padding around path bounds to allow for antialiased pixels | 121 // padding around path bounds to allow for antialiased pixels |
114 static const SkScalar kAntiAliasPad = 1.0f; | 122 static const SkScalar kAntiAliasPad = 1.0f; |
115 | 123 |
116 class AADistanceFieldPathBatch : public GrVertexBatch { | 124 class AADistanceFieldPathBatch : public GrVertexBatch { |
117 public: | 125 public: |
118 DEFINE_BATCH_CLASS_ID | 126 DEFINE_BATCH_CLASS_ID |
119 | 127 |
120 typedef GrAADistanceFieldPathRenderer::PathData PathData; | 128 typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData; |
121 typedef SkTDynamicHash<PathData, PathData::Key> PathCache; | 129 typedef SkTDynamicHash<ShapeData, ShapeData::Key> ShapeCache; |
122 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList; | 130 typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList; |
123 | 131 |
124 struct Geometry { | 132 struct Geometry { |
125 Geometry(const SkStrokeRec& stroke) : fStroke(stroke) { | 133 GrShape fShape; |
126 if (!stroke.needToApply()) { | |
127 // purify unused values to ensure binary equality | |
128 fStroke.setStrokeParams(SkPaint::kDefault_Cap, SkPaint::kDefault
_Join, | |
129 SkIntToScalar(4)); | |
130 if (fStroke.getWidth() < 0) { | |
131 fStroke.setStrokeStyle(-1.0f); | |
132 } | |
133 } | |
134 } | |
135 SkPath fPath; | |
136 // The unique ID of the path involved in this draw. This may be differen
t than the ID | |
137 // in fPath since that path may have resulted from a SkStrokeRec::applyT
oPath call. | |
138 uint32_t fGenID; | |
139 SkStrokeRec fStroke; | |
140 GrColor fColor; | 134 GrColor fColor; |
141 bool fAntiAlias; | 135 bool fAntiAlias; |
142 }; | 136 }; |
143 | 137 |
144 static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMat
rix, | 138 static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMat
rix, |
145 GrBatchAtlas* atlas, PathCache* pathCache, PathDa
taList* pathList, | 139 GrBatchAtlas* atlas, ShapeCache* shapeCache, |
146 bool gammaCorrect) { | 140 ShapeDataList* shapeList, bool gammaCorrect) { |
147 return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, pathCac
he, pathList, | 141 return new AADistanceFieldPathBatch(geometry, viewMatrix, atlas, shapeCa
che, shapeList, |
148 gammaCorrect); | 142 gammaCorrect); |
149 } | 143 } |
150 | 144 |
151 const char* name() const override { return "AADistanceFieldPathBatch"; } | 145 const char* name() const override { return "AADistanceFieldPathBatch"; } |
152 | 146 |
153 void computePipelineOptimizations(GrInitInvariantOutput* color, | 147 void computePipelineOptimizations(GrInitInvariantOutput* color, |
154 GrInitInvariantOutput* coverage, | 148 GrInitInvariantOutput* coverage, |
155 GrBatchToXPOverrides* overrides) const ove
rride { | 149 GrBatchToXPOverrides* overrides) const ove
rride { |
156 color->setKnownFourComponents(fGeoData[0].fColor); | 150 color->setKnownFourComponents(fGeoData[0].fColor); |
157 coverage->setUnknownSingleComponent(); | 151 coverage->setUnknownSingleComponent(); |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
222 SkDebugf("Could not allocate vertices\n"); | 216 SkDebugf("Could not allocate vertices\n"); |
223 return; | 217 return; |
224 } | 218 } |
225 | 219 |
226 flushInfo.fInstancesToFlush = 0; | 220 flushInfo.fInstancesToFlush = 0; |
227 for (int i = 0; i < instanceCount; i++) { | 221 for (int i = 0; i < instanceCount; i++) { |
228 const Geometry& args = fGeoData[i]; | 222 const Geometry& args = fGeoData[i]; |
229 | 223 |
230 // get mip level | 224 // get mip level |
231 SkScalar maxScale = this->viewMatrix().getMaxScale(); | 225 SkScalar maxScale = this->viewMatrix().getMaxScale(); |
232 const SkRect& bounds = args.fPath.getBounds(); | 226 const SkRect& bounds = args.fShape.bounds(); |
233 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); | 227 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); |
234 SkScalar size = maxScale * maxDim; | 228 SkScalar size = maxScale * maxDim; |
235 uint32_t desiredDimension; | 229 uint32_t desiredDimension; |
236 if (size <= kSmallMIP) { | 230 if (size <= kSmallMIP) { |
237 desiredDimension = kSmallMIP; | 231 desiredDimension = kSmallMIP; |
238 } else if (size <= kMediumMIP) { | 232 } else if (size <= kMediumMIP) { |
239 desiredDimension = kMediumMIP; | 233 desiredDimension = kMediumMIP; |
240 } else { | 234 } else { |
241 desiredDimension = kLargeMIP; | 235 desiredDimension = kLargeMIP; |
242 } | 236 } |
243 | 237 |
244 // check to see if path is cached | 238 // check to see if path is cached |
245 PathData::Key key(args.fGenID, desiredDimension, args.fStroke); | 239 ShapeData::Key key(args.fShape, desiredDimension); |
246 PathData* pathData = fPathCache->find(key); | 240 ShapeData* shapeData = fShapeCache->find(key); |
247 if (nullptr == pathData || !atlas->hasID(pathData->fID)) { | 241 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) { |
248 // Remove the stale cache entry | 242 // Remove the stale cache entry |
249 if (pathData) { | 243 if (shapeData) { |
250 fPathCache->remove(pathData->fKey); | 244 fShapeCache->remove(shapeData->fKey); |
251 fPathList->remove(pathData); | 245 fShapeList->remove(shapeData); |
252 delete pathData; | 246 delete shapeData; |
253 } | 247 } |
254 SkScalar scale = desiredDimension/maxDim; | 248 SkScalar scale = desiredDimension/maxDim; |
255 pathData = new PathData; | 249 shapeData = new ShapeData; |
256 if (!this->addPathToAtlas(target, | 250 if (!this->addPathToAtlas(target, |
257 &flushInfo, | 251 &flushInfo, |
258 atlas, | 252 atlas, |
259 pathData, | 253 shapeData, |
260 args.fPath, | 254 args.fShape, |
261 args.fGenID, | |
262 args.fStroke, | |
263 args.fAntiAlias, | 255 args.fAntiAlias, |
264 desiredDimension, | 256 desiredDimension, |
265 scale)) { | 257 scale)) { |
266 SkDebugf("Can't rasterize path\n"); | 258 SkDebugf("Can't rasterize path\n"); |
267 return; | 259 return; |
268 } | 260 } |
269 } | 261 } |
270 | 262 |
271 atlas->setLastUseToken(pathData->fID, target->nextDrawToken()); | 263 atlas->setLastUseToken(shapeData->fID, target->nextDrawToken()); |
272 | 264 |
273 // Now set vertices | 265 // Now set vertices |
274 intptr_t offset = reinterpret_cast<intptr_t>(vertices); | 266 intptr_t offset = reinterpret_cast<intptr_t>(vertices); |
275 offset += i * kVerticesPerQuad * vertexStride; | 267 offset += i * kVerticesPerQuad * vertexStride; |
276 this->writePathVertices(target, | 268 this->writePathVertices(target, |
277 atlas, | 269 atlas, |
278 offset, | 270 offset, |
279 args.fColor, | 271 args.fColor, |
280 vertexStride, | 272 vertexStride, |
281 this->viewMatrix(), | 273 this->viewMatrix(), |
282 args.fPath, | 274 args.fShape, |
283 pathData); | 275 shapeData); |
284 flushInfo.fInstancesToFlush++; | 276 flushInfo.fInstancesToFlush++; |
285 } | 277 } |
286 | 278 |
287 this->flush(target, &flushInfo); | 279 this->flush(target, &flushInfo); |
288 } | 280 } |
289 | 281 |
290 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
291 | |
292 AADistanceFieldPathBatch(const Geometry& geometry, | 282 AADistanceFieldPathBatch(const Geometry& geometry, |
293 const SkMatrix& viewMatrix, | 283 const SkMatrix& viewMatrix, |
294 GrBatchAtlas* atlas, | 284 GrBatchAtlas* atlas, |
295 PathCache* pathCache, PathDataList* pathList, | 285 ShapeCache* shapeCache, ShapeDataList* shapeList, |
296 bool gammaCorrect) | 286 bool gammaCorrect) |
297 : INHERITED(ClassID()) { | 287 : INHERITED(ClassID()) { |
| 288 SkASSERT(geometry.fShape.hasUnstyledKey()); |
298 fBatch.fViewMatrix = viewMatrix; | 289 fBatch.fViewMatrix = viewMatrix; |
299 fGeoData.push_back(geometry); | 290 fGeoData.push_back(geometry); |
| 291 SkASSERT(fGeoData[0].fShape.hasUnstyledKey()); |
300 | 292 |
301 fAtlas = atlas; | 293 fAtlas = atlas; |
302 fPathCache = pathCache; | 294 fShapeCache = shapeCache; |
303 fPathList = pathList; | 295 fShapeList = shapeList; |
304 fGammaCorrect = gammaCorrect; | 296 fGammaCorrect = gammaCorrect; |
305 | 297 |
306 // Compute bounds | 298 // Compute bounds |
307 fBounds = geometry.fPath.getBounds(); | 299 fBounds = geometry.fShape.bounds(); |
308 viewMatrix.mapRect(&fBounds); | 300 viewMatrix.mapRect(&fBounds); |
309 } | 301 } |
310 | 302 |
311 bool addPathToAtlas(GrVertexBatch::Target* target, | 303 bool addPathToAtlas(GrVertexBatch::Target* target, |
312 FlushInfo* flushInfo, | 304 FlushInfo* flushInfo, |
313 GrBatchAtlas* atlas, | 305 GrBatchAtlas* atlas, |
314 PathData* pathData, | 306 ShapeData* shapeData, |
315 const SkPath& path, | 307 const GrShape& shape, |
316 uint32_t genID, | |
317 const SkStrokeRec& stroke, | |
318 bool antiAlias, | 308 bool antiAlias, |
319 uint32_t dimension, | 309 uint32_t dimension, |
320 SkScalar scale) const { | 310 SkScalar scale) const { |
321 const SkRect& bounds = path.getBounds(); | 311 const SkRect& bounds = shape.bounds(); |
322 | 312 |
323 // generate bounding rect for bitmap draw | 313 // generate bounding rect for bitmap draw |
324 SkRect scaledBounds = bounds; | 314 SkRect scaledBounds = bounds; |
325 // scale to mip level size | 315 // scale to mip level size |
326 scaledBounds.fLeft *= scale; | 316 scaledBounds.fLeft *= scale; |
327 scaledBounds.fTop *= scale; | 317 scaledBounds.fTop *= scale; |
328 scaledBounds.fRight *= scale; | 318 scaledBounds.fRight *= scale; |
329 scaledBounds.fBottom *= scale; | 319 scaledBounds.fBottom *= scale; |
330 // move the origin to an integer boundary (gives better results) | 320 // move the origin to an integer boundary (gives better results) |
331 SkScalar dx = SkScalarFraction(scaledBounds.fLeft); | 321 SkScalar dx = SkScalarFraction(scaledBounds.fLeft); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 | 358 |
369 SkDraw draw; | 359 SkDraw draw; |
370 sk_bzero(&draw, sizeof(draw)); | 360 sk_bzero(&draw, sizeof(draw)); |
371 | 361 |
372 SkRasterClip rasterClip; | 362 SkRasterClip rasterClip; |
373 rasterClip.setRect(devPathBounds); | 363 rasterClip.setRect(devPathBounds); |
374 draw.fRC = &rasterClip; | 364 draw.fRC = &rasterClip; |
375 draw.fMatrix = &drawMatrix; | 365 draw.fMatrix = &drawMatrix; |
376 draw.fDst = dst; | 366 draw.fDst = dst; |
377 | 367 |
| 368 SkPath path; |
| 369 shape.asPath(&path); |
378 draw.drawPathCoverage(path, paint); | 370 draw.drawPathCoverage(path, paint); |
379 | 371 |
380 // generate signed distance field | 372 // generate signed distance field |
381 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); | 373 devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); |
382 width = devPathBounds.width(); | 374 width = devPathBounds.width(); |
383 height = devPathBounds.height(); | 375 height = devPathBounds.height(); |
384 // TODO We should really generate this directly into the plot somehow | 376 // TODO We should really generate this directly into the plot somehow |
385 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); | 377 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); |
386 | 378 |
387 // Generate signed distance field | 379 // Generate signed distance field |
388 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(), | 380 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(), |
389 (const unsigned char*)dst.addr(), | 381 (const unsigned char*)dst.addr(), |
390 dst.width(), dst.height(), dst.rowByt
es()); | 382 dst.width(), dst.height(), dst.rowByt
es()); |
391 | 383 |
392 // add to atlas | 384 // add to atlas |
393 SkIPoint16 atlasLocation; | 385 SkIPoint16 atlasLocation; |
394 GrBatchAtlas::AtlasID id; | 386 GrBatchAtlas::AtlasID id; |
395 bool success = atlas->addToAtlas(&id, target, width, height, dfStorage.g
et(), | 387 bool success = atlas->addToAtlas(&id, target, width, height, dfStorage.g
et(), |
396 &atlasLocation); | 388 &atlasLocation); |
397 if (!success) { | 389 if (!success) { |
398 this->flush(target, flushInfo); | 390 this->flush(target, flushInfo); |
399 | 391 |
400 SkDEBUGCODE(success =) atlas->addToAtlas(&id, target, width, height, | 392 SkDEBUGCODE(success =) atlas->addToAtlas(&id, target, width, height, |
401 dfStorage.get(), &atlasLoca
tion); | 393 dfStorage.get(), &atlasLoca
tion); |
402 SkASSERT(success); | 394 SkASSERT(success); |
403 | 395 |
404 } | 396 } |
405 | 397 |
406 // add to cache | 398 // add to cache |
407 pathData->fKey = PathData::Key(genID, dimension, stroke); | 399 shapeData->fKey.set(shape, dimension); |
408 pathData->fScale = scale; | 400 shapeData->fScale = scale; |
409 pathData->fID = id; | 401 shapeData->fID = id; |
410 // change the scaled rect to match the size of the inset distance field | 402 // change the scaled rect to match the size of the inset distance field |
411 scaledBounds.fRight = scaledBounds.fLeft + | 403 scaledBounds.fRight = scaledBounds.fLeft + |
412 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); | 404 SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); |
413 scaledBounds.fBottom = scaledBounds.fTop + | 405 scaledBounds.fBottom = scaledBounds.fTop + |
414 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); | 406 SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); |
415 // shift the origin to the correct place relative to the distance field | 407 // shift the origin to the correct place relative to the distance field |
416 // need to also restore the fractional translation | 408 // need to also restore the fractional translation |
417 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa
d + dx, | 409 scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa
d + dx, |
418 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa
d + dy); | 410 -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPa
d + dy); |
419 pathData->fBounds = scaledBounds; | 411 shapeData->fBounds = scaledBounds; |
420 // origin we render from is inset from distance field edge | 412 // origin we render from is inset from distance field edge |
421 atlasLocation.fX += SK_DistanceFieldInset; | 413 atlasLocation.fX += SK_DistanceFieldInset; |
422 atlasLocation.fY += SK_DistanceFieldInset; | 414 atlasLocation.fY += SK_DistanceFieldInset; |
423 pathData->fAtlasLocation = atlasLocation; | 415 shapeData->fAtlasLocation = atlasLocation; |
424 | 416 |
425 fPathCache->add(pathData); | 417 fShapeCache->add(shapeData); |
426 fPathList->addToTail(pathData); | 418 fShapeList->addToTail(shapeData); |
427 #ifdef DF_PATH_TRACKING | 419 #ifdef DF_PATH_TRACKING |
428 ++g_NumCachedPaths; | 420 ++g_NumCachedPaths; |
429 #endif | 421 #endif |
430 return true; | 422 return true; |
431 } | 423 } |
432 | 424 |
433 void writePathVertices(GrDrawBatch::Target* target, | 425 void writePathVertices(GrDrawBatch::Target* target, |
434 GrBatchAtlas* atlas, | 426 GrBatchAtlas* atlas, |
435 intptr_t offset, | 427 intptr_t offset, |
436 GrColor color, | 428 GrColor color, |
437 size_t vertexStride, | 429 size_t vertexStride, |
438 const SkMatrix& viewMatrix, | 430 const SkMatrix& viewMatrix, |
439 const SkPath& path, | 431 const GrShape& shape, |
440 const PathData* pathData) const { | 432 const ShapeData* shapeData) const { |
441 GrTexture* texture = atlas->getTexture(); | 433 GrTexture* texture = atlas->getTexture(); |
442 | 434 |
443 SkScalar dx = pathData->fBounds.fLeft; | 435 SkScalar dx = shapeData->fBounds.fLeft; |
444 SkScalar dy = pathData->fBounds.fTop; | 436 SkScalar dy = shapeData->fBounds.fTop; |
445 SkScalar width = pathData->fBounds.width(); | 437 SkScalar width = shapeData->fBounds.width(); |
446 SkScalar height = pathData->fBounds.height(); | 438 SkScalar height = shapeData->fBounds.height(); |
447 | 439 |
448 SkScalar invScale = 1.0f / pathData->fScale; | 440 SkScalar invScale = 1.0f / shapeData->fScale; |
449 dx *= invScale; | 441 dx *= invScale; |
450 dy *= invScale; | 442 dy *= invScale; |
451 width *= invScale; | 443 width *= invScale; |
452 height *= invScale; | 444 height *= invScale; |
453 | 445 |
454 SkPoint* positions = reinterpret_cast<SkPoint*>(offset); | 446 SkPoint* positions = reinterpret_cast<SkPoint*>(offset); |
455 | 447 |
456 // vertex positions | 448 // vertex positions |
457 // TODO make the vertex attributes a struct | 449 // TODO make the vertex attributes a struct |
458 SkRect r = SkRect::MakeXYWH(dx, dy, width, height); | 450 SkRect r = SkRect::MakeXYWH(dx, dy, width, height); |
459 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexSt
ride); | 451 positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexSt
ride); |
460 | 452 |
461 // colors | 453 // colors |
462 for (int i = 0; i < kVerticesPerQuad; i++) { | 454 for (int i = 0; i < kVerticesPerQuad; i++) { |
463 GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertex
Stride); | 455 GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertex
Stride); |
464 *colorPtr = color; | 456 *colorPtr = color; |
465 } | 457 } |
466 | 458 |
467 const SkScalar tx = SkIntToScalar(pathData->fAtlasLocation.fX); | 459 const SkScalar tx = SkIntToScalar(shapeData->fAtlasLocation.fX); |
468 const SkScalar ty = SkIntToScalar(pathData->fAtlasLocation.fY); | 460 const SkScalar ty = SkIntToScalar(shapeData->fAtlasLocation.fY); |
469 | 461 |
470 // vertex texture coords | 462 // vertex texture coords |
471 SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(Gr
Color)); | 463 SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(Gr
Color)); |
472 textureCoords->setRectFan(tx / texture->width(), | 464 textureCoords->setRectFan(tx / texture->width(), |
473 ty / texture->height(), | 465 ty / texture->height(), |
474 (tx + pathData->fBounds.width()) / texture->wi
dth(), | 466 (tx + shapeData->fBounds.width()) / texture->w
idth(), |
475 (ty + pathData->fBounds.height()) / texture->
height(), | 467 (ty + shapeData->fBounds.height()) / texture-
>height(), |
476 vertexStride); | 468 vertexStride); |
477 } | 469 } |
478 | 470 |
479 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) const { | 471 void flush(GrVertexBatch::Target* target, FlushInfo* flushInfo) const { |
480 GrMesh mesh; | 472 GrMesh mesh; |
481 int maxInstancesPerDraw = | 473 int maxInstancesPerDraw = |
482 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(u
int16_t) / 6); | 474 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(u
int16_t) / 6); |
483 mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer, | 475 mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer, |
484 flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad, | 476 flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad, |
485 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw); | 477 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw); |
(...skipping 11 matching lines...) Expand all Loading... |
497 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pi
peline(), | 489 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pi
peline(), |
498 that->bounds(), caps)) { | 490 that->bounds(), caps)) { |
499 return false; | 491 return false; |
500 } | 492 } |
501 | 493 |
502 // TODO We can position on the cpu | 494 // TODO We can position on the cpu |
503 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { | 495 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
504 return false; | 496 return false; |
505 } | 497 } |
506 | 498 |
507 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin())
; | 499 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
508 this->joinBounds(that->bounds()); | 500 this->joinBounds(that->bounds()); |
509 return true; | 501 return true; |
510 } | 502 } |
511 | 503 |
512 struct BatchTracker { | 504 struct BatchTracker { |
513 SkMatrix fViewMatrix; | 505 SkMatrix fViewMatrix; |
514 bool fUsesLocalCoords; | 506 bool fUsesLocalCoords; |
515 bool fColorIgnored; | 507 bool fColorIgnored; |
516 bool fCoverageIgnored; | 508 bool fCoverageIgnored; |
517 }; | 509 }; |
518 | 510 |
519 BatchTracker fBatch; | 511 BatchTracker fBatch; |
520 SkSTArray<1, Geometry, true> fGeoData; | 512 SkSTArray<1, Geometry> fGeoData; |
521 GrBatchAtlas* fAtlas; | 513 GrBatchAtlas* fAtlas; |
522 PathCache* fPathCache; | 514 ShapeCache* fShapeCache; |
523 PathDataList* fPathList; | 515 ShapeDataList* fShapeList; |
524 bool fGammaCorrect; | 516 bool fGammaCorrect; |
525 | 517 |
526 typedef GrVertexBatch INHERITED; | 518 typedef GrVertexBatch INHERITED; |
527 }; | 519 }; |
528 | 520 |
529 bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) { | 521 bool GrAADistanceFieldPathRenderer::onDrawPath(const DrawPathArgs& args) { |
530 GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(), | 522 GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(), |
531 "GrAADistanceFieldPathRenderer::onDrawPath"); | 523 "GrAADistanceFieldPathRenderer::onDrawPath"); |
532 SkASSERT(!args.fDrawContext->isUnifiedMultisampled()); | 524 SkASSERT(!args.fDrawContext->isUnifiedMultisampled()); |
| 525 SkASSERT(args.fShape->style().isSimpleFill()); |
533 | 526 |
534 // we've already bailed on inverse filled paths, so this is safe | 527 // we've already bailed on inverse filled paths, so this is safe |
535 SkASSERT(!args.fShape->isEmpty()); | 528 SkASSERT(!args.fShape->isEmpty()); |
536 | 529 SkASSERT(args.fShape->hasUnstyledKey()); |
537 if (!fAtlas) { | 530 if (!fAtlas) { |
538 fAtlas = args.fResourceProvider->createAtlas(kAlpha_8_GrPixelConfig, | 531 fAtlas = args.fResourceProvider->createAtlas(kAlpha_8_GrPixelConfig, |
539 ATLAS_TEXTURE_WIDTH, ATLAS_
TEXTURE_HEIGHT, | 532 ATLAS_TEXTURE_WIDTH, ATLAS_
TEXTURE_HEIGHT, |
540 NUM_PLOTS_X, NUM_PLOTS_Y, | 533 NUM_PLOTS_X, NUM_PLOTS_Y, |
541 &GrAADistanceFieldPathRende
rer::HandleEviction, | 534 &GrAADistanceFieldPathRende
rer::HandleEviction, |
542 (void*)this); | 535 (void*)this); |
543 if (!fAtlas) { | 536 if (!fAtlas) { |
544 return false; | 537 return false; |
545 } | 538 } |
546 } | 539 } |
547 | 540 |
548 const GrStyle& style = args.fShape->style(); | 541 AADistanceFieldPathBatch::Geometry geometry; |
549 // It's ok to ignore style's path effect because canDrawPath filtered out pa
th effects. | 542 geometry.fShape = *args.fShape; |
550 AADistanceFieldPathBatch::Geometry geometry(style.strokeRec()); | |
551 args.fShape->asPath(&geometry.fPath); | |
552 // Note: this is the generation ID of the _original_ path. When a new path i
s | |
553 // generated due to stroking it is important that the original path's id is
used | |
554 // for caching. | |
555 geometry.fGenID = geometry.fPath.getGenerationID(); | |
556 if (!style.isSimpleFill()) { | |
557 style.strokeRec().applyToPath(&geometry.fPath, geometry.fPath); | |
558 } | |
559 geometry.fColor = args.fColor; | 543 geometry.fColor = args.fColor; |
560 geometry.fAntiAlias = args.fAntiAlias; | 544 geometry.fAntiAlias = args.fAntiAlias; |
561 | 545 |
562 SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry, | 546 SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry, |
563 *args.fView
Matrix, fAtlas, | 547 *args.fView
Matrix, fAtlas, |
564 &fPathCache
, &fPathList, | 548 &fShapeCach
e, &fShapeList, |
565 args.fGamma
Correct)); | 549 args.fGamma
Correct)); |
566 | 550 |
567 GrPipelineBuilder pipelineBuilder(*args.fPaint); | 551 GrPipelineBuilder pipelineBuilder(*args.fPaint); |
568 pipelineBuilder.setUserStencil(args.fUserStencilSettings); | 552 pipelineBuilder.setUserStencil(args.fUserStencilSettings); |
569 | 553 |
570 args.fDrawContext->drawBatch(pipelineBuilder, *args.fClip, batch); | 554 args.fDrawContext->drawBatch(pipelineBuilder, *args.fClip, batch); |
571 | 555 |
572 return true; | 556 return true; |
573 } | 557 } |
574 | 558 |
575 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 559 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
576 | 560 |
577 #ifdef GR_TEST_UTILS | 561 #ifdef GR_TEST_UTILS |
578 | 562 |
579 struct PathTestStruct { | 563 struct PathTestStruct { |
580 typedef GrAADistanceFieldPathRenderer::PathCache PathCache; | 564 typedef GrAADistanceFieldPathRenderer::ShapeCache ShapeCache; |
581 typedef GrAADistanceFieldPathRenderer::PathData PathData; | 565 typedef GrAADistanceFieldPathRenderer::ShapeData ShapeData; |
582 typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList; | 566 typedef GrAADistanceFieldPathRenderer::ShapeDataList ShapeDataList; |
583 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {} | 567 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {} |
584 ~PathTestStruct() { this->reset(); } | 568 ~PathTestStruct() { this->reset(); } |
585 | 569 |
586 void reset() { | 570 void reset() { |
587 PathDataList::Iter iter; | 571 ShapeDataList::Iter iter; |
588 iter.init(fPathList, PathDataList::Iter::kHead_IterStart); | 572 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart); |
589 PathData* pathData; | 573 ShapeData* shapeData; |
590 while ((pathData = iter.get())) { | 574 while ((shapeData = iter.get())) { |
591 iter.next(); | 575 iter.next(); |
592 fPathList.remove(pathData); | 576 fShapeList.remove(shapeData); |
593 delete pathData; | 577 delete shapeData; |
594 } | 578 } |
595 delete fAtlas; | 579 delete fAtlas; |
596 fPathCache.reset(); | 580 fShapeCache.reset(); |
597 } | 581 } |
598 | 582 |
599 static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) { | 583 static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) { |
600 PathTestStruct* dfpr = (PathTestStruct*)pr; | 584 PathTestStruct* dfpr = (PathTestStruct*)pr; |
601 // remove any paths that use this plot | 585 // remove any paths that use this plot |
602 PathDataList::Iter iter; | 586 ShapeDataList::Iter iter; |
603 iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart); | 587 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart); |
604 PathData* pathData; | 588 ShapeData* shapeData; |
605 while ((pathData = iter.get())) { | 589 while ((shapeData = iter.get())) { |
606 iter.next(); | 590 iter.next(); |
607 if (id == pathData->fID) { | 591 if (id == shapeData->fID) { |
608 dfpr->fPathCache.remove(pathData->fKey); | 592 dfpr->fShapeCache.remove(shapeData->fKey); |
609 dfpr->fPathList.remove(pathData); | 593 dfpr->fShapeList.remove(shapeData); |
610 delete pathData; | 594 delete shapeData; |
611 } | 595 } |
612 } | 596 } |
613 } | 597 } |
614 | 598 |
615 uint32_t fContextID; | 599 uint32_t fContextID; |
616 GrBatchAtlas* fAtlas; | 600 GrBatchAtlas* fAtlas; |
617 PathCache fPathCache; | 601 ShapeCache fShapeCache; |
618 PathDataList fPathList; | 602 ShapeDataList fShapeList; |
619 }; | 603 }; |
620 | 604 |
621 DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) { | 605 DRAW_BATCH_TEST_DEFINE(AADistanceFieldPathBatch) { |
622 static PathTestStruct gTestStruct; | 606 static PathTestStruct gTestStruct; |
623 | 607 |
624 if (context->uniqueID() != gTestStruct.fContextID) { | 608 if (context->uniqueID() != gTestStruct.fContextID) { |
625 gTestStruct.fContextID = context->uniqueID(); | 609 gTestStruct.fContextID = context->uniqueID(); |
626 gTestStruct.reset(); | 610 gTestStruct.reset(); |
627 gTestStruct.fAtlas = | 611 gTestStruct.fAtlas = |
628 context->resourceProvider()->createAtlas(kAlpha_8_GrPixelConfig, | 612 context->resourceProvider()->createAtlas(kAlpha_8_GrPixelConfig, |
629 ATLAS_TEXTURE_WIDTH, ATLAS_
TEXTURE_HEIGHT, | 613 ATLAS_TEXTURE_WIDTH, ATLAS_
TEXTURE_HEIGHT, |
630 NUM_PLOTS_X, NUM_PLOTS_Y, | 614 NUM_PLOTS_X, NUM_PLOTS_Y, |
631 &PathTestStruct::HandleEvic
tion, | 615 &PathTestStruct::HandleEvic
tion, |
632 (void*)&gTestStruct); | 616 (void*)&gTestStruct); |
633 } | 617 } |
634 | 618 |
635 SkMatrix viewMatrix = GrTest::TestMatrix(random); | 619 SkMatrix viewMatrix = GrTest::TestMatrix(random); |
636 GrColor color = GrRandomColor(random); | 620 GrColor color = GrRandomColor(random); |
637 bool gammaCorrect = random->nextBool(); | 621 bool gammaCorrect = random->nextBool(); |
638 | 622 |
639 AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random)); | 623 AADistanceFieldPathBatch::Geometry geometry; |
| 624 // This path renderer only allows fill styles. |
| 625 GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill()); |
| 626 geometry.fShape = shape; |
640 geometry.fColor = color; | 627 geometry.fColor = color; |
641 geometry.fPath = GrTest::TestPath(random); | |
642 geometry.fAntiAlias = random->nextBool(); | 628 geometry.fAntiAlias = random->nextBool(); |
643 geometry.fGenID = random->nextU(); | |
644 | 629 |
645 return AADistanceFieldPathBatch::Create(geometry, viewMatrix, | 630 return AADistanceFieldPathBatch::Create(geometry, viewMatrix, |
646 gTestStruct.fAtlas, | 631 gTestStruct.fAtlas, |
647 &gTestStruct.fPathCache, | 632 &gTestStruct.fShapeCache, |
648 &gTestStruct.fPathList, | 633 &gTestStruct.fShapeList, |
649 gammaCorrect); | 634 gammaCorrect); |
650 } | 635 } |
651 | 636 |
652 #endif | 637 #endif |
OLD | NEW |