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