Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/gpu/ganesh/ops/PathInnerTriangulateOp.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2019 Google LLC.
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/PathInnerTriangulateOp.h"
9
10
#include "src/gpu/ganesh/GrEagerVertexAllocator.h"
11
#include "src/gpu/ganesh/GrGpu.h"
12
#include "src/gpu/ganesh/GrOpFlushState.h"
13
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
14
#include "src/gpu/ganesh/GrResourceProvider.h"
15
#include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
16
#include "src/gpu/ganesh/tessellate/GrPathTessellationShader.h"
17
#include "src/gpu/ganesh/tessellate/PathTessellator.h"
18
19
using namespace skia_private;
20
21
#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
22
23
namespace skgpu::ganesh {
24
25
namespace {
26
27
// Fills an array of convex hulls surrounding 4-point cubic or conic instances. This shader is used
28
// for the "cover" pass after the curves have been fully stencilled.
29
class HullShader : public GrPathTessellationShader {
30
public:
31
    HullShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
32
            : GrPathTessellationShader(kTessellate_HullShader_ClassID,
33
                                       GrPrimitiveType::kTriangleStrip,
34
                                       viewMatrix,
35
                                       color,
36
0
                                       PatchAttribs::kNone) {
37
0
        fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
38
0
        fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
39
0
        if (!shaderCaps.fInfinitySupport) {
40
            // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
41
            // infinity can't detect this. On these platforms we also write out an extra float with
42
            // each patch that explicitly tells the shader what type of curve it is.
43
0
            fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, SkSLType::kFloat);
44
0
        }
45
0
        this->setInstanceAttributesWithImplicitOffsets(fInstanceAttribs.data(),
46
0
                                                       fInstanceAttribs.size());
47
0
        SkASSERT(fInstanceAttribs.size() <= kMaxInstanceAttribCount);
48
49
0
        if (!shaderCaps.fVertexIDSupport) {
50
0
            constexpr static Attribute kVertexIdxAttrib("vertexidx", kFloat_GrVertexAttribType,
51
0
                                                        SkSLType::kFloat);
52
0
            this->setVertexAttributesWithImplicitOffsets(&kVertexIdxAttrib, 1);
53
0
        }
54
0
    }
Unexecuted instantiation: PathInnerTriangulateOp.cpp:skgpu::ganesh::(anonymous namespace)::HullShader::HullShader(SkMatrix const&, SkRGBA4f<(SkAlphaType)2>, GrShaderCaps const&)
Unexecuted instantiation: PathInnerTriangulateOp.cpp:skgpu::ganesh::(anonymous namespace)::HullShader::HullShader(SkMatrix const&, SkRGBA4f<(SkAlphaType)2>, GrShaderCaps const&)
55
56
private:
57
0
    const char* name() const final { return "tessellate_HullShader"; }
58
0
    void addToKey(const GrShaderCaps&, KeyBuilder*) const final {}
59
    std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
60
61
    constexpr static int kMaxInstanceAttribCount = 3;
62
    STArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs;
63
};
64
65
std::unique_ptr<GrGeometryProcessor::ProgramImpl> HullShader::makeProgramImpl(
66
0
        const GrShaderCaps&) const {
67
0
    class Impl : public GrPathTessellationShader::Impl {
68
0
        void emitVertexCode(const GrShaderCaps& shaderCaps,
69
0
                            const GrPathTessellationShader&,
70
0
                            GrGLSLVertexBuilder* v,
71
0
                            GrGLSLVaryingHandler*,
72
0
                            GrGPArgs* gpArgs) override {
73
0
            if (shaderCaps.fInfinitySupport) {
74
0
                v->insertFunction(
75
0
                "bool is_conic_curve() { return isinf(p23.w); }"
76
0
                "bool is_non_triangular_conic_curve() {"
77
                    // We consider a conic non-triangular as long as its weight isn't infinity.
78
                    // NOTE: "isinf == false" works on Mac Radeon GLSL; "!isinf" can get the wrong
79
                    // answer.
80
0
                    "return isinf(p23.z) == false;"
81
0
                "}"
82
0
                );
83
0
            } else {
84
0
                v->insertFunction(SkStringPrintf(
85
0
                "bool is_conic_curve() { return curveType != %g; }",
86
0
                        tess::kCubicCurveType).c_str());
87
0
                v->insertFunction(SkStringPrintf(
88
0
                "bool is_non_triangular_conic_curve() {"
89
0
                    "return curveType == %g;"
90
0
                "}", tess::kConicCurveType).c_str());
91
0
            }
92
0
            v->codeAppend(
93
0
            "float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;"
94
0
            "if (is_conic_curve()) {"
95
                // Conics are 3 points, with the weight in p3.
96
0
                "float w = p3.x;"
97
0
                "p3 = p2;"  // Duplicate the endpoint for shared code that also runs on cubics.
98
0
                "if (is_non_triangular_conic_curve()) {"
99
                    // Convert the points to a trapeziodal hull that circumcscribes the conic.
100
0
                    "float2 p1w = p1 * w;"
101
0
                    "float T = .51;"  // Bias outward a bit to ensure we cover the outermost samples.
102
0
                    "float2 c1 = mix(p0, p1w, T);"
103
0
                    "float2 c2 = mix(p2, p1w, T);"
104
0
                    "float iw = 1 / mix(1, w, T);"
105
0
                    "p2 = c2 * iw;"
106
0
                    "p1 = c1 * iw;"
107
0
                "}"
108
0
            "}"
109
110
            // Translate the points to v0..3 where v0=0.
111
0
            "float2 v1 = p1 - p0;"
112
0
            "float2 v2 = p2 - p0;"
113
0
            "float2 v3 = p3 - p0;"
114
115
            // Reorder the points so v2 bisects v1 and v3.
116
0
            "if (sign(cross_length_2d(v2, v1)) == sign(cross_length_2d(v2, v3))) {"
117
0
                "float2 tmp = p2;"
118
0
                "if (sign(cross_length_2d(v1, v2)) != sign(cross_length_2d(v1, v3))) {"
119
0
                    "p2 = p1;"  // swap(p2, p1)
120
0
                    "p1 = tmp;"
121
0
                "} else {"
122
0
                    "p2 = p3;"  // swap(p2, p3)
123
0
                    "p3 = tmp;"
124
0
                "}"
125
0
            "}"
126
0
            );
127
128
0
            if (shaderCaps.fVertexIDSupport) {
129
                // If we don't have sk_VertexID support then "vertexidx" already came in as a
130
                // vertex attrib.
131
0
                v->codeAppend(
132
                // sk_VertexID comes in fan order. Convert to strip order.
133
0
                "int vertexidx = sk_VertexID;"
134
0
                "vertexidx ^= vertexidx >> 1;");
135
0
            }
136
137
0
            v->codeAppend(
138
            // Find the "turn direction" of each corner and net turn direction.
139
0
            "float vertexdir = 0;"
140
0
            "float netdir = 0;"
141
0
            "float2 prev, next;"
142
0
            "float dir;"
143
0
            "float2 localcoord;"
144
0
            "float2 nextcoord;"
145
0
            );
146
147
0
            for (int i = 0; i < 4; ++i) {
148
0
                v->codeAppendf(
149
0
                "prev = p%i - p%i;", i, (i + 3) % 4);
150
0
                v->codeAppendf(
151
0
                "next = p%i - p%i;", (i + 1) % 4, i);
152
0
                v->codeAppendf(
153
0
                "dir = sign(cross_length_2d(prev, next));"
154
0
                "if (vertexidx == %i) {"
155
0
                    "vertexdir = dir;"
156
0
                    "localcoord = p%i;"
157
0
                    "nextcoord = p%i;"
158
0
                "}"
159
0
                "netdir += dir;", i, i, (i + 1) % 4);
160
0
            }
161
162
0
            v->codeAppend(
163
            // Remove the non-convex vertex, if any.
164
0
            "if (vertexdir != sign(netdir)) {"
165
0
                "localcoord = nextcoord;"
166
0
            "}"
167
168
0
            "float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;");
169
0
            gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord");
170
0
            gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertexpos");
171
0
        }
172
0
    };
173
0
    return std::make_unique<Impl>();
174
0
}
175
176
}  // anonymous namespace
177
178
0
void PathInnerTriangulateOp::visitProxies(const GrVisitProxyFunc& func) const {
179
0
    if (fPipelineForFills) {
180
0
        fPipelineForFills->visitProxies(func);
181
0
    } else {
182
0
        fProcessors.visitProxies(func);
183
0
    }
184
0
}
185
186
0
GrDrawOp::FixedFunctionFlags PathInnerTriangulateOp::fixedFunctionFlags() const {
187
0
    auto flags = FixedFunctionFlags::kUsesStencil;
188
0
    if (GrAAType::kNone != fAAType) {
189
0
        flags |= FixedFunctionFlags::kUsesHWAA;
190
0
    }
191
0
    return flags;
192
0
}
193
194
GrProcessorSet::Analysis PathInnerTriangulateOp::finalize(const GrCaps& caps,
195
                                                          const GrAppliedClip* clip,
196
0
                                                          GrClampType clampType) {
197
0
    return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
198
0
                                clampType, &fColor);
