Coverage Report

Created: 2021-08-22 09:07

/src/skia/src/gpu/ops/DefaultPathRenderer.cpp
Line
Count
Source (jump to first uncovered line)
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
41
37.3k
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
37.3k
    if (shape.inverseFilled()) {
47
107
        return false;
48
107
    }
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
37.2k
    if (shape.style().isSimpleFill()) {
53
25.3k
        return shape.knownToBeConvex();
54
25.3k
    }
55
11.8k
    return true;
56
11.8k
#endif
57
11.8k
}
58
59
class PathGeoBuilder {
60
public:
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
5.81k
            , fMeshes(meshes) {
71
5.81k
        this->allocNewBuffers();
72
5.81k
    }
73
74
5.81k
    ~PathGeoBuilder() {
75
5.81k
        this->createMeshAndPutBackReserve();
76
5.81k
    }
77
78
    /**
79
     *  Path verbs
80
     */
81
837k
    void moveTo(const SkPoint& p) {
82
837k
        if (!this->ensureSpace(1)) {
83
0
            return;
84
0
        }
85
86
837k
        if (!this->isHairline()) {
87
809k
            fSubpathIndexStart = this->currentIndex();
88
809k
            fSubpathStartPoint = p;
89
809k
        }
90
837k
        *(fCurVert++) = p;
91
837k
    }
92
93
212k
    void addLine(const SkPoint pts[]) {
94
212k
        if (!this->ensureSpace(1, this->indexScale(), &pts[0])) {
95
0
            return;
96
0
        }
97
98
212k
        if (this->isIndexed()) {
99
112k
            uint16_t prevIdx = this->currentIndex() - 1;
100
112k
            this->appendCountourEdgeIndices(prevIdx);
101
112k
        }
102
212k
        *(fCurVert++) = pts[1];
103
212k
    }
104
105
106k
    void addQuad(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
106
106k
        if (!this->ensureSpace(GrPathUtils::kMaxPointsPerCurve,
107
106k
                             GrPathUtils::kMaxPointsPerCurve * this->indexScale(),
108
0
                             &pts[0])) {
109
0
            return;
110
0
        }
111
112
        // First pt of quad is the pt we ended on in previous step
113
106k
        uint16_t firstQPtIdx = this->currentIndex() - 1;
114
106k
        uint16_t numPts = (uint16_t)GrPathUtils::generateQuadraticPoints(
115
106k
                pts[0], pts[1], pts[2], srcSpaceTolSqd, &fCurVert,
116
106k
                GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
117
106k
        if (this->isIndexed()) {
118
3.69M
            for (uint16_t i = 0; i < numPts; ++i) {
119
3.59M
                this->appendCountourEdgeIndices(firstQPtIdx + i);
120
3.59M
            }
121
106k
        }
122
106k
    }
123
124
    void addConic(SkScalar weight, const SkPoint pts[], SkScalar srcSpaceTolSqd,
125
9.91k
                  SkScalar srcSpaceTol) {
126
9.91k
        SkAutoConicToQuads converter;
127
9.91k
        const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
128
109k
        for (int i = 0; i < converter.countQuads(); ++i) {
129
99.3k
            this->addQuad(quadPts + i * 2, srcSpaceTolSqd, srcSpaceTol);
130
99.3k
        }
131
9.91k
    }
132
133
258
    void addCubic(const SkPoint pts[], SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol) {
134
258
        if (!this->ensureSpace(GrPathUtils::kMaxPointsPerCurve,
135
258
                             GrPathUtils::kMaxPointsPerCurve * this->indexScale(),
136
0
                             &pts[0])) {
137
0
            return;
138
0
        }
139
140
        // First pt of cubic is the pt we ended on in previous step
141
258
        uint16_t firstCPtIdx = this->currentIndex() - 1;
142
258
        uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
143
258
                pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &fCurVert,
144
258
                GrPathUtils::cubicPointCount(pts, srcSpaceTol));
145
258
        if (this->isIndexed()) {
146
772
            for (uint16_t i = 0; i < numPts; ++i) {
147
514
                this->appendCountourEdgeIndices(firstCPtIdx + i);
148
514
            }
149
258
        }
150
258
    }
151
152
6.76k
    void addPath(const SkPath& path, SkScalar srcSpaceTol) {
153
6.76k
        SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
154
155
6.76k
        SkPath::Iter iter(path, false);
156
6.76k
        SkPoint pts[4];
157
158
6.76k
        bool done = false;
159
1.09M
        while (!done) {
160
1.09M
            SkPath::Verb verb = iter.next(pts);
161
1.09M
            switch (verb) {
162
837k
                case SkPath::kMove_Verb:
163
837k
                    this->moveTo(pts[0]);
164
837k
                    break;
165
212k
                case SkPath::kLine_Verb:
166
212k
                    this->addLine(pts);
167
212k
                    break;
168
9.91k
                case SkPath::kConic_Verb:
169
9.91k
                    this->addConic(iter.conicWeight(), pts, srcSpaceTolSqd, srcSpaceTol);
170
9.91k
                    break;
171
7.28k
                case SkPath::kQuad_Verb:
172
7.28k
                    this->addQuad(pts, srcSpaceTolSqd, srcSpaceTol);
173
7.28k
                    break;
174
258
                case SkPath::kCubic_Verb:
175
258
                    this->addCubic(pts, srcSpaceTolSqd, srcSpaceTol);
176
258
                    break;
177
17.1k
                case SkPath::kClose_Verb:
178
17.1k
                    break;
179
6.76k
                case SkPath::kDone_Verb:
180
6.76k
                    done = true;
181
1.09M
            }
182
1.09M
        }
183
6.76k
    }
184
185
2.34k
    static bool PathHasMultipleSubpaths(const SkPath& path) {
186
2.34k
        bool first = true;
187
188
2.34k
        SkPath::Iter iter(path, false);
189
2.34k
        SkPath::Verb verb;
190
191
2.34k
        SkPoint pts[4];
192
215k
        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
193
215k
            if (SkPath::kMove_Verb == verb && !first) {
194
2.26k
                return true;
195
2.26k
            }
196
213k
            first = false;
197
213k
        }
198
80
        return false;
199
2.34k
    }
