blob: 1889ad2502d952884ea29d7fc1a1629ef966d16b [file] [log] [blame]
Chris Daltonb832ce62020-01-06 19:49:37 -07001/*
2 * Copyright 2019 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/GrTessellatePathOp.h"
9
Chris Daltond081dce2020-01-23 12:09:04 -070010#include "src/gpu/GrEagerVertexAllocator.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070011#include "src/gpu/GrGpu.h"
12#include "src/gpu/GrOpFlushState.h"
Chris Dalton17dc4182020-03-25 16:18:16 -060013#include "src/gpu/GrTriangulator.h"
Chris Dalton4328e922020-01-29 13:16:14 -070014#include "src/gpu/tessellate/GrFillPathShader.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060015#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Dalton42915c22020-04-22 16:24:43 -060016#include "src/gpu/tessellate/GrMidpointContourParser.h"
Chris Daltonb5391d92020-05-24 14:55:54 -060017#include "src/gpu/tessellate/GrResolveLevelCounter.h"
Chris Daltonf9aea7f2020-01-21 11:19:26 -070018#include "src/gpu/tessellate/GrStencilPathShader.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070019
Chris Daltonb5391d92020-05-24 14:55:54 -060020constexpr static int kMaxResolveLevel = GrMiddleOutCubicShader::kMaxResolveLevel;
21constexpr static float kTessellationIntolerance = 4; // 1/4 of a pixel.
22
Chris Daltonb832ce62020-01-06 19:49:37 -070023GrTessellatePathOp::FixedFunctionFlags GrTessellatePathOp::fixedFunctionFlags() const {
24 auto flags = FixedFunctionFlags::kUsesStencil;
25 if (GrAAType::kNone != fAAType) {
26 flags |= FixedFunctionFlags::kUsesHWAA;
27 }
28 return flags;
29}
30
Robert Phillipsc655c3a2020-03-18 13:23:45 -040031void GrTessellatePathOp::onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -040032 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -040033 GrAppliedClip*,
34 const GrXferProcessor::DstProxyView&) {
35}
36
Chris Daltonb5391d92020-05-24 14:55:54 -060037void GrTessellatePathOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton4328e922020-01-29 13:16:14 -070038 int numVerbs = fPath.countVerbs();
39 if (numVerbs <= 0) {
40 return;
41 }
Chris Daltonb5391d92020-05-24 14:55:54 -060042
43 // First check if the path is large and/or simple enough that we can actually triangulate the
44 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
45 // curves, and then fill the internal polygons directly to the final render target, thus drawing
46 // the majority of pixels in a single render pass.
47 SkScalar scales[2];
48 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
49 const SkRect& bounds = fPath.getBounds();
Chris Dalton4328e922020-01-29 13:16:14 -070050 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
51 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
52 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Daltonb5391d92020-05-24 14:55:54 -060053 int numCountedCubics;
Chris Dalton04f9cda2020-04-23 10:04:25 -060054 // This will fail if the inner triangles do not form a simple polygon (e.g., self
55 // intersection, double winding).
Chris Daltonb5391d92020-05-24 14:55:54 -060056 if (this->prepareNonOverlappingInnerTriangles(flushState, &numCountedCubics)) {
57 if (!numCountedCubics) {
58 return;
59 }
60 // Always use indirect draws for cubics instead of tessellation here. Our goal in this
61 // mode is to maximize GPU performance, and the middle-out topology used by our indirect
62 // draws is easier on the rasterizer than a tessellated fan. There also seems to be a
63 // small amount of fixed tessellation overhead that this avoids.
64 //
65 // NOTE: This will count fewer cubics than above if it discards any for resolveLevel=0.
66 GrResolveLevelCounter resolveLevelCounter;
67 numCountedCubics = resolveLevelCounter.reset(fPath, fViewMatrix,
68 kTessellationIntolerance);
69 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
Chris Dalton4328e922020-01-29 13:16:14 -070070 return;
71 }
72 }
73
Chris Daltonb5391d92020-05-24 14:55:54 -060074 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
75 // that contains both the inner triangles and the outer cubics, instead of using hardware
76 // tessellation. Also take this path if tessellation is not supported.
77 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
78 if (drawTrianglesAsIndirectCubicDraw ||
79 !flushState->caps().shaderCaps()->tessellationSupport()) {
80 // Prepare outer cubics with indirect draws.
81 GrResolveLevelCounter resolveLevelCounter;
82 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter,
83 drawTrianglesAsIndirectCubicDraw);
84 return;
85 }
86
87 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
88 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
89 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
90 // switches and draw calls.
Chris Dalton4328e922020-01-29 13:16:14 -070091 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
92 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonb5391d92020-05-24 14:55:54 -060093 if (rasterEdgeWork > 300 * 300) {
94 this->prepareMiddleOutTrianglesAndCubics(flushState);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070095 return;
96 }
97
98 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltonb5391d92020-05-24 14:55:54 -060099 this->prepareTessellatedCubicWedges(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700100}
101
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600102bool GrTessellatePathOp::prepareNonOverlappingInnerTriangles(GrMeshDrawOp::Target* target,
Chris Daltonf5132a02020-04-27 23:40:03 -0600103 int* numCountedCurves) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600104 SkASSERT(!fTriangleBuffer);
105 SkASSERT(!fDoStencilTriangleBuffer);
106 SkASSERT(!fDoFillTriangleBuffer);
107
108 using GrTriangulator::Mode;
109
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600110 GrEagerDynamicVertexAllocator vertexAlloc(target, &fTriangleBuffer, &fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600111 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
112 &vertexAlloc, Mode::kSimpleInnerPolygons,
113 numCountedCurves);
114 if (fTriangleVertexCount == 0) {
115 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
116 // simple.
117 return false;
118 }
119 if (((Flags::kStencilOnly | Flags::kWireframe) & fFlags) || GrAAType::kCoverage == fAAType ||
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600120 (target->appliedClip() && target->appliedClip()->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600121 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
122 // can't fill the inner polygon directly. Indicate that these triangles need to be
123 // stencilled.
124 fDoStencilTriangleBuffer = true;
125 }
126 if (!(Flags::kStencilOnly & fFlags)) {
127 fDoFillTriangleBuffer = true;
128 }
129 return true;
130}
131
Chris Daltonb5391d92020-05-24 14:55:54 -0600132void GrTessellatePathOp::prepareMiddleOutTrianglesAndCubics(
133 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter,
134 bool drawTrianglesAsIndirectCubicDraw) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600135 SkASSERT(!fTriangleBuffer);
136 SkASSERT(!fDoStencilTriangleBuffer);
137 SkASSERT(!fDoFillTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600138 SkASSERT(!fCubicBuffer);
139 SkASSERT(!fStencilCubicsShader);
140 SkASSERT(!fIndirectDrawBuffer);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600141
Chris Daltonf5132a02020-04-27 23:40:03 -0600142 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Daltonb5391d92020-05-24 14:55:54 -0600143 int maxInnerTriangles = fPath.countVerbs() - 1;
144 int maxCubics = fPath.countVerbs();
Chris Dalton42915c22020-04-22 16:24:43 -0600145
Chris Daltonb5391d92020-05-24 14:55:54 -0600146 SkPoint* vertexData;
147 int vertexAdvancePerTriangle;
148 if (drawTrianglesAsIndirectCubicDraw) {
149 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
150 SkASSERT(resolveLevelCounter);
151 vertexAdvancePerTriangle = 4;
152 int baseTriangleInstance;
153 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
154 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
155 &baseTriangleInstance));
156 fBaseCubicVertex = baseTriangleInstance * 4;
157 } else {
158 // Allocate the triangles as normal 3-point instances in the triangle buffer.
159 vertexAdvancePerTriangle = 3;
160 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
161 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
162 }
Chris Dalton42915c22020-04-22 16:24:43 -0600163 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600164 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600165 }
Chris Dalton42915c22020-04-22 16:24:43 -0600166
Chris Daltonb5391d92020-05-24 14:55:54 -0600167 GrVectorXform xform(fViewMatrix);
168 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
169 fPath.countVerbs());
170 if (resolveLevelCounter) {
171 resolveLevelCounter->reset();
172 }
173 int numCountedCurves = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600174 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
175 switch (verb) {
176 case SkPathVerb::kMove:
Chris Daltonb5391d92020-05-24 14:55:54 -0600177 middleOut.closeAndMove(pts[0]);
Chris Daltonf5132a02020-04-27 23:40:03 -0600178 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600179 case SkPathVerb::kLine:
180 middleOut.pushVertex(pts[1]);
181 break;
182 case SkPathVerb::kQuad:
183 middleOut.pushVertex(pts[2]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600184 if (resolveLevelCounter) {
185 // Quadratics get converted to cubics before rendering.
186 resolveLevelCounter->countCubic(GrWangsFormula::quadratic_log2(
187 kTessellationIntolerance, pts, xform));
188 break;
189 }
190 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600191 break;
192 case SkPathVerb::kCubic:
193 middleOut.pushVertex(pts[3]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600194 if (resolveLevelCounter) {
195 resolveLevelCounter->countCubic(GrWangsFormula::cubic_log2(
196 kTessellationIntolerance, pts, xform));
197 break;
198 }
199 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600200 break;
201 case SkPathVerb::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600202 middleOut.close();
203 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600204 case SkPathVerb::kConic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600205 SkUNREACHABLE;
Chris Daltonf5132a02020-04-27 23:40:03 -0600206 }
Chris Dalton42915c22020-04-22 16:24:43 -0600207 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600208 int triangleCount = middleOut.close();
209 SkASSERT(triangleCount <= maxInnerTriangles);
Chris Dalton42915c22020-04-22 16:24:43 -0600210
Chris Daltonb5391d92020-05-24 14:55:54 -0600211 if (drawTrianglesAsIndirectCubicDraw) {
212 SkASSERT(resolveLevelCounter);
213 int totalInstanceCount = triangleCount + resolveLevelCounter->totalCubicInstanceCount();
214 SkASSERT(vertexAdvancePerTriangle == 4);
215 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
216 sizeof(SkPoint) * 4);
217 if (totalInstanceCount) {
218 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
219 triangleCount);
220 }
221 } else {
222 SkASSERT(vertexAdvancePerTriangle == 3);
223 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
224 fTriangleVertexCount = triangleCount * 3;
225 if (fTriangleVertexCount) {
226 fDoStencilTriangleBuffer = true;
227 }
228 if (resolveLevelCounter) {
229 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
230 } else {
231 this->prepareTessellatedOuterCubics(target, numCountedCurves);
232 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600233 }
Chris Dalton42915c22020-04-22 16:24:43 -0600234}
235
236static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
237 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
238 return (b - a) * T + a;
239}
240
Chris Daltonf7a33072020-05-01 10:33:08 -0600241static void line2cubic(const SkPoint& p0, const SkPoint& p1, SkPoint* out) {
242 out[0] = p0;
243 out[1] = lerp(p0, p1, 1/3.f);
244 out[2] = lerp(p0, p1, 2/3.f);
245 out[3] = p1;
Chris Dalton42915c22020-04-22 16:24:43 -0600246}
247
Chris Daltonf7a33072020-05-01 10:33:08 -0600248static void quad2cubic(const SkPoint pts[], SkPoint* out) {
249 out[0] = pts[0];
250 out[1] = lerp(pts[0], pts[1], 2/3.f);
251 out[2] = lerp(pts[1], pts[2], 1/3.f);
252 out[3] = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600253}
254
Chris Daltonb5391d92020-05-24 14:55:54 -0600255void GrTessellatePathOp::prepareIndirectOuterCubics(
256 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
257 SkASSERT(resolveLevelCounter.totalCubicInstanceCount() >= 0);
258 if (resolveLevelCounter.totalCubicInstanceCount() == 0) {
259 return;
260 }
261 // Allocate a buffer to store the cubic data.
262 SkPoint* cubicData;
263 int baseInstance;
264 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
265 sizeof(SkPoint) * 4, resolveLevelCounter.totalCubicInstanceCount(), &fCubicBuffer,
266 &baseInstance));
267 if (!cubicData) {
268 return;
269 }
270 fBaseCubicVertex = baseInstance * 4;
271 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
272 /*numTrianglesAtBeginningOfData=*/0);
273}
274
275void GrTessellatePathOp::prepareIndirectOuterCubicsAndTriangles(
276 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
277 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600278 SkASSERT(target->caps().drawInstancedSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600279 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalCubicInstanceCount() > 0);
280 SkASSERT(!fStencilCubicsShader);
281 SkASSERT(cubicData);
282
Chris Dalton1443c9d2020-05-27 09:43:34 -0600283 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
284 target->resourceProvider());
285 if (!fIndirectIndexBuffer) {
286 return;
287 }
288
Chris Daltonb5391d92020-05-24 14:55:54 -0600289 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
290 // vertex on an instance boundary in order to accommodate this.
291 SkASSERT(fBaseCubicVertex % 4 == 0);
292 int baseInstance = fBaseCubicVertex >> 2;
293
294 // Start preparing the indirect draw buffer.
295 fIndirectDrawCount = resolveLevelCounter.totalCubicIndirectDrawCount();
296 if (numTrianglesAtBeginningOfData) {
297 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
298 }
299
300 // Allocate space for the GrDrawIndexedIndirectCommand structs.
301 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
302 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
303 if (!indirectData) {
304 SkASSERT(!fIndirectDrawBuffer);
305 return;
306 }
307
308 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
309 // location at each resolve level.
310 SkPoint* instanceLocations[kMaxResolveLevel + 1];
311 int indirectIdx = 0;
312 int runningInstanceCount = 0;
313 if (numTrianglesAtBeginningOfData) {
314 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
315 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
316 // emit the triangles [P0, P1, P2] from these 4-point instances.
317 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
318 numTrianglesAtBeginningOfData, baseInstance);
319 indirectIdx = 1;
320 runningInstanceCount = numTrianglesAtBeginningOfData;
321 }
322 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
323 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
324 if (int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel]) {
325 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
326 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
327 runningInstanceCount += instanceCountAtCurrLevel;
328 }
329 }
330
331#ifdef SK_DEBUG
332 SkASSERT(indirectIdx == fIndirectDrawCount);
333 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
334 resolveLevelCounter.totalCubicInstanceCount());
335 SkASSERT(fIndirectDrawCount > 0);
336
337 SkPoint* endLocations[kMaxResolveLevel + 1];
338 memcpy(endLocations, instanceLocations + 1, kMaxResolveLevel * sizeof(SkPoint*));
339 int totalInstanceCount = numTrianglesAtBeginningOfData +
340 resolveLevelCounter.totalCubicInstanceCount();
341 endLocations[kMaxResolveLevel] = cubicData + totalInstanceCount * 4;
342#endif
343
344 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
345
346 if (resolveLevelCounter.totalCubicInstanceCount()) {
347 GrVectorXform xform(fViewMatrix);
348 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
349 int level;
350 switch (verb) {
351 default:
352 continue;
353 case SkPathVerb::kQuad:
354 level = GrWangsFormula::quadratic_log2(kTessellationIntolerance, pts, xform);
355 if (level == 0) {
356 continue;
357 }
358 level = std::min(level, kMaxResolveLevel);
359 quad2cubic(pts, instanceLocations[level]);
360 break;
361 case SkPathVerb::kCubic:
362 level = GrWangsFormula::cubic_log2(kTessellationIntolerance, pts, xform);
363 if (level == 0) {
364 continue;
365 }
366 level = std::min(level, kMaxResolveLevel);
367 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
368 break;
369 }
370 instanceLocations[level] += 4;
371 fCubicVertexCount += 4;
372 }
373 }
374
375#ifdef SK_DEBUG
376 for (int i = 1; i <= kMaxResolveLevel; ++i) {
377 SkASSERT(instanceLocations[i] == endLocations[i]);
378 }
379 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
380 resolveLevelCounter.totalCubicInstanceCount()) * 4);
381#endif
382
383 fStencilCubicsShader = target->allocator()->make<GrMiddleOutCubicShader>(fViewMatrix);
384}
385
386void GrTessellatePathOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
387 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600388 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600389 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600390 SkASSERT(!fCubicBuffer);
391 SkASSERT(!fStencilCubicsShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600392
393 if (numCountedCurves == 0) {
394 return;
395 }
396
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600397 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600398 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600399 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600400 return;
401 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600402 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600403
Chris Daltonf7a33072020-05-01 10:33:08 -0600404 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
405 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600406 default:
407 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600408 case SkPathVerb::kQuad:
409 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
410 quad2cubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600411 break;
412 case SkPathVerb::kCubic:
413 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
414 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600415 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600416 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600417 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600418 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600419 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
420
Chris Daltonb5391d92020-05-24 14:55:54 -0600421 fStencilCubicsShader = target->allocator()->make<GrTessellateCubicShader>(fViewMatrix);
Chris Dalton42915c22020-04-22 16:24:43 -0600422}
423
Chris Daltonb5391d92020-05-24 14:55:54 -0600424void GrTessellatePathOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600425 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600426 SkASSERT(!fCubicBuffer);
427 SkASSERT(!fStencilCubicsShader);
428
Chris Dalton42915c22020-04-22 16:24:43 -0600429 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
430 // Each wedge has 5 vertices.
431 int maxVertices = (fPath.countVerbs() + 1) * 5;
432
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600433 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600434 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
435 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600436 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600437 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600438 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600439
440 GrMidpointContourParser parser(fPath);
441 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600442 SkPoint midpoint = parser.currentMidpoint();
443 SkPoint startPoint = {0, 0};
444 SkPoint lastPoint = startPoint;
445 for (auto [verb, pts, w] : parser.currentContour()) {
446 switch (verb) {
447 case SkPathVerb::kMove:
448 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600449 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600450 case SkPathVerb::kClose:
451 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600452 case SkPathVerb::kLine:
Chris Daltonf7a33072020-05-01 10:33:08 -0600453 line2cubic(pts[0], pts[1], vertexData + fCubicVertexCount);
454 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600455 break;
456 case SkPathVerb::kQuad:
Chris Daltonf7a33072020-05-01 10:33:08 -0600457 quad2cubic(pts, vertexData + fCubicVertexCount);
458 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600459 break;
460 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600461 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
462 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600463 break;
464 case SkPathVerb::kConic:
465 SkUNREACHABLE;
466 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600467 vertexData[fCubicVertexCount + 4] = midpoint;
468 fCubicVertexCount += 5;
469 }
470 if (lastPoint != startPoint) {
471 line2cubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
472 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600473 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600474 }
475 }
476
Chris Dalton04f9cda2020-04-23 10:04:25 -0600477 vertexAlloc.unlock(fCubicVertexCount);
478
479 if (fCubicVertexCount) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600480 fStencilCubicsShader = target->allocator()->make<GrTessellateWedgeShader>(fViewMatrix);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600481 }
Chris Dalton42915c22020-04-22 16:24:43 -0600482}
483
Chris Daltonb5391d92020-05-24 14:55:54 -0600484void GrTessellatePathOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
485 this->drawStencilPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700486 if (!(Flags::kStencilOnly & fFlags)) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600487 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700488 }
489}
490
Chris Daltonb5391d92020-05-24 14:55:54 -0600491void GrTessellatePathOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700492 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
493 constexpr static GrUserStencilSettings kIncrDecrStencil(
494 GrUserStencilSettings::StaticInitSeparate<
495 0x0000, 0x0000,
496 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
497 0xffff, 0xffff,
498 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
499 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
500 0xffff, 0xffff>());
501
502 // Inverts the bottom stencil bit. Used for "even/odd" fill.
503 constexpr static GrUserStencilSettings kInvertStencil(
504 GrUserStencilSettings::StaticInit<
505 0x0000,
506 GrUserStencilTest::kAlwaysIfInClip,
507 0xffff,
508 GrUserStencilOp::kInvert,
509 GrUserStencilOp::kKeep,
510 0x0001>());
511
512 GrPipeline::InitArgs initArgs;
513 if (GrAAType::kNone != fAAType) {
514 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
515 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600516 if (flushState->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700517 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
518 }
519 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
520 SkPathFillType::kEvenOdd == fPath.getFillType());
521 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
522 &kIncrDecrStencil : &kInvertStencil;
Chris Daltonb5391d92020-05-24 14:55:54 -0600523 initArgs.fCaps = &flushState->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600524 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
Chris Daltonb5391d92020-05-24 14:55:54 -0600525 flushState->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700526
Chris Dalton04f9cda2020-04-23 10:04:25 -0600527 if (fDoStencilTriangleBuffer) {
528 SkASSERT(fTriangleBuffer);
529 GrStencilTriangleShader stencilTriangleShader(fViewMatrix);
Chris Daltonb5391d92020-05-24 14:55:54 -0600530 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600531 &stencilTriangleShader);
Chris Daltonb5391d92020-05-24 14:55:54 -0600532 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
533 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
534 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700535 }
536
Chris Dalton04f9cda2020-04-23 10:04:25 -0600537 if (fStencilCubicsShader) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600538 SkASSERT(fCubicBuffer);
539 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
540 fStencilCubicsShader);
541 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
542 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600543 SkASSERT(fIndirectIndexBuffer);
544 flushState->bindBuffers(fIndirectIndexBuffer.get(), fCubicBuffer.get(), nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600545 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
546 fIndirectDrawCount);
547 } else {
548 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer.get());
549 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
550 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
551 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
552 }
553 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700554 }
555}
556
Chris Daltonb5391d92020-05-24 14:55:54 -0600557void GrTessellatePathOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700558 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
559 // to zero; discards immediately on stencil values of zero.
560 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
561 // samples already inside the clip.
562 constexpr static GrUserStencilSettings kTestAndResetStencil(
563 GrUserStencilSettings::StaticInit<
564 0x0000,
565 GrUserStencilTest::kNotEqual,
566 0xffff,
567 GrUserStencilOp::kZero,
568 GrUserStencilOp::kKeep,
569 0xffff>());
570
571 GrPipeline::InitArgs initArgs;
572 if (GrAAType::kNone != fAAType) {
573 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
Chris Daltonb5391d92020-05-24 14:55:54 -0600574 if (1 == flushState->proxy()->numSamples()) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700575 SkASSERT(GrAAType::kCoverage == fAAType);
576 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
577 // at every fragment. This way we will still get a double hit on shared edges, but
578 // whichever side comes first will cover every sample and will clear the stencil. The
579 // other side will then be discarded and not cause a double blend.
580 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
581 }
582 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600583 initArgs.fCaps = &flushState->caps();
584 initArgs.fDstProxyView = flushState->drawOpArgs().dstProxyView();
585 initArgs.fWriteSwizzle = flushState->drawOpArgs().writeSwizzle();
586 GrPipeline pipeline(initArgs, std::move(fProcessors), flushState->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700587
Chris Dalton04f9cda2020-04-23 10:04:25 -0600588 if (fDoFillTriangleBuffer) {
589 SkASSERT(fTriangleBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700590
Chris Dalton04f9cda2020-04-23 10:04:25 -0600591 // These are a twist on the standard red book stencil settings that allow us to fill the
Chris Dalton4328e922020-01-29 13:16:14 -0700592 // inner polygon directly to the final render target. At this point, the curves are already
593 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
594 // not affected by any curves and we fill the path in directly. If the stencil value is
595 // nonzero, then we don't fill and instead continue the standard red book stencil process.
596 //
597 // NOTE: These settings are currently incompatible with a stencil clip.
598 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
599 GrUserStencilSettings::StaticInitSeparate<
600 0x0000, 0x0000,
601 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
602 0xffff, 0xffff,
603 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
604 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
605 0xffff, 0xffff>());
606
607 constexpr static GrUserStencilSettings kFillOrInvertStencil(
608 GrUserStencilSettings::StaticInit<
609 0x0000,
610 GrUserStencilTest::kEqual,
611 0xffff,
612 GrUserStencilOp::kKeep,
613 GrUserStencilOp::kZero,
614 0xffff>());
615
Chris Dalton04f9cda2020-04-23 10:04:25 -0600616 if (fDoStencilTriangleBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700617 // The path was already stencilled. Here we just need to do a cover pass.
618 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600619 } else if (!fStencilCubicsShader) {
620 // There are no stencilled curves. We can ignore stencil and fill the path directly.
Chris Dalton4328e922020-01-29 13:16:14 -0700621 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
622 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
623 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
624 SkASSERT(!pipeline.hasStencilClip());
625 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
626 } else {
627 // Fill in the path pixels not touched by curves, invert stencil otherwise.
628 SkASSERT(!pipeline.hasStencilClip());
629 pipeline.setUserStencil(&kFillOrInvertStencil);
630 }
Chris Dalton4328e922020-01-29 13:16:14 -0700631
Chris Dalton04f9cda2020-04-23 10:04:25 -0600632 GrFillTriangleShader fillTriangleShader(fViewMatrix, fColor);
Chris Daltonb5391d92020-05-24 14:55:54 -0600633 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
634 &fillTriangleShader);
635 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
636 flushState->bindTextures(fillTriangleShader, nullptr, pipeline);
637 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
638 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600639
640 if (fStencilCubicsShader) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600641 SkASSERT(fCubicBuffer);
642
Chris Dalton4328e922020-01-29 13:16:14 -0700643 // At this point, every pixel is filled in except the ones touched by curves. Issue a
644 // final cover pass over the curves by drawing their convex hulls. This will fill in any
645 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700646 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600647 GrFillCubicHullShader fillCubicHullShader(fViewMatrix, fColor);
Chris Daltonb5391d92020-05-24 14:55:54 -0600648 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600649 &fillCubicHullShader);
Chris Daltonb5391d92020-05-24 14:55:54 -0600650 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
651 flushState->bindTextures(fillCubicHullShader, nullptr, pipeline);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600652
653 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
654 // the base vertex on an instance boundary in order to accommodate this.
655 SkASSERT((fCubicVertexCount % 4) == 0);
656 SkASSERT((fBaseCubicVertex % 4) == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600657 flushState->bindBuffers(nullptr, fCubicBuffer.get(), nullptr);
658 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700659 }
Chris Dalton42915c22020-04-22 16:24:43 -0600660 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700661 }
Chris Dalton42915c22020-04-22 16:24:43 -0600662
Chris Dalton04f9cda2020-04-23 10:04:25 -0600663 // There are no triangles to fill. Just draw a bounding box.
Chris Dalton42915c22020-04-22 16:24:43 -0600664 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600665 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600666 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
667 &fillBoundingBoxShader);
668 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
669 flushState->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
670 flushState->bindBuffers(nullptr, nullptr, nullptr);
671 flushState->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700672}