199
0
}
200
201
void PathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args,
202
                                                   const GrPipeline* pipelineForStencils,
203
0
                                                   const GrUserStencilSettings* stencil) {
204
0
    SkASSERT(pipelineForStencils);
205
0
    auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
206
0
                                                                     SK_PMColor4fTRANSPARENT);
207
0
    fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils,
208
0
                                                             stencil)); }
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanStencilProgram(GrTessellationShader::ProgramArgs const&, GrPipeline const*, GrUserStencilSettings const*)
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanStencilProgram(GrTessellationShader::ProgramArgs const&, GrPipeline const*, GrUserStencilSettings const*)
209
210
void PathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args,
211
0
                                                const GrUserStencilSettings* stencil) {
212
0
    SkASSERT(fPipelineForFills);
213
0
    auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
214
0
                                                                     fColor);
215
0
    fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills,
216
0
                                                             stencil));
217
0
}
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanFillProgram(GrTessellationShader::ProgramArgs const&, GrUserStencilSettings const*)
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanFillProgram(GrTessellationShader::ProgramArgs const&, GrUserStencilSettings const*)
218
219
void PathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
220
0
                                                GrAppliedClip&& appliedClip) {
221
0
    SkASSERT(!fFanTriangulator);
222
0
    SkASSERT(!fFanPolys);
223
0
    SkASSERT(!fPipelineForFills);
224
0
    SkASSERT(!fTessellator);
225
0
    SkASSERT(!fStencilCurvesProgram);
226
0
    SkASSERT(fFanPrograms.empty());
227
0
    SkASSERT(!fCoverHullsProgram);
228
229
0
    if (fPath.countVerbs() <= 0) {
230
0
        return;
231
0
    }
232
233
    // If using wireframe, we have to fall back on a standard Redbook "stencil then cover" algorithm
234
    // instead of bypassing the stencil buffer to fill the fan directly.
235
0
    bool forceRedbookStencilPass =
236
0
            (fPathFlags & (FillPathFlags::kStencilOnly | FillPathFlags::kWireframe));
237
0
    bool doFill = !(fPathFlags & FillPathFlags::kStencilOnly);
238
239
0
    bool isLinear;
240
0
    fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena);
