1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2021 Google LLC.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/gpu/tessellate/StrokeFixedCountTessellator.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h"
11cb93a386Sopenharmony_ci#include "src/gpu/GrMeshDrawTarget.h"
12cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProvider.h"
13cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrPathUtils.h"
14cb93a386Sopenharmony_ci#include "src/gpu/tessellate/StrokeIterator.h"
15cb93a386Sopenharmony_ci#include "src/gpu/tessellate/WangsFormula.h"
16cb93a386Sopenharmony_ci#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_ci#if SK_GPU_V1
19cb93a386Sopenharmony_ci#include "src/gpu/GrOpFlushState.h"
20cb93a386Sopenharmony_ci#endif
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_cinamespace skgpu {
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_cinamespace {
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ciconstexpr static float kMaxParametricSegments_pow4 =
27cb93a386Sopenharmony_ci        StrokeFixedCountTessellator::kMaxParametricSegments_pow4;
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci// Writes out strokes to the given instance chunk array, chopping if necessary so that all instances
30cb93a386Sopenharmony_ci// require 32 parametric segments or less. (We don't consider radial segments here. The tessellator
31cb93a386Sopenharmony_ci// will just add enough additional segments to handle a worst-case 180 degree stroke.)
32cb93a386Sopenharmony_ciclass InstanceWriter {
33cb93a386Sopenharmony_cipublic:
34cb93a386Sopenharmony_ci    InstanceWriter(PatchAttribs attribs,
35cb93a386Sopenharmony_ci                   GrMeshDrawTarget* target,
36cb93a386Sopenharmony_ci                   float matrixMaxScale,
37cb93a386Sopenharmony_ci                   const SkMatrix& viewMatrix,
38cb93a386Sopenharmony_ci                   GrVertexChunkArray* patchChunks,
39cb93a386Sopenharmony_ci                   size_t instanceStride,
40cb93a386Sopenharmony_ci                   int minInstancesPerChunk)
41cb93a386Sopenharmony_ci            : fAttribs(attribs)
42cb93a386Sopenharmony_ci            , fChunkBuilder(target, patchChunks, instanceStride, minInstancesPerChunk)
43cb93a386Sopenharmony_ci            , fParametricPrecision(StrokeTolerances::CalcParametricPrecision(matrixMaxScale)) {
44cb93a386Sopenharmony_ci    }
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_ci    float parametricPrecision() const { return fParametricPrecision; }
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci    // maxParametricSegments^4, or the number of parametric segments, raised to the 4th power,
49cb93a386Sopenharmony_ci    // that are required by the single instance we've written that requires the most segments.
50cb93a386Sopenharmony_ci    float maxParametricSegments_pow4() const { return fMaxParametricSegments_pow4; }
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci    // Updates the dynamic stroke state that we will write out with each instance.
53cb93a386Sopenharmony_ci    void updateDynamicStroke(const SkStrokeRec& stroke) {
54cb93a386Sopenharmony_ci        SkASSERT(!fHasDeferredFirstStroke);
55cb93a386Sopenharmony_ci        SkASSERT(fAttribs & PatchAttribs::kStrokeParams);
56cb93a386Sopenharmony_ci        fDynamicStroke.set(stroke);
57cb93a386Sopenharmony_ci    }
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ci    // Updates the dynamic color state that we will write out with each instance.
60cb93a386Sopenharmony_ci    void updateDynamicColor(const SkPMColor4f& color) {
61cb93a386Sopenharmony_ci        SkASSERT(!fHasDeferredFirstStroke);
62cb93a386Sopenharmony_ci        SkASSERT(fAttribs & PatchAttribs::kColor);
63cb93a386Sopenharmony_ci        bool wideColor = fAttribs & PatchAttribs::kWideColorIfEnabled;
64cb93a386Sopenharmony_ci        SkASSERT(wideColor || color.fitsInBytes());
65cb93a386Sopenharmony_ci        fDynamicColor.set(color, wideColor);
66cb93a386Sopenharmony_ci    }
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ci    SK_ALWAYS_INLINE void lineTo(SkPoint start, SkPoint end) {
69cb93a386Sopenharmony_ci        SkPoint cubic[] = {start, start, end, end};
70cb93a386Sopenharmony_ci        SkPoint endControlPoint = start;
71cb93a386Sopenharmony_ci        this->writeStroke(cubic, endControlPoint, GrTessellationShader::kCubicCurveType);
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci    SK_ALWAYS_INLINE void quadraticTo(const SkPoint p[3]) {
75cb93a386Sopenharmony_ci        float numParametricSegments_pow4 = wangs_formula::quadratic_pow4(fParametricPrecision, p);
76cb93a386Sopenharmony_ci        if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) {
77cb93a386Sopenharmony_ci            this->chopQuadraticTo(p);
78cb93a386Sopenharmony_ci            return;
79cb93a386Sopenharmony_ci        }
80cb93a386Sopenharmony_ci        SkPoint cubic[4];
81cb93a386Sopenharmony_ci        GrPathUtils::convertQuadToCubic(p, cubic);
82cb93a386Sopenharmony_ci        SkPoint endControlPoint = cubic[2];
83cb93a386Sopenharmony_ci        this->writeStroke(cubic, endControlPoint, GrTessellationShader::kCubicCurveType);
84cb93a386Sopenharmony_ci        fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
85cb93a386Sopenharmony_ci                                               fMaxParametricSegments_pow4);
86cb93a386Sopenharmony_ci    }
87cb93a386Sopenharmony_ci
88cb93a386Sopenharmony_ci    SK_ALWAYS_INLINE void conicTo(const SkPoint p[3], float w) {
89cb93a386Sopenharmony_ci        float n = wangs_formula::conic_pow2(fParametricPrecision, p, w);
90cb93a386Sopenharmony_ci        float numParametricSegments_pow4 = n*n;
91cb93a386Sopenharmony_ci        if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) {
92cb93a386Sopenharmony_ci            this->chopConicTo({p, w});
93cb93a386Sopenharmony_ci            return;
94cb93a386Sopenharmony_ci        }
95cb93a386Sopenharmony_ci        SkPoint conic[4];
96cb93a386Sopenharmony_ci        GrTessellationShader::WriteConicPatch(p, w, conic);
97cb93a386Sopenharmony_ci        SkPoint endControlPoint = conic[1];
98cb93a386Sopenharmony_ci        this->writeStroke(conic, endControlPoint, GrTessellationShader::kConicCurveType);
99cb93a386Sopenharmony_ci        fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
100cb93a386Sopenharmony_ci                                               fMaxParametricSegments_pow4);
101cb93a386Sopenharmony_ci    }
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci    SK_ALWAYS_INLINE void cubicConvex180To(const SkPoint p[4]) {
104cb93a386Sopenharmony_ci        float numParametricSegments_pow4 = wangs_formula::cubic_pow4(fParametricPrecision, p);
105cb93a386Sopenharmony_ci        if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) {
106cb93a386Sopenharmony_ci            this->chopCubicConvex180To(p);
107cb93a386Sopenharmony_ci            return;
108cb93a386Sopenharmony_ci        }
109cb93a386Sopenharmony_ci        SkPoint endControlPoint = (p[3] != p[2]) ? p[2] : (p[2] != p[1]) ? p[1] : p[0];
110cb93a386Sopenharmony_ci        this->writeStroke(p, endControlPoint, GrTessellationShader::kCubicCurveType);
111cb93a386Sopenharmony_ci        fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
112cb93a386Sopenharmony_ci                                               fMaxParametricSegments_pow4);
113cb93a386Sopenharmony_ci    }
114cb93a386Sopenharmony_ci
115cb93a386Sopenharmony_ci    // Called when we encounter the verb "kMoveWithinContour". Moves invalidate the previous control
116cb93a386Sopenharmony_ci    // point. The stroke iterator tells us the new value to use for the previous control point.
117cb93a386Sopenharmony_ci    void setLastControlPoint(SkPoint newLastControlPoint) {
118cb93a386Sopenharmony_ci        fLastControlPoint = newLastControlPoint;
119cb93a386Sopenharmony_ci        fHasLastControlPoint = true;
120cb93a386Sopenharmony_ci    }
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    // Draws a circle whose diameter is equal to the stroke width. We emit circles at cusp points
123cb93a386Sopenharmony_ci    // round caps, and empty strokes that are specified to be drawn as circles.
124cb93a386Sopenharmony_ci    void writeCircle(SkPoint location) {
125cb93a386Sopenharmony_ci        if (VertexWriter writer = fChunkBuilder.appendVertex()) {
126cb93a386Sopenharmony_ci            // The shader interprets an empty stroke + empty join as a special case that denotes a
127cb93a386Sopenharmony_ci            // circle, or 180-degree point stroke.
128cb93a386Sopenharmony_ci            writer.fill(location, 5);
129cb93a386Sopenharmony_ci            this->writeDynamicAttribs(&writer, GrTessellationShader::kCubicCurveType);
130cb93a386Sopenharmony_ci        }
131cb93a386Sopenharmony_ci    }
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci    void finishContour() {
134cb93a386Sopenharmony_ci        if (fHasDeferredFirstStroke) {
135cb93a386Sopenharmony_ci            // We deferred the first stroke because we didn't know the previous control point to use
136cb93a386Sopenharmony_ci            // for its join. We write it out now.
137cb93a386Sopenharmony_ci            SkASSERT(fHasLastControlPoint);
138cb93a386Sopenharmony_ci            this->writeStroke(fDeferredFirstStroke, SkPoint(),
139cb93a386Sopenharmony_ci                              fDeferredCurveTypeIfUnsupportedInfinity);
140cb93a386Sopenharmony_ci            fHasDeferredFirstStroke = false;
141cb93a386Sopenharmony_ci        }
142cb93a386Sopenharmony_ci        fHasLastControlPoint = false;
143cb93a386Sopenharmony_ci    }
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_ciprivate:
146cb93a386Sopenharmony_ci    void chopQuadraticTo(const SkPoint p[3]) {
147cb93a386Sopenharmony_ci        SkPoint chops[5];
148cb93a386Sopenharmony_ci        SkChopQuadAtHalf(p, chops);
149cb93a386Sopenharmony_ci        this->quadraticTo(chops);
150cb93a386Sopenharmony_ci        this->quadraticTo(chops + 2);
151cb93a386Sopenharmony_ci    }
152cb93a386Sopenharmony_ci
153cb93a386Sopenharmony_ci    void chopConicTo(const SkConic& conic) {
154cb93a386Sopenharmony_ci        SkConic chops[2];
155cb93a386Sopenharmony_ci        if (!conic.chopAt(.5f, chops)) {
156cb93a386Sopenharmony_ci            return;
157cb93a386Sopenharmony_ci        }
158cb93a386Sopenharmony_ci        this->conicTo(chops[0].fPts, chops[0].fW);
159cb93a386Sopenharmony_ci        this->conicTo(chops[1].fPts, chops[1].fW);
160cb93a386Sopenharmony_ci    }
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci    void chopCubicConvex180To(const SkPoint p[4]) {
163cb93a386Sopenharmony_ci        SkPoint chops[7];
164cb93a386Sopenharmony_ci        SkChopCubicAtHalf(p, chops);
165cb93a386Sopenharmony_ci        this->cubicConvex180To(chops);
166cb93a386Sopenharmony_ci        this->cubicConvex180To(chops + 3);
167cb93a386Sopenharmony_ci    }
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci    SK_ALWAYS_INLINE void writeStroke(const SkPoint p[4], SkPoint endControlPoint,
170cb93a386Sopenharmony_ci                                      float curveTypeIfUnsupportedInfinity) {
171cb93a386Sopenharmony_ci        if (!fHasLastControlPoint) {
172cb93a386Sopenharmony_ci            // We don't know the previous control point yet to use for the join. Defer writing out
173cb93a386Sopenharmony_ci            // this stroke until the end.
174cb93a386Sopenharmony_ci            memcpy(fDeferredFirstStroke, p, sizeof(fDeferredFirstStroke));
175cb93a386Sopenharmony_ci            fDeferredCurveTypeIfUnsupportedInfinity = curveTypeIfUnsupportedInfinity;
176cb93a386Sopenharmony_ci            fHasDeferredFirstStroke = true;
177cb93a386Sopenharmony_ci            fHasLastControlPoint = true;
178cb93a386Sopenharmony_ci        } else if (VertexWriter writer = fChunkBuilder.appendVertex()) {
179cb93a386Sopenharmony_ci            writer.writeArray(p, 4);
180cb93a386Sopenharmony_ci            writer << fLastControlPoint;
181cb93a386Sopenharmony_ci            this->writeDynamicAttribs(&writer, curveTypeIfUnsupportedInfinity);
182cb93a386Sopenharmony_ci        }
183cb93a386Sopenharmony_ci        fLastControlPoint = endControlPoint;
184cb93a386Sopenharmony_ci    }
185cb93a386Sopenharmony_ci
186cb93a386Sopenharmony_ci    SK_ALWAYS_INLINE void writeDynamicAttribs(VertexWriter* writer,
187cb93a386Sopenharmony_ci                                              float curveTypeIfUnsupportedInfinity) {
188cb93a386Sopenharmony_ci        if (fAttribs & PatchAttribs::kStrokeParams) {
189cb93a386Sopenharmony_ci            *writer << fDynamicStroke;
190cb93a386Sopenharmony_ci        }
191cb93a386Sopenharmony_ci        if (fAttribs & PatchAttribs::kColor) {
192cb93a386Sopenharmony_ci            *writer << fDynamicColor;
193cb93a386Sopenharmony_ci        }
194cb93a386Sopenharmony_ci        if (fAttribs & PatchAttribs::kExplicitCurveType) {
195cb93a386Sopenharmony_ci            *writer << curveTypeIfUnsupportedInfinity;
196cb93a386Sopenharmony_ci        }
197cb93a386Sopenharmony_ci    }
198cb93a386Sopenharmony_ci
199cb93a386Sopenharmony_ci    void discardStroke(const SkPoint p[], int numPts) {
200cb93a386Sopenharmony_ci        // Set fLastControlPoint to the next stroke's p0 (which will be equal to the final point of
201cb93a386Sopenharmony_ci        // this stroke). This has the effect of disabling the next stroke's join.
202cb93a386Sopenharmony_ci        fLastControlPoint = p[numPts - 1];
203cb93a386Sopenharmony_ci        fHasLastControlPoint = true;
204cb93a386Sopenharmony_ci    }
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_ci    const PatchAttribs fAttribs;
207cb93a386Sopenharmony_ci    GrVertexChunkBuilder fChunkBuilder;
208cb93a386Sopenharmony_ci    const float fParametricPrecision;
209cb93a386Sopenharmony_ci    float fMaxParametricSegments_pow4 = 1;
210cb93a386Sopenharmony_ci
211cb93a386Sopenharmony_ci    // We can't write out the first stroke until we know the previous control point for its join.
212cb93a386Sopenharmony_ci    SkPoint fDeferredFirstStroke[4];
213cb93a386Sopenharmony_ci    float fDeferredCurveTypeIfUnsupportedInfinity;
214cb93a386Sopenharmony_ci    SkPoint fLastControlPoint;  // Used to configure the joins in the instance data.
215cb93a386Sopenharmony_ci    bool fHasDeferredFirstStroke = false;
216cb93a386Sopenharmony_ci    bool fHasLastControlPoint = false;
217cb93a386Sopenharmony_ci
218cb93a386Sopenharmony_ci    // Values for the current dynamic state (if any) that will get written out with each instance.
219cb93a386Sopenharmony_ci    StrokeParams fDynamicStroke;
220cb93a386Sopenharmony_ci    GrVertexColor fDynamicColor;
221cb93a386Sopenharmony_ci};
222cb93a386Sopenharmony_ci
223cb93a386Sopenharmony_ci// Returns the worst-case number of edges we will need in order to draw a join of the given type.
224cb93a386Sopenharmony_ciint worst_case_edges_in_join(SkPaint::Join joinType, float numRadialSegmentsPerRadian) {
225cb93a386Sopenharmony_ci    int numEdges = StrokeFixedCountTessellator::NumFixedEdgesInJoin(joinType);
226cb93a386Sopenharmony_ci    if (joinType == SkPaint::kRound_Join) {
227cb93a386Sopenharmony_ci        // For round joins we need to count the radial edges on our own. Account for a worst-case
228cb93a386Sopenharmony_ci        // join of 180 degrees (SK_ScalarPI radians).
229cb93a386Sopenharmony_ci        numEdges += std::max(SkScalarCeilToInt(numRadialSegmentsPerRadian * SK_ScalarPI) - 1, 0);
230cb93a386Sopenharmony_ci    }
231cb93a386Sopenharmony_ci    return numEdges;
232cb93a386Sopenharmony_ci}
233cb93a386Sopenharmony_ci
234cb93a386Sopenharmony_ci}  // namespace
235cb93a386Sopenharmony_ci
236cb93a386Sopenharmony_ci
237cb93a386Sopenharmony_ciGR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
238cb93a386Sopenharmony_ci
239cb93a386Sopenharmony_ciint StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
240cb93a386Sopenharmony_ci                                         const SkMatrix& shaderMatrix,
241cb93a386Sopenharmony_ci                                         std::array<float,2> matrixMinMaxScales,
242cb93a386Sopenharmony_ci                                         PathStrokeList* pathStrokeList,
243cb93a386Sopenharmony_ci                                         int totalCombinedVerbCnt) {
244cb93a386Sopenharmony_ci    int maxEdgesInJoin = 0;
245cb93a386Sopenharmony_ci    float maxRadialSegmentsPerRadian = 0;
246cb93a386Sopenharmony_ci
247cb93a386Sopenharmony_ci    // Over-allocate enough patches for each stroke to chop once, and for 8 extra caps. Since we
248cb93a386Sopenharmony_ci    // have to chop at inflections, points of 180 degree rotation, and anywhere a stroke requires
249cb93a386Sopenharmony_ci    // too many parametric segments, many strokes will end up getting choppped.
250cb93a386Sopenharmony_ci    int strokePreallocCount = totalCombinedVerbCnt * 2;
251cb93a386Sopenharmony_ci    int capPreallocCount = 8;
252cb93a386Sopenharmony_ci    int minInstancesPerChunk = strokePreallocCount + capPreallocCount;
253cb93a386Sopenharmony_ci    InstanceWriter instanceWriter(fAttribs,
254cb93a386Sopenharmony_ci                                  target,
255cb93a386Sopenharmony_ci                                  matrixMinMaxScales[1],
256cb93a386Sopenharmony_ci                                  shaderMatrix,
257cb93a386Sopenharmony_ci                                  &fInstanceChunks,
258cb93a386Sopenharmony_ci                                  sizeof(SkPoint) * 5 + PatchAttribsStride(fAttribs),
259cb93a386Sopenharmony_ci                                  minInstancesPerChunk);
260cb93a386Sopenharmony_ci
261cb93a386Sopenharmony_ci    if (!(fAttribs & PatchAttribs::kStrokeParams)) {
262cb93a386Sopenharmony_ci        // Strokes are static. Calculate tolerances once.
263cb93a386Sopenharmony_ci        const SkStrokeRec& stroke = pathStrokeList->fStroke;
264cb93a386Sopenharmony_ci        float localStrokeWidth = StrokeTolerances::GetLocalStrokeWidth(matrixMinMaxScales.data(),
265cb93a386Sopenharmony_ci                                                                       stroke.getWidth());
266cb93a386Sopenharmony_ci        float numRadialSegmentsPerRadian = StrokeTolerances::CalcNumRadialSegmentsPerRadian(
267cb93a386Sopenharmony_ci                instanceWriter.parametricPrecision(), localStrokeWidth);
268cb93a386Sopenharmony_ci        maxEdgesInJoin = worst_case_edges_in_join(stroke.getJoin(), numRadialSegmentsPerRadian);
269cb93a386Sopenharmony_ci        maxRadialSegmentsPerRadian = numRadialSegmentsPerRadian;
270cb93a386Sopenharmony_ci    }
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci    // Fast SIMD queue that buffers up values for "numRadialSegmentsPerRadian". Only used when we
273cb93a386Sopenharmony_ci    // have dynamic stroke.
274cb93a386Sopenharmony_ci    StrokeToleranceBuffer toleranceBuffer(instanceWriter.parametricPrecision());
275cb93a386Sopenharmony_ci
276cb93a386Sopenharmony_ci    for (PathStrokeList* pathStroke = pathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
277cb93a386Sopenharmony_ci        const SkStrokeRec& stroke = pathStroke->fStroke;
278cb93a386Sopenharmony_ci        if (fAttribs & PatchAttribs::kStrokeParams) {
279cb93a386Sopenharmony_ci            // Strokes are dynamic. Calculate tolerances every time.
280cb93a386Sopenharmony_ci            float numRadialSegmentsPerRadian =
281cb93a386Sopenharmony_ci                    toleranceBuffer.fetchRadialSegmentsPerRadian(pathStroke);
282cb93a386Sopenharmony_ci            maxEdgesInJoin = std::max(
283cb93a386Sopenharmony_ci                    worst_case_edges_in_join(stroke.getJoin(), numRadialSegmentsPerRadian),
284cb93a386Sopenharmony_ci                    maxEdgesInJoin);
285cb93a386Sopenharmony_ci            maxRadialSegmentsPerRadian = std::max(numRadialSegmentsPerRadian,
286cb93a386Sopenharmony_ci                                                  maxRadialSegmentsPerRadian);
287cb93a386Sopenharmony_ci            instanceWriter.updateDynamicStroke(stroke);
288cb93a386Sopenharmony_ci        }
289cb93a386Sopenharmony_ci        if (fAttribs & PatchAttribs::kColor) {
290cb93a386Sopenharmony_ci            instanceWriter.updateDynamicColor(pathStroke->fColor);
291cb93a386Sopenharmony_ci        }
292cb93a386Sopenharmony_ci        StrokeIterator strokeIter(pathStroke->fPath, &pathStroke->fStroke, &shaderMatrix);
293cb93a386Sopenharmony_ci        while (strokeIter.next()) {
294cb93a386Sopenharmony_ci            const SkPoint* p = strokeIter.pts();
295cb93a386Sopenharmony_ci            switch (strokeIter.verb()) {
296cb93a386Sopenharmony_ci                using Verb = StrokeIterator::Verb;
297cb93a386Sopenharmony_ci                int numChops;
298cb93a386Sopenharmony_ci                case Verb::kContourFinished:
299cb93a386Sopenharmony_ci                    instanceWriter.finishContour();
300cb93a386Sopenharmony_ci                    break;
301cb93a386Sopenharmony_ci                case Verb::kCircle:
302cb93a386Sopenharmony_ci                    // Round cap or else an empty stroke that is specified to be drawn as a circle.
303cb93a386Sopenharmony_ci                    instanceWriter.writeCircle(p[0]);
304cb93a386Sopenharmony_ci                    [[fallthrough]];
305cb93a386Sopenharmony_ci                case Verb::kMoveWithinContour:
306cb93a386Sopenharmony_ci                    instanceWriter.setLastControlPoint(p[0]);
307cb93a386Sopenharmony_ci                    break;
308cb93a386Sopenharmony_ci                case Verb::kLine:
309cb93a386Sopenharmony_ci                    instanceWriter.lineTo(p[0], p[1]);
310cb93a386Sopenharmony_ci                    break;
311cb93a386Sopenharmony_ci                case Verb::kQuad:
312cb93a386Sopenharmony_ci                    if (GrPathUtils::conicHasCusp(p)) {
313cb93a386Sopenharmony_ci                        // The cusp is always at the midtandent.
314cb93a386Sopenharmony_ci                        SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p));
315cb93a386Sopenharmony_ci                        instanceWriter.writeCircle(cusp);
316cb93a386Sopenharmony_ci                        // A quad can only have a cusp if it's flat with a 180-degree turnaround.
317cb93a386Sopenharmony_ci                        instanceWriter.lineTo(p[0], cusp);
318cb93a386Sopenharmony_ci                        instanceWriter.lineTo(cusp, p[2]);
319cb93a386Sopenharmony_ci                    } else {
320cb93a386Sopenharmony_ci                        instanceWriter.quadraticTo(p);
321cb93a386Sopenharmony_ci                    }
322cb93a386Sopenharmony_ci                    break;
323cb93a386Sopenharmony_ci                case Verb::kConic:
324cb93a386Sopenharmony_ci                    if (GrPathUtils::conicHasCusp(p)) {
325cb93a386Sopenharmony_ci                        // The cusp is always at the midtandent.
326cb93a386Sopenharmony_ci                        SkConic conic(p, strokeIter.w());
327cb93a386Sopenharmony_ci                        SkPoint cusp = conic.evalAt(conic.findMidTangent());
328cb93a386Sopenharmony_ci                        instanceWriter.writeCircle(cusp);
329cb93a386Sopenharmony_ci                        // A conic can only have a cusp if it's flat with a 180-degree turnaround.
330cb93a386Sopenharmony_ci                        instanceWriter.lineTo(p[0], cusp);
331cb93a386Sopenharmony_ci                        instanceWriter.lineTo(cusp, p[2]);
332cb93a386Sopenharmony_ci                    } else {
333cb93a386Sopenharmony_ci                        instanceWriter.conicTo(p, strokeIter.w());
334cb93a386Sopenharmony_ci                    }
335cb93a386Sopenharmony_ci                    break;
336cb93a386Sopenharmony_ci                case Verb::kCubic:
337cb93a386Sopenharmony_ci                    SkPoint chops[10];
338cb93a386Sopenharmony_ci                    float T[2];
339cb93a386Sopenharmony_ci                    bool areCusps;
340cb93a386Sopenharmony_ci                    numChops = GrPathUtils::findCubicConvex180Chops(p, T, &areCusps);
341cb93a386Sopenharmony_ci                    if (numChops == 0) {
342cb93a386Sopenharmony_ci                        instanceWriter.cubicConvex180To(p);
343cb93a386Sopenharmony_ci                    } else if (numChops == 1) {
344cb93a386Sopenharmony_ci                        SkChopCubicAt(p, chops, T[0]);
345cb93a386Sopenharmony_ci                        if (areCusps) {
346cb93a386Sopenharmony_ci                            instanceWriter.writeCircle(chops[3]);
347cb93a386Sopenharmony_ci                            // In a perfect world, these 3 points would be be equal after chopping
348cb93a386Sopenharmony_ci                            // on a cusp.
349cb93a386Sopenharmony_ci                            chops[2] = chops[4] = chops[3];
350cb93a386Sopenharmony_ci                        }
351cb93a386Sopenharmony_ci                        instanceWriter.cubicConvex180To(chops);
352cb93a386Sopenharmony_ci                        instanceWriter.cubicConvex180To(chops + 3);
353cb93a386Sopenharmony_ci                    } else {
354cb93a386Sopenharmony_ci                        SkASSERT(numChops == 2);
355cb93a386Sopenharmony_ci                        SkChopCubicAt(p, chops, T[0], T[1]);
356cb93a386Sopenharmony_ci                        if (areCusps) {
357cb93a386Sopenharmony_ci                            instanceWriter.writeCircle(chops[3]);
358cb93a386Sopenharmony_ci                            instanceWriter.writeCircle(chops[6]);
359cb93a386Sopenharmony_ci                            // Two cusps are only possible if it's a flat line with two 180-degree
360cb93a386Sopenharmony_ci                            // turnarounds.
361cb93a386Sopenharmony_ci                            instanceWriter.lineTo(chops[0], chops[3]);
362cb93a386Sopenharmony_ci                            instanceWriter.lineTo(chops[3], chops[6]);
363cb93a386Sopenharmony_ci                            instanceWriter.lineTo(chops[6], chops[9]);
364cb93a386Sopenharmony_ci                        } else {
365cb93a386Sopenharmony_ci                            instanceWriter.cubicConvex180To(chops);
366cb93a386Sopenharmony_ci                            instanceWriter.cubicConvex180To(chops + 3);
367cb93a386Sopenharmony_ci                            instanceWriter.cubicConvex180To(chops + 6);
368cb93a386Sopenharmony_ci                        }
369cb93a386Sopenharmony_ci                    }
370cb93a386Sopenharmony_ci                    break;
371cb93a386Sopenharmony_ci            }
372cb93a386Sopenharmony_ci        }
373cb93a386Sopenharmony_ci    }
374cb93a386Sopenharmony_ci
375cb93a386Sopenharmony_ci    // The maximum rotation we can have in a stroke is 180 degrees (SK_ScalarPI radians).
376cb93a386Sopenharmony_ci    int maxRadialSegmentsInStroke =
377cb93a386Sopenharmony_ci            std::max(SkScalarCeilToInt(maxRadialSegmentsPerRadian * SK_ScalarPI), 1);
378cb93a386Sopenharmony_ci
379cb93a386Sopenharmony_ci    int maxParametricSegmentsInStroke = SkScalarCeilToInt(sqrtf(sqrtf(
380cb93a386Sopenharmony_ci            instanceWriter.maxParametricSegments_pow4())));
381cb93a386Sopenharmony_ci    SkASSERT(maxParametricSegmentsInStroke >= 1);  // maxParametricSegments_pow4 is always >= 1.
382cb93a386Sopenharmony_ci
383cb93a386Sopenharmony_ci    // Now calculate the maximum number of edges we will need in the stroke portion of the instance.
384cb93a386Sopenharmony_ci    // The first and last edges in a stroke are shared by both the parametric and radial sets of
385cb93a386Sopenharmony_ci    // edges, so the total number of edges is:
386cb93a386Sopenharmony_ci    //
387cb93a386Sopenharmony_ci    //   numCombinedEdges = numParametricEdges + numRadialEdges - 2
388cb93a386Sopenharmony_ci    //
389cb93a386Sopenharmony_ci    // It's also important to differentiate between the number of edges and segments in a strip:
390cb93a386Sopenharmony_ci    //
391cb93a386Sopenharmony_ci    //   numSegments = numEdges - 1
392cb93a386Sopenharmony_ci    //
393cb93a386Sopenharmony_ci    // So the total number of combined edges in the stroke is:
394cb93a386Sopenharmony_ci    //
395cb93a386Sopenharmony_ci    //   numEdgesInStroke = numParametricSegments + 1 + numRadialSegments + 1 - 2
396cb93a386Sopenharmony_ci    //                    = numParametricSegments + numRadialSegments
397cb93a386Sopenharmony_ci    //
398cb93a386Sopenharmony_ci    int maxEdgesInStroke = maxRadialSegmentsInStroke + maxParametricSegmentsInStroke;
399cb93a386Sopenharmony_ci
400cb93a386Sopenharmony_ci    // Each triangle strip has two sections: It starts with a join then transitions to a stroke. The
401cb93a386Sopenharmony_ci    // number of edges in an instance is the sum of edges from the join and stroke sections both.
402cb93a386Sopenharmony_ci    // NOTE: The final join edge and the first stroke edge are co-located, however we still need to
403cb93a386Sopenharmony_ci    // emit both because the join's edge is half-width and the stroke's is full-width.
404cb93a386Sopenharmony_ci    fFixedEdgeCount = maxEdgesInJoin + maxEdgesInStroke;
405cb93a386Sopenharmony_ci
406cb93a386Sopenharmony_ci#if !SK_GPU_V1
407cb93a386Sopenharmony_ci    // Don't draw more vertices than can be indexed by a signed short. We just have to draw the line
408cb93a386Sopenharmony_ci    // somewhere and this seems reasonable enough. (There are two vertices per edge, so 2^14 edges
409cb93a386Sopenharmony_ci    // make 2^15 vertices.)
410cb93a386Sopenharmony_ci    fFixedEdgeCount = std::min(fFixedEdgeCount, (1 << 14) - 1);
411cb93a386Sopenharmony_ci#else
412cb93a386Sopenharmony_ci    // Decrease the upper limit of edge/vertex numbers to avoid Vulkan GPU OOM and page fault with stream id 0x3
413cb93a386Sopenharmony_ci    fFixedEdgeCount = std::min(fFixedEdgeCount, (1 << 10));
414cb93a386Sopenharmony_ci#endif
415cb93a386Sopenharmony_ci
416cb93a386Sopenharmony_ci    if (!target->caps().shaderCaps()->vertexIDSupport()) {
417cb93a386Sopenharmony_ci        // Our shader won't be able to use sk_VertexID. Bind a fallback vertex buffer with the IDs
418cb93a386Sopenharmony_ci        // in it instead.
419cb93a386Sopenharmony_ci        constexpr static int kMaxVerticesInFallbackBuffer = 2048;
420cb93a386Sopenharmony_ci        fFixedEdgeCount = std::min(fFixedEdgeCount, kMaxVerticesInFallbackBuffer/2);
421cb93a386Sopenharmony_ci
422cb93a386Sopenharmony_ci        GR_DEFINE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
423cb93a386Sopenharmony_ci
424cb93a386Sopenharmony_ci        fVertexBufferIfNoIDSupport = target->resourceProvider()->findOrMakeStaticBuffer(
425cb93a386Sopenharmony_ci                GrGpuBufferType::kVertex,
426cb93a386Sopenharmony_ci                kMaxVerticesInFallbackBuffer * sizeof(float),
427cb93a386Sopenharmony_ci                gVertexIDFallbackBufferKey,
428cb93a386Sopenharmony_ci                InitializeVertexIDFallbackBuffer);
429cb93a386Sopenharmony_ci    }
430cb93a386Sopenharmony_ci
431cb93a386Sopenharmony_ci    return fFixedEdgeCount;
432cb93a386Sopenharmony_ci}
433cb93a386Sopenharmony_ci
434cb93a386Sopenharmony_civoid StrokeFixedCountTessellator::InitializeVertexIDFallbackBuffer(VertexWriter vertexWriter,
435cb93a386Sopenharmony_ci                                                                   size_t bufferSize) {
436cb93a386Sopenharmony_ci    SkASSERT(bufferSize % (sizeof(float) * 2) == 0);
437cb93a386Sopenharmony_ci    int edgeCount = bufferSize / (sizeof(float) * 2);
438cb93a386Sopenharmony_ci    for (int i = 0; i < edgeCount; ++i) {
439cb93a386Sopenharmony_ci        vertexWriter << (float)i << (float)-i;
440cb93a386Sopenharmony_ci    }
441cb93a386Sopenharmony_ci}
442cb93a386Sopenharmony_ci
443cb93a386Sopenharmony_ci#if SK_GPU_V1
444cb93a386Sopenharmony_civoid StrokeFixedCountTessellator::draw(GrOpFlushState* flushState) const {
445cb93a386Sopenharmony_ci    if (fInstanceChunks.empty() || fFixedEdgeCount <= 0) {
446cb93a386Sopenharmony_ci        return;
447cb93a386Sopenharmony_ci    }
448cb93a386Sopenharmony_ci    if (!flushState->caps().shaderCaps()->vertexIDSupport() &&
449cb93a386Sopenharmony_ci        !fVertexBufferIfNoIDSupport) {
450cb93a386Sopenharmony_ci        return;
451cb93a386Sopenharmony_ci    }
452cb93a386Sopenharmony_ci    for (const auto& instanceChunk : fInstanceChunks) {
453cb93a386Sopenharmony_ci        flushState->bindBuffers(nullptr, instanceChunk.fBuffer, fVertexBufferIfNoIDSupport);
454cb93a386Sopenharmony_ci        flushState->drawInstanced(instanceChunk.fCount,
455cb93a386Sopenharmony_ci                                  instanceChunk.fBase,
456cb93a386Sopenharmony_ci                                  fFixedEdgeCount * 2,
457cb93a386Sopenharmony_ci                                  0);
458cb93a386Sopenharmony_ci    }
459cb93a386Sopenharmony_ci}
460cb93a386Sopenharmony_ci#endif
461cb93a386Sopenharmony_ci
462cb93a386Sopenharmony_ci}  // namespace skgpu
463