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