• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google Inc.
3  * Copyright 2017 ARM Ltd.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include "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, IsZeroArea::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             flushInfo.fFixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i].get();
330         }
331 
332         // Setup GrGeometryProcessor
333         const SkMatrix& ctm = fShapes[0].fViewMatrix;
334         if (fUsesDistanceField) {
335             uint32_t flags = 0;
336             // Still need to key off of ctm to pick the right shader for the transformed quad
337             flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
338             flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
339             flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
340 
341             const SkMatrix* matrix;
342             SkMatrix invert;
343             if (ctm.hasPerspective()) {
344                 matrix = &ctm;
345             } else if (fHelper.usesLocalCoords()) {
346                 if (!ctm.invert(&invert)) {
347                     return;
348                 }
349                 matrix = &invert;
350             } else {
351                 matrix = &SkMatrix::I();
352             }
353             flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
354                     *target->caps().shaderCaps(), *matrix, fWideColor, fAtlas->getProxies(),
355                     fAtlas->numActivePages(), GrSamplerState::ClampBilerp(), flags);
356         } else {
357             SkMatrix invert;
358             if (fHelper.usesLocalCoords()) {
359                 if (!ctm.invert(&invert)) {
360                     return;
361                 }
362             }
363 
364             flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
365                     *target->caps().shaderCaps(), this->color(), fWideColor, fAtlas->getProxies(),
366                     fAtlas->numActivePages(), GrSamplerState::ClampNearest(), kA8_GrMaskFormat,
367                     invert, false);
368         }
369 
370         // allocate vertices
371         const size_t kVertexStride = flushInfo.fGeometryProcessor->vertexStride();
372 
373         // We need to make sure we don't overflow a 32 bit int when we request space in the
374         // makeVertexSpace call below.
375         if (instanceCount > SK_MaxS32 / kVerticesPerQuad) {
376             return;
377         }
378         GrVertexWriter vertices{target->makeVertexSpace(kVertexStride,
379                                                         kVerticesPerQuad * instanceCount,
380                                                         &flushInfo.fVertexBuffer,
381                                                         &flushInfo.fVertexOffset)};
382         flushInfo.fIndexBuffer = target->resourceProvider()->refQuadIndexBuffer();
383         if (!vertices.fPtr || !flushInfo.fIndexBuffer) {
384             SkDebugf("Could not allocate vertices\n");
385             return;
386         }
387 
388         flushInfo.fInstancesToFlush = 0;
389         for (int i = 0; i < instanceCount; i++) {
390             const Entry& args = fShapes[i];
391 
392             ShapeData* shapeData;
393             if (fUsesDistanceField) {
394                 // get mip level
395                 SkScalar maxScale;
396                 const SkRect& bounds = args.fShape.bounds();
397                 if (args.fViewMatrix.hasPerspective()) {
398                     // approximate the scale since we can't get it from the matrix
399                     SkRect xformedBounds;
400                     args.fViewMatrix.mapRect(&xformedBounds, bounds);
401                     maxScale = SkScalarAbs(SkTMax(xformedBounds.width() / bounds.width(),
402                                                   xformedBounds.height() / bounds.height()));
403                 } else {
404                     maxScale = SkScalarAbs(args.fViewMatrix.getMaxScale());
405                 }
406                 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
407                 // We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
408                 // In the majority of cases this will yield a crisper rendering.
409                 SkScalar mipScale = 1.0f;
410                 // Our mipscale is the maxScale clamped to the next highest power of 2
411                 if (maxScale <= SK_ScalarHalf) {
412                     SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
413                     mipScale = SkScalarPow(2, -log);
414                 } else if (maxScale > SK_Scalar1) {
415                     SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
416                     mipScale = SkScalarPow(2, log);
417                 }
418                 SkASSERT(maxScale <= mipScale);
419 
420                 SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
421                 // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
422                 // so we can preserve as much detail as possible. However, we can't scale down more
423                 // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
424                 // just bigger than the ideal, and then scale down until we are no more than 4x the
425                 // original mipsize.
426                 if (mipSize < kIdealMinMIP) {
427                     SkScalar newMipSize = mipSize;
428                     do {
429                         newMipSize *= 2;
430                     } while (newMipSize < kIdealMinMIP);
431                     while (newMipSize > 4 * mipSize) {
432                         newMipSize *= 0.25f;
433                     }
434                     mipSize = newMipSize;
435                 }
436                 SkScalar desiredDimension = SkTMin(mipSize, kMaxMIP);
437 
438                 // check to see if df path is cached
439                 ShapeDataKey key(args.fShape, SkScalarCeilToInt(desiredDimension));
440                 shapeData = fShapeCache->find(key);
441                 if (nullptr == shapeData || !fAtlas->hasID(shapeData->fID)) {
442                     // Remove the stale cache entry
443                     if (shapeData) {
444                         fShapeCache->remove(shapeData->fKey);
445                         fShapeList->remove(shapeData);
446                         delete shapeData;
447                     }
448                     SkScalar scale = desiredDimension / maxDim;
449 
450                     shapeData = new ShapeData;
451                     if (!this->addDFPathToAtlas(target,
452                                                 &flushInfo,
453                                                 fAtlas,
454                                                 shapeData,
455                                                 args.fShape,
456                                                 SkScalarCeilToInt(desiredDimension),
457                                                 scale)) {
458                         delete shapeData;
459                         continue;
460                     }
461                 }
462             } else {
463                 // check to see if bitmap path is cached
464                 ShapeDataKey key(args.fShape, args.fViewMatrix);
465                 shapeData = fShapeCache->find(key);
466                 if (nullptr == shapeData || !fAtlas->hasID(shapeData->fID)) {
467                     // Remove the stale cache entry
468                     if (shapeData) {
469                         fShapeCache->remove(shapeData->fKey);
470                         fShapeList->remove(shapeData);
471                         delete shapeData;
472                     }
473 
474                     shapeData = new ShapeData;
475                     if (!this->addBMPathToAtlas(target,
476                                                 &flushInfo,
477                                                 fAtlas,
478                                                 shapeData,
479                                                 args.fShape,
480                                                 args.fViewMatrix)) {
481                         delete shapeData;
482                         continue;
483                     }
484                 }
485             }
486 
487             auto uploadTarget = target->deferredUploadTarget();
488             fAtlas->setLastUseToken(shapeData->fID, uploadTarget->tokenTracker()->nextDrawToken());
489 
490             this->writePathVertices(fAtlas, vertices, GrVertexColor(args.fColor, fWideColor),
491                                     args.fViewMatrix, shapeData);
492             flushInfo.fInstancesToFlush++;
493         }
494 
495         this->flush(target, &flushInfo);
496     }
497 
addToAtlas(GrMeshDrawOp::Target * target,FlushInfo * flushInfo,GrDrawOpAtlas * atlas,int width,int height,const void * image,GrDrawOpAtlas::AtlasID * id,SkIPoint16 * atlasLocation) const498     bool addToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo, GrDrawOpAtlas* atlas,
499                     int width, int height, const void* image,
500                     GrDrawOpAtlas::AtlasID* id, SkIPoint16* atlasLocation) const {
501         auto resourceProvider = target->resourceProvider();
502         auto uploadTarget = target->deferredUploadTarget();
503 
504         GrDrawOpAtlas::ErrorCode code = atlas->addToAtlas(resourceProvider, id,
505                                                           uploadTarget, width, height,
506                                                           image, atlasLocation);
507         if (GrDrawOpAtlas::ErrorCode::kError == code) {
508             return false;
509         }
510 
511         if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
512             this->flush(target, flushInfo);
513 
514             code = atlas->addToAtlas(resourceProvider, id, uploadTarget, width, height,
515                                      image, atlasLocation);
516         }
517 
518         return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
519     }
520 
addDFPathToAtlas(GrMeshDrawOp::Target * target,FlushInfo * flushInfo,GrDrawOpAtlas * atlas,ShapeData * shapeData,const GrShape & shape,uint32_t dimension,SkScalar scale) const521     bool addDFPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo,
522                           GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
523                           uint32_t dimension, SkScalar scale) const {
524 
525         const SkRect& bounds = shape.bounds();
526 
527         // generate bounding rect for bitmap draw
528         SkRect scaledBounds = bounds;
529         // scale to mip level size
530         scaledBounds.fLeft *= scale;
531         scaledBounds.fTop *= scale;
532         scaledBounds.fRight *= scale;
533         scaledBounds.fBottom *= scale;
534         // subtract out integer portion of origin
535         // (SDF created will be placed with fractional offset burnt in)
536         SkScalar dx = SkScalarFloorToScalar(scaledBounds.fLeft);
537         SkScalar dy = SkScalarFloorToScalar(scaledBounds.fTop);
538         scaledBounds.offset(-dx, -dy);
539         // get integer boundary
540         SkIRect devPathBounds;
541         scaledBounds.roundOut(&devPathBounds);
542         // pad to allow room for antialiasing
543         const int intPad = SkScalarCeilToInt(kAntiAliasPad);
544         // place devBounds at origin
545         int width = devPathBounds.width() + 2*intPad;
546         int height = devPathBounds.height() + 2*intPad;
547         devPathBounds = SkIRect::MakeWH(width, height);
548         SkScalar translateX = intPad - dx;
549         SkScalar translateY = intPad - dy;
550 
551         // draw path to bitmap
552         SkMatrix drawMatrix;
553         drawMatrix.setScale(scale, scale);
554         drawMatrix.postTranslate(translateX, translateY);
555 
556         SkASSERT(devPathBounds.fLeft == 0);
557         SkASSERT(devPathBounds.fTop == 0);
558         SkASSERT(devPathBounds.width() > 0);
559         SkASSERT(devPathBounds.height() > 0);
560 
561         // setup signed distance field storage
562         SkIRect dfBounds = devPathBounds.makeOutset(SK_DistanceFieldPad, SK_DistanceFieldPad);
563         width = dfBounds.width();
564         height = dfBounds.height();
565         // TODO We should really generate this directly into the plot somehow
566         SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
567 
568         SkPath path;
569         shape.asPath(&path);
570 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
571         // Generate signed distance field directly from SkPath
572         bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dfStorage.get(),
573                                         path, drawMatrix,
574                                         width, height, width * sizeof(unsigned char));
575         if (!succeed) {
576 #endif
577             // setup bitmap backing
578             SkAutoPixmapStorage dst;
579             if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
580                                                   devPathBounds.height()))) {
581                 return false;
582             }
583             sk_bzero(dst.writable_addr(), dst.computeByteSize());
584 
585             // rasterize path
586             SkPaint paint;
587             paint.setStyle(SkPaint::kFill_Style);
588             paint.setAntiAlias(true);
589 
590             SkDraw draw;
591 
592             SkRasterClip rasterClip;
593             rasterClip.setRect(devPathBounds);
594             draw.fRC = &rasterClip;
595             draw.fMatrix = &drawMatrix;
596             draw.fDst = dst;
597 
598             draw.drawPathCoverage(path, paint);
599 
600             // Generate signed distance field
601             SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
602                                                (const unsigned char*)dst.addr(),
603                                                dst.width(), dst.height(), dst.rowBytes());
604 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
605         }
606 #endif
607 
608         // add to atlas
609         SkIPoint16 atlasLocation;
610         GrDrawOpAtlas::AtlasID id;
611 
612         if (!this->addToAtlas(target, flushInfo, atlas,
613                               width, height, dfStorage.get(), &id, &atlasLocation)) {
614             return false;
615         }
616 
617         // add to cache
618         shapeData->fKey.set(shape, dimension);
619         shapeData->fID = id;
620 
621         shapeData->fBounds = SkRect::Make(devPathBounds);
622         shapeData->fBounds.offset(-translateX, -translateY);
623         shapeData->fBounds.fLeft /= scale;
624         shapeData->fBounds.fTop /= scale;
625         shapeData->fBounds.fRight /= scale;
626         shapeData->fBounds.fBottom /= scale;
627 
628         // We pack the 2bit page index in the low bit of the u and v texture coords
629         uint16_t pageIndex = GrDrawOpAtlas::GetPageIndexFromID(id);
630         SkASSERT(pageIndex < 4);
631         uint16_t uBit = (pageIndex >> 1) & 0x1;
632         uint16_t vBit = pageIndex & 0x1;
633         shapeData->fTextureCoords.set((atlasLocation.fX+SK_DistanceFieldPad) << 1 | uBit,
634                                       (atlasLocation.fY+SK_DistanceFieldPad) << 1 | vBit,
635                                       (atlasLocation.fX+SK_DistanceFieldPad+
636                                        devPathBounds.width()) << 1 | uBit,
637                                       (atlasLocation.fY+SK_DistanceFieldPad+
638                                        devPathBounds.height()) << 1 | vBit);
639 
640         fShapeCache->add(shapeData);
641         fShapeList->addToTail(shapeData);
642 #ifdef DF_PATH_TRACKING
643         ++g_NumCachedPaths;
644 #endif
645         return true;
646     }
647 
addBMPathToAtlas(GrMeshDrawOp::Target * target,FlushInfo * flushInfo,GrDrawOpAtlas * atlas,ShapeData * shapeData,const GrShape & shape,const SkMatrix & ctm) const648     bool addBMPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo,
649                           GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
650                           const SkMatrix& ctm) const {
651         const SkRect& bounds = shape.bounds();
652         if (bounds.isEmpty()) {
653             return false;
654         }
655         SkMatrix drawMatrix(ctm);
656         SkScalar tx = ctm.getTranslateX();
657         SkScalar ty = ctm.getTranslateY();
658         tx -= SkScalarFloorToScalar(tx);
659         ty -= SkScalarFloorToScalar(ty);
660         drawMatrix.set(SkMatrix::kMTransX, tx);
661         drawMatrix.set(SkMatrix::kMTransY, ty);
662         SkRect shapeDevBounds;
663         drawMatrix.mapRect(&shapeDevBounds, bounds);
664         SkScalar dx = SkScalarFloorToScalar(shapeDevBounds.fLeft);
665         SkScalar dy = SkScalarFloorToScalar(shapeDevBounds.fTop);
666 
667         // get integer boundary
668         SkIRect devPathBounds;
669         shapeDevBounds.roundOut(&devPathBounds);
670         // pad to allow room for antialiasing
671         const int intPad = SkScalarCeilToInt(kAntiAliasPad);
672         // place devBounds at origin
673         int width = devPathBounds.width() + 2 * intPad;
674         int height = devPathBounds.height() + 2 * intPad;
675         devPathBounds = SkIRect::MakeWH(width, height);
676         SkScalar translateX = intPad - dx;
677         SkScalar translateY = intPad - dy;
678 
679         SkASSERT(devPathBounds.fLeft == 0);
680         SkASSERT(devPathBounds.fTop == 0);
681         SkASSERT(devPathBounds.width() > 0);
682         SkASSERT(devPathBounds.height() > 0);
683 
684         SkPath path;
685         shape.asPath(&path);
686         // setup bitmap backing
687         SkAutoPixmapStorage dst;
688         if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
689                                               devPathBounds.height()))) {
690             return false;
691         }
692         sk_bzero(dst.writable_addr(), dst.computeByteSize());
693 
694         // rasterize path
695         SkPaint paint;
696         paint.setStyle(SkPaint::kFill_Style);
697         paint.setAntiAlias(true);
698 
699         SkDraw draw;
700 
701         SkRasterClip rasterClip;
702         rasterClip.setRect(devPathBounds);
703         draw.fRC = &rasterClip;
704         drawMatrix.postTranslate(translateX, translateY);
705         draw.fMatrix = &drawMatrix;
706         draw.fDst = dst;
707 
708         draw.drawPathCoverage(path, paint);
709 
710         // add to atlas
711         SkIPoint16 atlasLocation;
712         GrDrawOpAtlas::AtlasID id;
713 
714         if (!this->addToAtlas(target, flushInfo, atlas,
715                               dst.width(), dst.height(), dst.addr(), &id, &atlasLocation)) {
716             return false;
717         }
718 
719         // add to cache
720         shapeData->fKey.set(shape, ctm);
721         shapeData->fID = id;
722 
723         shapeData->fBounds = SkRect::Make(devPathBounds);
724         shapeData->fBounds.offset(-translateX, -translateY);
725 
726         // We pack the 2bit page index in the low bit of the u and v texture coords
727         uint16_t pageIndex = GrDrawOpAtlas::GetPageIndexFromID(id);
728         SkASSERT(pageIndex < 4);
729         uint16_t uBit = (pageIndex >> 1) & 0x1;
730         uint16_t vBit = pageIndex & 0x1;
731         shapeData->fTextureCoords.set(atlasLocation.fX << 1 | uBit, atlasLocation.fY << 1 | vBit,
732                                       (atlasLocation.fX+width) << 1 | uBit,
733                                       (atlasLocation.fY+height) << 1 | vBit);
734 
735         fShapeCache->add(shapeData);
736         fShapeList->addToTail(shapeData);
737 #ifdef DF_PATH_TRACKING
738         ++g_NumCachedPaths;
739 #endif
740         return true;
741     }
742 
writePathVertices(GrDrawOpAtlas * atlas,GrVertexWriter & vertices,const GrVertexColor & color,const SkMatrix & ctm,const ShapeData * shapeData) const743     void writePathVertices(GrDrawOpAtlas* atlas,
744                            GrVertexWriter& vertices,
745                            const GrVertexColor& color,
746                            const SkMatrix& ctm,
747                            const ShapeData* shapeData) const {
748         SkRect translatedBounds(shapeData->fBounds);
749         if (!fUsesDistanceField) {
750             translatedBounds.offset(SkScalarFloorToScalar(ctm.get(SkMatrix::kMTransX)),
751                                     SkScalarFloorToScalar(ctm.get(SkMatrix::kMTransY)));
752         }
753 
754         // set up texture coordinates
755         GrVertexWriter::TriStrip<uint16_t> texCoords{
756             (uint16_t)shapeData->fTextureCoords.fLeft,
757             (uint16_t)shapeData->fTextureCoords.fTop,
758             (uint16_t)shapeData->fTextureCoords.fRight,
759             (uint16_t)shapeData->fTextureCoords.fBottom
760         };
761 
762         if (fUsesDistanceField && !ctm.hasPerspective()) {
763             vertices.writeQuad(GrQuad::MakeFromRect(translatedBounds, ctm),
764                                color,
765                                texCoords);
766         } else {
767             vertices.writeQuad(GrVertexWriter::TriStripFromRect(translatedBounds),
768                                color,
769                                texCoords);
770         }
771     }
772 
flush(GrMeshDrawOp::Target * target,FlushInfo * flushInfo) const773     void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
774         GrGeometryProcessor* gp = flushInfo->fGeometryProcessor.get();
775         int numAtlasTextures = SkToInt(fAtlas->numActivePages());
776         auto proxies = fAtlas->getProxies();
777         if (gp->numTextureSamplers() != numAtlasTextures) {
778             for (int i = gp->numTextureSamplers(); i < numAtlasTextures; ++i) {
779                 flushInfo->fFixedDynamicState->fPrimitiveProcessorTextures[i] = proxies[i].get();
780             }
781             // During preparation the number of atlas pages has increased.
782             // Update the proxies used in the GP to match.
783             if (fUsesDistanceField) {
784                 reinterpret_cast<GrDistanceFieldPathGeoProc*>(gp)->addNewProxies(
785                     fAtlas->getProxies(), fAtlas->numActivePages(), GrSamplerState::ClampBilerp());
786             } else {
787                 reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewProxies(
788                     fAtlas->getProxies(), fAtlas->numActivePages(), GrSamplerState::ClampNearest());
789             }
790         }
791 
792         if (flushInfo->fInstancesToFlush) {
793             GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
794             int maxInstancesPerDraw =
795                     static_cast<int>(flushInfo->fIndexBuffer->size() / sizeof(uint16_t) / 6);
796             mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerQuad, kVerticesPerQuad,
797                                       flushInfo->fInstancesToFlush, maxInstancesPerDraw);
798             mesh->setVertexData(flushInfo->fVertexBuffer, flushInfo->fVertexOffset);
799             target->recordDraw(
800                     flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fFixedDynamicState, nullptr);
801             flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
802             flushInfo->fInstancesToFlush = 0;
803         }
804     }
805 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)806     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
807         fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
808     }
809 
color() const810     const SkPMColor4f& color() const { return fShapes[0].fColor; }
usesDistanceField() const811     bool usesDistanceField() const { return fUsesDistanceField; }
812 
onCombineIfPossible(GrOp * t,const GrCaps & caps)813     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
814         SmallPathOp* that = t->cast<SmallPathOp>();
815         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
816             return CombineResult::kCannotCombine;
817         }
818 
819         if (this->usesDistanceField() != that->usesDistanceField()) {
820             return CombineResult::kCannotCombine;
821         }
822 
823         const SkMatrix& thisCtm = this->fShapes[0].fViewMatrix;
824         const SkMatrix& thatCtm = that->fShapes[0].fViewMatrix;
825 
826         if (thisCtm.hasPerspective() != thatCtm.hasPerspective()) {
827             return CombineResult::kCannotCombine;
828         }
829 
830         // We can position on the cpu unless we're in perspective,
831         // but also need to make sure local matrices are identical
832         if ((thisCtm.hasPerspective() || fHelper.usesLocalCoords()) &&
833             !thisCtm.cheapEqualTo(thatCtm)) {
834             return CombineResult::kCannotCombine;
835         }
836 
837         // Depending on the ctm we may have a different shader for SDF paths
838         if (this->usesDistanceField()) {
839             if (thisCtm.isScaleTranslate() != thatCtm.isScaleTranslate() ||
840                 thisCtm.isSimilarity() != thatCtm.isSimilarity()) {
841                 return CombineResult::kCannotCombine;
842             }
843         }
844 
845         fShapes.push_back_n(that->fShapes.count(), that->fShapes.begin());
846         fWideColor |= that->fWideColor;
847         return CombineResult::kMerged;
848     }
849 
850     bool fUsesDistanceField;
851 
852     struct Entry {
853         SkPMColor4f fColor;
854         GrShape     fShape;
855         SkMatrix    fViewMatrix;
856     };
857 
858     SkSTArray<1, Entry> fShapes;
859     Helper fHelper;
860     GrDrawOpAtlas* fAtlas;
861     ShapeCache* fShapeCache;
862     ShapeDataList* fShapeList;
863     bool fGammaCorrect;
864     bool fWideColor;
865 
866     typedef GrMeshDrawOp INHERITED;
867 };
868 
onDrawPath(const DrawPathArgs & args)869 bool GrSmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
870     GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
871                               "GrSmallPathRenderer::onDrawPath");
872 
873     // we've already bailed on inverse filled paths, so this is safe
874     SkASSERT(!args.fShape->isEmpty());
875     SkASSERT(args.fShape->hasUnstyledKey());
876     if (!fAtlas) {
877         const GrBackendFormat format = args.fContext->priv().caps()->getDefaultBackendFormat(
878                 GrColorType::kAlpha_8, GrRenderable::kNo);
879         fAtlas = GrDrawOpAtlas::Make(args.fContext->priv().proxyProvider(),
880                                      format,
881                                      GrColorType::kAlpha_8,
882                                      ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
883                                      PLOT_WIDTH, PLOT_HEIGHT,
884                                      GrDrawOpAtlas::AllowMultitexturing::kYes,
885                                      &GrSmallPathRenderer::HandleEviction,
886                                      (void*)this);
887         if (!fAtlas) {
888             return false;
889         }
890     }
891 
892     std::unique_ptr<GrDrawOp> op = SmallPathOp::Make(
893             args.fContext, std::move(args.fPaint), *args.fShape, *args.fViewMatrix, fAtlas.get(),
894             &fShapeCache, &fShapeList, args.fGammaCorrect, args.fUserStencilSettings);
895     args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
896 
897     return true;
898 }
899 
900 ///////////////////////////////////////////////////////////////////////////////////////////////////
901 
902 #if GR_TEST_UTILS
903 
904 struct GrSmallPathRenderer::PathTestStruct {
PathTestStructGrSmallPathRenderer::PathTestStruct905     PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
~PathTestStructGrSmallPathRenderer::PathTestStruct906     ~PathTestStruct() { this->reset(); }
907 
resetGrSmallPathRenderer::PathTestStruct908     void reset() {
909         ShapeDataList::Iter iter;
910         iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
911         ShapeData* shapeData;
912         while ((shapeData = iter.get())) {
913             iter.next();
914             fShapeList.remove(shapeData);
915             delete shapeData;
916         }
917         fAtlas = nullptr;
918         fShapeCache.reset();
919     }
920 
HandleEvictionGrSmallPathRenderer::PathTestStruct921     static void HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
922         PathTestStruct* dfpr = (PathTestStruct*)pr;
923         // remove any paths that use this plot
924         ShapeDataList::Iter iter;
925         iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
926         ShapeData* shapeData;
927         while ((shapeData = iter.get())) {
928             iter.next();
929             if (id == shapeData->fID) {
930                 dfpr->fShapeCache.remove(shapeData->fKey);
931                 dfpr->fShapeList.remove(shapeData);
932                 delete shapeData;
933             }
934         }
935     }
936 
937     uint32_t fContextID;
938     std::unique_ptr<GrDrawOpAtlas> fAtlas;
939     ShapeCache fShapeCache;
940     ShapeDataList fShapeList;
941 };
942 
createOp_TestingOnly(GrRecordingContext * context,GrPaint && paint,const GrShape & shape,const SkMatrix & viewMatrix,GrDrawOpAtlas * atlas,ShapeCache * shapeCache,ShapeDataList * shapeList,bool gammaCorrect,const GrUserStencilSettings * stencil)943 std::unique_ptr<GrDrawOp> GrSmallPathRenderer::createOp_TestingOnly(
944                                                         GrRecordingContext* context,
945                                                         GrPaint&& paint,
946                                                         const GrShape& shape,
947                                                         const SkMatrix& viewMatrix,
948                                                         GrDrawOpAtlas* atlas,
949                                                         ShapeCache* shapeCache,
950                                                         ShapeDataList* shapeList,
951                                                         bool gammaCorrect,
952                                                         const GrUserStencilSettings* stencil) {
953 
954     return GrSmallPathRenderer::SmallPathOp::Make(context, std::move(paint), shape, viewMatrix,
955                                                   atlas, shapeCache, shapeList, gammaCorrect,
956                                                   stencil);
957 
958 }
959 
GR_DRAW_OP_TEST_DEFINE(SmallPathOp)960 GR_DRAW_OP_TEST_DEFINE(SmallPathOp) {
961     using PathTestStruct = GrSmallPathRenderer::PathTestStruct;
962     static PathTestStruct gTestStruct;
963 
964     if (context->priv().contextID() != gTestStruct.fContextID) {
965         gTestStruct.fContextID = context->priv().contextID();
966         gTestStruct.reset();
967         const GrBackendFormat format = context->priv().caps()->getDefaultBackendFormat(
968                 GrColorType::kAlpha_8, GrRenderable::kNo);
969         gTestStruct.fAtlas = GrDrawOpAtlas::Make(context->priv().proxyProvider(),
970                                                  format, GrColorType::kAlpha_8,
971                                                  ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
972                                                  PLOT_WIDTH, PLOT_HEIGHT,
973                                                  GrDrawOpAtlas::AllowMultitexturing::kYes,
974                                                  &PathTestStruct::HandleEviction,
975                                                  (void*)&gTestStruct);
976     }
977 
978     SkMatrix viewMatrix = GrTest::TestMatrix(random);
979     bool gammaCorrect = random->nextBool();
980 
981     // This path renderer only allows fill styles.
982     GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
983     return GrSmallPathRenderer::createOp_TestingOnly(
984                                          context,
985                                          std::move(paint), shape, viewMatrix,
986                                          gTestStruct.fAtlas.get(),
987                                          &gTestStruct.fShapeCache,
988                                          &gTestStruct.fShapeList,
989                                          gammaCorrect,
990                                          GrGetRandomStencil(random, context));
991 }
992 
993 #endif
994