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