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