Coverage Report

Created: 2021-08-22 09:07

/src/skia/src/gpu/tessellate/GrPathWedgeTessellator.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2021 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/tessellate/GrPathWedgeTessellator.h"
9
10
#include "src/gpu/GrMeshDrawTarget.h"
11
#include "src/gpu/GrResourceProvider.h"
12
#include "src/gpu/geometry/GrPathUtils.h"
13
#include "src/gpu/geometry/GrWangsFormula.h"
14
#include "src/gpu/tessellate/GrCullTest.h"
15
#include "src/gpu/tessellate/GrPathXform.h"
16
#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
17
18
namespace {
19
20
constexpr static float kPrecision = GrTessellationShader::kLinearizationPrecision;
21
22
// Parses out each contour in a path and tracks the midpoint. Example usage:
23
//
24
//   SkTPathContourParser parser;
25
//   while (parser.parseNextContour()) {
26
//       SkPoint midpoint = parser.currentMidpoint();
27
//       for (auto [verb, pts] : parser.currentContour()) {
28
//           ...
29
//       }
30
//   }
31
//
32
class MidpointContourParser {
33
public:
34
    MidpointContourParser(const SkPath& path)
35
            : fPath(path)
36
            , fVerbs(SkPathPriv::VerbData(fPath))
37
            , fNumRemainingVerbs(fPath.countVerbs())
38
            , fPoints(SkPathPriv::PointData(fPath))
39
0
            , fWeights(SkPathPriv::ConicWeightData(fPath)) {}
40
    // Advances the internal state to the next contour in the path. Returns false if there are no
41
    // more contours.
42
0
    bool parseNextContour() {
43
0
        bool hasGeometry = false;
44
0
        for (; fVerbsIdx < fNumRemainingVerbs; ++fVerbsIdx) {
45
0
            switch (fVerbs[fVerbsIdx]) {
46
0
                case SkPath::kMove_Verb:
47
0
                    if (!hasGeometry) {
48
0
                        fMidpoint = fPoints[fPtsIdx];
49
0
                        fMidpointWeight = 1;
50
0
                        this->advance();
51
0
                        ++fPtsIdx;
52
0
                        continue;
53
0
                    }
54
0
                    return true;
55
0
                default:
56
0
                    continue;
57
0
                case SkPath::kLine_Verb:
58
0
                    ++fPtsIdx;
59
0
                    break;
60
0
                case SkPath::kConic_Verb:
61
0
                    ++fWtsIdx;
62
0
                    [[fallthrough]];
63
0
                case SkPath::kQuad_Verb:
64
0
                    fPtsIdx += 2;
65
0
                    break;
66
0
                case SkPath::kCubic_Verb:
67
0
                    fPtsIdx += 3;
68
0
                    break;
69
0
            }
70
0
            fMidpoint += fPoints[fPtsIdx - 1];
71
0
            ++fMidpointWeight;
72
0
            hasGeometry = true;
73
0
        }
74
0
        return hasGeometry;
75
0
    }
76
77
    // Allows for iterating the current contour using a range-for loop.
78
0
    SkPathPriv::Iterate currentContour() {
79
0
        return SkPathPriv::Iterate(fVerbs, fVerbs + fVerbsIdx, fPoints, fWeights);
80
0
    }
81
82
0
    SkPoint currentMidpoint() { return fMidpoint * (1.f / fMidpointWeight); }
83
84
private:
85
0
    void advance() {
86
0
        fVerbs += fVerbsIdx;
87
0
        fNumRemainingVerbs -= fVerbsIdx;
88
0
        fVerbsIdx = 0;
89
0
        fPoints += fPtsIdx;
90
0
        fPtsIdx = 0;
91
0
        fWeights += fWtsIdx;
92
0
        fWtsIdx = 0;
93
0
    }
94
95
    const SkPath& fPath;
96
97
    const uint8_t* fVerbs;
98
    int fNumRemainingVerbs = 0;
99
    int fVerbsIdx = 0;
100
101
    const SkPoint* fPoints;
102
    int fPtsIdx = 0;
103
104
    const float* fWeights;
105
    int fWtsIdx = 0;
106
107
    SkPoint fMidpoint;
108
    int fMidpointWeight;
109
};
110
111
// Writes out wedge patches, chopping as necessary so none require more segments than are supported
112
// by the hardware.
113
class WedgeWriter {
114
public:
115
    WedgeWriter(GrMeshDrawTarget* target,
116
                GrVertexChunkArray* vertexChunkArray,
117
                size_t patchStride,
118
                int initialPatchAllocCount,
119
                int maxSegments)
120
            : fChunker(target, vertexChunkArray, patchStride, initialPatchAllocCount)
121
            , fMaxSegments_pow2(maxSegments * maxSegments)
122
0
            , fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
123
0
    }
124
125
    void setMatrices(const SkRect& cullBounds,
126
                     const SkMatrix& shaderMatrix,
127
0
                     const SkMatrix& pathMatrix) {
128
0
        SkMatrix totalMatrix;
129
0
        totalMatrix.setConcat(shaderMatrix, pathMatrix);
130
0
        fCullTest.set(cullBounds, totalMatrix);
131
0
        fTotalVectorXform = totalMatrix;
132
0
        fPathXform = pathMatrix;
133
0
    }
134
135
0
    const GrPathXform& pathXform() const { return fPathXform; }
136
137
    SK_ALWAYS_INLINE void writeFlatWedge(const GrShaderCaps& shaderCaps,
138
                                         SkPoint p0,
139
                                         SkPoint p1,
140
0
                                         SkPoint midpoint) {
141
0
        if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
142
0
            fPathXform.mapLineToCubic(&vertexWriter, p0, p1);
143
0
            vertexWriter.write(midpoint);
144
0
            vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
145
0
                                                  GrTessellationShader::kCubicCurveType));
