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