blob: 5949795cf08c7cbde8bace6cefc7fdd0f231cb57 [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
Chris Dalton50516f32021-07-18 22:26:27 -0600120 SK_ALWAYS_INLINE void writeFlatWedge(const GrShaderCaps& shaderCaps,
121 GrVertexChunkBuilder* chunker, SkPoint p0, SkPoint p1,
Chris Daltond9bdc322021-06-01 19:22:05 -0600122 SkPoint midpoint) {
123 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
124 GrPathUtils::writeLineAsCubic(p0, p1, &vertexWriter);
125 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600126 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
127 GrTessellationShader::kCubicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600128 }
129 }
130
Chris Dalton50516f32021-07-18 22:26:27 -0600131 SK_ALWAYS_INLINE void writeQuadraticWedge(const GrShaderCaps& shaderCaps,
132 GrVertexChunkBuilder* chunker, const SkPoint p[3],
Chris Daltond9bdc322021-06-01 19:22:05 -0600133 SkPoint midpoint) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600134 float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fVectorXform);
135 if (numSegments_pow4 > fMaxSegments_pow4) {
Chris Dalton50516f32021-07-18 22:26:27 -0600136 this->chopAndWriteQuadraticWedges(shaderCaps, chunker, p, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600137 return;
138 }
139 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
140 GrPathUtils::writeQuadAsCubic(p, &vertexWriter);
141 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600142 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
143 GrTessellationShader::kCubicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600144 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600145 fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600146 }
147
Chris Dalton50516f32021-07-18 22:26:27 -0600148 SK_ALWAYS_INLINE void writeConicWedge(const GrShaderCaps& shaderCaps,
149 GrVertexChunkBuilder* chunker, const SkPoint p[3],
Chris Daltond9bdc322021-06-01 19:22:05 -0600150 float w, SkPoint midpoint) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600151 float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform);
Chris Daltone6f45312021-06-02 12:00:01 -0600152 if (GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
Chris Dalton50516f32021-07-18 22:26:27 -0600153 this->chopAndWriteConicWedges(shaderCaps, chunker, {p, w}, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600154 return;
155 }
156 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
Chris Dalton3febc612021-07-14 13:47:07 -0600157 GrTessellationShader::WriteConicPatch(p, w, &vertexWriter);
Chris Daltond9bdc322021-06-01 19:22:05 -0600158 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600159 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
160 GrTessellationShader::kConicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600161 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600162 fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
163 fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600164 }
165
Chris Dalton50516f32021-07-18 22:26:27 -0600166 SK_ALWAYS_INLINE void writeCubicWedge(const GrShaderCaps& shaderCaps,
167 GrVertexChunkBuilder* chunker, const SkPoint p[4],
Chris Daltond9bdc322021-06-01 19:22:05 -0600168 SkPoint midpoint) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600169 float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fVectorXform);
170 if (numSegments_pow4 > fMaxSegments_pow4) {
Chris Dalton50516f32021-07-18 22:26:27 -0600171 this->chopAndWriteCubicWedges(shaderCaps, chunker, p, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600172 return;
173 }
174 if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
175 vertexWriter.writeArray(p, 4);
176 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600177 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
178 GrTessellationShader::kCubicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600179 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600180 fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600181 }
182
Chris Daltond2b8ba32021-06-09 00:12:59 -0600183 int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
184
Chris Daltond9bdc322021-06-01 19:22:05 -0600185private:
Chris Dalton50516f32021-07-18 22:26:27 -0600186 void chopAndWriteQuadraticWedges(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
187 const SkPoint p[3], SkPoint midpoint) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600188 SkPoint chops[5];
189 SkChopQuadAtHalf(p, chops);
190 for (int i = 0; i < 2; ++i) {
191 const SkPoint* q = chops + i*2;
192 if (fCullTest.areVisible3(q)) {
Chris Dalton50516f32021-07-18 22:26:27 -0600193 this->writeQuadraticWedge(shaderCaps, chunker, q, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600194 } else {
Chris Dalton50516f32021-07-18 22:26:27 -0600195 this->writeFlatWedge(shaderCaps, chunker, q[0], q[2], midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600196 }
197 }
198 }
199
Chris Dalton50516f32021-07-18 22:26:27 -0600200 void chopAndWriteConicWedges(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
201 const SkConic& conic, SkPoint midpoint) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600202 SkConic chops[2];
203 if (!conic.chopAt(.5, chops)) {
204 return;
205 }
206 for (int i = 0; i < 2; ++i) {
207 if (fCullTest.areVisible3(chops[i].fPts)) {
Chris Dalton50516f32021-07-18 22:26:27 -0600208 this->writeConicWedge(shaderCaps, chunker, chops[i].fPts, chops[i].fW, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600209 } else {
Chris Dalton50516f32021-07-18 22:26:27 -0600210 this->writeFlatWedge(shaderCaps, chunker, chops[i].fPts[0], chops[i].fPts[2],
211 midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600212 }
213 }
214 }
215
Chris Dalton50516f32021-07-18 22:26:27 -0600216 void chopAndWriteCubicWedges(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
217 const SkPoint p[4], SkPoint midpoint) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600218 SkPoint chops[7];
219 SkChopCubicAtHalf(p, chops);
220 for (int i = 0; i < 2; ++i) {
221 const SkPoint* c = chops + i*3;
222 if (fCullTest.areVisible4(c)) {
Chris Dalton50516f32021-07-18 22:26:27 -0600223 this->writeCubicWedge(shaderCaps, chunker, c, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600224 } else {
Chris Dalton50516f32021-07-18 22:26:27 -0600225 this->writeFlatWedge(shaderCaps, chunker, c[0], c[3], midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600226 }
227 }
228 }
229
230 GrCullTest fCullTest;
231 GrVectorXform fVectorXform;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600232 const float fMaxSegments_pow2;
233 const float fMaxSegments_pow4;
234
235 // If using fixed count, this is the max number of curve segments we need to draw per instance.
236 float fNumFixedSegments_pow4 = 1;
Chris Daltond9bdc322021-06-01 19:22:05 -0600237};
238
239} // namespace
240
241
242GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600243 const SkPMColor4f& color, int numPathVerbs,
Chris Dalton198ac152021-06-09 13:49:43 -0600244 const GrPipeline& pipeline, const GrCaps& caps) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600245 using PatchType = GrPathTessellationShader::PatchType;
246 GrPathTessellationShader* shader;
247 if (caps.shaderCaps()->tessellationSupport() &&
Chris Dalton50516f32021-07-18 22:26:27 -0600248 caps.shaderCaps()->infinitySupport() && // The hw tessellation shaders use infinity.
Chris Dalton198ac152021-06-09 13:49:43 -0600249 !pipeline.usesVaryingCoords() && // Our tessellation back door doesn't handle varyings.
Chris Daltond2b8ba32021-06-09 00:12:59 -0600250 numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
251 shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
252 PatchType::kWedges);
253 } else {
Chris Dalton50516f32021-07-18 22:26:27 -0600254 shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
255 viewMatrix, color,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600256 PatchType::kWedges);
257 }
258 return arena->make([=](void* objStart) {
259 return new(objStart) GrPathWedgeTessellator(shader);
260 });
Chris Daltond9bdc322021-06-01 19:22:05 -0600261}
262
Chris Daltone1f72372021-06-29 16:45:49 -0600263GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
264GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
265
Robert Phillips71143952021-06-17 14:55:07 -0400266void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cullBounds,
Chris Daltond9bdc322021-06-01 19:22:05 -0600267 const SkPath& path,
268 const BreadcrumbTriangleList* breadcrumbTriangleList) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600269 SkASSERT(!breadcrumbTriangleList);
270 SkASSERT(fVertexChunkArray.empty());
271
Chris Dalton50516f32021-07-18 22:26:27 -0600272 const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
273
Chris Daltond9bdc322021-06-01 19:22:05 -0600274 // Over-allocate enough wedges for 1 in 4 to chop.
275 int maxWedges = GrPathTessellator::MaxSegmentsInPath(path);
276 int wedgeAllocCount = (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4)
277 if (!wedgeAllocCount) {
278 return;
279 }
Chris Dalton50516f32021-07-18 22:26:27 -0600280 size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 5
281 : fShader->instanceStride();
282 GrVertexChunkBuilder chunker(target, &fVertexChunkArray, patchStride, wedgeAllocCount);
Chris Daltond9bdc322021-06-01 19:22:05 -0600283
Chris Daltond2b8ba32021-06-09 00:12:59 -0600284 int maxSegments;
285 if (fShader->willUseTessellationShaders()) {
Chris Dalton50516f32021-07-18 22:26:27 -0600286 maxSegments = shaderCaps.maxTessellationSegments();
Chris Daltond2b8ba32021-06-09 00:12:59 -0600287 } else {
Chris Daltone1f72372021-06-29 16:45:49 -0600288 maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600289 }
290
291 WedgeWriter wedgeWriter(cullBounds, fShader->viewMatrix(), maxSegments);
Chris Daltond9bdc322021-06-01 19:22:05 -0600292 MidpointContourParser parser(path);
293 while (parser.parseNextContour()) {
294 SkPoint midpoint = parser.currentMidpoint();
295 SkPoint startPoint = {0, 0};
296 SkPoint lastPoint = startPoint;
297 for (auto [verb, pts, w] : parser.currentContour()) {
298 switch (verb) {
299 case SkPathVerb::kMove:
300 startPoint = lastPoint = pts[0];
301 break;
302 case SkPathVerb::kClose:
303 break; // Ignore. We can assume an implicit close at the end.
304 case SkPathVerb::kLine:
Chris Dalton50516f32021-07-18 22:26:27 -0600305 wedgeWriter.writeFlatWedge(shaderCaps, &chunker, pts[0], pts[1], midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600306 lastPoint = pts[1];
307 break;
308 case SkPathVerb::kQuad:
Chris Dalton50516f32021-07-18 22:26:27 -0600309 wedgeWriter.writeQuadraticWedge(shaderCaps, &chunker, pts, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600310 lastPoint = pts[2];
311 break;
312 case SkPathVerb::kConic:
Chris Dalton50516f32021-07-18 22:26:27 -0600313 wedgeWriter.writeConicWedge(shaderCaps, &chunker, pts, *w, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600314 lastPoint = pts[2];
315 break;
316 case SkPathVerb::kCubic:
Chris Dalton50516f32021-07-18 22:26:27 -0600317 wedgeWriter.writeCubicWedge(shaderCaps, &chunker, pts, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600318 lastPoint = pts[3];
319 break;
320 }
321 }
322 if (lastPoint != startPoint) {
Chris Dalton50516f32021-07-18 22:26:27 -0600323 wedgeWriter.writeFlatWedge(shaderCaps, &chunker, lastPoint, startPoint, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600324 }
325 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600326
327 if (!fShader->willUseTessellationShaders()) {
328 // log2(n) == log16(n^4).
329 int fixedResolveLevel = GrWangsFormula::nextlog16(wedgeWriter.numFixedSegments_pow4());
330 int numCurveTriangles =
331 GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel);
332 // Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
Chris Daltone1f72372021-06-29 16:45:49 -0600333 fFixedIndexCount = numCurveTriangles * 3 + 3;
334
335 GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
336
337 fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
338 GrGpuBufferType::kVertex,
339 GrPathTessellationShader::SizeOfVertexBufferForMiddleOutWedges(),
340 gFixedCountVertexBufferKey,
341 GrPathTessellationShader::InitializeVertexBufferForMiddleOutWedges);
342
343 GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
344
345 fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
346 GrGpuBufferType::kIndex,
347 GrPathTessellationShader::SizeOfIndexBufferForMiddleOutWedges(),
348 gFixedCountIndexBufferKey,
349 GrPathTessellationShader::InitializeIndexBufferForMiddleOutWedges);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600350 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600351}
352
353void GrPathWedgeTessellator::draw(GrOpFlushState* flushState) const {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600354 if (fShader->willUseTessellationShaders()) {
355 for (const GrVertexChunk& chunk : fVertexChunkArray) {
356 flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
357 flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
358 }
359 } else {
360 SkASSERT(fShader->hasInstanceAttributes());
361 for (const GrVertexChunk& chunk : fVertexChunkArray) {
Chris Daltone1f72372021-06-29 16:45:49 -0600362 flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer);
363 flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600364 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600365 }
366}