xref: /aosp_15_r20/external/skia/src/gpu/ganesh/tessellate/StrokeTessellator.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 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/tessellate/StrokeTessellator.h"
9 
10 #include "include/core/SkMatrix.h"
11 #include "include/private/base/SkAlignedStorage.h"
12 #include "include/private/base/SkAssert.h"
13 #include "include/private/base/SkOnce.h"
14 #include "include/private/base/SkPoint_impl.h"
15 #include "include/private/gpu/ganesh/GrTypesPriv.h"
16 #include "src/core/SkGeometry.h"
17 #include "src/gpu/ResourceKey.h"
18 #include "src/gpu/ganesh/GrBuffer.h"
19 #include "src/gpu/ganesh/GrCaps.h"
20 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
21 #include "src/gpu/ganesh/GrOpFlushState.h"
22 #include "src/gpu/ganesh/GrResourceProvider.h"
23 #include "src/gpu/ganesh/GrShaderCaps.h"
24 #include "src/gpu/ganesh/tessellate/VertexChunkPatchAllocator.h"
25 #include "src/gpu/tessellate/FixedCountBufferUtils.h"
26 #include "src/gpu/tessellate/LinearTolerances.h"
27 #include "src/gpu/tessellate/PatchWriter.h"
28 #include "src/gpu/tessellate/StrokeIterator.h"
29 #include "src/gpu/tessellate/WangsFormula.h"
30 
31 #include <algorithm>
32 #include <cmath>
33 #include <utility>
34 
35 namespace skgpu::ganesh {
36 
37 namespace {
38 
39 using namespace skgpu::tess;
40 
41 using StrokeWriter = PatchWriter<VertexChunkPatchAllocator,
42                                  Required<PatchAttribs::kJoinControlPoint>,
43                                  Optional<PatchAttribs::kStrokeParams>,
44                                  Optional<PatchAttribs::kColor>,
45                                  Optional<PatchAttribs::kWideColorIfEnabled>,
46                                  Optional<PatchAttribs::kExplicitCurveType>,
47                                  ReplicateLineEndPoints,
48                                  TrackJoinControlPoints>;
49 
write_fixed_count_patches(StrokeWriter && patchWriter,const SkMatrix & shaderMatrix,StrokeTessellator::PathStrokeList * pathStrokeList)50 void write_fixed_count_patches(StrokeWriter&& patchWriter,
51                                const SkMatrix& shaderMatrix,
52                                StrokeTessellator::PathStrokeList* pathStrokeList) {
53     // The vector xform approximates how the control points are transformed by the shader to
54     // more accurately compute how many *parametric* segments are needed.
55     // getMaxScale() returns -1 if it can't compute a scale factor (e.g. perspective), taking the
56     // absolute value automatically converts that to an identity scale factor for our purposes.
57     patchWriter.setShaderTransform(wangs_formula::VectorXform{shaderMatrix},
58                                    std::abs(shaderMatrix.getMaxScale()));
59     if (!(patchWriter.attribs() & PatchAttribs::kStrokeParams)) {
60         // Strokes are static. Calculate tolerances once.
61         patchWriter.updateUniformStrokeParams(pathStrokeList->fStroke);
62     }
63 
64     for (auto* pathStroke = pathStrokeList; pathStroke; pathStroke = pathStroke->fNext) {
65         const SkStrokeRec& stroke = pathStroke->fStroke;
66         if (patchWriter.attribs() & PatchAttribs::kStrokeParams) {
67             // Strokes are dynamic. Calculate tolerances every time.
68             patchWriter.updateStrokeParamsAttrib(stroke);
69         }
70         if (patchWriter.attribs() & PatchAttribs::kColor) {
71             patchWriter.updateColorAttrib(pathStroke->fColor);
72         }
73 
74         StrokeIterator strokeIter(pathStroke->fPath, &pathStroke->fStroke, &shaderMatrix);
75         while (strokeIter.next()) {
76             using Verb = StrokeIterator::Verb;
77             const SkPoint* p = strokeIter.pts();
78             int numChops;
79             switch (strokeIter.verb()) {
80                 case Verb::kContourFinished:
81                     patchWriter.writeDeferredStrokePatch();
82                     break;
83                 case Verb::kCircle:
84                     // Round cap or else an empty stroke that is specified to be drawn as a circle.
85                     patchWriter.writeCircle(p[0]);
86                     [[fallthrough]];
87                 case Verb::kMoveWithinContour:
88                     // A regular kMove invalidates the previous control point; the stroke iterator
89                     // tells us a new value to use.
90                     patchWriter.updateJoinControlPointAttrib(p[0]);
91                     break;
92                 case Verb::kLine:
93                     patchWriter.writeLine(p[0], p[1]);
94                     break;
95                 case Verb::kQuad:
96                     if (ConicHasCusp(p)) {
97                         // The cusp is always at the midtandent.
98                         SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p));
99                         patchWriter.writeCircle(cusp);
100                         // A quad can only have a cusp if it's flat with a 180-degree turnaround.
101                         patchWriter.writeLine(p[0], cusp);
102                         patchWriter.writeLine(cusp, p[2]);
103                     } else {
104                         patchWriter.writeQuadratic(p);
105                     }
106                     break;
107                 case Verb::kConic:
108                     if (ConicHasCusp(p)) {
109                         // The cusp is always at the midtandent.
110                         SkConic conic(p, strokeIter.w());
111                         SkPoint cusp = conic.evalAt(conic.findMidTangent());
112                         patchWriter.writeCircle(cusp);
113                         // A conic can only have a cusp if it's flat with a 180-degree turnaround.
114                         patchWriter.writeLine(p[0], cusp);
115                         patchWriter.writeLine(cusp, p[2]);
116                     } else {
117                         patchWriter.writeConic(p, strokeIter.w());
118                     }
119                     break;
120                 case Verb::kCubic:
121                     SkPoint chops[10];
122                     float T[2];
123                     bool areCusps;
124                     numChops = FindCubicConvex180Chops(p, T, &areCusps);
125                     if (numChops == 0) {
126                         patchWriter.writeCubic(p);
127                     } else if (numChops == 1) {
128                         SkChopCubicAt(p, chops, T[0]);
129                         if (areCusps) {
130                             patchWriter.writeCircle(chops[3]);
131                             // In a perfect world, these 3 points would be be equal after chopping
132                             // on a cusp.
133                             chops[2] = chops[4] = chops[3];
134                         }
135                         patchWriter.writeCubic(chops);
136                         patchWriter.writeCubic(chops + 3);
137                     } else {
138                         SkASSERT(numChops == 2);
139                         SkChopCubicAt(p, chops, T[0], T[1]);
140                         if (areCusps) {
141                             patchWriter.writeCircle(chops[3]);
142                             patchWriter.writeCircle(chops[6]);
143                             // Two cusps are only possible if it's a flat line with two 180-degree
144                             // turnarounds.
145                             patchWriter.writeLine(chops[0], chops[3]);
146                             patchWriter.writeLine(chops[3], chops[6]);
147                             patchWriter.writeLine(chops[6], chops[9]);
148                         } else {
149                             patchWriter.writeCubic(chops);
150                             patchWriter.writeCubic(chops + 3);
151                             patchWriter.writeCubic(chops + 6);
152                         }
153                     }
154                     break;
155             }
156         }
157     }
158 }
159 
160 }  // namespace
161 
162 
163 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
164 
prepare(GrMeshDrawTarget * target,const SkMatrix & shaderMatrix,PathStrokeList * pathStrokeList,int totalCombinedStrokeVerbCnt)165 void StrokeTessellator::prepare(GrMeshDrawTarget* target,
166                                 const SkMatrix& shaderMatrix,
167                                 PathStrokeList* pathStrokeList,
168                                 int totalCombinedStrokeVerbCnt) {
169     LinearTolerances worstCase;
170     const int preallocCount = FixedCountStrokes::PreallocCount(totalCombinedStrokeVerbCnt);
171     StrokeWriter patchWriter{fAttribs, &worstCase,  target, &fVertexChunkArray, preallocCount};
172 
173     write_fixed_count_patches(std::move(patchWriter), shaderMatrix, pathStrokeList);
174     fVertexCount = FixedCountStrokes::VertexCount(worstCase);
175 
176     if (!target->caps().shaderCaps()->fVertexIDSupport) {
177         // Our shader won't be able to use sk_VertexID. Bind a fallback vertex buffer with the IDs
178         // in it instead.
179         fVertexCount = std::min(fVertexCount, 2 * FixedCountStrokes::kMaxEdgesNoVertexIDs);
180 
181         SKGPU_DEFINE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
182 
183         fVertexBufferIfNoIDSupport = target->resourceProvider()->findOrMakeStaticBuffer(
184                 GrGpuBufferType::kVertex,
185                 FixedCountStrokes::VertexBufferSize(),
186                 gVertexIDFallbackBufferKey,
187                 FixedCountStrokes::WriteVertexBuffer);
188     }
189 }
190 
draw(GrOpFlushState * flushState) const191 void StrokeTessellator::draw(GrOpFlushState* flushState) const {
192     if (fVertexChunkArray.empty() || fVertexCount <= 0) {
193         return;
194     }
195     if (!flushState->caps().shaderCaps()->fVertexIDSupport &&
196         !fVertexBufferIfNoIDSupport) {
197         return;
198     }
199     for (const auto& instanceChunk : fVertexChunkArray) {
200         flushState->bindBuffers(nullptr, instanceChunk.fBuffer, fVertexBufferIfNoIDSupport);
201         flushState->drawInstanced(instanceChunk.fCount,
202                                   instanceChunk.fBase,
203                                   fVertexCount,
204                                   0);
205     }
206 }
207 
208 }  // namespace skgpu::ganesh
209