1 /*
2 * Copyright 2014 Google Inc.
3 * Copyright 2017 ARM Ltd.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9 #include "GrSmallPathRenderer.h"
10
11 #include "GrBuffer.h"
12 #include "GrContext.h"
13 #include "GrDistanceFieldGenFromVector.h"
14 #include "GrDrawOpTest.h"
15 #include "GrOpFlushState.h"
16 #include "GrPipelineBuilder.h"
17 #include "GrResourceProvider.h"
18 #include "GrSWMaskHelper.h"
19 #include "GrSurfacePriv.h"
20 #include "GrSurfaceProxyPriv.h"
21 #include "GrTexturePriv.h"
22 #include "effects/GrBitmapTextGeoProc.h"
23 #include "effects/GrDistanceFieldGeoProc.h"
24 #include "ops/GrMeshDrawOp.h"
25
26 #include "SkAutoMalloc.h"
27 #include "SkDistanceFieldGen.h"
28 #include "SkPathOps.h"
29
30 #define ATLAS_TEXTURE_WIDTH 2048
31 #define ATLAS_TEXTURE_HEIGHT 2048
32 #define PLOT_WIDTH 512
33 #define PLOT_HEIGHT 256
34
35 #define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
36 #define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
37
38 #ifdef DF_PATH_TRACKING
39 static int g_NumCachedShapes = 0;
40 static int g_NumFreedShapes = 0;
41 #endif
42
43 // mip levels
44 static const SkScalar kIdealMinMIP = 12;
45 static const SkScalar kMaxMIP = 162;
46
47 static const SkScalar kMaxDim = 73;
48 static const SkScalar kMinSize = SK_ScalarHalf;
49 static const SkScalar kMaxSize = 2*kMaxMIP;
50
51 // Callback to clear out internal path cache when eviction occurs
HandleEviction(GrDrawOpAtlas::AtlasID id,void * pr)52 void GrSmallPathRenderer::HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
53 GrSmallPathRenderer* dfpr = (GrSmallPathRenderer*)pr;
54 // remove any paths that use this plot
55 ShapeDataList::Iter iter;
56 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
57 ShapeData* shapeData;
58 while ((shapeData = iter.get())) {
59 iter.next();
60 if (id == shapeData->fID) {
61 dfpr->fShapeCache.remove(shapeData->fKey);
62 dfpr->fShapeList.remove(shapeData);
63 delete shapeData;
64 #ifdef DF_PATH_TRACKING
65 ++g_NumFreedPaths;
66 #endif
67 }
68 }
69 }
70
71 ////////////////////////////////////////////////////////////////////////////////
GrSmallPathRenderer()72 GrSmallPathRenderer::GrSmallPathRenderer() : fAtlas(nullptr) {}
73
~GrSmallPathRenderer()74 GrSmallPathRenderer::~GrSmallPathRenderer() {
75 ShapeDataList::Iter iter;
76 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
77 ShapeData* shapeData;
78 while ((shapeData = iter.get())) {
79 iter.next();
80 delete shapeData;
81 }
82
83 #ifdef DF_PATH_TRACKING
84 SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFreedShapes);
85 #endif
86 }
87
88 ////////////////////////////////////////////////////////////////////////////////
onCanDrawPath(const CanDrawPathArgs & args) const89 bool GrSmallPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
90 if (!args.fShaderCaps->shaderDerivativeSupport()) {
91 return false;
92 }
93 // If the shape has no key then we won't get any reuse.
94 if (!args.fShape->hasUnstyledKey()) {
95 return false;
96 }
97 // This only supports filled paths, however, the caller may apply the style to make a filled
98 // path and try again.
99 if (!args.fShape->style().isSimpleFill()) {
100 return false;
101 }
102 // This does non-inverse coverage-based antialiased fills.
103 if (GrAAType::kCoverage != args.fAAType) {
104 return false;
105 }
106 // TODO: Support inverse fill
107 if (args.fShape->inverseFilled()) {
108 return false;
109 }
110 // currently don't support perspective
111 if (args.fViewMatrix->hasPerspective()) {
112 return false;
113 }
114
115 // Only support paths with bounds within kMaxDim by kMaxDim,
116 // scaled to have bounds within kMaxSize by kMaxSize.
117 // The goal is to accelerate rendering of lots of small paths that may be scaling.
118 SkScalar scaleFactors[2];
119 if (!args.fViewMatrix->getMinMaxScales(scaleFactors)) {
120 return false;
121 }
122 SkRect bounds = args.fShape->styledBounds();
123 SkScalar minDim = SkMinScalar(bounds.width(), bounds.height());
124 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
125 SkScalar minSize = minDim * SkScalarAbs(scaleFactors[0]);
126 SkScalar maxSize = maxDim * SkScalarAbs(scaleFactors[1]);
127
128 return maxDim <= kMaxDim && kMinSize <= minSize && maxSize <= kMaxSize;
129 }
130
131 ////////////////////////////////////////////////////////////////////////////////
132
133 // padding around path bounds to allow for antialiased pixels
134 static const SkScalar kAntiAliasPad = 1.0f;
135
136 class SmallPathOp final : public GrMeshDrawOp {
137 public:
138 DEFINE_OP_CLASS_ID
139
140 using ShapeData = GrSmallPathRenderer::ShapeData;
141 using ShapeCache = SkTDynamicHash<ShapeData, ShapeData::Key>;
142 using ShapeDataList = GrSmallPathRenderer::ShapeDataList;
143
Make(GrColor color,const GrShape & shape,const SkMatrix & viewMatrix,GrDrawOpAtlas * atlas,ShapeCache * shapeCache,ShapeDataList * shapeList,bool gammaCorrect)144 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const GrShape& shape,
145 const SkMatrix& viewMatrix, GrDrawOpAtlas* atlas,
146 ShapeCache* shapeCache, ShapeDataList* shapeList,
147 bool gammaCorrect) {
148 return std::unique_ptr<GrMeshDrawOp>(new SmallPathOp(color, shape, viewMatrix, atlas,
149 shapeCache, shapeList, gammaCorrect));
150 }
151
name() const152 const char* name() const override { return "SmallPathOp"; }
153
dumpInfo() const154 SkString dumpInfo() const override {
155 SkString string;
156 for (const auto& geo : fShapes) {
157 string.appendf("Color: 0x%08x\n", geo.fColor);
158 }
159 string.append(DumpPipelineInfo(*this->pipeline()));
160 string.append(INHERITED::dumpInfo());
161 return string;
162 }
163
164 private:
SmallPathOp(GrColor color,const GrShape & shape,const SkMatrix & viewMatrix,GrDrawOpAtlas * atlas,ShapeCache * shapeCache,ShapeDataList * shapeList,bool gammaCorrect)165 SmallPathOp(GrColor color, const GrShape& shape, const SkMatrix& viewMatrix,
166 GrDrawOpAtlas* atlas, ShapeCache* shapeCache, ShapeDataList* shapeList,
167 bool gammaCorrect)
168 : INHERITED(ClassID()) {
169 SkASSERT(shape.hasUnstyledKey());
170 // Compute bounds
171 this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
172
173 #if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
174 fUsesDistanceField = true;
175 #else
176 // only use distance fields on desktop and Android framework to save space in the atlas
177 fUsesDistanceField = this->bounds().width() > kMaxMIP || this->bounds().height() > kMaxMIP;
178 #endif
179 fViewMatrix = viewMatrix;
180 SkVector translate = SkVector::Make(0, 0);
181 if (!fUsesDistanceField) {
182 // In this case we don't apply a view matrix, so we need to remove the non-subpixel
183 // translation and add it back when we generate the quad for the path
184 SkScalar translateX = viewMatrix.getTranslateX();
185 SkScalar translateY = viewMatrix.getTranslateY();
186 translate = SkVector::Make(SkScalarFloorToScalar(translateX),
187 SkScalarFloorToScalar(translateY));
188 // Only store the fractional part of the translation in the view matrix
189 fViewMatrix.setTranslateX(translateX - translate.fX);
190 fViewMatrix.setTranslateY(translateY - translate.fY);
191 }
192
193 fShapes.emplace_back(Entry{color, shape, translate});
194
195 fAtlas = atlas;
196 fShapeCache = shapeCache;
197 fShapeList = shapeList;
198 fGammaCorrect = gammaCorrect;
199
200 }
201
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const202 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
203 GrPipelineAnalysisCoverage* coverage) const override {
204 color->setToConstant(fShapes[0].fColor);
205 *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
206 }
207
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)208 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
209 optimizations.getOverrideColorIfSet(&fShapes[0].fColor);
210 fUsesLocalCoords = optimizations.readsLocalCoords();
211 }
212
213 struct FlushInfo {
214 sk_sp<const GrBuffer> fVertexBuffer;
215 sk_sp<const GrBuffer> fIndexBuffer;
216 sk_sp<GrGeometryProcessor> fGeometryProcessor;
217 int fVertexOffset;
218 int fInstancesToFlush;
219 };
220
onPrepareDraws(Target * target) const221 void onPrepareDraws(Target* target) const override {
222 int instanceCount = fShapes.count();
223
224 const SkMatrix& ctm = this->viewMatrix();
225
226 FlushInfo flushInfo;
227
228 // Setup GrGeometryProcessor
229 GrDrawOpAtlas* atlas = fAtlas;
230 if (fUsesDistanceField) {
231 GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
232
233 uint32_t flags = 0;
234 flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
235 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
236 flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
237
238 flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
239 atlas->context()->resourceProvider(),
240 this->color(), this->viewMatrix(), atlas->getProxy(), params, flags,
241 this->usesLocalCoords());
242 } else {
243 GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
244
245 SkMatrix invert;
246 if (this->usesLocalCoords()) {
247 if (!this->viewMatrix().invert(&invert)) {
248 SkDebugf("Could not invert viewmatrix\n");
249 return;
250 }
251 // for local coords, we need to add the translation back in that we removed
252 // from the stored view matrix
253 invert.preTranslate(-fShapes[0].fTranslate.fX, -fShapes[0].fTranslate.fY);
254 }
255
256 flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
257 atlas->context()->resourceProvider(),
258 this->color(), atlas->getProxy(), params, kA8_GrMaskFormat, invert,
259 this->usesLocalCoords());
260 }
261
262 // allocate vertices
263 size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
264 SkASSERT(vertexStride == sizeof(SkPoint) + sizeof(GrColor) + 2*sizeof(uint16_t));
265
266 const GrBuffer* vertexBuffer;
267 void* vertices = target->makeVertexSpace(vertexStride,
268 kVerticesPerQuad * instanceCount,
269 &vertexBuffer,
270 &flushInfo.fVertexOffset);
271 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
272 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
273 if (!vertices || !flushInfo.fIndexBuffer) {
274 SkDebugf("Could not allocate vertices\n");
275 return;
276 }
277
278 flushInfo.fInstancesToFlush = 0;
279 // Pointer to the next set of vertices to write.
280 intptr_t offset = reinterpret_cast<intptr_t>(vertices);
281 for (int i = 0; i < instanceCount; i++) {
282 const Entry& args = fShapes[i];
283
284 ShapeData* shapeData;
285 SkScalar maxScale;
286 if (fUsesDistanceField) {
287 // get mip level
288 maxScale = SkScalarAbs(this->viewMatrix().getMaxScale());
289 const SkRect& bounds = args.fShape.bounds();
290 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
291 // We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
292 // In the majority of cases this will yield a crisper rendering.
293 SkScalar mipScale = 1.0f;
294 // Our mipscale is the maxScale clamped to the next highest power of 2
295 if (maxScale <= SK_ScalarHalf) {
296 SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
297 mipScale = SkScalarPow(2, -log);
298 } else if (maxScale > SK_Scalar1) {
299 SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
300 mipScale = SkScalarPow(2, log);
301 }
302 SkASSERT(maxScale <= mipScale);
303
304 SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
305 // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
306 // so we can preserve as much detail as possible. However, we can't scale down more
307 // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
308 // just bigger than the ideal, and then scale down until we are no more than 4x the
309 // original mipsize.
310 if (mipSize < kIdealMinMIP) {
311 SkScalar newMipSize = mipSize;
312 do {
313 newMipSize *= 2;
314 } while (newMipSize < kIdealMinMIP);
315 while (newMipSize > 4 * mipSize) {
316 newMipSize *= 0.25f;
317 }
318 mipSize = newMipSize;
319 }
320 SkScalar desiredDimension = SkTMin(mipSize, kMaxMIP);
321
322 // check to see if df path is cached
323 ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
324 shapeData = fShapeCache->find(key);
325 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
326 // Remove the stale cache entry
327 if (shapeData) {
328 fShapeCache->remove(shapeData->fKey);
329 fShapeList->remove(shapeData);
330 delete shapeData;
331 }
332 SkScalar scale = desiredDimension / maxDim;
333
334 shapeData = new ShapeData;
335 if (!this->addDFPathToAtlas(target,
336 &flushInfo,
337 atlas,
338 shapeData,
339 args.fShape,
340 SkScalarCeilToInt(desiredDimension),
341 scale)) {
342 delete shapeData;
343 SkDebugf("Can't rasterize path\n");
344 continue;
345 }
346 }
347 } else {
348 // check to see if bitmap path is cached
349 ShapeData::Key key(args.fShape, this->viewMatrix());
350 shapeData = fShapeCache->find(key);
351 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
352 // Remove the stale cache entry
353 if (shapeData) {
354 fShapeCache->remove(shapeData->fKey);
355 fShapeList->remove(shapeData);
356 delete shapeData;
357 }
358
359 shapeData = new ShapeData;
360 if (!this->addBMPathToAtlas(target,
361 &flushInfo,
362 atlas,
363 shapeData,
364 args.fShape,
365 this->viewMatrix())) {
366 delete shapeData;
367 SkDebugf("Can't rasterize path\n");
368 continue;
369 }
370 }
371 maxScale = 1;
372 }
373
374 atlas->setLastUseToken(shapeData->fID, target->nextDrawToken());
375
376 this->writePathVertices(target,
377 atlas,
378 offset,
379 args.fColor,
380 vertexStride,
381 maxScale,
382 args.fTranslate,
383 shapeData);
384 offset += kVerticesPerQuad * vertexStride;
385 flushInfo.fInstancesToFlush++;
386 }
387
388 this->flush(target, &flushInfo);
389 }
390
addDFPathToAtlas(GrMeshDrawOp::Target * target,FlushInfo * flushInfo,GrDrawOpAtlas * atlas,ShapeData * shapeData,const GrShape & shape,uint32_t dimension,SkScalar scale) const391 bool addDFPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo, GrDrawOpAtlas* atlas,
392 ShapeData* shapeData, const GrShape& shape, uint32_t dimension,
393 SkScalar scale) const {
394 const SkRect& bounds = shape.bounds();
395
396 // generate bounding rect for bitmap draw
397 SkRect scaledBounds = bounds;
398 // scale to mip level size
399 scaledBounds.fLeft *= scale;
400 scaledBounds.fTop *= scale;
401 scaledBounds.fRight *= scale;
402 scaledBounds.fBottom *= scale;
403 // subtract out integer portion of origin
404 // (SDF created will be placed with fractional offset burnt in)
405 SkScalar dx = SkScalarFloorToScalar(scaledBounds.fLeft);
406 SkScalar dy = SkScalarFloorToScalar(scaledBounds.fTop);
407 scaledBounds.offset(-dx, -dy);
408 // get integer boundary
409 SkIRect devPathBounds;
410 scaledBounds.roundOut(&devPathBounds);
411 // pad to allow room for antialiasing
412 const int intPad = SkScalarCeilToInt(kAntiAliasPad);
413 // place devBounds at origin
414 int width = devPathBounds.width() + 2*intPad;
415 int height = devPathBounds.height() + 2*intPad;
416 devPathBounds = SkIRect::MakeWH(width, height);
417
418 // draw path to bitmap
419 SkMatrix drawMatrix;
420 drawMatrix.setScale(scale, scale);
421 drawMatrix.postTranslate(intPad - dx, intPad - dy);
422
423 SkASSERT(devPathBounds.fLeft == 0);
424 SkASSERT(devPathBounds.fTop == 0);
425 SkASSERT(devPathBounds.width() > 0);
426 SkASSERT(devPathBounds.height() > 0);
427
428 // setup signed distance field storage
429 SkIRect dfBounds = devPathBounds.makeOutset(SK_DistanceFieldPad, SK_DistanceFieldPad);
430 width = dfBounds.width();
431 height = dfBounds.height();
432 // TODO We should really generate this directly into the plot somehow
433 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
434
435 SkPath path;
436 shape.asPath(&path);
437 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
438 // Generate signed distance field directly from SkPath
439 bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dfStorage.get(),
440 path, drawMatrix,
441 width, height, width * sizeof(unsigned char));
442 if (!succeed) {
443 #endif
444 // setup bitmap backing
445 SkAutoPixmapStorage dst;
446 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
447 devPathBounds.height()))) {
448 return false;
449 }
450 sk_bzero(dst.writable_addr(), dst.getSafeSize());
451
452 // rasterize path
453 SkPaint paint;
454 paint.setStyle(SkPaint::kFill_Style);
455 paint.setAntiAlias(true);
456
457 SkDraw draw;
458 sk_bzero(&draw, sizeof(draw));
459
460 SkRasterClip rasterClip;
461 rasterClip.setRect(devPathBounds);
462 draw.fRC = &rasterClip;
463 draw.fMatrix = &drawMatrix;
464 draw.fDst = dst;
465
466 draw.drawPathCoverage(path, paint);
467
468 // Generate signed distance field
469 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
470 (const unsigned char*)dst.addr(),
471 dst.width(), dst.height(), dst.rowBytes());
472 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
473 }
474 #endif
475
476 // add to atlas
477 SkIPoint16 atlasLocation;
478 GrDrawOpAtlas::AtlasID id;
479 if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
480 this->flush(target, flushInfo);
481 if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
482 return false;
483 }
484 }
485
486 // add to cache
487 shapeData->fKey.set(shape, dimension);
488 shapeData->fID = id;
489
490 // set the bounds rect to the original bounds
491 shapeData->fBounds = bounds;
492
493 // set up path to texture coordinate transform
494 shapeData->fScale = scale;
495 dx -= SK_DistanceFieldPad + kAntiAliasPad;
496 dy -= SK_DistanceFieldPad + kAntiAliasPad;
497 shapeData->fTranslate.fX = atlasLocation.fX - dx;
498 shapeData->fTranslate.fY = atlasLocation.fY - dy;
499
500 fShapeCache->add(shapeData);
501 fShapeList->addToTail(shapeData);
502 #ifdef DF_PATH_TRACKING
503 ++g_NumCachedPaths;
504 #endif
505 return true;
506 }
507
addBMPathToAtlas(GrMeshDrawOp::Target * target,FlushInfo * flushInfo,GrDrawOpAtlas * atlas,ShapeData * shapeData,const GrShape & shape,const SkMatrix & ctm) const508 bool addBMPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo,
509 GrDrawOpAtlas* atlas, ShapeData* shapeData,
510 const GrShape& shape, const SkMatrix& ctm) const {
511 const SkRect& bounds = shape.bounds();
512 if (bounds.isEmpty()) {
513 return false;
514 }
515 SkMatrix drawMatrix(ctm);
516 drawMatrix.set(SkMatrix::kMTransX, SkScalarFraction(ctm.get(SkMatrix::kMTransX)));
517 drawMatrix.set(SkMatrix::kMTransY, SkScalarFraction(ctm.get(SkMatrix::kMTransY)));
518 SkRect shapeDevBounds;
519 drawMatrix.mapRect(&shapeDevBounds, bounds);
520 SkScalar dx = SkScalarFloorToScalar(shapeDevBounds.fLeft);
521 SkScalar dy = SkScalarFloorToScalar(shapeDevBounds.fTop);
522
523 // get integer boundary
524 SkIRect devPathBounds;
525 shapeDevBounds.roundOut(&devPathBounds);
526 // pad to allow room for antialiasing
527 const int intPad = SkScalarCeilToInt(kAntiAliasPad);
528 // place devBounds at origin
529 int width = devPathBounds.width() + 2 * intPad;
530 int height = devPathBounds.height() + 2 * intPad;
531 devPathBounds = SkIRect::MakeWH(width, height);
532 SkScalar translateX = intPad - dx;
533 SkScalar translateY = intPad - dy;
534
535 SkASSERT(devPathBounds.fLeft == 0);
536 SkASSERT(devPathBounds.fTop == 0);
537 SkASSERT(devPathBounds.width() > 0);
538 SkASSERT(devPathBounds.height() > 0);
539
540 SkPath path;
541 shape.asPath(&path);
542 // setup bitmap backing
543 SkAutoPixmapStorage dst;
544 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
545 devPathBounds.height()))) {
546 return false;
547 }
548 sk_bzero(dst.writable_addr(), dst.getSafeSize());
549
550 // rasterize path
551 SkPaint paint;
552 paint.setStyle(SkPaint::kFill_Style);
553 paint.setAntiAlias(true);
554
555 SkDraw draw;
556 sk_bzero(&draw, sizeof(draw));
557
558 SkRasterClip rasterClip;
559 rasterClip.setRect(devPathBounds);
560 draw.fRC = &rasterClip;
561 drawMatrix.postTranslate(translateX, translateY);
562 draw.fMatrix = &drawMatrix;
563 draw.fDst = dst;
564
565 draw.drawPathCoverage(path, paint);
566
567 // add to atlas
568 SkIPoint16 atlasLocation;
569 GrDrawOpAtlas::AtlasID id;
570 if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
571 &atlasLocation)) {
572 this->flush(target, flushInfo);
573 if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
574 &atlasLocation)) {
575 return false;
576 }
577 }
578
579 // add to cache
580 shapeData->fKey.set(shape, ctm);
581 shapeData->fID = id;
582
583 // set the bounds rect to the original bounds
584 shapeData->fBounds = SkRect::Make(devPathBounds);
585 shapeData->fBounds.offset(-translateX, -translateY);
586
587 // set up path to texture coordinate transform
588 shapeData->fScale = SK_Scalar1;
589 shapeData->fTranslate.fX = atlasLocation.fX + translateX;
590 shapeData->fTranslate.fY = atlasLocation.fY + translateY;
591
592 fShapeCache->add(shapeData);
593 fShapeList->addToTail(shapeData);
594 #ifdef DF_PATH_TRACKING
595 ++g_NumCachedPaths;
596 #endif
597 return true;
598 }
599
writePathVertices(GrDrawOp::Target * target,GrDrawOpAtlas * atlas,intptr_t offset,GrColor color,size_t vertexStride,SkScalar maxScale,const SkVector & preTranslate,const ShapeData * shapeData) const600 void writePathVertices(GrDrawOp::Target* target,
601 GrDrawOpAtlas* atlas,
602 intptr_t offset,
603 GrColor color,
604 size_t vertexStride,
605 SkScalar maxScale,
606 const SkVector& preTranslate,
607 const ShapeData* shapeData) const {
608 SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
609
610 SkRect bounds = shapeData->fBounds;
611 if (fUsesDistanceField) {
612 // outset bounds to include ~1 pixel of AA in device space
613 SkScalar outset = SkScalarInvert(maxScale);
614 bounds.outset(outset, outset);
615 }
616
617 // vertex positions
618 // TODO make the vertex attributes a struct
619 positions->setRectFan(bounds.left() + preTranslate.fX,
620 bounds.top() + preTranslate.fY,
621 bounds.right() + preTranslate.fX,
622 bounds.bottom() + preTranslate.fY,
623 vertexStride);
624
625 // colors
626 for (int i = 0; i < kVerticesPerQuad; i++) {
627 GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride);
628 *colorPtr = color;
629 }
630
631 // set up texture coordinates
632 SkScalar texLeft = bounds.fLeft;
633 SkScalar texTop = bounds.fTop;
634 SkScalar texRight = bounds.fRight;
635 SkScalar texBottom = bounds.fBottom;
636
637 // transform original path's bounds to texture space
638 SkScalar scale = shapeData->fScale;
639 const SkVector& translate = shapeData->fTranslate;
640 texLeft *= scale;
641 texTop *= scale;
642 texRight *= scale;
643 texBottom *= scale;
644 texLeft += translate.fX;
645 texTop += translate.fY;
646 texRight += translate.fX;
647 texBottom += translate.fY;
648
649 // convert texcoords to unsigned short format
650 sk_sp<GrTextureProxy> proxy = atlas->getProxy();
651
652 // The proxy must be functionally exact for this normalization to work correctly
653 SkASSERT(GrResourceProvider::IsFunctionallyExact(proxy.get()));
654 SkScalar uFactor = 65535.f / proxy->width();
655 SkScalar vFactor = 65535.f / proxy->height();
656 uint16_t l = (uint16_t)(texLeft*uFactor);
657 uint16_t t = (uint16_t)(texTop*vFactor);
658 uint16_t r = (uint16_t)(texRight*uFactor);
659 uint16_t b = (uint16_t)(texBottom*vFactor);
660
661 // set vertex texture coords
662 intptr_t textureCoordOffset = offset + sizeof(SkPoint) + sizeof(GrColor);
663 uint16_t* textureCoords = (uint16_t*) textureCoordOffset;
664 textureCoords[0] = l;
665 textureCoords[1] = t;
666 textureCoordOffset += vertexStride;
667 textureCoords = (uint16_t*)textureCoordOffset;
668 textureCoords[0] = l;
669 textureCoords[1] = b;
670 textureCoordOffset += vertexStride;
671 textureCoords = (uint16_t*)textureCoordOffset;
672 textureCoords[0] = r;
673 textureCoords[1] = b;
674 textureCoordOffset += vertexStride;
675 textureCoords = (uint16_t*)textureCoordOffset;
676 textureCoords[0] = r;
677 textureCoords[1] = t;
678 }
679
flush(GrMeshDrawOp::Target * target,FlushInfo * flushInfo) const680 void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
681 if (flushInfo->fInstancesToFlush) {
682 GrMesh mesh;
683 int maxInstancesPerDraw =
684 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
685 mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
686 flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerQuad,
687 kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
688 target->draw(flushInfo->fGeometryProcessor.get(), mesh);
689 flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
690 flushInfo->fInstancesToFlush = 0;
691 }
692 }
693
color() const694 GrColor color() const { return fShapes[0].fColor; }
viewMatrix() const695 const SkMatrix& viewMatrix() const { return fViewMatrix; }
usesLocalCoords() const696 bool usesLocalCoords() const { return fUsesLocalCoords; }
usesDistanceField() const697 bool usesDistanceField() const { return fUsesDistanceField; }
698
onCombineIfPossible(GrOp * t,const GrCaps & caps)699 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
700 SmallPathOp* that = t->cast<SmallPathOp>();
701 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
702 that->bounds(), caps)) {
703 return false;
704 }
705
706 if (this->usesDistanceField() != that->usesDistanceField()) {
707 return false;
708 }
709
710 // TODO We can position on the cpu for distance field paths
711 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
712 return false;
713 }
714
715 if (!this->usesDistanceField() && this->usesLocalCoords() &&
716 !this->fShapes[0].fTranslate.equalsWithinTolerance(that->fShapes[0].fTranslate)) {
717 return false;
718 }
719
720 fShapes.push_back_n(that->fShapes.count(), that->fShapes.begin());
721 this->joinBounds(*that);
722 return true;
723 }
724
725 SkMatrix fViewMatrix;
726 bool fUsesLocalCoords;
727 bool fUsesDistanceField;
728
729 struct Entry {
730 GrColor fColor;
731 GrShape fShape;
732 SkVector fTranslate;
733 };
734
735 SkSTArray<1, Entry> fShapes;
736 GrDrawOpAtlas* fAtlas;
737 ShapeCache* fShapeCache;
738 ShapeDataList* fShapeList;
739 bool fGammaCorrect;
740
741 typedef GrMeshDrawOp INHERITED;
742 };
743
onDrawPath(const DrawPathArgs & args)744 bool GrSmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
745 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
746 "GrSmallPathRenderer::onDrawPath");
747
748 // we've already bailed on inverse filled paths, so this is safe
749 SkASSERT(!args.fShape->isEmpty());
750 SkASSERT(args.fShape->hasUnstyledKey());
751 if (!fAtlas) {
752 fAtlas = GrDrawOpAtlas::Make(args.fContext,
753 kAlpha_8_GrPixelConfig,
754 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
755 NUM_PLOTS_X, NUM_PLOTS_Y,
756 &GrSmallPathRenderer::HandleEviction,
757 (void*)this);
758 if (!fAtlas) {
759 return false;
760 }
761 }
762
763 std::unique_ptr<GrMeshDrawOp> op = SmallPathOp::Make(
764 args.fPaint.getColor(), *args.fShape, *args.fViewMatrix, fAtlas.get(), &fShapeCache,
765 &fShapeList, args.fGammaCorrect);
766 GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
767 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
768
769 args.fRenderTargetContext->addMeshDrawOp(pipelineBuilder, *args.fClip, std::move(op));
770
771 return true;
772 }
773
774 ///////////////////////////////////////////////////////////////////////////////////////////////////
775
776 #if GR_TEST_UTILS
777
778 struct PathTestStruct {
779 typedef GrSmallPathRenderer::ShapeCache ShapeCache;
780 typedef GrSmallPathRenderer::ShapeData ShapeData;
781 typedef GrSmallPathRenderer::ShapeDataList ShapeDataList;
PathTestStructPathTestStruct782 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
~PathTestStructPathTestStruct783 ~PathTestStruct() { this->reset(); }
784
resetPathTestStruct785 void reset() {
786 ShapeDataList::Iter iter;
787 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
788 ShapeData* shapeData;
789 while ((shapeData = iter.get())) {
790 iter.next();
791 fShapeList.remove(shapeData);
792 delete shapeData;
793 }
794 fAtlas = nullptr;
795 fShapeCache.reset();
796 }
797
HandleEvictionPathTestStruct798 static void HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
799 PathTestStruct* dfpr = (PathTestStruct*)pr;
800 // remove any paths that use this plot
801 ShapeDataList::Iter iter;
802 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
803 ShapeData* shapeData;
804 while ((shapeData = iter.get())) {
805 iter.next();
806 if (id == shapeData->fID) {
807 dfpr->fShapeCache.remove(shapeData->fKey);
808 dfpr->fShapeList.remove(shapeData);
809 delete shapeData;
810 }
811 }
812 }
813
814 uint32_t fContextID;
815 std::unique_ptr<GrDrawOpAtlas> fAtlas;
816 ShapeCache fShapeCache;
817 ShapeDataList fShapeList;
818 };
819
DRAW_OP_TEST_DEFINE(SmallPathOp)820 DRAW_OP_TEST_DEFINE(SmallPathOp) {
821 static PathTestStruct gTestStruct;
822
823 if (context->uniqueID() != gTestStruct.fContextID) {
824 gTestStruct.fContextID = context->uniqueID();
825 gTestStruct.reset();
826 gTestStruct.fAtlas = GrDrawOpAtlas::Make(context, kAlpha_8_GrPixelConfig,
827 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
828 NUM_PLOTS_X, NUM_PLOTS_Y,
829 &PathTestStruct::HandleEviction,
830 (void*)&gTestStruct);
831 }
832
833 SkMatrix viewMatrix = GrTest::TestMatrix(random);
834 GrColor color = GrRandomColor(random);
835 bool gammaCorrect = random->nextBool();
836
837 // This path renderer only allows fill styles.
838 GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
839
840 return SmallPathOp::Make(color, shape, viewMatrix, gTestStruct.fAtlas.get(),
841 &gTestStruct.fShapeCache, &gTestStruct.fShapeList, gammaCorrect);
842 }
843
844 #endif
845