200
201
private:
202
    /**
203
     *  Derived properties
204
     *  TODO: Cache some of these for better performance, rather than re-computing?
205
     */
206
337k
    bool isIndexed() const {
207
337k
        return GrPrimitiveType::kLines == fPrimitiveType ||
208
318k
               GrPrimitiveType::kTriangles == fPrimitiveType;
209
337k
    }
210
4.54M
    bool isHairline() const {
211
4.54M
        return GrPrimitiveType::kLines == fPrimitiveType ||
212
3.59M
               GrPrimitiveType::kLineStrip == fPrimitiveType;
213
4.54M
    }
214
331k
    int indexScale() const {
215
331k
        switch (fPrimitiveType) {
216
18.0k
            case GrPrimitiveType::kLines:
217
18.0k
                return 2;
218
213k
            case GrPrimitiveType::kTriangles:
219
213k
                return 3;
220
100k
            default:
221
100k
                return 0;
222
331k
        }
223
331k
    }
224
225
1.02M
    uint16_t currentIndex() const { return fCurVert - fVertices; }
226
227
    // Allocate vertex and (possibly) index buffers
228
6.13k
    void allocNewBuffers() {
229
6.13k
        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
6.13k
        static const int kMinVerticesPerChunk = GrPathUtils::kMaxPointsPerCurve + 2;
236
6.13k
        static const int kFallbackVerticesPerChunk = 16384;
237
238
6.13k
        fVertices = static_cast<SkPoint*>(fTarget->makeVertexSpaceAtLeast(fVertexStride,
239
6.13k
                                                                          kMinVerticesPerChunk,
240
6.13k
                                                                          kFallbackVerticesPerChunk,
241
6.13k
                                                                          &fVertexBuffer,
242
6.13k
                                                                          &fFirstVertex,
243
6.13k
                                                                          &fVerticesInChunk));
244
6.13k
        if (!fVertices) {
245
0
            SkDebugf("WARNING: Failed to allocate vertex buffer for DefaultPathRenderer.\n");
246
0
            fCurVert = nullptr;
247
0
            fCurIdx = fIndices = nullptr;
248
0
            fSubpathIndexStart = 0;
249
0
            fValid = false;
250
0
            return;
251
0
        }
252
253
6.13k
        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
6.08k
            const int kMinIndicesPerChunk = GrPathUtils::kMaxPointsPerCurve * this->indexScale();
258
6.08k
            const int kFallbackIndicesPerChunk = kFallbackVerticesPerChunk * this->indexScale();
259
260
6.08k
            fIndices = fTarget->makeIndexSpaceAtLeast(kMinIndicesPerChunk, kFallbackIndicesPerChunk,
261
6.08k
                                                      &fIndexBuffer, &fFirstIndex,
262
6.08k
                                                      &fIndicesInChunk);
263
6.08k
            if (!fIndices) {
264
0
                SkDebugf("WARNING: Failed to allocate index buffer for DefaultPathRenderer.\n");
265
0
                fVertices = nullptr;
266
0
                fValid = false;
267
0
            }
268
6.08k
        }