241
0
    fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear);
242
243
    // Create a pipeline for stencil passes if needed.
244
0
    const GrPipeline* pipelineForStencils = nullptr;
245
0
    if (forceRedbookStencilPass || !isLinear) {  // Curves always get stencilled.
246
0
        auto pipelineFlags = (fPathFlags & FillPathFlags::kWireframe)
247
0
                ? GrPipeline::InputFlags::kWireframe
248
0
                : GrPipeline::InputFlags::kNone;
249
0
        pipelineForStencils = GrPathTessellationShader::MakeStencilOnlyPipeline(
250
0
                args, fAAType, appliedClip.hardClip(), pipelineFlags);
251
0
    }
252
253
    // Create a pipeline for fill passes if needed.
254
0
    if (doFill) {
255
0
        fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType,
256
0
                                                               std::move(appliedClip),
257
0
                                                               std::move(fProcessors));
258
0
    }
259
260
    // Pass 1: Tessellate the outer curves into the stencil buffer.
261
0
    if (!isLinear) {
262
0
        fTessellator = PathCurveTessellator::Make(args.fArena,
263
0
                                                  args.fCaps->shaderCaps()->fInfinitySupport);
264
0
        auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(),
265
0
                                                          args.fArena,
266
0
                                                          fViewMatrix,
267
0
                                                          SK_PMColor4fTRANSPARENT,
268
0
                                                          fTessellator->patchAttribs());
269
0
        const GrUserStencilSettings* stencilPathSettings =
270
0
                GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
271
0
        fStencilCurvesProgram = GrTessellationShader::MakeProgram(args,
272
0
                                                                  tessShader,
273
0
                                                                  pipelineForStencils,
274
0
                                                                  stencilPathSettings);
275
0
    }
276
277
    // Pass 2: Fill the path's inner fan with a stencil test against the curves.