146
0
        }
147
0
    }
148
149
    SK_ALWAYS_INLINE void writeQuadraticWedge(const GrShaderCaps& shaderCaps,
150
                                              const SkPoint p[3],
151
0
                                              SkPoint midpoint) {
152
0
        float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fTotalVectorXform);
153
0
        if (numSegments_pow4 > fMaxSegments_pow4) {
154
0
            this->chopAndWriteQuadraticWedges(shaderCaps, p, midpoint);
155
0
            return;
156
0
        }
157
0
        if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
158
0
            fPathXform.mapQuadToCubic(&vertexWriter, p);
159
0
            vertexWriter.write(midpoint);
160
0
            vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
161
0
                                                  GrTessellationShader::kCubicCurveType));
162
0
        }
163
0
        fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
164
0
    }
165
166
    SK_ALWAYS_INLINE void writeConicWedge(const GrShaderCaps& shaderCaps,
167
                                          const SkPoint p[3],
168
                                          float w,
169
0
                                          SkPoint midpoint) {
170
0
        float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fTotalVectorXform);
171
0
        if (numSegments_pow2 > fMaxSegments_pow2) {
172
0
            this->chopAndWriteConicWedges(shaderCaps, {p, w}, midpoint);
173
0
            return;
174
0
        }
175
0
        if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
176
0
            fPathXform.mapConicToPatch(&vertexWriter, p, w);
177
0
            vertexWriter.write(midpoint);
178
0
            vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
179
0
                                                  GrTessellationShader::kConicCurveType));
180
0
        }
181
0
        fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
182
0
                                          fNumFixedSegments_pow4);
183
0
    }
184
185
    SK_ALWAYS_INLINE void writeCubicWedge(const GrShaderCaps& shaderCaps,
186
                                          const SkPoint p[4],
187
0
                                          SkPoint midpoint) {
188
0
        float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fTotalVectorXform);
189
0
        if (numSegments_pow4 > fMaxSegments_pow4) {
190
0
            this->chopAndWriteCubicWedges(shaderCaps, p, midpoint);
191
0
            return;
192
0
        }
193
0
        if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
194
0
            fPathXform.map4Points(&vertexWriter, p);
195
0
            vertexWriter.write(midpoint);
196
0
            vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
197
0
                                                  GrTessellationShader::kCubicCurveType));
198
0
        }
199
0
        fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
200
0
    }
201
202
0
    int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
203
204
private:
205
    void chopAndWriteQuadraticWedges(const GrShaderCaps& shaderCaps,
206
                                     const SkPoint p[3],
207
0
                                     SkPoint midpoint) {
208
0
        SkPoint chops[5];
209
0
        SkChopQuadAtHalf(p, chops);
210
0
        for (int i = 0; i < 2; ++i) {
211
0
            const SkPoint* q = chops + i*2;
212
0
            if (fCullTest.areVisible3(q)) {
213
0
                this->writeQuadraticWedge(shaderCaps, q, midpoint);
214
0
            } else {
215
0
                this->writeFlatWedge(shaderCaps, q[0], q[2], midpoint);
216
0
            }
217
0
        }
218
0
    }
