Coverage Report

Created: 2021-08-22 09:07

/src/skia/src/gpu/tessellate/GrPathCurveTessellator.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/GrPathCurveTessellator.h"
9
10
#include "src/core/SkUtils.h"
11
#include "src/gpu/GrMeshDrawTarget.h"
12
#include "src/gpu/GrResourceProvider.h"
13
#include "src/gpu/geometry/GrPathUtils.h"
14
#include "src/gpu/geometry/GrWangsFormula.h"
15
#include "src/gpu/tessellate/GrCullTest.h"
16
#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
17
#include "src/gpu/tessellate/GrPathXform.h"
18
#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
19
20
namespace {
21
22
constexpr static float kPrecision = GrTessellationShader::kLinearizationPrecision;
23
24
// Writes out curve patches, chopping as necessary so none require more segments than are
25
// supported by the hardware.
26
class CurveWriter {
27
public:
28
    CurveWriter(int maxSegments)
29
            : fMaxSegments_pow2(maxSegments * maxSegments)
30
0
            , fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
31
0
    }
32
33
    void setMatrices(const SkRect& cullBounds,
34
                     const SkMatrix& shaderMatrix,
35
0
                     const SkMatrix& pathMatrix) {
36
0
        SkMatrix totalMatrix;
37
0
        totalMatrix.setConcat(shaderMatrix, pathMatrix);
38
0
        fCullTest.set(cullBounds, totalMatrix);
39
0
        fTotalVectorXform = totalMatrix;
40
0
        fPathXform = pathMatrix;
41
0
    }
42
43
    SK_ALWAYS_INLINE void writeQuadratic(const GrShaderCaps& shaderCaps,
44
0
                                         GrVertexChunkBuilder* chunker, const SkPoint p[3]) {
45
0
        float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fTotalVectorXform);
46
0
        if (numSegments_pow4 > fMaxSegments_pow4) {
47
0
            this->chopAndWriteQuadratic(shaderCaps, chunker, p);
48
0
            return;
49
0
        }
50
0
        if (numSegments_pow4 > 1) {
51
0
            if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
52
0
                fPathXform.mapQuadToCubic(&vertexWriter, p);
53
0
                vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
54
0
                                                      GrTessellationShader::kCubicCurveType));
55
0
            }
56
0
            fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
57
0
        }
58
0
    }
59
60
    SK_ALWAYS_INLINE void writeConic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
61
0
                                     const SkPoint p[3], float w) {
62
0
        float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fTotalVectorXform);
63
0
        if (numSegments_pow2 > fMaxSegments_pow2) {
64
0
            this->chopAndWriteConic(shaderCaps, chunker, {p, w});
65
0
            return;
66
0
        }
67
0
        if (numSegments_pow2 > 1) {
68
0
            if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
69
0
                fPathXform.mapConicToPatch(&vertexWriter, p, w);
70
0
                vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
71
0
                                                      GrTessellationShader::kConicCurveType));
72
0
            }
73
0
            fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
74
0
                                              fNumFixedSegments_pow4);
75
0
        }
76
0
    }
77
78
    SK_ALWAYS_INLINE void writeCubic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
79
0
                                     const SkPoint p[4]) {
80
0
        float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fTotalVectorXform);
81
0
        if (numSegments_pow4 > fMaxSegments_pow4) {
82
0
            this->chopAndWriteCubic(shaderCaps, chunker, p);
83
0
            return;
84
0
        }
85
0
        if (numSegments_pow4 > 1) {
86
0
            if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
87
0
                fPathXform.map4Points(&vertexWriter, p);
88
0
                vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
89
0
                                                      GrTessellationShader::kCubicCurveType));
90
0
            }
91
0
            fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
92
0
        }
93
0
    }
94
95
0
    int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
96
97
private:
98
    void chopAndWriteQuadratic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
99
0
                               const SkPoint p[3]) {
100
0
        SkPoint chops[5];
101
0
        SkChopQuadAtHalf(p, chops);
102
0
        for (int i = 0; i < 2; ++i) {
103
0
            const SkPoint* q = chops + i*2;
104
0
            if (fCullTest.areVisible3(q)) {
105
0
                this->writeQuadratic(shaderCaps, chunker, q);
106
0
            }
107
0
        }
108
        // Connect the two halves.
109
0
        this->writeTriangle(shaderCaps, chunker, chops[0], chops[2], chops[4]);
110
0
    }
