• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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