278
0
    if (fFanPolys) {
279
0
        if (forceRedbookStencilPass) {
280
            // Use a standard Redbook "stencil then cover" algorithm instead of bypassing the
281
            // stencil buffer to fill the fan directly.
282
0
            const GrUserStencilSettings* stencilPathSettings =
283
0
                    GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
284
0
            this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings);
285
0
            if (doFill) {
286
0
                this->pushFanFillProgram(args,
287
0
                                         GrPathTessellationShader::TestAndResetStencilSettings());
288
0
            }
289
0
        } else if (isLinear) {
290
            // There are no outer curves! Ignore stencil and fill the path directly.
291
0
            SkASSERT(!pipelineForStencils);
292
0
            this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
293
0
        } else if (!fPipelineForFills->hasStencilClip()) {
294
            // These are a twist on the standard Redbook stencil settings that allow us to fill the
295
            // inner polygon directly to the final render target. By the time these programs
296
            // execute, the outer curves will already be stencilled in. So if the stencil value is
297
            // zero, then it means the sample in question is not affected by any curves and we can
298
            // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
299
            // continue the standard Redbook counting process.
300
0
            constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
301
0
                GrUserStencilSettings::StaticInitSeparate<
302
0
                    0x0000,                       0x0000,
303
0
                    GrUserStencilTest::kEqual,    GrUserStencilTest::kEqual,
304
0
                    0xffff,                       0xffff,
305
0
                    GrUserStencilOp::kKeep,       GrUserStencilOp::kKeep,
306
0
                    GrUserStencilOp::kIncWrap,    GrUserStencilOp::kDecWrap,
307
0
                    0xffff,                       0xffff>());
308
309
0
            constexpr static GrUserStencilSettings kFillOrInvertStencil(
310
0
                GrUserStencilSettings::StaticInit<
311
0
                    0x0000,
312
0
                    GrUserStencilTest::kEqual,
313
0
                    0xffff,
314
0
                    GrUserStencilOp::kKeep,
315
                    // "Zero" instead of "Invert" because the fan only touches any given pixel once.
316
0
                    GrUserStencilOp::kZero,
317
0
                    0xffff>());
318
319
0
            auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
320
0
                    ? &kFillOrIncrDecrStencil
321
0
                    : &kFillOrInvertStencil;
322
0
            this->pushFanFillProgram(args, stencil);
323
0
        } else {
324
            // This is the same idea as above, but we use two passes instead of one because there is
325
            // a stencil clip. The stencil test isn't expressive enough to do the above tests and
326
            // also check the clip bit in a single pass.
327
0
            constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
328
0
                GrUserStencilSettings::StaticInit<
329
0
                    0x0000,
330
0
                    GrUserStencilTest::kEqualIfInClip,
331
0
                    0xffff,
332
0
                    GrUserStencilOp::kKeep,
333
0
                    GrUserStencilOp::kKeep,
334
0
                    0xffff>());
335
336
0
            constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
337
0
                GrUserStencilSettings::StaticInitSeparate<
338
0
                    0x0000,                         0x0000,
339
                    // No need to check the clip because the previous stencil pass will have only
340
                    // written to samples already inside the clip.
341
0
                    GrUserStencilTest::kNotEqual,   GrUserStencilTest::kNotEqual,
342
0
                    0xffff,                         0xffff,
343
0
                    GrUserStencilOp::kIncWrap,      GrUserStencilOp::kDecWrap,
344
0
                    GrUserStencilOp::kKeep,         GrUserStencilOp::kKeep,
345
0
                    0xffff,                         0xffff>());
346
347
0
            constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
348
0
                GrUserStencilSettings::StaticInit<
349
0
                    0x0000,
350
                    // No need to check the clip because the previous stencil pass will have only
351
                    // written to samples already inside the clip.
352
0
                    GrUserStencilTest::kNotEqual,
353
0
                    0xffff,
354
                    // "Zero" instead of "Invert" because the fan only touches any given pixel once.
355
0
                    GrUserStencilOp::kZero,
356
0
                    GrUserStencilOp::kKeep,
357
0
                    0xffff>());
358
359
            // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
360
0
            this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
361
362
            // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
363
0
            auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
364
0
                    ? &kIncrDecrStencilIfNonzero
365
0
                    : &kInvertStencilIfNonZero;
366
0
            this->pushFanStencilProgram(args, pipelineForStencils, stencil);
367
0
        }
368
0
    }
369
370
    // Pass 3: Draw convex hulls around each curve.
371
0
    if (doFill && !isLinear) {
372
        // By the time this program executes, every pixel will be filled in except the ones touched
373
        // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
374
        // This will fill in any remaining samples and reset the stencil values back to zero.
375
0
        SkASSERT(fTessellator);
376
0
        auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor,
377
0
                                                         *args.fCaps->shaderCaps());