111
112
    void chopAndWriteConic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
113
0
                           const SkConic& conic) {
114
0
        SkConic chops[2];
115
0
        if (!conic.chopAt(.5, chops)) {
116
0
            return;
117
0
        }
118
0
        for (int i = 0; i < 2; ++i) {
119
0
            if (fCullTest.areVisible3(chops[i].fPts)) {
120
0
                this->writeConic(shaderCaps, chunker, chops[i].fPts, chops[i].fW);
121
0
            }
122
0
        }
123
        // Connect the two halves.
124
0
        this->writeTriangle(shaderCaps, chunker, conic.fPts[0], chops[0].fPts[2], chops[1].fPts[2]);
125
0
    }
126
127
    void chopAndWriteCubic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
128
0
                           const SkPoint p[4]) {
129
0
        SkPoint chops[7];
130
0
        SkChopCubicAtHalf(p, chops);
131
0
        for (int i = 0; i < 2; ++i) {
132
0
            const SkPoint* c = chops + i*3;
133
0
            if (fCullTest.areVisible4(c)) {
134
0
                this->writeCubic(shaderCaps, chunker, c);
135
0
            }
136
0
        }
137
        // Connect the two halves.
138
0
        this->writeTriangle(shaderCaps, chunker, chops[0], chops[3], chops[6]);
139
0
    }
140
141
    void writeTriangle(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker, SkPoint p0,
142
0
                       SkPoint p1, SkPoint p2) {
143
0
        if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
144
0
            vertexWriter.write(fPathXform.mapPoint(p0),
145
0
                               fPathXform.mapPoint(p1),
146
0
                               fPathXform.mapPoint(p2));
147
            // Mark this instance as a triangle by setting it to a conic with w=Inf.
148
0
            vertexWriter.fill(GrVertexWriter::kIEEE_32_infinity, 2);
149
0
            vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
150
0
                                                  GrTessellationShader::kTriangularConicCurveType));
151
0
        }
152
0
    }
153
154
    GrCullTest fCullTest;
155
    GrVectorXform fTotalVectorXform;
156
    GrPathXform fPathXform;
157
    const float fMaxSegments_pow2;
158
    const float fMaxSegments_pow4;
159
160
    // If using fixed count, this is the number of segments we need to emit per instance. Always
161
    // emit at least 2 segments so we can support triangles.
162
    float fNumFixedSegments_pow4 = 2*2*2*2;
163
};
164
165
}  // namespace
166
167
168
GrPathCurveTessellator* GrPathCurveTessellator::Make(SkArenaAlloc* arena,
169
                                                     const SkMatrix& viewMatrix,
170
                                                     const SkPMColor4f& color,
171
                                                     DrawInnerFan drawInnerFan, int numPathVerbs,
172
                                                     const GrPipeline& pipeline,
173
0
                                                     const GrCaps& caps) {
174
0
    using PatchType = GrPathTessellationShader::PatchType;
175
0
    GrPathTessellationShader* shader;
176
0
    if (caps.shaderCaps()->tessellationSupport() &&
177
0
        caps.shaderCaps()->infinitySupport() &&  // The hw tessellation shaders use infinity.
178
0
        !pipeline.usesLocalCoords() &&  // Our tessellation back door doesn't handle varyings.
179
0
        numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
180
0
        shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
181
0
                                                                          PatchType::kCurves);
182
0
    } else {
183
0
        shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
184
0
                                                                         viewMatrix, color,
185
0
                                                                         PatchType::kCurves);
186
0
    }
187
0
    return arena->make([=](void* objStart) {
188
0
        return new(objStart) GrPathCurveTessellator(shader, drawInnerFan);
189
0
    });
190
0
}
191
192
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
193
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
194
195
void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target,
196
                                     const SkRect& cullBounds,
197
                                     const PathDrawList& pathDrawList,
198
                                     int totalCombinedPathVerbCnt,
199
0
                                     const BreadcrumbTriangleList* breadcrumbTriangleList) {
200
0
    SkASSERT(fVertexChunkArray.empty());
201
202
0
    const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
203
204
    // Determine how many triangles to allocate.
205
0
    int maxTriangles = 0;
206
0
    if (fDrawInnerFan) {
207
0
        int maxCombinedFanEdges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt);
208
        // A single n-sided polygon is fanned by n-2 triangles. Multiple polygons with a combined
209
        // edge count of n are fanned by strictly fewer triangles.
210
0
        int maxTrianglesInFans = std::max(maxCombinedFanEdges - 2, 0);
211
0
        maxTriangles += maxTrianglesInFans;
212
0
    }