269
270
6.13k
        fCurVert = fVertices;
271
6.13k
        fCurIdx = fIndices;
272
6.13k
        fSubpathIndexStart = 0;
273
6.13k
    }
274
275
3.70M
    void appendCountourEdgeIndices(uint16_t edgeV0Idx) {
276
3.70M
        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
3.70M
        if (!this->isHairline()) {
281
2.78M
            *(fCurIdx++) = fSubpathIndexStart;
282
2.78M
        }
283
3.70M
        *(fCurIdx++) = edgeV0Idx;
284
3.70M
        *(fCurIdx++) = edgeV0Idx + 1;
285
3.70M
    }
286
287
    // Emits a single draw with all accumulated vertex/index data
288
6.13k
    void createMeshAndPutBackReserve() {
289
6.13k
        if (!fValid) {
290
0
            return;
291
0
        }
292
293
6.13k
        int vertexCount = fCurVert - fVertices;
294
6.13k
        int indexCount = fCurIdx - fIndices;
295
6.13k
        SkASSERT(vertexCount <= fVerticesInChunk);
296
6.13k
        SkASSERT(indexCount <= fIndicesInChunk);
297
298
6.13k
        GrSimpleMesh* mesh = nullptr;
299
6.13k
        if (this->isIndexed() ? SkToBool(indexCount) : SkToBool(vertexCount)) {
300
6.05k
            mesh = fTarget->allocMesh();
301
6.05k
            if (!this->isIndexed()) {
302
46
                mesh->set(std::move(fVertexBuffer), vertexCount, fFirstVertex);
303
6.01k
            } else {
304
6.01k
                mesh->setIndexed(std::move(fIndexBuffer), indexCount, fFirstIndex, 0,
305
6.01k
                                 vertexCount - 1, GrPrimitiveRestart::kNo, std::move(fVertexBuffer),
306
6.01k
                                 fFirstVertex);
307
6.01k
            }
308
6.05k
        }
309
310
6.13k
        fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
311
6.13k
        fTarget->putBackVertices((size_t)(fVerticesInChunk - vertexCount), fVertexStride);
312
313
6.13k
        if (mesh) {
314
6.05k
            fMeshes->push_back(mesh);
315
6.05k
        }
316
6.13k
    }
317
318
1.15M
    bool ensureSpace(int vertsNeeded, int indicesNeeded = 0, const SkPoint* lastPoint = nullptr) {
319
1.15M
        if (!fValid) {
320
0
            return false;
321
0
        }
322
323
1.15M
        if (fCurVert + vertsNeeded > fVertices + fVerticesInChunk ||
324
1.15M
            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
316
            this->createMeshAndPutBackReserve();
344
345
            // Get new buffers
346
316
            this->allocNewBuffers();
347
316
            if (!fValid) {
348
0
                return false;
349
0
            }
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
316
            if (lastPoint) {
354
                // Append copies of the points we saved so the two meshes will weld properly
355
269
                if (!this->isHairline()) {
356
205
                    *(fCurVert++) = fSubpathStartPoint;
357
205
                }
358
269
                *(fCurVert++) = *lastPoint;
359
269
            }
360
316
        }
361
362
1.15M
        return true;
363
1.15M
    }
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
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
9.24k
                            const GrUserStencilSettings* stencilSettings) {
404
9.24k
        return Helper::FactoryHelper<DefaultPathOp>(context, std::move(paint), path, tolerance,
405
9.24k
                                                    coverage, viewMatrix, isHairline, aaType,
406
9.24k
                                                    devBounds, stencilSettings);
407
9.24k
    }
408
409
0
    const char* name() const override { return "DefaultPathOp"; }