378
0
        fCoverHullsProgram = GrTessellationShader::MakeProgram(
379
0
                args, hullShader, fPipelineForFills,
380
0
                GrPathTessellationShader::TestAndResetStencilSettings());
381
0
    }
382
0
}
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::prePreparePrograms(GrTessellationShader::ProgramArgs const&, GrAppliedClip&&)
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::prePreparePrograms(GrTessellationShader::ProgramArgs const&, GrAppliedClip&&)
383
384
void PathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
385
                                          const GrSurfaceProxyView& writeView,
386
                                          GrAppliedClip* clip,
387
                                          const GrDstProxyView& dstProxyView,
388
                                          GrXferBarrierFlags renderPassXferBarriers,
389
0
                                          GrLoadOp colorLoadOp) {
390
    // DMSAA is not supported on DDL.
391
0
    bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
392
0
    this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
393
0
                             &dstProxyView, renderPassXferBarriers, colorLoadOp,
394
0
                             context->priv().caps()},
395
0
                             (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
396
0
    if (fStencilCurvesProgram) {
397
0
        context->priv().recordProgramInfo(fStencilCurvesProgram);
398
0
    }
399
0
    for (const GrProgramInfo* fanProgram : fFanPrograms) {
400
0
        context->priv().recordProgramInfo(fanProgram);
401
0
    }
402
0
    if (fCoverHullsProgram) {
403
0
        context->priv().recordProgramInfo(fCoverHullsProgram);
404
0
    }
405
0
}
406
407
SKGPU_DECLARE_STATIC_UNIQUE_KEY(gHullVertexBufferKey);
408
409
0
void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
410
0
    const GrCaps& caps = flushState->caps();
411
412
0
    if (!fFanTriangulator) {
413
0
        this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
414
0
                                 flushState->usesMSAASurface(), &flushState->dstProxyView(),
415
0
                                 flushState->renderPassBarriers(), flushState->colorLoadOp(),
416
0
                                 &caps}, flushState->detachAppliedClip());
417
0
        if (!fFanTriangulator) {
418
0
            return;
419
0
        }
420
0
    }
421
422
0
    if (fFanPolys) {
423
0
        GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
424
0
        fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
425
0
    }
426
427
0
    if (fTessellator) {
428
0
        auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
429
0
        fTessellator->prepareWithTriangles(flushState,
430
0
                                           tessShader->viewMatrix(),
431
0
                                           &fFanBreadcrumbs,
432
0
                                           {SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT},
433
0
                                           fPath.countVerbs());
434
0
    }
435
436
0
    if (!caps.shaderCaps()->fVertexIDSupport) {
437
0
        constexpr static float kStripOrderIDs[4] = {0, 1, 3, 2};
438
439
0
        SKGPU_DEFINE_STATIC_UNIQUE_KEY(gHullVertexBufferKey);
440
441
0
        fHullVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
442
0
                GrGpuBufferType::kVertex, sizeof(kStripOrderIDs), kStripOrderIDs,
443
0
                gHullVertexBufferKey);
444
0
    }
445
0
}
446
447
0
void PathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
448
0
    if (fCoverHullsProgram &&
449
0
        fCoverHullsProgram->geomProc().hasVertexAttributes() &&
450
0
        !fHullVertexBufferIfNoIDSupport) {
451
0
        return;
452
0
    }
453
454
0
    if (fStencilCurvesProgram) {
455
0
        SkASSERT(fTessellator);
456
0
        flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
457
0
        fTessellator->draw(flushState);
458
0
    }
459
460
    // Allocation of the fan vertex buffer may have failed but we already pushed back fan programs.
461
0
    if (fFanBuffer) {
462
0
        for (const GrProgramInfo* fanProgram : fFanPrograms) {
463
0
            flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
464
0
            flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
465
0
            flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
466
0
            flushState->draw(fFanVertexCount, fBaseFanVertex);
467
0
        }
468
0
    }
469
470
0
    if (fCoverHullsProgram) {
471
0
        SkASSERT(fTessellator);
472
0
        flushState->bindPipelineAndScissorClip(*fCoverHullsProgram, this->bounds());
473
0
        flushState->bindTextures(fCoverHullsProgram->geomProc(), nullptr, *fPipelineForFills);
474
0
        fTessellator->drawHullInstances(flushState, fHullVertexBufferIfNoIDSupport);
475
0
    }
476
0
}
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::onExecute(GrOpFlushState*, SkRect const&)
Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::onExecute(GrOpFlushState*, SkRect const&)
477
478
}  // namespace skgpu::ganesh
479
480
#endif // SK_ENABLE_OPTIMIZE_SIZE