219
220
    void chopAndWriteConicWedges(const GrShaderCaps& shaderCaps,
221
                                 const SkConic& conic,
222
0
                                 SkPoint midpoint) {
223
0
        SkConic chops[2];
224
0
        if (!conic.chopAt(.5, chops)) {
225
0
            return;
226
0
        }
227
0
        for (int i = 0; i < 2; ++i) {
228
0
            if (fCullTest.areVisible3(chops[i].fPts)) {
229
0
                this->writeConicWedge(shaderCaps, chops[i].fPts, chops[i].fW, midpoint);
230
0
            } else {
231
0
                this->writeFlatWedge(shaderCaps, chops[i].fPts[0], chops[i].fPts[2], midpoint);
232
0
            }
233
0
        }
234
0
    }
235
236
    void chopAndWriteCubicWedges(const GrShaderCaps& shaderCaps,
237
                                 const SkPoint p[4],
238
0
                                 SkPoint midpoint) {
239
0
        SkPoint chops[7];
240
0
        SkChopCubicAtHalf(p, chops);
241
0
        for (int i = 0; i < 2; ++i) {
242
0
            const SkPoint* c = chops + i*3;
243
0
            if (fCullTest.areVisible4(c)) {
244
0
                this->writeCubicWedge(shaderCaps, c, midpoint);
245
0
            } else {
246
0
                this->writeFlatWedge(shaderCaps, c[0], c[3], midpoint);
247
0
            }
248
0
        }
249
0
    }
250
251
    GrVertexChunkBuilder fChunker;
252
    GrCullTest fCullTest;
253
    GrVectorXform fTotalVectorXform;
254
    GrPathXform fPathXform;
255
    const float fMaxSegments_pow2;
256
    const float fMaxSegments_pow4;
257
258
    // If using fixed count, this is the max number of curve segments we need to draw per instance.
259
    float fNumFixedSegments_pow4 = 1;
260
};
261
262
}  // namespace
263
264
265
GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
266
                                                const SkPMColor4f& color, int numPathVerbs,
267
0
                                                const GrPipeline& pipeline, const GrCaps& caps) {
268
0
    using PatchType = GrPathTessellationShader::PatchType;
269
0
    GrPathTessellationShader* shader;
270
0
    if (caps.shaderCaps()->tessellationSupport() &&
271
0
        caps.shaderCaps()->infinitySupport() &&  // The hw tessellation shaders use infinity.
272
0
        !pipeline.usesLocalCoords() &&  // Our tessellation back door doesn't handle varyings.
273
0
        numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
274
0
        shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
275
0
                                                                          PatchType::kWedges);
276
0
    } else {
277
0
        shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
278
0
                                                                         viewMatrix, color,
279
0
                                                                         PatchType::kWedges);
280
0
    }
281
0
    return arena->make([=](void* objStart) {
282
0
        return new(objStart) GrPathWedgeTessellator(shader);
283
0
    });
284
0
}
285
286
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
287
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
288
289
void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target,
290
                                     const SkRect& cullBounds,
291
                                     const PathDrawList& pathDrawList,
292
0
                                     int totalCombinedPathVerbCnt) {
293
0
    SkASSERT(fVertexChunkArray.empty());
294
295
0
    const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
296
297
    // Over-allocate enough wedges for 1 in 4 to chop.
298
0
    int maxWedges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt);
299
0
    int wedgeAllocCount = (maxWedges * 5 + 3) / 4;  // i.e., ceil(maxWedges * 5/4)
300
0
    if (!wedgeAllocCount) {
301
0
        return;
302
0
    }
303
0
    size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 5
304
0
                                                               : fShader->instanceStride();
305
306
0
    int maxSegments;
307
0
    if (fShader->willUseTessellationShaders()) {
308
0
        maxSegments = shaderCaps.maxTessellationSegments();
309
0
    } else {
310
0
        maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
311
0
    }
312
313
0
    WedgeWriter wedgeWriter(target, &fVertexChunkArray, patchStride, wedgeAllocCount, maxSegments);
