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