213
0
    if (breadcrumbTriangleList) {
214
0
        maxTriangles += breadcrumbTriangleList->count();
215
0
    }
216
    // Over-allocate enough curves for 1 in 4 to chop.
217
0
    int curveAllocCount = (totalCombinedPathVerbCnt * 5 + 3) / 4;  // i.e., ceil(numVerbs * 5/4)
218
0
    int patchAllocCount = maxTriangles + curveAllocCount;
219
0
    if (!patchAllocCount) {
220
0
        return;
221
0
    }
222
0
    size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 4
223
0
                                                               : fShader->instanceStride();
224
0
    GrVertexChunkBuilder chunker(target, &fVertexChunkArray, patchStride, patchAllocCount);
225
226
    // Write out the triangles.
227
0
    if (maxTriangles) {
228
0
        GrVertexWriter vertexWriter = chunker.appendVertices(maxTriangles);
229
0
        if (!vertexWriter) {
230
0
            return;
231
0
        }
232
0
        int numRemainingTriangles = maxTriangles;
233
0
        if (fDrawInnerFan) {
234
            // Pad the triangles with 2 infinities. This produces conic patches with w=Infinity. In
235
            // the case where infinity is not supported, we also write out a 3rd float that
236
            // explicitly tells the shader to interpret these patches as triangles.
237
0
            int pad32Count = shaderCaps.infinitySupport() ? 2 : 3;
238
0
            uint32_t pad32Value = shaderCaps.infinitySupport()
239
0
                    ? GrVertexWriter::kIEEE_32_infinity
240
0
                    : sk_bit_cast<uint32_t>(GrTessellationShader::kTriangularConicCurveType);
241
0
            for (auto [pathMatrix, path] : pathDrawList) {
242
0
                int numTrianglesWritten;
243
0
                vertexWriter = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
244
0
                        std::move(vertexWriter),
245
0
                        pad32Count,
246
0
                        pad32Value,
247
0
                        pathMatrix,
248
0
                        path,
249
0
                        &numTrianglesWritten);
250
0
                numRemainingTriangles -= numTrianglesWritten;
251
0
            }
252
0
        }
253
0
        if (breadcrumbTriangleList) {
254
0
            int numWritten = 0;
255
0
            SkDEBUGCODE(int count = 0;)
256
#ifdef SK_DEBUG
257
0
            for (auto [pathMatrix, path] : pathDrawList) {
258
                // This assert isn't actually necessary, but we currently only use breadcrumb
259
                // triangles with an identity pathMatrix. If that ever changes, this assert will
260
                // serve as a gentle reminder to make sure the breadcrumb triangles are also
261
                // transformed on the CPU.
262
0
                SkASSERT(pathMatrix.isIdentity());
263
0
            }
264
#endif
265
0
            for (const auto* tri = breadcrumbTriangleList->head(); tri; tri = tri->fNext) {
266
0
                SkDEBUGCODE(++count;)
267
0
                auto p0 = grvx::float2::Load(tri->fPts);
268
0
                auto p1 = grvx::float2::Load(tri->fPts + 1);
269
0
                auto p2 = grvx::float2::Load(tri->fPts + 2);
270
0
                if (skvx::any((p0 == p1) & (p1 == p2))) {
271
                    // Cull completely horizontal or vertical triangles. GrTriangulator can't always
272
                    // get these breadcrumb edges right when they run parallel to the sweep
273
                    // direction because their winding is undefined by its current definition.
274
                    // FIXME(skia:12060): This seemed safe, but if there is a view matrix it will
275
                    // introduce T-junctions.
276
0
                    continue;
277
0
                }
278
0
                vertexWriter.writeArray(tri->fPts, 3);
279
                // Mark this instance as a triangle by setting it to a conic with w=Inf.
280
0
                vertexWriter.fill(GrVertexWriter::kIEEE_32_infinity, 2);
281
0
                vertexWriter.write(
282
0
                        GrVertexWriter::If(!shaderCaps.infinitySupport(),
283
0
                                           GrTessellationShader::kTriangularConicCurveType));
284
0
                ++numWritten;
285
0
            }
286
0
            SkASSERT(count == breadcrumbTriangleList->count());
287
0
            numRemainingTriangles -= numWritten;
288
0
        }
