blob: f311b2aebfae0445b4e658ce687da607e1fe2f7b [file] [log] [blame]
Chris Daltond9bdc322021-06-01 19:22:05 -06001/*
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/GrPathWedgeTessellator.h"
9
Robert Phillips1a82a4e2021-07-01 10:27:44 -040010#include "src/gpu/GrResourceProvider.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060011#include "src/gpu/geometry/GrPathUtils.h"
12#include "src/gpu/geometry/GrWangsFormula.h"
13#include "src/gpu/tessellate/GrCullTest.h"
14#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
15
16namespace {
17
Chris Daltone1f72372021-06-29 16:45:49 -060018constexpr static float kPrecision = GrTessellationShader::kLinearizationPrecision;
Chris Daltond9bdc322021-06-01 19:22:05 -060019
20// Parses out each contour in a path and tracks the midpoint. Example usage:
21//
22// SkTPathContourParser parser;
23// while (parser.parseNextContour()) {
24// SkPoint midpoint = parser.currentMidpoint();
25// for (auto [verb, pts] : parser.currentContour()) {
26// ...
27// }
28// }
29//
30class MidpointContourParser {
31public:
32 MidpointContourParser(const SkPath& path)
33 : fPath(path)
34 , fVerbs(SkPathPriv::VerbData(fPath))
35 , fNumRemainingVerbs(fPath.countVerbs())
36 , fPoints(SkPathPriv::PointData(fPath))
37 , fWeights(SkPathPriv::ConicWeightData(fPath)) {}
38 // Advances the internal state to the next contour in the path. Returns false if there are no
39 // more contours.
40 bool parseNextContour() {
41 bool hasGeometry = false;
42 for (; fVerbsIdx < fNumRemainingVerbs; ++fVerbsIdx) {
43 switch (fVerbs[fVerbsIdx]) {
44 case SkPath::kMove_Verb:
45 if (!hasGeometry) {
46 fMidpoint = fPoints[fPtsIdx];
47 fMidpointWeight = 1;
48 this->advance();
49 ++fPtsIdx;
50 continue;
51 }
52 return true;
53 default:
54 continue;
55 case SkPath::kLine_Verb:
56 ++fPtsIdx;
57 break;
58 case SkPath::kConic_Verb:
59 ++fWtsIdx;
60 [[fallthrough]];
61 case SkPath::kQuad_Verb:
62 fPtsIdx += 2;
63 break;
64 case SkPath::kCubic_Verb:
65 fPtsIdx += 3;
66 break;
67 }
68 fMidpoint += fPoints[fPtsIdx - 1];
69 ++fMidpointWeight;
70 hasGeometry = true;
71 }
72 return hasGeometry;
73 }
74
75 // Allows for iterating the current contour using a range-for loop.
76 SkPathPriv::Iterate currentContour() {
77 return SkPathPriv::Iterate(fVerbs, fVerbs + fVerbsIdx, fPoints, fWeights);
78 }
79
80 SkPoint currentMidpoint() { return fMidpoint * (1.f / fMidpointWeight); }
81
82private:
83 void advance() {
84 fVerbs += fVerbsIdx;
85 fNumRemainingVerbs -= fVerbsIdx;
86 fVerbsIdx = 0;
87 fPoints += fPtsIdx;
88 fPtsIdx = 0;
89 fWeights += fWtsIdx;
90 fWtsIdx = 0;
91 }
92
93 const SkPath& fPath;
94
95 const uint8_t* fVerbs;
96 int fNumRemainingVerbs = 0;
97 int fVerbsIdx = 0;
98
99 const SkPoint* fPoints;
100 int fPtsIdx = 0;
101
102 const float* fWeights;
103 int fWtsIdx = 0;
104
105 SkPoint fMidpoint;
106 int fMidpointWeight;
107};
108
109// Writes out wedge patches, chopping as necessary so none require more segments than are supported
110// by the hardware.
111class WedgeWriter {
112public:
Chris Daltond2b8ba32021-06-09 00:12:59 -0600113 WedgeWriter(const SkRect& cullBounds, const SkMatrix& viewMatrix, int maxSegments)
Chris Daltond9bdc322021-06-01 19:22:05 -0600114 : fCullTest(cullBounds, viewMatrix)
Chris Daltond2b8ba32021-06-09 00:12:59 -0600115 , fVectorXform(viewMatrix)
116 , fMaxSegments_pow2(maxSegments * maxSegments)
117 , fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600118 }
119
120 SK_ALWAYS_INLINE void writeFlatWedge(GrVertexChunkBuilder* chunker, SkPoint p0, SkPoint p1,
121 SkPoint midpoint) {
122 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
123 GrPathUtils::writeLineAsCubic(p0, p1, &vertexWriter);
124 vertexWriter.write(midpoint);
125 }
126 }
127
128 SK_ALWAYS_INLINE void writeQuadraticWedge(GrVertexChunkBuilder* chunker, const SkPoint p[3],
129 SkPoint midpoint) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600130 float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fVectorXform);
131 if (numSegments_pow4 > fMaxSegments_pow4) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600132 this->chopAndWriteQuadraticWedges(chunker, p, midpoint);
133 return;
134 }
135 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
136 GrPathUtils::writeQuadAsCubic(p, &vertexWriter);
137 vertexWriter.write(midpoint);
138 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600139 fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600140 }
141
142 SK_ALWAYS_INLINE void writeConicWedge(GrVertexChunkBuilder* chunker, const SkPoint p[3],
143 float w, SkPoint midpoint) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600144 float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform);
Chris Daltone6f45312021-06-02 12:00:01 -0600145 if (GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600146 this->chopAndWriteConicWedges(chunker, {p, w}, midpoint);
147 return;
148 }
149 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
150 GrTessellationShader::WriteConicPatch(p, w, &vertexWriter);
151 vertexWriter.write(midpoint);
152 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600153 fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
154 fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600155 }
156
157 SK_ALWAYS_INLINE void writeCubicWedge(GrVertexChunkBuilder* chunker, const SkPoint p[4],
158 SkPoint midpoint) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600159 float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fVectorXform);
160 if (numSegments_pow4 > fMaxSegments_pow4) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600161 this->chopAndWriteCubicWedges(chunker, p, midpoint);
162 return;
163 }
164 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
165 vertexWriter.writeArray(p, 4);
166 vertexWriter.write(midpoint);
167 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600168 fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600169 }
170
Chris Daltond2b8ba32021-06-09 00:12:59 -0600171 int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
172
Chris Daltond9bdc322021-06-01 19:22:05 -0600173private:
174 void chopAndWriteQuadraticWedges(GrVertexChunkBuilder* chunker, const SkPoint p[3],
175 SkPoint midpoint) {
176 SkPoint chops[5];
177 SkChopQuadAtHalf(p, chops);
178 for (int i = 0; i < 2; ++i) {
179 const SkPoint* q = chops + i*2;
180 if (fCullTest.areVisible3(q)) {
181 this->writeQuadraticWedge(chunker, q, midpoint);
182 } else {
183 this->writeFlatWedge(chunker, q[0], q[2], midpoint);
184 }
185 }
186 }
187
188 void chopAndWriteConicWedges(GrVertexChunkBuilder* chunker, const SkConic& conic,
189 SkPoint midpoint) {
190 SkConic chops[2];
191 if (!conic.chopAt(.5, chops)) {
192 return;
193 }
194 for (int i = 0; i < 2; ++i) {
195 if (fCullTest.areVisible3(chops[i].fPts)) {
196 this->writeConicWedge(chunker, chops[i].fPts, chops[i].fW, midpoint);
197 } else {
198 this->writeFlatWedge(chunker, chops[i].fPts[0], chops[i].fPts[2], midpoint);
199 }
200 }
201 }
202
203 void chopAndWriteCubicWedges(GrVertexChunkBuilder* chunker, const SkPoint p[4],
204 SkPoint midpoint) {
205 SkPoint chops[7];
206 SkChopCubicAtHalf(p, chops);
207 for (int i = 0; i < 2; ++i) {
208 const SkPoint* c = chops + i*3;
209 if (fCullTest.areVisible4(c)) {
210 this->writeCubicWedge(chunker, c, midpoint);
211 } else {
212 this->writeFlatWedge(chunker, c[0], c[3], midpoint);
213 }
214 }
215 }
216
217 GrCullTest fCullTest;
218 GrVectorXform fVectorXform;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600219 const float fMaxSegments_pow2;
220 const float fMaxSegments_pow4;
221
222 // If using fixed count, this is the max number of curve segments we need to draw per instance.
223 float fNumFixedSegments_pow4 = 1;
Chris Daltond9bdc322021-06-01 19:22:05 -0600224};
225
226} // namespace
227
228
229GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600230 const SkPMColor4f& color, int numPathVerbs,
Chris Dalton198ac152021-06-09 13:49:43 -0600231 const GrPipeline& pipeline, const GrCaps& caps) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600232 using PatchType = GrPathTessellationShader::PatchType;
233 GrPathTessellationShader* shader;
234 if (caps.shaderCaps()->tessellationSupport() &&
Chris Dalton198ac152021-06-09 13:49:43 -0600235 !pipeline.usesVaryingCoords() && // Our tessellation back door doesn't handle varyings.
Chris Daltond2b8ba32021-06-09 00:12:59 -0600236 numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
237 shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
238 PatchType::kWedges);
239 } else {
240 shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(arena, viewMatrix, color,
241 PatchType::kWedges);
242 }
243 return arena->make([=](void* objStart) {
244 return new(objStart) GrPathWedgeTessellator(shader);
245 });
Chris Daltond9bdc322021-06-01 19:22:05 -0600246}
247
Chris Daltone1f72372021-06-29 16:45:49 -0600248GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
249GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
250
Robert Phillips71143952021-06-17 14:55:07 -0400251void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cullBounds,
Chris Daltond9bdc322021-06-01 19:22:05 -0600252 const SkPath& path,
253 const BreadcrumbTriangleList* breadcrumbTriangleList) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600254 SkASSERT(!breadcrumbTriangleList);
255 SkASSERT(fVertexChunkArray.empty());
256
257 // Over-allocate enough wedges for 1 in 4 to chop.
258 int maxWedges = GrPathTessellator::MaxSegmentsInPath(path);
259 int wedgeAllocCount = (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4)
260 if (!wedgeAllocCount) {
261 return;
262 }
263 GrVertexChunkBuilder chunker(target, &fVertexChunkArray, sizeof(SkPoint) * 5, wedgeAllocCount);
264
Chris Daltond2b8ba32021-06-09 00:12:59 -0600265 int maxSegments;
266 if (fShader->willUseTessellationShaders()) {
267 maxSegments = target->caps().shaderCaps()->maxTessellationSegments();
268 } else {
Chris Daltone1f72372021-06-29 16:45:49 -0600269 maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600270 }
271
272 WedgeWriter wedgeWriter(cullBounds, fShader->viewMatrix(), maxSegments);
Chris Daltond9bdc322021-06-01 19:22:05 -0600273 MidpointContourParser parser(path);
274 while (parser.parseNextContour()) {
275 SkPoint midpoint = parser.currentMidpoint();
276 SkPoint startPoint = {0, 0};
277 SkPoint lastPoint = startPoint;
278 for (auto [verb, pts, w] : parser.currentContour()) {
279 switch (verb) {
280 case SkPathVerb::kMove:
281 startPoint = lastPoint = pts[0];
282 break;
283 case SkPathVerb::kClose:
284 break; // Ignore. We can assume an implicit close at the end.
285 case SkPathVerb::kLine:
286 wedgeWriter.writeFlatWedge(&chunker, pts[0], pts[1], midpoint);
287 lastPoint = pts[1];
288 break;
289 case SkPathVerb::kQuad:
290 wedgeWriter.writeQuadraticWedge(&chunker, pts, midpoint);
291 lastPoint = pts[2];
292 break;
293 case SkPathVerb::kConic:
294 wedgeWriter.writeConicWedge(&chunker, pts, *w, midpoint);
295 lastPoint = pts[2];
296 break;
297 case SkPathVerb::kCubic:
298 wedgeWriter.writeCubicWedge(&chunker, pts, midpoint);
299 lastPoint = pts[3];
300 break;
301 }
302 }
303 if (lastPoint != startPoint) {
304 wedgeWriter.writeFlatWedge(&chunker, lastPoint, startPoint, midpoint);
305 }
306 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600307
308 if (!fShader->willUseTessellationShaders()) {
309 // log2(n) == log16(n^4).
310 int fixedResolveLevel = GrWangsFormula::nextlog16(wedgeWriter.numFixedSegments_pow4());
311 int numCurveTriangles =
312 GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel);
313 // Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
Chris Daltone1f72372021-06-29 16:45:49 -0600314 fFixedIndexCount = numCurveTriangles * 3 + 3;
315
316 GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
317
318 fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
319 GrGpuBufferType::kVertex,
320 GrPathTessellationShader::SizeOfVertexBufferForMiddleOutWedges(),
321 gFixedCountVertexBufferKey,
322 GrPathTessellationShader::InitializeVertexBufferForMiddleOutWedges);
323
324 GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
325
326 fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
327 GrGpuBufferType::kIndex,
328 GrPathTessellationShader::SizeOfIndexBufferForMiddleOutWedges(),
329 gFixedCountIndexBufferKey,
330 GrPathTessellationShader::InitializeIndexBufferForMiddleOutWedges);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600331 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600332}
333
334void GrPathWedgeTessellator::draw(GrOpFlushState* flushState) const {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600335 if (fShader->willUseTessellationShaders()) {
336 for (const GrVertexChunk& chunk : fVertexChunkArray) {
337 flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
338 flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
339 }
340 } else {
341 SkASSERT(fShader->hasInstanceAttributes());
342 for (const GrVertexChunk& chunk : fVertexChunkArray) {
Chris Daltone1f72372021-06-29 16:45:49 -0600343 flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer);
344 flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600345 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600346 }
347}