314
0
    for (auto [pathMatrix, path] : pathDrawList) {
315
0
        wedgeWriter.setMatrices(cullBounds, fShader->viewMatrix(), pathMatrix);
316
0
        MidpointContourParser parser(path);
317
0
        while (parser.parseNextContour()) {
318
0
            SkPoint midpoint = wedgeWriter.pathXform().mapPoint(parser.currentMidpoint());
319
0
            SkPoint startPoint = {0, 0};
320
0
            SkPoint lastPoint = startPoint;
321
0
            for (auto [verb, pts, w] : parser.currentContour()) {
322
0
                switch (verb) {
323
0
                    case SkPathVerb::kMove:
324
0
                        startPoint = lastPoint = pts[0];
325
0
                        break;
326
0
                    case SkPathVerb::kClose:
327
0
                        break;  // Ignore. We can assume an implicit close at the end.
328
0
                    case SkPathVerb::kLine:
329
0
                        wedgeWriter.writeFlatWedge(shaderCaps, pts[0], pts[1], midpoint);
330
0
                        lastPoint = pts[1];
331
0
                        break;
332
0
                    case SkPathVerb::kQuad:
333
0
                        wedgeWriter.writeQuadraticWedge(shaderCaps, pts, midpoint);
334
0
                        lastPoint = pts[2];
335
0
                        break;
336
0
                    case SkPathVerb::kConic:
337
0
                        wedgeWriter.writeConicWedge(shaderCaps, pts, *w, midpoint);
338
0
                        lastPoint = pts[2];
339
0
                        break;
340
0
                    case SkPathVerb::kCubic:
341
0
                        wedgeWriter.writeCubicWedge(shaderCaps, pts, midpoint);
342
0
                        lastPoint = pts[3];
343
0
                        break;
344
0
                }
345
0
            }
346
0
            if (lastPoint != startPoint) {
347
0
                wedgeWriter.writeFlatWedge(shaderCaps, lastPoint, startPoint, midpoint);
348
0
            }
349
0
        }
350
0
    }
351
352
0
    if (!fShader->willUseTessellationShaders()) {
353
        // log2(n) == log16(n^4).
354
0
        int fixedResolveLevel = GrWangsFormula::nextlog16(wedgeWriter.numFixedSegments_pow4());
355
0
        int numCurveTriangles =
356
0
                GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel);
357
        // Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
358
0
        fFixedIndexCount = numCurveTriangles * 3 + 3;
359
360
0
        GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
361
362
0
        fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
363
0
                GrGpuBufferType::kVertex,
364
0
                GrPathTessellationShader::SizeOfVertexBufferForMiddleOutWedges(),
365
0
                gFixedCountVertexBufferKey,
366
0
                GrPathTessellationShader::InitializeVertexBufferForMiddleOutWedges);
367
368
0
        GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
369
370
0
        fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
371
0
                GrGpuBufferType::kIndex,
372
0
                GrPathTessellationShader::SizeOfIndexBufferForMiddleOutWedges(),
373
0
                gFixedCountIndexBufferKey,
374
0
                GrPathTessellationShader::InitializeIndexBufferForMiddleOutWedges);
375
0
    }
376
0
}
Unexecuted instantiation: GrPathWedgeTessellator::prepare(GrMeshDrawTarget*, SkRect const&, GrPathTessellator::PathDrawList const&, int)
Unexecuted instantiation: GrPathWedgeTessellator::prepare(GrMeshDrawTarget*, SkRect const&, GrPathTessellator::PathDrawList const&, int)
377
378
#if SK_GPU_V1
379
#include "src/gpu/GrOpFlushState.h"
380
381
0
void GrPathWedgeTessellator::draw(GrOpFlushState* flushState) const {
382
0
    if (fShader->willUseTessellationShaders()) {
383
0
        for (const GrVertexChunk& chunk : fVertexChunkArray) {
384
0
            flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
385
0
            flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
386
0
        }
387
0
    } else {
388
0
        SkASSERT(fShader->hasInstanceAttributes());
389
0
        for (const GrVertexChunk& chunk : fVertexChunkArray) {
390
0
            flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer);
391
0
            flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
392
0
        }
393
0
    }
394
0
}
Unexecuted instantiation: GrPathWedgeTessellator::draw(GrOpFlushState*) const
Unexecuted instantiation: GrPathWedgeTessellator::draw(GrOpFlushState*) const
395
#endif