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