• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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