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