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/PathWedgeTessellator.h"
9
10 #include "src/core/SkPathPriv.h"
11 #include "src/gpu/tessellate/AffineMatrix.h"
12 #include "src/gpu/tessellate/PatchWriter.h"
13 #include "src/gpu/tessellate/PathCurveTessellator.h"
14 #include "src/gpu/tessellate/WangsFormula.h"
15
16 #if SK_GPU_V1
17 #include "src/gpu/GrMeshDrawTarget.h"
18 #include "src/gpu/GrOpFlushState.h"
19 #include "src/gpu/GrResourceProvider.h"
20 #endif
21
22 namespace skgpu {
23
24 namespace {
25
26 // Parses out each contour in a path and tracks the midpoint. Example usage:
27 //
28 // SkTPathContourParser parser;
29 // while (parser.parseNextContour()) {
30 // SkPoint midpoint = parser.currentMidpoint();
31 // for (auto [verb, pts] : parser.currentContour()) {
32 // ...
33 // }
34 // }
35 //
36 class MidpointContourParser {
37 public:
MidpointContourParser(const SkPath & path)38 MidpointContourParser(const SkPath& path)
39 : fPath(path)
40 , fVerbs(SkPathPriv::VerbData(fPath))
41 , fNumRemainingVerbs(fPath.countVerbs())
42 , fPoints(SkPathPriv::PointData(fPath))
43 , fWeights(SkPathPriv::ConicWeightData(fPath)) {}
44 // Advances the internal state to the next contour in the path. Returns false if there are no
45 // more contours.
parseNextContour()46 bool parseNextContour() {
47 bool hasGeometry = false;
48 for (; fVerbsIdx < fNumRemainingVerbs; ++fVerbsIdx) {
49 switch (fVerbs[fVerbsIdx]) {
50 case SkPath::kMove_Verb:
51 if (!hasGeometry) {
52 fMidpoint = {0,0};
53 fMidpointWeight = 0;
54 this->advance(); // Resets fPtsIdx to 0 and advances fPoints.
55 fPtsIdx = 1; // Increment fPtsIdx past the kMove.
56 continue;
57 }
58 if (fPoints[0] != fPoints[fPtsIdx - 1]) {
59 // There's an implicit close at the end. Add the start point to our mean.
60 fMidpoint += fPoints[0];
61 ++fMidpointWeight;
62 }
63 return true;
64 default:
65 continue;
66 case SkPath::kLine_Verb:
67 ++fPtsIdx;
68 break;
69 case SkPath::kConic_Verb:
70 ++fWtsIdx;
71 [[fallthrough]];
72 case SkPath::kQuad_Verb:
73 fPtsIdx += 2;
74 break;
75 case SkPath::kCubic_Verb:
76 fPtsIdx += 3;
77 break;
78 }
79 fMidpoint += fPoints[fPtsIdx - 1];
80 ++fMidpointWeight;
81 hasGeometry = true;
82 }
83 if (hasGeometry && fPoints[0] != fPoints[fPtsIdx - 1]) {
84 // There's an implicit close at the end. Add the start point to our mean.
85 fMidpoint += fPoints[0];
86 ++fMidpointWeight;
87 }
88 return hasGeometry;
89 }
90
91 // Allows for iterating the current contour using a range-for loop.
currentContour()92 SkPathPriv::Iterate currentContour() {
93 return SkPathPriv::Iterate(fVerbs, fVerbs + fVerbsIdx, fPoints, fWeights);
94 }
95
currentMidpoint()96 SkPoint currentMidpoint() { return fMidpoint * (1.f / fMidpointWeight); }
97
98 private:
advance()99 void advance() {
100 fVerbs += fVerbsIdx;
101 fNumRemainingVerbs -= fVerbsIdx;
102 fVerbsIdx = 0;
103 fPoints += fPtsIdx;
104 fPtsIdx = 0;
105 fWeights += fWtsIdx;
106 fWtsIdx = 0;
107 }
108
109 const SkPath& fPath;
110
111 const uint8_t* fVerbs;
112 int fNumRemainingVerbs = 0;
113 int fVerbsIdx = 0;
114
115 const SkPoint* fPoints;
116 int fPtsIdx = 0;
117
118 const float* fWeights;
119 int fWtsIdx = 0;
120
121 SkPoint fMidpoint;
122 int fMidpointWeight;
123 };
124
125 } // namespace
126
patchPreallocCount(int totalCombinedPathVerbCnt) const127 int PathWedgeTessellator::patchPreallocCount(int totalCombinedPathVerbCnt) const {
128 // Over-allocate enough wedges for 1 in 4 to chop.
129 int maxWedges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt);
130 return (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4)
131 }
132
writePatches(PatchWriter & patchWriter,const SkMatrix & shaderMatrix,const PathDrawList & pathDrawList)133 void PathWedgeTessellator::writePatches(PatchWriter& patchWriter,
134 const SkMatrix& shaderMatrix,
135 const PathDrawList& pathDrawList) {
136 wangs_formula::VectorXform shaderXform(shaderMatrix);
137 for (auto [pathMatrix, path, color] : pathDrawList) {
138 AffineMatrix m(pathMatrix);
139 if (fAttribs & PatchAttribs::kColor) {
140 patchWriter.updateColorAttrib(color);
141 }
142 MidpointContourParser parser(path);
143 while (parser.parseNextContour()) {
144 patchWriter.updateFanPointAttrib(m.mapPoint(parser.currentMidpoint()));
145 SkPoint lastPoint = {0, 0};
146 SkPoint startPoint = {0, 0};
147 for (auto [verb, pts, w] : parser.currentContour()) {
148 switch (verb) {
149 case SkPathVerb::kMove: {
150 startPoint = lastPoint = pts[0];
151 break;
152 }
153
154 case SkPathVerb::kLine: {
155 // Explicitly convert the line to an equivalent cubic w/ four distinct
156 // control points because it fans better and avoids double-hitting pixels.
157 patchWriter.writeLine(m.map2Points(pts));
158 lastPoint = pts[1];
159 break;
160 }
161
162 case SkPathVerb::kQuad: {
163 auto [p0, p1] = m.map2Points(pts);
164 auto p2 = m.map1Point(pts+2);
165
166 patchWriter.writeQuadratic(p0, p1, p2, shaderXform);
167 lastPoint = pts[2];
168 break;
169 }
170
171 case SkPathVerb::kConic: {
172 auto [p0, p1] = m.map2Points(pts);
173 auto p2 = m.map1Point(pts+2);
174
175 patchWriter.writeConic(p0, p1, p2, *w, shaderXform);
176 lastPoint = pts[2];
177 break;
178 }
179
180 case SkPathVerb::kCubic: {
181 auto [p0, p1] = m.map2Points(pts);
182 auto [p2, p3] = m.map2Points(pts+2);
183
184 patchWriter.writeCubic(p0, p1, p2, p3, shaderXform);
185 lastPoint = pts[3];
186 break;
187 }
188
189 case SkPathVerb::kClose: {
190 break; // Ignore. We can assume an implicit close at the end.
191 }
192 }
193 }
194 if (lastPoint != startPoint) {
195 SkPoint pts[2] = {lastPoint, startPoint};
196 patchWriter.writeLine(m.map2Points(pts));
197 }
198 }
199 }
200
201 // We already chopped curves to make sure none needed a higher resolveLevel than
202 // kMaxFixedResolveLevel.
203 fFixedResolveLevel = SkTPin(patchWriter.requiredResolveLevel(),
204 fFixedResolveLevel,
205 int(kMaxFixedResolveLevel));
206 }
207
WriteFixedVertexBuffer(VertexWriter vertexWriter,size_t bufferSize)208 void PathWedgeTessellator::WriteFixedVertexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
209 SkASSERT(bufferSize >= sizeof(SkPoint));
210
211 // Start out with the fan point. A negative resolve level indicates the fan point.
212 vertexWriter << -1.f/*resolveLevel*/ << -1.f/*idx*/;
213
214 // The rest is the same as for curves.
215 PathCurveTessellator::WriteFixedVertexBuffer(std::move(vertexWriter),
216 bufferSize - sizeof(SkPoint));
217 }
218
WriteFixedIndexBuffer(VertexWriter vertexWriter,size_t bufferSize)219 void PathWedgeTessellator::WriteFixedIndexBuffer(VertexWriter vertexWriter, size_t bufferSize) {
220 SkASSERT(bufferSize >= sizeof(uint16_t) * 3);
221
222 // Start out with the fan triangle.
223 vertexWriter << (uint16_t)0 << (uint16_t)1 << (uint16_t)2;
224
225 // The rest is the same as for curves, with a baseIndex of 1.
226 PathCurveTessellator::WriteFixedIndexBufferBaseIndex(std::move(vertexWriter),
227 bufferSize - sizeof(uint16_t) * 3,
228 1);
229 }
230
231 #if SK_GPU_V1
232
233 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
234 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
235
prepareFixedCountBuffers(GrMeshDrawTarget * target)236 void PathWedgeTessellator::prepareFixedCountBuffers(GrMeshDrawTarget* target) {
237 GrResourceProvider* rp = target->resourceProvider();
238
239 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedVertexBufferKey);
240
241 fFixedVertexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kVertex,
242 FixedVertexBufferSize(kMaxFixedResolveLevel),
243 gFixedVertexBufferKey,
244 WriteFixedVertexBuffer);
245
246 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gFixedIndexBufferKey);
247
248 fFixedIndexBuffer = rp->findOrMakeStaticBuffer(GrGpuBufferType::kIndex,
249 FixedIndexBufferSize(kMaxFixedResolveLevel),
250 gFixedIndexBufferKey,
251 WriteFixedIndexBuffer);
252 }
253
drawTessellated(GrOpFlushState * flushState) const254 void PathWedgeTessellator::drawTessellated(GrOpFlushState* flushState) const {
255 for (const GrVertexChunk& chunk : fVertexChunkArray) {
256 flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
257 flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
258 }
259 }
260
drawFixedCount(GrOpFlushState * flushState) const261 void PathWedgeTessellator::drawFixedCount(GrOpFlushState* flushState) const {
262 if (!fFixedVertexBuffer || !fFixedIndexBuffer) {
263 return;
264 }
265 // Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
266 int fixedIndexCount = (NumCurveTrianglesAtResolveLevel(fFixedResolveLevel) + 1) * 3;
267 for (const GrVertexChunk& chunk : fVertexChunkArray) {
268 flushState->bindBuffers(fFixedIndexBuffer, chunk.fBuffer, fFixedVertexBuffer);
269 flushState->drawIndexedInstanced(fixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
270 }
271 }
272
273 #endif
274
275 } // namespace skgpu
276