• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/ops/TriangulatingPathRenderer.h"
9 
10 #include "include/private/SkIDChangeListener.h"
11 #include "src/core/SkGeometry.h"
12 #include "src/gpu/GrAuditTrail.h"
13 #include "src/gpu/GrCaps.h"
14 #include "src/gpu/GrDefaultGeoProcFactory.h"
15 #include "src/gpu/GrDrawOpTest.h"
16 #include "src/gpu/GrEagerVertexAllocator.h"
17 #include "src/gpu/GrOpFlushState.h"
18 #include "src/gpu/GrProgramInfo.h"
19 #include "src/gpu/GrRecordingContextPriv.h"
20 #include "src/gpu/GrResourceCache.h"
21 #include "src/gpu/GrResourceProvider.h"
22 #include "src/gpu/GrSimpleMesh.h"
23 #include "src/gpu/GrStyle.h"
24 #include "src/gpu/GrThreadSafeCache.h"
25 #include "src/gpu/geometry/GrAATriangulator.h"
26 #include "src/gpu/geometry/GrPathUtils.h"
27 #include "src/gpu/geometry/GrStyledShape.h"
28 #include "src/gpu/geometry/GrTriangulator.h"
29 #include "src/gpu/ops/GrMeshDrawOp.h"
30 #include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
31 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
32 
33 #include <cstdio>
34 
35 #ifndef GR_AA_TESSELLATOR_MAX_VERB_COUNT
36 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 10
37 #endif
38 
39 /*
40  * This path renderer linearizes and decomposes the path into triangles using GrTriangulator,
41  * uploads the triangles to a vertex buffer, and renders them with a single draw call. It can do
42  * screenspace antialiasing with a one-pixel coverage ramp.
43  */
44 namespace {
45 
46 // The TessInfo struct contains ancillary data not specifically required for the triangle
47 // data (which is stored in a GrThreadSafeCache::VertexData object).
48 // The 'fNumVertices' field is a temporary exception. It is still needed to support the
49 // AA triangulated path case - which doesn't use the GrThreadSafeCache nor the VertexData object).
50 // When there is an associated VertexData, its numVertices should always match the TessInfo's
51 // value.
52 struct TessInfo {
53     int       fNumVertices;
54     bool      fIsLinear;
55     SkScalar  fTolerance;
56 };
57 
create_data(int numVertices,bool isLinear,SkScalar tol)58 static sk_sp<SkData> create_data(int numVertices, bool isLinear, SkScalar tol) {
59     TessInfo info { numVertices, isLinear, tol };
60     return SkData::MakeWithCopy(&info, sizeof(info));
61 }
62 
cache_match(const SkData * data,SkScalar tol)63 bool cache_match(const SkData* data, SkScalar tol) {
64     SkASSERT(data);
65 
66     const TessInfo* info = static_cast<const TessInfo*>(data->data());
67 
68     return info->fIsLinear || info->fTolerance < 3.0f * tol;
69 }
70 
71 // Should 'challenger' replace 'incumbent' in the cache if there is a collision?
is_newer_better(SkData * incumbent,SkData * challenger)72 bool is_newer_better(SkData* incumbent, SkData* challenger) {
73     const TessInfo* i = static_cast<const TessInfo*>(incumbent->data());
74     const TessInfo* c = static_cast<const TessInfo*>(challenger->data());
75 
76     if (i->fIsLinear || i->fTolerance <= c->fTolerance) {
77         return false;  // prefer the incumbent
78     }
79 
80     return true;
81 }
82 
83 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key.
84 class UniqueKeyInvalidator : public SkIDChangeListener {
85 public:
UniqueKeyInvalidator(const GrUniqueKey & key,uint32_t contextUniqueID)86     UniqueKeyInvalidator(const GrUniqueKey& key, uint32_t contextUniqueID)
87             : fMsg(key, contextUniqueID, /* inThreadSafeCache */ true) {}
88 
89 private:
90     GrUniqueKeyInvalidatedMessage fMsg;
91 
changed()92     void changed() override { SkMessageBus<GrUniqueKeyInvalidatedMessage, uint32_t>::Post(fMsg); }
93 };
94 
95 class StaticVertexAllocator : public GrEagerVertexAllocator {
96 public:
StaticVertexAllocator(GrResourceProvider * resourceProvider,bool canMapVB)97     StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB)
98             : fResourceProvider(resourceProvider)
99             , fCanMapVB(canMapVB) {
100     }
101 
102 #ifdef SK_DEBUG
~StaticVertexAllocator()103     ~StaticVertexAllocator() override {
104         SkASSERT(!fLockStride && !fVertices && !fVertexBuffer && !fVertexData);
105     }
106 #endif
107 
lock(size_t stride,int eagerCount)108     void* lock(size_t stride, int eagerCount) override {
109         SkASSERT(!fLockStride && !fVertices && !fVertexBuffer && !fVertexData);
110         SkASSERT(stride && eagerCount);
111 
112         size_t size = eagerCount * stride;
113         fVertexBuffer = fResourceProvider->createBuffer(size, GrGpuBufferType::kVertex,
114                                                         kStatic_GrAccessPattern);
115         if (!fVertexBuffer) {
116             return nullptr;
117         }
118         if (fCanMapVB) {
119             fVertices = fVertexBuffer->map();
120         }
121         if (!fVertices) {
122             fVertices = sk_malloc_throw(eagerCount * stride);
123             fCanMapVB = false;
124         }
125         fLockStride = stride;
126         return fVertices;
127     }
128 
unlock(int actualCount)129     void unlock(int actualCount) override {
130         SkASSERT(fLockStride && fVertices && fVertexBuffer && !fVertexData);
131 
132         if (fCanMapVB) {
133             fVertexBuffer->unmap();
134         } else {
135             fVertexBuffer->updateData(fVertices, actualCount * fLockStride);
136             sk_free(fVertices);
137         }
138 
139         fVertexData = GrThreadSafeCache::MakeVertexData(std::move(fVertexBuffer),
140                                                         actualCount, fLockStride);
141 
142         fVertices = nullptr;
143         fLockStride = 0;
144     }
145 
detachVertexData()146     sk_sp<GrThreadSafeCache::VertexData> detachVertexData() {
147         SkASSERT(!fLockStride && !fVertices && !fVertexBuffer && fVertexData);
148 
149         return std::move(fVertexData);
150     }
151 
152 private:
153     sk_sp<GrThreadSafeCache::VertexData> fVertexData;
154     sk_sp<GrGpuBuffer> fVertexBuffer;
155     GrResourceProvider* fResourceProvider;
156     bool fCanMapVB;
157     void* fVertices = nullptr;
158     size_t fLockStride = 0;
159 };
160 
161 class TriangulatingPathOp final : public GrMeshDrawOp {
162 private:
163     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
164 
165 public:
166     DEFINE_OP_CLASS_ID
167 
Make(GrRecordingContext * context,GrPaint && paint,const GrStyledShape & shape,const SkMatrix & viewMatrix,SkIRect devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)168     static GrOp::Owner Make(GrRecordingContext* context,
169                             GrPaint&& paint,
170                             const GrStyledShape& shape,
171                             const SkMatrix& viewMatrix,
172                             SkIRect devClipBounds,
173                             GrAAType aaType,
174                             const GrUserStencilSettings* stencilSettings) {
175         return Helper::FactoryHelper<TriangulatingPathOp>(context, std::move(paint), shape,
176                                                           viewMatrix, devClipBounds, aaType,
177                                                           stencilSettings);
178     }
179 
name() const180     const char* name() const override { return "TriangulatingPathOp"; }
181 
visitProxies(const GrVisitProxyFunc & func) const182     void visitProxies(const GrVisitProxyFunc& func) const override {
183         if (fProgramInfo) {
184             fProgramInfo->visitFPProxies(func);
185         } else {
186             fHelper.visitProxies(func);
187         }
188     }
189 
TriangulatingPathOp(GrProcessorSet * processorSet,const SkPMColor4f & color,const GrStyledShape & shape,const SkMatrix & viewMatrix,const SkIRect & devClipBounds,GrAAType aaType,const GrUserStencilSettings * stencilSettings)190     TriangulatingPathOp(GrProcessorSet* processorSet,
191                         const SkPMColor4f& color,
192                         const GrStyledShape& shape,
193                         const SkMatrix& viewMatrix,
194                         const SkIRect& devClipBounds,
195                         GrAAType aaType,
196                         const GrUserStencilSettings* stencilSettings)
197             : INHERITED(ClassID())
198             , fHelper(processorSet, aaType, stencilSettings)
199             , fColor(color)
200             , fShape(shape)
201             , fViewMatrix(viewMatrix)
202             , fDevClipBounds(devClipBounds)
203             , fAntiAlias(GrAAType::kCoverage == aaType) {
204         SkRect devBounds;
205         viewMatrix.mapRect(&devBounds, shape.bounds());
206         if (shape.inverseFilled()) {
207             // Because the clip bounds are used to add a contour for inverse fills, they must also
208             // include the path bounds.
209             devBounds.join(SkRect::Make(fDevClipBounds));
210         }
211         this->setBounds(devBounds, HasAABloat(fAntiAlias), IsHairline::kNo);
212     }
213 
fixedFunctionFlags() const214     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
215 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)216     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
217                                       GrClampType clampType) override {
218         GrProcessorAnalysisCoverage coverage = fAntiAlias
219                                                        ? GrProcessorAnalysisCoverage::kSingleChannel
220                                                        : GrProcessorAnalysisCoverage::kNone;
221         // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
222         return fHelper.finalizeProcessors(caps, clip, clampType, coverage, &fColor, nullptr);
223     }
224 
225 private:
getPath() const226     SkPath getPath() const {
227         SkASSERT(!fShape.style().applies());
228         SkPath path;
229         fShape.asPath(&path);
230         return path;
231     }
232 
CreateKey(GrUniqueKey * key,const GrStyledShape & shape,const SkIRect & devClipBounds)233     static void CreateKey(GrUniqueKey* key,
234                           const GrStyledShape& shape,
235                           const SkIRect& devClipBounds) {
236         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
237 
238         bool inverseFill = shape.inverseFilled();
239 
240         static constexpr int kClipBoundsCnt = sizeof(devClipBounds) / sizeof(uint32_t);
241         int shapeKeyDataCnt = shape.unstyledKeySize();
242         SkASSERT(shapeKeyDataCnt >= 0);
243         GrUniqueKey::Builder builder(key, kDomain, shapeKeyDataCnt + kClipBoundsCnt, "Path");
244         shape.writeUnstyledKey(&builder[0]);
245         // For inverse fills, the tessellation is dependent on clip bounds.
246         if (inverseFill) {
247             memcpy(&builder[shapeKeyDataCnt], &devClipBounds, sizeof(devClipBounds));
248         } else {
249             memset(&builder[shapeKeyDataCnt], 0, sizeof(devClipBounds));
250         }
251 
252         builder.finish();
253     }
254 
255     // Triangulate the provided 'shape' in the shape's coordinate space. 'tol' should already
256     // have been mapped back from device space.
Triangulate(GrEagerVertexAllocator * allocator,const SkMatrix & viewMatrix,const GrStyledShape & shape,const SkIRect & devClipBounds,SkScalar tol,bool * isLinear)257     static int Triangulate(GrEagerVertexAllocator* allocator,
258                            const SkMatrix& viewMatrix,
259                            const GrStyledShape& shape,
260                            const SkIRect& devClipBounds,
261                            SkScalar tol,
262                            bool* isLinear) {
263         SkRect clipBounds = SkRect::Make(devClipBounds);
264 
265         SkMatrix vmi;
266         if (!viewMatrix.invert(&vmi)) {
267             return 0;
268         }
269         vmi.mapRect(&clipBounds);
270 
271         SkASSERT(!shape.style().applies());
272         SkPath path;
273         shape.asPath(&path);
274 
275         return GrTriangulator::PathToTriangles(path, tol, clipBounds, allocator, isLinear);
276     }
277 
createNonAAMesh(GrMeshDrawTarget * target)278     void createNonAAMesh(GrMeshDrawTarget* target) {
279         SkASSERT(!fAntiAlias);
280         GrResourceProvider* rp = target->resourceProvider();
281         auto threadSafeCache = target->threadSafeCache();
282 
283         GrUniqueKey key;
284         CreateKey(&key, fShape, fDevClipBounds);
285 
286         SkScalar tol = GrPathUtils::scaleToleranceToSrc(GrPathUtils::kDefaultTolerance,
287                                                         fViewMatrix, fShape.bounds());
288 
289         if (!fVertexData) {
290             auto [cachedVerts, data] = threadSafeCache->findVertsWithData(key);
291             if (cachedVerts && cache_match(data.get(), tol)) {
292                 fVertexData = std::move(cachedVerts);
293             }
294         }
295 
296         if (fVertexData) {
297             if (!fVertexData->gpuBuffer()) {
298                 sk_sp<GrGpuBuffer> buffer = rp->createBuffer(fVertexData->size(),
299                                                              GrGpuBufferType::kVertex,
300                                                              kStatic_GrAccessPattern,
301                                                              fVertexData->vertices());
302                 if (!buffer) {
303                     return;
304                 }
305 
306                 // Since we have a direct context and a ref on 'fVertexData' we need not worry
307                 // about any threading issues in this call.
308                 fVertexData->setGpuBuffer(std::move(buffer));
309             }
310 
311             fMesh = CreateMesh(target, fVertexData->refGpuBuffer(), 0, fVertexData->numVertices());
312             return;
313         }
314 
315         bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
316         StaticVertexAllocator allocator(rp, canMapVB);
317 
318         bool isLinear;
319         int vertexCount = Triangulate(&allocator, fViewMatrix, fShape, fDevClipBounds, tol,
320                                       &isLinear);
321         if (vertexCount == 0) {
322             return;
323         }
324 
325         fVertexData = allocator.detachVertexData();
326 
327         key.setCustomData(create_data(vertexCount, isLinear, tol));
328 
329         auto [tmpV, tmpD] = threadSafeCache->addVertsWithData(key, fVertexData, is_newer_better);
330         if (tmpV != fVertexData) {
331             SkASSERT(!tmpV->gpuBuffer());
332             // In this case, although the different triangulation found in the cache is better,
333             // we will continue on with the current triangulation since it is already on the gpu.
334         } else {
335             // This isn't perfect. The current triangulation is in the cache but it may have
336             // replaced a pre-existing one. A duplicated listener is unlikely and not that
337             // expensive so we just roll with it.
338             fShape.addGenIDChangeListener(
339                 sk_make_sp<UniqueKeyInvalidator>(key, target->contextUniqueID()));
340         }
341 
342         fMesh = CreateMesh(target, fVertexData->refGpuBuffer(), 0, fVertexData->numVertices());
343     }
344 
createAAMesh(GrMeshDrawTarget * target)345     void createAAMesh(GrMeshDrawTarget* target) {
346         SkASSERT(!fVertexData);
347         SkASSERT(fAntiAlias);
348         SkPath path = this->getPath();
349         if (path.isEmpty()) {
350             return;
351         }
352         SkRect clipBounds = SkRect::Make(fDevClipBounds);
353         path.transform(fViewMatrix);
354         SkScalar tol = GrPathUtils::kDefaultTolerance;
355         sk_sp<const GrBuffer> vertexBuffer;
356         int firstVertex;
357         GrEagerDynamicVertexAllocator allocator(target, &vertexBuffer, &firstVertex);
358         int vertexCount = GrAATriangulator::PathToAATriangles(path, tol, clipBounds, &allocator);
359         if (vertexCount == 0) {
360             return;
361         }
362         fMesh = CreateMesh(target, std::move(vertexBuffer), firstVertex, vertexCount);
363     }
364 
programInfo()365     GrProgramInfo* programInfo() override { return fProgramInfo; }
366 
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)367     void onCreateProgramInfo(const GrCaps* caps,
368                              SkArenaAlloc* arena,
369                              const GrSurfaceProxyView& writeView,
370                              bool usesMSAASurface,
371                              GrAppliedClip&& appliedClip,
372                              const GrDstProxyView& dstProxyView,
373                              GrXferBarrierFlags renderPassXferBarriers,
374                              GrLoadOp colorLoadOp) override {
375         GrGeometryProcessor* gp;
376         {
377             using namespace GrDefaultGeoProcFactory;
378 
379             Color color(fColor);
380             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
381                                                         ? LocalCoords::kUsePosition_Type
382                                                         : LocalCoords::kUnused_Type;
383             Coverage::Type coverageType;
384             if (fAntiAlias) {
385                 if (fHelper.compatibleWithCoverageAsAlpha()) {
386                     coverageType = Coverage::kAttributeTweakAlpha_Type;
387                 } else {
388                     coverageType = Coverage::kAttribute_Type;
389                 }
390             } else {
391                 coverageType = Coverage::kSolid_Type;
392             }
393             if (fAntiAlias) {
394                 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(arena, color, coverageType,
395                                                                  localCoordsType, fViewMatrix);
396             } else {
397                 gp = GrDefaultGeoProcFactory::Make(arena, color, coverageType, localCoordsType,
398                                                    fViewMatrix);
399             }
400         }
401         if (!gp) {
402             return;
403         }
404 
405 #ifdef SK_DEBUG
406         auto vertexStride = sizeof(SkPoint);
407         if (fAntiAlias) {
408             vertexStride += sizeof(float);
409         }
410         SkASSERT(vertexStride == gp->vertexStride());
411 #endif
412 
413         GrPrimitiveType primitiveType = TRIANGULATOR_WIREFRAME ? GrPrimitiveType::kLines
414                                                                : GrPrimitiveType::kTriangles;
415 
416         fProgramInfo =  fHelper.createProgramInfoWithStencil(caps, arena, writeView,
417                                                              usesMSAASurface,
418                                                              std::move(appliedClip), dstProxyView,
419                                                              gp, primitiveType,
420                                                              renderPassXferBarriers, colorLoadOp);
421     }
422 
onPrePrepareDraws(GrRecordingContext * rContext,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)423     void onPrePrepareDraws(GrRecordingContext* rContext,
424                            const GrSurfaceProxyView& writeView,
425                            GrAppliedClip* clip,
426                            const GrDstProxyView& dstProxyView,
427                            GrXferBarrierFlags renderPassXferBarriers,
428                            GrLoadOp colorLoadOp) override {
429         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
430 
431         INHERITED::onPrePrepareDraws(rContext, writeView, clip, dstProxyView,
432                                      renderPassXferBarriers, colorLoadOp);
433 
434         if (fAntiAlias) {
435             // TODO: pull the triangulation work forward to the recording thread for the AA case
436             // too.
437             return;
438         }
439 
440         auto threadSafeViewCache = rContext->priv().threadSafeCache();
441 
442         GrUniqueKey key;
443         CreateKey(&key, fShape, fDevClipBounds);
444 
445         SkScalar tol = GrPathUtils::scaleToleranceToSrc(GrPathUtils::kDefaultTolerance,
446                                                         fViewMatrix, fShape.bounds());
447 
448         auto [cachedVerts, data] = threadSafeViewCache->findVertsWithData(key);
449         if (cachedVerts && cache_match(data.get(), tol)) {
450             fVertexData = std::move(cachedVerts);
451             return;
452         }
453 
454         GrCpuVertexAllocator allocator;
455 
456         bool isLinear;
457         int vertexCount = Triangulate(&allocator, fViewMatrix, fShape, fDevClipBounds, tol,
458                                       &isLinear);
459         if (vertexCount == 0) {
460             return;
461         }
462 
463         fVertexData = allocator.detachVertexData();
464 
465         key.setCustomData(create_data(vertexCount, isLinear, tol));
466 
467         // If some other thread created and cached its own triangulation, the 'is_newer_better'
468         // predicate will replace the version in the cache if 'fVertexData' is a more accurate
469         // triangulation. This will leave some other recording threads using a poorer triangulation
470         // but will result in a version with greater applicability being in the cache.
471         auto [tmpV, tmpD] = threadSafeViewCache->addVertsWithData(key, fVertexData,
472                                                                   is_newer_better);
473         if (tmpV != fVertexData) {
474             // Someone beat us to creating the triangulation (and it is better than ours) so
475             // just go ahead and use it.
476             SkASSERT(cache_match(tmpD.get(), tol));
477             fVertexData = std::move(tmpV);
478         } else {
479             // This isn't perfect. The current triangulation is in the cache but it may have
480             // replaced a pre-existing one. A duplicated listener is unlikely and not that
481             // expensive so we just roll with it.
482             fShape.addGenIDChangeListener(
483                     sk_make_sp<UniqueKeyInvalidator>(key, rContext->priv().contextID()));
484         }
485     }
486 
onPrepareDraws(GrMeshDrawTarget * target)487     void onPrepareDraws(GrMeshDrawTarget* target) override {
488         if (fAntiAlias) {
489             this->createAAMesh(target);
490         } else {
491             this->createNonAAMesh(target);
492         }
493     }
494 
CreateMesh(GrMeshDrawTarget * target,sk_sp<const GrBuffer> vb,int firstVertex,int count)495     static GrSimpleMesh* CreateMesh(GrMeshDrawTarget* target,
496                                     sk_sp<const GrBuffer> vb,
497                                     int firstVertex,
498                                     int count) {
499         auto mesh = target->allocMesh();
500         mesh->set(std::move(vb), count, firstVertex);
501         return mesh;
502     }
503 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)504     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
505         if (!fProgramInfo) {
506             this->createProgramInfo(flushState);
507         }
508 
509         if (!fProgramInfo || !fMesh) {
510             return;
511         }
512 
513         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
514         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
515         flushState->drawMesh(*fMesh);
516     }
517 
518 #if GR_TEST_UTILS
onDumpInfo() const519     SkString onDumpInfo() const override {
520         return SkStringPrintf("Color 0x%08x, aa: %d\n%s",
521                               fColor.toBytes_RGBA(), fAntiAlias, fHelper.dumpInfo().c_str());
522     }
523 #endif
524 
525     Helper         fHelper;
526     SkPMColor4f    fColor;
527     GrStyledShape  fShape;
528     SkMatrix       fViewMatrix;
529     SkIRect        fDevClipBounds;
530     bool           fAntiAlias;
531 
532     GrSimpleMesh*  fMesh = nullptr;
533     GrProgramInfo* fProgramInfo = nullptr;
534 
535     sk_sp<GrThreadSafeCache::VertexData> fVertexData;
536 
537     using INHERITED = GrMeshDrawOp;
538 };
539 
540 }  // anonymous namespace
541 
542 ///////////////////////////////////////////////////////////////////////////////////////////////////
543 
544 #if GR_TEST_UTILS
545 
GR_DRAW_OP_TEST_DEFINE(TriangulatingPathOp)546 GR_DRAW_OP_TEST_DEFINE(TriangulatingPathOp) {
547     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
548     const SkPath& path = GrTest::TestPath(random);
549     SkIRect devClipBounds = SkIRect::MakeLTRB(
550         random->nextU(), random->nextU(), random->nextU(), random->nextU());
551     devClipBounds.sort();
552     static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kMSAA, GrAAType::kCoverage};
553     GrAAType aaType;
554     do {
555         aaType = kAATypes[random->nextULessThan(SK_ARRAY_COUNT(kAATypes))];
556     } while(GrAAType::kMSAA == aaType && numSamples <= 1);
557     GrStyle style;
558     do {
559         GrTest::TestStyle(random, &style);
560     } while (!style.isSimpleFill());
561     GrStyledShape shape(path, style);
562     return TriangulatingPathOp::Make(context, std::move(paint), shape, viewMatrix, devClipBounds,
563                                      aaType, GrGetRandomStencil(random, context));
564 }
565 
566 #endif
567 
568 ///////////////////////////////////////////////////////////////////////////////////////////////////
569 
570 namespace skgpu::v1 {
571 
TriangulatingPathRenderer()572 TriangulatingPathRenderer::TriangulatingPathRenderer()
573     : fMaxVerbCount(GR_AA_TESSELLATOR_MAX_VERB_COUNT) {
574 }
575 
onCanDrawPath(const CanDrawPathArgs & args) const576 PathRenderer::CanDrawPath TriangulatingPathRenderer::onCanDrawPath(
577         const CanDrawPathArgs& args) const {
578 
579     // Don't use this path renderer with dynamic MSAA. DMSAA tries to not rely on caching.
580     if (args.fSurfaceProps->flags() & SkSurfaceProps::kDynamicMSAA_Flag) {
581         return CanDrawPath::kNo;
582     }
583     // This path renderer can draw fill styles, and can do screenspace antialiasing via a
584     // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex
585     // ones to simpler algorithms. We pass on paths that have styles, though they may come back
586     // around after applying the styling information to the geometry to create a filled path.
587     if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) {
588         return CanDrawPath::kNo;
589     }
590     switch (args.fAAType) {
591         case GrAAType::kNone:
592         case GrAAType::kMSAA:
593             // Prefer MSAA, if any antialiasing. In the non-analytic-AA case, We skip paths that
594             // don't have a key since the real advantage of this path renderer comes from caching
595             // the tessellated geometry.
596             if (!args.fShape->hasUnstyledKey()) {
597                 return CanDrawPath::kNo;
598             }
599             break;
600         case GrAAType::kCoverage:
601             // Use analytic AA if we don't have MSAA. In this case, we do not cache, so we accept
602             // paths without keys.
603             SkPath path;
604             args.fShape->asPath(&path);
605             if (path.countVerbs() > fMaxVerbCount) {
606                 return CanDrawPath::kNo;
607             }
608             break;
609     }
610     return CanDrawPath::kYes;
611 }
612 
onDrawPath(const DrawPathArgs & args)613 bool TriangulatingPathRenderer::onDrawPath(const DrawPathArgs& args) {
614     GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
615                               "GrTriangulatingPathRenderer::onDrawPath");
616 
617     GrOp::Owner op = TriangulatingPathOp::Make(
618             args.fContext, std::move(args.fPaint), *args.fShape, *args.fViewMatrix,
619             *args.fClipConservativeBounds, args.fAAType, args.fUserStencilSettings);
620     args.fSurfaceDrawContext->addDrawOp(args.fClip, std::move(op));
621     return true;
622 }
623 
624 } // namespace skgpu::v1
625