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