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