410
411
12.9k
    void visitProxies(const GrVisitProxyFunc& func) const override {
412
12.9k
        if (fProgramInfo) {
413
0
            fProgramInfo->visitFPProxies(func);
414
12.9k
        } else {
415
12.9k
            fHelper.visitProxies(func);
416
12.9k
        }
417
12.9k
    }
418
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
9.24k
            , fIsHairline(isHairline) {
429
9.24k
        fPaths.emplace_back(PathData{path, tolerance});
430
431
9.24k
        HasAABloat aaBloat = (aaType == GrAAType::kNone) ? HasAABloat ::kNo : HasAABloat::kYes;
432
9.24k
        this->setBounds(devBounds, aaBloat,
433
7.15k
                        isHairline ? IsHairline::kYes : IsHairline::kNo);
434
9.24k
    }
435
436
16.0k
    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
437
438
    GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
439
6.76k
                                      GrClampType clampType) override {
440
6.76k
        GrProcessorAnalysisCoverage gpCoverage =
441
6.38k
                this->coverage() == 0xFF ? GrProcessorAnalysisCoverage::kNone
442
387
                                         : GrProcessorAnalysisCoverage::kSingleChannel;
443
        // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
444
6.76k
        return fHelper.finalizeProcessors(caps, clip, clampType, gpCoverage, &fColor, nullptr);
445
6.76k
    }
446
447
private:
448
11.6k
    GrPrimitiveType primType() const {
449
11.6k
        if (this->isHairline()) {
450
2.39k
            int instanceCount = fPaths.count();
451
452
            // We avoid indices when we have a single hairline contour.
453
2.39k
            bool isIndexed = instanceCount > 1 ||
454
2.34k
                                PathGeoBuilder::PathHasMultipleSubpaths(fPaths[0].fPath);
455
456
2.31k
            return isIndexed ? GrPrimitiveType::kLines : GrPrimitiveType::kLineStrip;
457
2.39k
        }
458
459
9.23k
        return GrPrimitiveType::kTriangles;
460
9.23k
    }
461
462
0
    GrProgramInfo* programInfo() override { return fProgramInfo; }
463
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
5.81k
                             GrLoadOp colorLoadOp) override {
472
5.81k
        GrGeometryProcessor* gp;
473
5.81k
        {
474
5.81k
            using namespace GrDefaultGeoProcFactory;
475
5.81k
            Color color(this->color());
476
5.81k
            Coverage coverage(this->coverage());
477
869
            LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
478
4.94k
                                                              : LocalCoords::kUnused_Type);
479
5.81k
            gp = GrDefaultGeoProcFactory::Make(arena,
480
5.81k
                                               color,
481
5.81k
                                               coverage,
482
5.81k
                                               localCoords,
483
5.81k
                                               this->viewMatrix());
484
5.81k
        }
485
486
5.81k
        SkASSERT(gp->vertexStride() == sizeof(SkPoint));
487
488
5.81k
        fProgramInfo =  fHelper.createProgramInfoWithStencil(caps, arena, writeView,
489
5.81k
                                                             std::move(appliedClip),
490
5.81k
                                                             dstProxyView, gp, this->primType(),
491
5.81k
                                                             renderPassXferBarriers, colorLoadOp);
492
493
5.81k
    }
494
495
5.81k
    void onPrepareDraws(GrMeshDrawTarget* target) override {
496
5.81k
        PathGeoBuilder pathGeoBuilder(this->primType(), target, &fMeshes);
497
498
        // fill buffers
499
12.5k
        for (int i = 0; i < fPaths.count(); i++) {
500
6.76k
            const PathData& args = fPaths[i];
501
6.76k
            pathGeoBuilder.addPath(args.fPath, args.fTolerance);
502
6.76k
        }
503
5.81k
    }
504
505
5.81k
    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
506
5.81k
        if (!fProgramInfo) {
507
5.81k
            this->createProgramInfo(flushState);
508
5.81k
        }
509
510
5.81k
        if (!fProgramInfo || !fMeshes.count()) {
511
27
            return;
512
27
        }
513
514
5.79k
        flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
515
5.79k
        flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
516
11.8k
        for (int i = 0; i < fMeshes.count(); ++i) {
517
6.05k
            flushState->drawMesh(*fMeshes[i]);
518
6.05k
        }