289
0
        chunker.popVertices(numRemainingTriangles);
290
0
    }
291
292
0
    int maxSegments;
293
0
    if (fShader->willUseTessellationShaders()) {
294
        // The curve shader tessellates T=0..(1/2) on the first side of the canonical triangle and
295
        // T=(1/2)..1 on the second side. This means we get double the max tessellation segments
296
        // for the range T=0..1.
297
0
        maxSegments = shaderCaps.maxTessellationSegments() * 2;
298
0
    } else {
299
0
        maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
300
0
    }
301
302
0
    CurveWriter curveWriter(maxSegments);
303
0
    for (auto [pathMatrix, path] : pathDrawList) {
304
0
        curveWriter.setMatrices(cullBounds, fShader->viewMatrix(), pathMatrix);
305
0
        for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
306
0
            switch (verb) {
307
0
                case SkPathVerb::kQuad:
308
0
                    curveWriter.writeQuadratic(shaderCaps, &chunker, pts);
309
0
                    break;
310
0
                case SkPathVerb::kConic:
311
0
                    curveWriter.writeConic(shaderCaps, &chunker, pts, *w);
312
0
                    break;
313
0
                case SkPathVerb::kCubic:
314
0
                    curveWriter.writeCubic(shaderCaps, &chunker, pts);
315
0
                    break;
316
0
                default:
317
0
                    break;
318
0
            }
319
0
        }
320
0
    }
321
322
0
    if (!fShader->willUseTessellationShaders()) {
323
        // log2(n) == log16(n^4).
324
0
        int fixedResolveLevel = GrWangsFormula::nextlog16(curveWriter.numFixedSegments_pow4());
325
0
        fFixedIndexCount =
326
0
                GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel) * 3;
327
328
0
        GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
329
330
0
        fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
331
0
                GrGpuBufferType::kVertex,
332
0
                GrPathTessellationShader::SizeOfVertexBufferForMiddleOutCurves(),
333
0
                gFixedCountVertexBufferKey,
334
0
                GrPathTessellationShader::InitializeVertexBufferForMiddleOutCurves);
335
336
0
        GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
337
338
0
        fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
339
0
                GrGpuBufferType::kIndex,
340
0
                GrPathTessellationShader::SizeOfIndexBufferForMiddleOutCurves(),
341
0
                gFixedCountIndexBufferKey,
342
0
                GrPathTessellationShader::InitializeIndexBufferForMiddleOutCurves);
343
0
    }
344
0
}
Unexecuted instantiation: GrPathCurveTessellator::prepare(GrMeshDrawTarget*, SkRect const&, GrPathTessellator::PathDrawList const&, int, GrTriangulator::BreadcrumbTriangleList const*)
Unexecuted instantiation: GrPathCurveTessellator::prepare(GrMeshDrawTarget*, SkRect const&, GrPathTessellator::PathDrawList const&, int, GrTriangulator::BreadcrumbTriangleList const*)
345
346
#if SK_GPU_V1
347
#include "src/gpu/GrOpFlushState.h"
348
349
0
void GrPathCurveTessellator::draw(GrOpFlushState* flushState) const {
350
0
    if (fShader->willUseTessellationShaders()) {
351
0
        for (const GrVertexChunk& chunk : fVertexChunkArray) {
352
0
            flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
353
0
            flushState->draw(chunk.fCount * 4, chunk.fBase * 4);
354
0
        }
355
0
    } else {
356
0
        SkASSERT(fShader->hasInstanceAttributes());
357
0
        for (const GrVertexChunk& chunk : fVertexChunkArray) {
358
0
            flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer);
359
0
            flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
360
0
        }
361
0
    }
362
0
}
Unexecuted instantiation: GrPathCurveTessellator::draw(GrOpFlushState*) const
Unexecuted instantiation: GrPathCurveTessellator::draw(GrOpFlushState*) const
363
364
void GrPathCurveTessellator::drawHullInstances(
365
0
        GrOpFlushState* flushState, sk_sp<const GrGpuBuffer> vertexBufferIfNeeded) const {
366
0
    for (const GrVertexChunk& chunk : fVertexChunkArray) {
367
0
        flushState->bindBuffers(nullptr, chunk.fBuffer, vertexBufferIfNeeded);
368
0
        flushState->drawInstanced(chunk.fCount, chunk.fBase, 4, 0);
369
0
    }
370
0
}
371
#endif