xref: /aosp_15_r20/external/skia/src/gpu/ganesh/ops/DefaultPathRenderer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 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/DefaultPathRenderer.h"
8 
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkPath.h"
11 #include "include/core/SkPathTypes.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkString.h"
16 #include "include/core/SkStrokeRec.h"
17 #include "include/gpu/ganesh/GrRecordingContext.h"
18 #include "include/private/SkColorData.h"
19 #include "include/private/base/SkAssert.h"
20 #include "include/private/base/SkDebug.h"
21 #include "include/private/base/SkPoint_impl.h"
22 #include "include/private/base/SkTArray.h"
23 #include "include/private/base/SkTDArray.h"
24 #include "include/private/base/SkTo.h"
25 #include "include/private/gpu/ganesh/GrTypesPriv.h"
26 #include "src/core/SkGeometry.h"
27 #include "src/core/SkMatrixPriv.h"
28 #include "src/gpu/ganesh/GrAppliedClip.h"
29 #include "src/gpu/ganesh/GrAuditTrail.h"
30 #include "src/gpu/ganesh/GrBuffer.h"
31 #include "src/gpu/ganesh/GrCaps.h"
32 #include "src/gpu/ganesh/GrClip.h"
33 #include "src/gpu/ganesh/GrDefaultGeoProcFactory.h"
34 #include "src/gpu/ganesh/GrGeometryProcessor.h"
35 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
36 #include "src/gpu/ganesh/GrOpFlushState.h"
37 #include "src/gpu/ganesh/GrPaint.h"
38 #include "src/gpu/ganesh/GrProcessorAnalysis.h"
39 #include "src/gpu/ganesh/GrProcessorSet.h"
40 #include "src/gpu/ganesh/GrProgramInfo.h"
41 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
42 #include "src/gpu/ganesh/GrRenderTargetProxy.h"
43 #include "src/gpu/ganesh/GrSimpleMesh.h"
44 #include "src/gpu/ganesh/GrStyle.h"
45 #include "src/gpu/ganesh/GrTestUtils.h"
46 #include "src/gpu/ganesh/GrUserStencilSettings.h"
47 #include "src/gpu/ganesh/GrUtil.h"
48 #include "src/gpu/ganesh/SurfaceDrawContext.h"
49 #include "src/gpu/ganesh/effects/GrDisableColorXP.h"
50 #include "src/gpu/ganesh/geometry/GrPathUtils.h"
51 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
52 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
53 #include "src/gpu/ganesh/ops/GrOp.h"
54 #include "src/gpu/ganesh/ops/GrPathStencilSettings.h"
55 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
56 
57 #if defined(GPU_TEST_UTILS)
58 #include "src/base/SkRandom.h"
59 #include "src/gpu/ganesh/GrDrawOpTest.h"
60 #endif
61 
62 #include <cstddef>
63 #include <cstdint>
64 #include <utility>
65 
66 class GrDstProxyView;
67 class GrSurfaceProxyView;
68 class SkArenaAlloc;
69 enum class GrXferBarrierFlags;
70 
71 using namespace skia_private;
72 
73 ////////////////////////////////////////////////////////////////////////////////
74 // Helpers for drawPath
75 
76 namespace {
77 
78 #define STENCIL_OFF     0   // Always disable stencil (even when needed)
79 
single_pass_shape(const GrStyledShape & shape)80 inline bool single_pass_shape(const GrStyledShape& shape) {
81 #if STENCIL_OFF
82     return true;
83 #else
84     // Inverse fill is always two pass.
85     if (shape.inverseFilled()) {
86         return false;
87     }
88     // This path renderer only accepts simple fill paths or stroke paths that are either hairline
89     // or have a stroke width small enough to treat as hairline. Hairline paths are always single
90     // pass. Filled paths are single pass if they're convex.
91     if (shape.style().isSimpleFill()) {
92         return shape.knownToBeConvex();
93     }
94     return true;
95 #endif
96 }
97 
98 class PathGeoBuilder {
99 public:
PathGeoBuilder(GrPrimitiveType primitiveType,GrMeshDrawTarget * target,SkTDArray<GrSimpleMesh * > * meshes)100     PathGeoBuilder(GrPrimitiveType primitiveType,
101                    GrMeshDrawTarget* target,
102                    SkTDArray<GrSimpleMesh*>* meshes)
103             : fPrimitiveType(primitiveType)
104             , fTarget(target)
105             , fVertexStride(sizeof(SkPoint))
106             , fFirstIndex(0)
107             , fIndicesInChunk(0)
108             , fIndices(nullptr)
109             , fMeshes(meshes) {
110         this->allocNewBuffers();
111     }
112 
~PathGeoBuilder()113     ~PathGeoBuilder() {
114         this->createMeshAndPutBackReserve();
115     }
116 
117     /**
118      *  Path verbs
119      */
moveTo(const SkPoint & p)120     void moveTo(const SkPoint& p) {
121         if (!this->ensureSpace(1)) {
122             return;
123         }
124 
125         if (!this->isHairline()) {
126             fSubpathIndexStart = this->currentIndex();
127             fSubpathStartPoint = p;
128         }
129         *(fCurVert++) = p;
130     }
131 
addLine(const SkPoint pts[])132     void addLine(const SkPoint pts[]) {
133         if (!this->ensureSpace(1, this->indexScale(), &pts[0])) {
134             return;
135         }
136 
137         if (this->isIndexed()) {
138             uint16_t prevIdx = this->currentIndex() - 1;
139             this->appendCountourEdgeIndices(prevIdx);
140         }
141         *(fCurVert++) = pts[1];
142     }
143 
addQuad(const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)144     void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
145         if (!this->ensureSpace(GrPathUtils::kMaxPointsPerCurve,
146                              GrPathUtils::kMaxPointsPerCurve * this->indexScale(),
147                              &pts[0])) {
148             return;
149         }
150 
151         // First pt of quad is the pt we ended on in previous step
152         uint16_t firstQPtIdx = this->currentIndex() - 1;
153         uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
154                 pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
155                 GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
156         if (this->isIndexed()) {
157             for (uint16_t i = 0; i < numPts; ++i) {
158                 this->appendCountourEdgeIndices(firstQPtIdx + i);
159             }
160         }
161     }
162 
addConic(SkScalar weight,const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)163     void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
164                   SkScalar srcSpaceTol) {
165         SkAutoConicToQuads converter;
166         const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
167         for (int i = 0; i < converter.countQuads(); ++i) {
168             this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
169         }
170     }
171 
addCubic(const SkPoint pts[],SkScalar srcSpaceTolSqd,SkScalar srcSpaceTol)172     void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
173         if (!this->ensureSpace(GrPathUtils::kMaxPointsPerCurve,
174                              GrPathUtils::kMaxPointsPerCurve * this->indexScale(),
175                              &pts[0])) {
176             return;
177         }
178 
179         // First pt of cubic is the pt we ended on in previous step
180         uint16_t firstCPtIdx = this->currentIndex() - 1;
181         uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
182                 pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
183                 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
184         if (this->isIndexed()) {
185             for (uint16_t i = 0; i < numPts; ++i) {
186                 this->appendCountourEdgeIndices(firstCPtIdx + i);
187             }
188         }
189     }
190 
addPath(const SkPath & path,SkScalar srcSpaceTol)191     void addPath(const SkPath& path, SkScalar srcSpaceTol) {
192         SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
193 
194         SkPath::Iter iter(path, false);
195         SkPoint pts[4];
196 
197         bool done = false;
198         while (!done) {
199             SkPath::Verb verb = iter.next(pts);
200             switch (verb) {
201                 case SkPath::kMove_Verb:
202                     this->moveTo(pts[0]);
203                     break;
204                 case SkPath::kLine_Verb:
205                     this->addLine(pts);
206                     break;
207                 case SkPath::kConic_Verb:
208                     this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
209                     break;
210                 case SkPath::kQuad_Verb:
211                     this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
212                     break;
213                 case SkPath::kCubic_Verb:
214                     this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
215                     break;
216                 case SkPath::kClose_Verb:
217                     break;
218                 case SkPath::kDone_Verb:
219                     done = true;
220             }
221         }
222     }
223 
PathHasMultipleSubpaths(const SkPath & path)224     static bool PathHasMultipleSubpaths(const SkPath& path) {
225         bool first = true;
226 
227         SkPath::Iter iter(path, false);
228         SkPath::Verb verb;
229 
230         SkPoint pts[4];
231         while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
232             if (SkPath::kMove_Verb == verb && !first) {
233                 return true;
234             }
235             first = false;
236         }
237         return false;
238     }
239 
240 private:
241     /**
242      *  Derived properties
243      *  TODO: Cache some of these for better performance, rather than re-computing?
244      */
isIndexed() const245     bool isIndexed() const {
246         return GrPrimitiveType::kLines == fPrimitiveType ||
247                GrPrimitiveType::kTriangles == fPrimitiveType;
248     }
isHairline() const249     bool isHairline() const {
250         return GrPrimitiveType::kLines == fPrimitiveType ||
251                GrPrimitiveType::kLineStrip == fPrimitiveType;
252     }
indexScale() const253     int indexScale() const {
254         switch (fPrimitiveType) {
255             case GrPrimitiveType::kLines:
256                 return 2;
257             case GrPrimitiveType::kTriangles:
258                 return 3;
259             default:
260                 return 0;
261         }
262     }
263 
currentIndex() const264     uint16_t currentIndex() const { return fCurVert - fVertices; }
265 
266     // Allocate vertex and (possibly) index buffers
allocNewBuffers()267     void allocNewBuffers() {
268         SkASSERT(fValid);
269 
270         // Ensure that we always get enough verts for a worst-case quad/cubic, plus leftover points
271         // from previous mesh piece (up to two verts to continue fanning). If we can't get that
272         // many, ask for a much larger number. This needs to be fairly big to handle  quads/cubics,
273         // which have a worst-case of 1k points.
274         static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
275         static const int kFallbackVerticesPerChunk = 16384;
276 
277         fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
278                                                                           kMinVerticesPerChunk,
279                                                                           kFallbackVerticesPerChunk,
280                                                                           &fVertexBuffer,
281                                                                           &fFirstVertex,
282                                                                           &fVerticesInChunk));
283         if (!fVertices) {
284             SkDebugf("WARNING: Failed to allocate vertex buffer for DefaultPathRenderer.\n");
285             fCurVert = nullptr;
286             fCurIdx = fIndices = nullptr;
287             fSubpathIndexStart = 0;
288             fValid = false;
289             return;
290         }
291 
292         if (this->isIndexed()) {
293             // Similar to above: Ensure we get enough indices for one worst-case quad/cubic.
294             // No extra indices are needed for stitching, though. If we can't get that many, ask
295             // for enough to match our large vertex request.
296             const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
297             const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
298 
299             fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
300                                                       &fIndexBuffer, &fFirstIndex,
301                                                       &fIndicesInChunk);
302             if (!fIndices) {
303                 SkDebugf("WARNING: Failed to allocate index buffer for DefaultPathRenderer.\n");
304                 fVertices = nullptr;
305                 fValid = false;
306             }
307         }
308 
309         fCurVert = fVertices;
310         fCurIdx = fIndices;
311         fSubpathIndexStart = 0;
312     }
313 
appendCountourEdgeIndices(uint16_t edgeV0Idx)314     void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
315         SkASSERT(fCurIdx);
316 
317         // When drawing lines we're appending line segments along the countour. When applying the
318         // other fill rules we're drawing triangle fans around the start of the current (sub)path.
319         if (!this->isHairline()) {
320             *(fCurIdx++) = fSubpathIndexStart;
321         }
322         *(fCurIdx++) = edgeV0Idx;
323         *(fCurIdx++) = edgeV0Idx + 1;
324     }
325 
326     // Emits a single draw with all accumulated vertex/index data
createMeshAndPutBackReserve()327     void createMeshAndPutBackReserve() {
328         if (!fValid) {
329             return;
330         }
331 
332         int vertexCount = fCurVert - fVertices;
333         int indexCount = fCurIdx - fIndices;
334         SkASSERT(vertexCount <= fVerticesInChunk);
335         SkASSERT(indexCount <= fIndicesInChunk);
336 
337         GrSimpleMesh* mesh = nullptr;
338         if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
339             mesh = fTarget->allocMesh();
340             if (!this->isIndexed()) {
341                 mesh->set(std::move(fVertexBuffer), vertexCount, fFirstVertex);
342             } else {
343                 mesh->setIndexed(std::move(fIndexBuffer), indexCount, fFirstIndex, 0,
344                                  vertexCount - 1, GrPrimitiveRestart::kNo, std::move(fVertexBuffer),
345                                  fFirstVertex);
346             }
347         }
348 
349         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
350         fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
351 
352         if (mesh) {
353             fMeshes->push_back(mesh);
354         }
355     }
356 
ensureSpace(int vertsNeeded,int indicesNeeded=0,const SkPoint * lastPoint=nullptr)357     bool ensureSpace(int vertsNeeded, int indicesNeeded = 0, const SkPoint* lastPoint = nullptr) {
358         if (!fValid) {
359             return false;
360         }
361 
362         if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
363             fCurIdx + indicesNeeded > fIndices + fIndicesInChunk) {
364             // We are about to run out of space (possibly)
365 
366 #ifdef SK_DEBUG
367             // To maintain continuity, we need to remember one or two points from the current mesh.
368             // Lines only need the last point, fills need the first point from the current contour.
369             // We always grab both here, and append the ones we need at the end of this process.
370             SkASSERT(fSubpathIndexStart < fVerticesInChunk);
371             // This assert is reading from the gpu buffer fVertices and will be slow, but for debug
372             // that is okay.
373             if (!this->isHairline()) {
374                 SkASSERT(fSubpathStartPoint == fVertices[fSubpathIndexStart]);
375             }
376             if (lastPoint) {
377                 SkASSERT(*(fCurVert - 1) == *lastPoint);
378             }
379 #endif
380 
381             // Draw the mesh we've accumulated, and put back any unused space
382             this->createMeshAndPutBackReserve();
383 
384             // Get new buffers
385             this->allocNewBuffers();
386             if (!fValid) {
387                 return false;
388             }
389 
390             // On moves we don't need to copy over any points to the new buffer and we pass in a
391             // null lastPoint.
392             if (lastPoint) {
393                 // Append copies of the points we saved so the two meshes will weld properly
394                 if (!this->isHairline()) {
395                     *(fCurVert++) = fSubpathStartPoint;
396                 }
397                 *(fCurVert++) = *lastPoint;
398             }
399         }
400 
401         return true;
402     }
403 
404     GrPrimitiveType fPrimitiveType;
405     GrMeshDrawTarget* fTarget;
406     size_t fVertexStride;
407 
408     sk_sp<const GrBuffer> fVertexBuffer;
409     int fFirstVertex;
410     int fVerticesInChunk;
411     SkPoint* fVertices;
412     SkPoint* fCurVert;
413 
414     sk_sp<const GrBuffer> fIndexBuffer;
415     int fFirstIndex;
416     int fIndicesInChunk;
417     uint16_t* fIndices;
418     uint16_t* fCurIdx;
419     uint16_t fSubpathIndexStart;
420     SkPoint fSubpathStartPoint;
421 
422     bool fValid = true;
423     SkTDArray<GrSimpleMesh*>* fMeshes;
424 };
425 
426 class DefaultPathOp final : public GrMeshDrawOp {
427 private:
428     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
429 
430 public:
431     DEFINE_OP_CLASS_ID
432 
Make(GrRecordingContext * context,GrPaint && paint,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,GrAAType aaType,const SkRect & devBounds,const GrUserStencilSettings * stencilSettings)433     static GrOp::Owner Make(GrRecordingContext* context,
434                             GrPaint&& paint,
435                             const SkPath& path,
436                             SkScalar tolerance,
437                             uint8_t coverage,
438                             const SkMatrix& viewMatrix,
439                             bool isHairline,
440                             GrAAType aaType,
441                             const SkRect& devBounds,
442                             const GrUserStencilSettings* stencilSettings) {
443         return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
444                                                     coverage, viewMatrix, isHairline, aaType,
445                                                     devBounds, stencilSettings);
446     }
447 
name() const448     const char* name() const override { return "DefaultPathOp"; }
449 
visitProxies(const GrVisitProxyFunc & func) const450     void visitProxies(const GrVisitProxyFunc& func) const override {
451         if (fProgramInfo) {
452             fProgramInfo->visitFPProxies(func);
453         } else {
454             fHelper.visitProxies(func);
455         }
456     }
457 
DefaultPathOp(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkPath & path,SkScalar tolerance,uint8_t coverage,const SkMatrix & viewMatrix,bool isHairline,GrAAType aaType,const SkRect & devBounds,const GrUserStencilSettings * stencilSettings)458     DefaultPathOp(GrProcessorSet* processorSet, const SkPMColor4f& color, const SkPath& path,
459                   SkScalar tolerance, uint8_t coverage, const SkMatrix& viewMatrix, bool isHairline,
460                   GrAAType aaType, const SkRect& devBounds,
461                   const GrUserStencilSettings* stencilSettings)
462             : INHERITED(ClassID())
463             , fHelper(processorSet, aaType, stencilSettings)
464             , fColor(color)
465             , fCoverage(coverage)
466             , fViewMatrix(viewMatrix)
467             , fIsHairline(isHairline) {
468         fPaths.emplace_back(PathData{path, tolerance});
469 
470         HasAABloat aaBloat = (aaType == GrAAType::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
471         this->setBounds(devBounds, aaBloat,
472                         isHairline ? IsHairline::kYes : IsHairline::kNo);
473     }
474 
fixedFunctionFlags() const475     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
476 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)477     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
478                                       GrClampType clampType) override {
479         GrProcessorAnalysisCoverage gpCoverage =
480                 this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
481                                          : GrProcessorAnalysisCoverage::kSingleChannel;
482         // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
483         return fHelper.finalizeProcessors(caps, clip, clampType, gpCoverage, &fColor, nullptr);
484     }
485 
486 private:
primType() const487     GrPrimitiveType primType() const {
488         if (this->isHairline()) {
489             int instanceCount = fPaths.size();
490 
491             // We avoid indices when we have a single hairline contour.
492             bool isIndexed = instanceCount > 1 ||
493                                 PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
494 
495             return isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
496         }
497 
498         return GrPrimitiveType::kTriangles;
499     }
500 
programInfo()501     GrProgramInfo* programInfo() override { return fProgramInfo; }
502 
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)503     void onCreateProgramInfo(const GrCaps* caps,
504                              SkArenaAlloc* arena,
505                              const GrSurfaceProxyView& writeView,
506                              bool usesMSAASurface,
507                              GrAppliedClip&& appliedClip,
508                              const GrDstProxyView& dstProxyView,
509                              GrXferBarrierFlags renderPassXferBarriers,
510                              GrLoadOp colorLoadOp) override {
511         GrGeometryProcessor* gp;
512         {
513             using namespace GrDefaultGeoProcFactory;
514             Color color(this->color());
515             Coverage coverage(this->coverage());
516             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
517                                                               : LocalCoords::kUnused_Type);
518             gp = GrDefaultGeoProcFactory::Make(arena,
519                                                color,
520                                                coverage,
521                                                localCoords,
522                                                this->viewMatrix());
523         }
524 
525         SkASSERT(gp->vertexStride() == sizeof(SkPoint));
526 
527         fProgramInfo =  fHelper.createProgramInfoWithStencil(caps, arena, writeView,
528                                                              usesMSAASurface,
529                                                              std::move(appliedClip), dstProxyView,
530                                                              gp, this->primType(),
531                                                              renderPassXferBarriers, colorLoadOp);
532 
533     }
534 
onPrepareDraws(GrMeshDrawTarget * target)535     void onPrepareDraws(GrMeshDrawTarget* target) override {
536         PathGeoBuilder pathGeoBuilder(this->primType(), target, &fMeshes);
537 
538         // fill buffers
539         for (int i = 0; i < fPaths.size(); i++) {
540             const PathData& args = fPaths[i];
541             pathGeoBuilder.addPath(args.fPath, args.fTolerance);
542         }
543     }
544 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)545     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
546         if (!fProgramInfo) {
547             this->createProgramInfo(flushState);
548         }
549 
550         if (!fProgramInfo || fMeshes.empty()) {
551             return;
552         }
553 
554         flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
555         flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
556         for (int i = 0; i < fMeshes.size(); ++i) {
557             flushState->drawMesh(*fMeshes[i]);
558         }
559     }
560 
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)561     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
562         DefaultPathOp* that = t->cast<DefaultPathOp>();
563         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
564             return CombineResult::kCannotCombine;
565         }
566 
567         if (this->color() != that->color()) {
568             return CombineResult::kCannotCombine;
569         }
570 
571         if (this->coverage() != that->coverage()) {
572             return CombineResult::kCannotCombine;
573         }
574 
575         if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
576             return CombineResult::kCannotCombine;
577         }
578 
579         if (this->isHairline() != that->isHairline()) {
580             return CombineResult::kCannotCombine;
581         }
582 
583         fPaths.push_back_n(that->fPaths.size(), that->fPaths.begin());
584         return CombineResult::kMerged;
585     }
586 
587 #if defined(GPU_TEST_UTILS)
onDumpInfo() const588     SkString onDumpInfo() const override {
589         SkString string = SkStringPrintf("Color: 0x%08x Count: %d\n",
590                                          fColor.toBytes_RGBA(), fPaths.size());
591         for (const auto& path : fPaths) {
592             string.appendf("Tolerance: %.2f\n", path.fTolerance);
593         }
594         string += fHelper.dumpInfo();
595         return string;
596     }
597 #endif
598 
color() const599     const SkPMColor4f& color() const { return fColor; }
coverage() const600     uint8_t coverage() const { return fCoverage; }
viewMatrix() const601     const SkMatrix& viewMatrix() const { return fViewMatrix; }
isHairline() const602     bool isHairline() const { return fIsHairline; }
603 
604     struct PathData {
605         SkPath fPath;
606         SkScalar fTolerance;
607     };
608 
609     STArray<1, PathData, true> fPaths;
610     Helper fHelper;
611     SkPMColor4f fColor;
612     uint8_t fCoverage;
613     SkMatrix fViewMatrix;
614     bool fIsHairline;
615 
616     SkTDArray<GrSimpleMesh*> fMeshes;
617     GrProgramInfo* fProgramInfo = nullptr;
618 
619     using INHERITED = GrMeshDrawOp;
620 };
621 
622 }  // anonymous namespace
623 
624 ///////////////////////////////////////////////////////////////////////////////////////////////////
625 
626 #if defined(GPU_TEST_UTILS)
627 
GR_DRAW_OP_TEST_DEFINE(DefaultPathOp)628 GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
629     SkMatrix viewMatrix = GrTest::TestMatrix(random);
630 
631     // For now just hairlines because the other types of draws require two ops.
632     // TODO we should figure out a way to combine the stencil and cover steps into one op.
633     GrStyle style(SkStrokeRec::kHairline_InitStyle);
634     const SkPath& path = GrTest::TestPath(random);
635 
636     // Compute srcSpaceTol
637     SkRect bounds = path.getBounds();
638     SkScalar tol = GrPathUtils::kDefaultTolerance;
639     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
640 
641     viewMatrix.mapRect(&bounds);
642     uint8_t coverage = GrTest::RandomCoverage(random);
643     GrAAType aaType = GrAAType::kNone;
644     if (numSamples > 1 && random->nextBool()) {
645         aaType = GrAAType::kMSAA;
646     }
647     return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
648                                true, aaType, bounds, GrGetRandomStencil(random, context));
649 }
650 
651 #endif
652 
653 ///////////////////////////////////////////////////////////////////////////////////////////////////
654 
655 namespace skgpu::ganesh {
656 
internalDrawPath(skgpu::ganesh::SurfaceDrawContext * sdc,GrPaint && paint,GrAAType aaType,const GrUserStencilSettings & userStencilSettings,const GrClip * clip,const SkMatrix & viewMatrix,const GrStyledShape & shape,bool stencilOnly)657 bool DefaultPathRenderer::internalDrawPath(skgpu::ganesh::SurfaceDrawContext* sdc,
658                                            GrPaint&& paint,
659                                            GrAAType aaType,
660                                            const GrUserStencilSettings& userStencilSettings,
661                                            const GrClip* clip,
662                                            const SkMatrix& viewMatrix,
663                                            const GrStyledShape& shape,
664                                            bool stencilOnly) {
665     auto context = sdc->recordingContext();
666 
667     SkASSERT(GrAAType::kCoverage != aaType);
668     SkPath path;
669     shape.asPath(&path);
670 
671     SkScalar hairlineCoverage;
672     uint8_t newCoverage = 0xff;
673     bool isHairline = false;
674     if (GrIsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
675         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
676         isHairline = true;
677     } else {
678         SkASSERT(shape.style().isSimpleFill());
679     }
680 
681     int                          passCount = 0;
682     const GrUserStencilSettings* passes[2];
683     bool                         reverse = false;
684     bool                         lastPassIsBounds;
685 
686     if (isHairline) {
687         passCount = 1;
688         if (stencilOnly) {
689             passes[0] = &gDirectToStencil;
690         } else {
691             passes[0] = &userStencilSettings;
692         }
693         lastPassIsBounds = false;
694     } else {
695         if (single_pass_shape(shape)) {
696             passCount = 1;
697             if (stencilOnly) {
698                 passes[0] = &gDirectToStencil;
699             } else {
700                 passes[0] = &userStencilSettings;
701             }
702             lastPassIsBounds = false;
703         } else {
704             switch (path.getFillType()) {
705                 case SkPathFillType::kInverseEvenOdd:
706                     reverse = true;
707                     [[fallthrough]];
708                 case SkPathFillType::kEvenOdd:
709                     passes[0] = &gEOStencilPass;
710                     if (stencilOnly) {
711                         passCount = 1;
712                         lastPassIsBounds = false;
713                     } else {
714                         passCount = 2;
715                         lastPassIsBounds = true;
716                         if (reverse) {
717                             passes[1] = &gInvEOColorPass;
718                         } else {
719                             passes[1] = &gEOColorPass;
720                         }
721                     }
722                     break;
723 
724                 case SkPathFillType::kInverseWinding:
725                     reverse = true;
726                     [[fallthrough]];
727                 case SkPathFillType::kWinding:
728                     passes[0] = &gWindStencilPass;
729                     passCount = 2;
730                     if (stencilOnly) {
731                         lastPassIsBounds = false;
732                         --passCount;
733                     } else {
734                         lastPassIsBounds = true;
735                         if (reverse) {
736                             passes[passCount-1] = &gInvWindColorPass;
737                         } else {
738                             passes[passCount-1] = &gWindColorPass;
739                         }
740                     }
741                     break;
742                 default:
743                     SkDEBUGFAIL("Unknown path fFill!");
744                     return false;
745             }
746         }
747     }
748 
749     SkScalar tol = GrPathUtils::kDefaultTolerance;
750     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
751 
752     SkRect devBounds;
753     GetPathDevBounds(path, sdc->asRenderTargetProxy()->backingStoreDimensions(),
754                      viewMatrix, &devBounds);
755 
756     for (int p = 0; p < passCount; ++p) {
757         if (lastPassIsBounds && (p == passCount-1)) {
758             SkRect bounds;
759             SkMatrix localMatrix = SkMatrix::I();
760             if (reverse) {
761                 // draw over the dev bounds (which will be the whole dst surface for inv fill).
762                 bounds = devBounds;
763                 SkMatrix vmi;
764                 // mapRect through persp matrix may not be correct
765                 if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
766                     vmi.mapRect(&bounds);
767                 } else {
768                     if (!viewMatrix.invert(&localMatrix)) {
769                         return false;
770                     }
771                 }
772             } else {
773                 bounds = path.getBounds();
774             }
775             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
776                                                                                viewMatrix;
777             // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
778             assert_alive(paint);
779             sdc->stencilRect(clip, passes[p], std::move(paint),
780                              GrAA(aaType == GrAAType::kMSAA), viewM, bounds,
781                              &localMatrix);
782         } else {
783             bool stencilPass = stencilOnly || passCount > 1;
784             GrOp::Owner op;
785             if (stencilPass) {
786                 GrPaint stencilPaint;
787                 stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
788                 op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
789                                          newCoverage, viewMatrix, isHairline, aaType, devBounds,
790                                          passes[p]);
791             } else {
792                 assert_alive(paint);
793                 op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
794                                          viewMatrix, isHairline, aaType, devBounds, passes[p]);
795             }
796             sdc->addDrawOp(clip, std::move(op));
797         }
798     }
799     return true;
800 }
801 
802 PathRenderer::StencilSupport
onGetStencilSupport(const GrStyledShape & shape) const803 DefaultPathRenderer::onGetStencilSupport(const GrStyledShape& shape) const {
804     if (single_pass_shape(shape)) {
805         return kNoRestriction_StencilSupport;
806     } else {
807         return kStencilOnly_StencilSupport;
808     }
809 }
810 
onCanDrawPath(const CanDrawPathArgs & args) const811 PathRenderer::CanDrawPath DefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
812     bool isHairline = GrIsStrokeHairlineOrEquivalent(
813             args.fShape->style(), *args.fViewMatrix, nullptr);
814     // If we aren't a single_pass_shape or hairline, we require stencil buffers.
815     if (!(single_pass_shape(*args.fShape) || isHairline) &&
816         !args.fProxy->canUseStencil(*args.fCaps)) {
817         return CanDrawPath::kNo;
818     }
819     // If antialiasing is required, we only support MSAA.
820     if (GrAAType::kNone != args.fAAType && GrAAType::kMSAA != args.fAAType) {
821         return CanDrawPath::kNo;
822     }
823     // This can draw any path with any simple fill style.
824     if (!args.fShape->style().isSimpleFill() && !isHairline) {
825         return CanDrawPath::kNo;
826     }
827     // Don't try to draw hairlines with DefaultPathRenderer if avoidLineDraws is true.
828     // Alternatively, we could try to implement hairline draws without line primitives in
829     // DefaultPathRenderer, but this is simpler.
830     if (args.fCaps->avoidLineDraws() && isHairline) {
831         return CanDrawPath::kNo;
832     }
833     // This is the fallback renderer for when a path is too complicated for the others to draw.
834     return CanDrawPath::kAsBackup;
835 }
836 
onDrawPath(const DrawPathArgs & args)837 bool DefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
838     GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
839                               "DefaultPathRenderer::onDrawPath");
840     GrAAType aaType = (GrAAType::kNone != args.fAAType) ? GrAAType::kMSAA : GrAAType::kNone;
841 
842     return this->internalDrawPath(
843             args.fSurfaceDrawContext, std::move(args.fPaint), aaType, *args.fUserStencilSettings,
844             args.fClip, *args.fViewMatrix, *args.fShape, false);
845 }
846 
onStencilPath(const StencilPathArgs & args)847 void DefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
848     GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
849                               "DefaultPathRenderer::onStencilPath");
850     SkASSERT(!args.fShape->inverseFilled());
851 
852     GrPaint paint;
853     paint.setXPFactory(GrDisableColorXPFactory::Get());
854 
855     auto aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
856 
857     this->internalDrawPath(
858             args.fSurfaceDrawContext, std::move(paint), aaType, GrUserStencilSettings::kUnused,
859             args.fClip, *args.fViewMatrix, *args.fShape, true);
860 }
861 
862 }  // namespace skgpu::ganesh
863