519
5.79k
    }
520
521
5.31k
    CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
522
5.31k
        DefaultPathOp* that = t->cast<DefaultPathOp>();
523
5.31k
        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
524
2.89k
            return CombineResult::kCannotCombine;
525
2.89k
        }
526
527
2.41k
        if (this->color() != that->color()) {
528
0
            return CombineResult::kCannotCombine;
529
0
        }
530
531
2.41k
        if (this->coverage() != that->coverage()) {
532
0
            return CombineResult::kCannotCombine;
533
0
        }
534
535
2.41k
        if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
536
1.46k
            return CombineResult::kCannotCombine;
537
1.46k
        }
538
539
951
        if (this->isHairline() != that->isHairline()) {
540
0
            return CombineResult::kCannotCombine;
541
0
        }
542
543
951
        fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
544
951
        return CombineResult::kMerged;
545
951
    }
546
547
#if GR_TEST_UTILS
548
0
    SkString onDumpInfo() const override {
549
0
        SkString string = SkStringPrintf("Color: 0x%08x Count: %d\n",
550
0
                                         fColor.toBytes_RGBA(), fPaths.count());
551
0
        for (const auto& path : fPaths) {
552
0
            string.appendf("Tolerance: %.2f\n", path.fTolerance);
553
0
        }
554
0
        string += fHelper.dumpInfo();
555
0
        return string;
556
0
    }
557
#endif
558
559
10.6k
    const SkPMColor4f& color() const { return fColor; }
560
17.4k
    uint8_t coverage() const { return fCoverage; }
561
10.6k
    const SkMatrix& viewMatrix() const { return fViewMatrix; }
562
13.5k
    bool isHairline() const { return fIsHairline; }
563
564
    struct PathData {
565
        SkPath fPath;
566
        SkScalar fTolerance;
567
    };
568
569
    SkSTArray<1, PathData, true> fPaths;
570
    Helper fHelper;
571
    SkPMColor4f fColor;
572
    uint8_t fCoverage;
573
    SkMatrix fViewMatrix;
574
    bool fIsHairline;
575
576
    SkTDArray<GrSimpleMesh*> fMeshes;
577
    GrProgramInfo* fProgramInfo = nullptr;
578
579
    using INHERITED = GrMeshDrawOp;
580
};
581
582
}  // anonymous namespace
583
584
///////////////////////////////////////////////////////////////////////////////////////////////////
585
586
#if GR_TEST_UTILS
587
588
0
GR_DRAW_OP_TEST_DEFINE(DefaultPathOp) {
589
0
    SkMatrix viewMatrix = GrTest::TestMatrix(random);
590
591
    // For now just hairlines because the other types of draws require two ops.
592
    // TODO we should figure out a way to combine the stencil and cover steps into one op.
593
0
    GrStyle style(SkStrokeRec::kHairline_InitStyle);
594
0
    const SkPath& path = GrTest::TestPath(random);
595
596
    // Compute srcSpaceTol
597
0
    SkRect bounds = path.getBounds();
598
0
    SkScalar tol = GrPathUtils::kDefaultTolerance;
599
0
    SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
600
601
0
    viewMatrix.mapRect(&bounds);
602
0
    uint8_t coverage = GrTest::RandomCoverage(random);
603
0
    GrAAType aaType = GrAAType::kNone;
604
0
    if (numSamples > 1 && random->nextBool()) {
605
0
        aaType = GrAAType::kMSAA;
606
0
    }
607
0
    return DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, coverage, viewMatrix,
608
0
                               true, aaType, bounds, GrGetRandomStencil(random, context));
