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