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