609
0
}
610
611
#endif
612
613
///////////////////////////////////////////////////////////////////////////////////////////////////
614
615
namespace skgpu::v1 {
616
617
bool DefaultPathRenderer::internalDrawPath(skgpu::v1::SurfaceDrawContext* sdc,
618
                                           GrPaint&& paint,
619
                                           GrAAType aaType,
620
                                           const GrUserStencilSettings& userStencilSettings,
621
                                           const GrClip* clip,
622
                                           const SkMatrix& viewMatrix,
623
                                           const GrStyledShape& shape,
624
9.24k
                                           bool stencilOnly) {
625
9.24k
    auto context = sdc->recordingContext();
626
627
9.24k
    SkASSERT(GrAAType::kCoverage != aaType);
628
9.24k
    SkPath path;
629
9.24k
    shape.asPath(&path);
630
631
9.24k
    SkScalar hairlineCoverage;
632
9.24k
    uint8_t newCoverage = 0xff;
633
9.24k
    bool isHairline = false;
634
9.24k
    if (GrIsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
635
2.09k
        newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
636
2.09k
        isHairline = true;
637
7.15k
    } else {
638
7.15k
        SkASSERT(shape.style().isSimpleFill());
639
7.15k
    }
640
641
9.24k
    int                          passCount = 0;
642
9.24k
    const GrUserStencilSettings* passes[2];
643
9.24k
    bool                         reverse = false;
644
9.24k
    bool                         lastPassIsBounds;
645
646
9.24k
    if (isHairline) {
647
2.09k
        passCount = 1;
648
2.09k
        if (stencilOnly) {
649
0
            passes[0] = &gDirectToStencil;
650
2.09k
        } else {
651
2.09k
            passes[0] = &userStencilSettings;
652
2.09k
        }
653
2.09k
        lastPassIsBounds = false;
654
7.15k
    } else {
655
7.15k
        if (single_pass_shape(shape)) {
656
2.19k
            passCount = 1;
657
2.19k
            if (stencilOnly) {
658
0
                passes[0] = &gDirectToStencil;
659
2.19k
            } else {
660
2.19k
                passes[0] = &userStencilSettings;
661
2.19k
            }
662
2.19k
            lastPassIsBounds = false;
663
4.96k
        } else {
664
4.96k
            switch (path.getFillType()) {
665
1
                case SkPathFillType::kInverseEvenOdd:
666
1
                    reverse = true;
667
1
                    [[fallthrough]];
668
1
                case SkPathFillType::kEvenOdd:
669
1
                    passes[0] = &gEOStencilPass;
670
1
                    if (stencilOnly) {
671
0
                        passCount = 1;
672
0
                        lastPassIsBounds = false;
673
1
                    } else {
674
1
                        passCount = 2;
675
1
                        lastPassIsBounds = true;
676
1
                        if (reverse) {
677
1
                            passes[1] = &gInvEOColorPass;
678
0
                        } else {
679
0
                            passes[1] = &gEOColorPass;
680
0
                        }
681
1
                    }
682
1
                    break;
683
684
0
                case SkPathFillType::kInverseWinding:
685
0
                    reverse = true;
686
0
                    [[fallthrough]];
687
4.96k
                case SkPathFillType::kWinding:
688
4.96k
                    passes[0] = &gWindStencilPass;
689
4.96k
                    passCount = 2;
690
4.96k
                    if (stencilOnly) {
691
8
                        lastPassIsBounds = false;
692
8
                        --passCount;
693
4.95k
                    } else {
694
4.95k
                        lastPassIsBounds = true;
695
4.95k
                        if (reverse) {
696
0
                            passes[passCount-1] = &gInvWindColorPass;
697
4.95k
                        } else {
698
4.95k
                            passes[passCount-1] = &gWindColorPass;
699
4.95k
                        }
700
4.95k
                    }
701
4.96k
                    break;
702
0
                default:
703
0
                    SkDEBUGFAIL("Unknown path fFill!");
704
0
                    return false;
705
9.24k
            }
706
9.24k
        }
707
7.15k
    }
708
709
9.24k
    SkScalar tol = GrPathUtils::kDefaultTolerance;
710
9.24k
    SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
711
712
9.24k
    SkRect devBounds;
713
9.24k
    GetPathDevBounds(path, sdc->asRenderTargetProxy()->backingStoreDimensions(),
714
9.24k
                     viewMatrix, &devBounds);
715
716
23.4k
    for (int p = 0; p < passCount; ++p) {
717
14.2k
        if (lastPassIsBounds && (p == passCount-1)) {
718
4.95k
            SkRect bounds;
719
4.95k
            SkMatrix localMatrix = SkMatrix::I();
720
4.95k
            if (reverse) {
721
                // draw over the dev bounds (which will be the whole dst surface for inv fill).
722
1
                bounds = devBounds;
723
1
                SkMatrix vmi;
724
                // mapRect through persp matrix may not be correct
725
1
                if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
726
1
                    vmi.mapRect(&bounds);
727
0
                } else {
728
0
                    if (!viewMatrix.invert(&localMatrix)) {
729
0
                        return false;
730
0
                    }
731
4.95k
                }
732
4.95k
            } else {
733
4.95k
                bounds = path.getBounds();
734
4.95k
            }
735
4.95k
            const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
736
4.95k
                                                                               viewMatrix;
737
            // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start
738
4.95k
            assert_alive(paint);
739
4.95k
            sdc->stencilRect(clip, passes[p], std::move(paint),
740
4.95k
                             GrAA(aaType == GrAAType::kMSAA), viewM, bounds,
741
4.95k
                             &localMatrix);
742
9.24k
        } else {
743
9.24k
            bool stencilPass = stencilOnly || passCount > 1;
744
9.24k
            GrOp::Owner op;
745
9.24k
            if (stencilPass) {
746
4.96k
                GrPaint stencilPaint;
747
4.96k
                stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
748
4.96k
                op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol,
749
4.96k
                                         newCoverage, viewMatrix, isHairline, aaType, devBounds,
750
4.96k
                                         passes[p]);
751
4.28k
            } else {
752
4.28k
                assert_alive(paint);
753
4.28k
                op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage,
754
4.28k
                                         viewMatrix, isHairline, aaType, devBounds, passes[p]);
755
4.28k
            }
756
9.24k
            sdc->addDrawOp(clip, std::move(op));
757
9.24k
        }
758
14.2k
    }
759
9.24k
    return true;
760
9.24k
}
761
762
763
PathRenderer::StencilSupport
764
1.89k
DefaultPathRenderer::onGetStencilSupport(const GrStyledShape& shape) const {
765
1.89k
    if (single_pass_shape(shape)) {
766
1.88k
        return kNoRestriction_StencilSupport;
767
8
    } else {
768
8
        return kStencilOnly_StencilSupport;
769
8
    }
770
1.89k
}
771
772
28.2k
PathRenderer::CanDrawPath DefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
773
28.2k
    bool isHairline = GrIsStrokeHairlineOrEquivalent(
774
28.2k
            args.fShape->style(), *args.fViewMatrix, nullptr);
775
    // If we aren't a single_pass_shape or hairline, we require stencil buffers.
776
28.2k
    if (!(single_pass_shape(*args.fShape) || isHairline) &&
777
14.2k
        !args.fProxy->canUseStencil(*args.fCaps)) {
778
0
        return CanDrawPath::kNo;
779
0
    }
780
    // If antialiasing is required, we only support MSAA.
781
28.2k
    if (GrAAType::kNone != args.fAAType && GrAAType::kMSAA != args.fAAType) {
782
15.3k
        return CanDrawPath::kNo;
783
15.3k
    }
784
    // This can draw any path with any simple fill style.
785
12.8k
    if (!args.fShape->style().isSimpleFill() && !isHairline) {
786
3.62k
        return CanDrawPath::kNo;
787
3.62k
    }
788
    // This is the fallback renderer for when a path is too complicated for the others to draw.
789
9.24k
    return CanDrawPath::kAsBackup;
790
9.24k
}
791
792
9.24k
bool DefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
793
9.24k
    GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
794
9.24k
                              "DefaultPathRenderer::onDrawPath");
795
9.24k
    GrAAType aaType = (GrAAType::kNone != args.fAAType) ? GrAAType::kMSAA : GrAAType::kNone;
796
797
9.24k
    return this->internalDrawPath(
798
9.24k
            args.fSurfaceDrawContext, std::move(args.fPaint), aaType, *args.fUserStencilSettings,
799
9.24k
            args.fClip, *args.fViewMatrix, *args.fShape, false);
800
9.24k
}
801
802
8
void DefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
803
8
    GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
804
8
                              "DefaultPathRenderer::onStencilPath");
805
8
    SkASSERT(!args.fShape->inverseFilled());
806
807
8
    GrPaint paint;
808
8
    paint.setXPFactory(GrDisableColorXPFactory::Get());
809
810
8
    auto aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
811
812
8
    this->internalDrawPath(
813
8
            args.fSurfaceDrawContext, std::move(paint), aaType, GrUserStencilSettings::kUnused,
814
8
            args.fClip, *args.fViewMatrix, *args.fShape, true);
815
8
}
816
817
} // namespace skgpu::v1