1/*
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/GrPathTessellateOp.h"
9
10#include "src/gpu/GrEagerVertexAllocator.h"
11#include "src/gpu/GrGpu.h"
12#include "src/gpu/GrOpFlushState.h"
13#include "src/gpu/GrTriangulator.h"
14#include "src/gpu/tessellate/GrFillPathShader.h"
15#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
16#include "src/gpu/tessellate/GrMidpointContourParser.h"
17#include "src/gpu/tessellate/GrResolveLevelCounter.h"
18#include "src/gpu/tessellate/GrStencilPathShader.h"
19#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
20
21constexpr static float kLinearizationIntolerance =
22 GrTessellationPathRenderer::kLinearizationIntolerance;
23
24constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel;
25
26using OpFlags = GrTessellationPathRenderer::OpFlags;
27
28GrPathTessellateOp::FixedFunctionFlags GrPathTessellateOp::fixedFunctionFlags() const {
29 auto flags = FixedFunctionFlags::kUsesStencil;
30 if (GrAAType::kNone != fAAType) {
31 flags |= FixedFunctionFlags::kUsesHWAA;
32 }
33 return flags;
34}
35
36void GrPathTessellateOp::onPrePrepare(GrRecordingContext*,
37 const GrSurfaceProxyView* writeView,
38 GrAppliedClip*,
39 const GrXferProcessor::DstProxyView&) {
40}
41
42void GrPathTessellateOp::onPrepare(GrOpFlushState* flushState) {
43 int numVerbs = fPath.countVerbs();
44 if (numVerbs <= 0) {
45 return;
46 }
47
48 // First check if the path is large and/or simple enough that we can actually triangulate the
49 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
50 // curves, and then fill the internal polygons directly to the final render target, thus drawing
51 // the majority of pixels in a single render pass.
52 SkScalar scales[2];
53 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
54 const SkRect& bounds = fPath.getBounds();
55 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
56 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
57 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
58 int numCountedCubics;
59 // This will fail if the inner triangles do not form a simple polygon (e.g., self
60 // intersection, double winding).
61 if (this->prepareNonOverlappingInnerTriangles(flushState, &numCountedCubics)) {
62 if (!numCountedCubics) {
63 return;
64 }
65 // Always use indirect draws for cubics instead of tessellation here. Our goal in this
66 // mode is to maximize GPU performance, and the middle-out topology used by our indirect
67 // draws is easier on the rasterizer than a tessellated fan. There also seems to be a
68 // small amount of fixed tessellation overhead that this avoids.
69 GrResolveLevelCounter resolveLevelCounter;
70 resolveLevelCounter.reset(fPath, fViewMatrix, kLinearizationIntolerance);
71 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
72 return;
73 }
74 }
75
76 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
77 // that contains both the inner triangles and the outer cubics, instead of using hardware
78 // tessellation. Also take this path if tessellation is not supported.
79 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
80 if (drawTrianglesAsIndirectCubicDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
81 // Prepare outer cubics with indirect draws.
82 GrResolveLevelCounter resolveLevelCounter;
83 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter,
84 drawTrianglesAsIndirectCubicDraw);
85 return;
86 }
87
88 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
89 SkASSERT(flushState->caps().shaderCaps()->tessellationSupport());
90
91 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
92 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
93 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
94 // switches and draw calls.
95 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
96 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
97 if (rasterEdgeWork > 300 * 300) {
98 this->prepareMiddleOutTrianglesAndCubics(flushState);
99 return;
100 }
101
102 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
103 this->prepareTessellatedCubicWedges(flushState);
104}
105
106bool GrPathTessellateOp::prepareNonOverlappingInnerTriangles(GrMeshDrawOp::Target* target,
107 int* numCountedCurves) {
108 SkASSERT(!fTriangleBuffer);
109 SkASSERT(!fDoStencilTriangleBuffer);
110 SkASSERT(!fDoFillTriangleBuffer);
111
112 using GrTriangulator::Mode;
113
114 GrEagerDynamicVertexAllocator vertexAlloc(target, &fTriangleBuffer, &fBaseTriangleVertex);
115 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
116 &vertexAlloc, Mode::kSimpleInnerPolygons,
117 numCountedCurves);
118 if (fTriangleVertexCount == 0) {
119 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
120 // simple.
121 return false;
122 }
123 if (((OpFlags::kStencilOnly | OpFlags::kWireframe) & fOpFlags) ||
124 GrAAType::kCoverage == fAAType ||
125 (target->appliedClip() && target->appliedClip()->hasStencilClip())) {
126 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
127 // can't fill the inner polygon directly. Indicate that these triangles need to be
128 // stencilled.
129 fDoStencilTriangleBuffer = true;
130 }
131 if (!(OpFlags::kStencilOnly & fOpFlags)) {
132 fDoFillTriangleBuffer = true;
133 }
134 return true;
135}
136
137void GrPathTessellateOp::prepareMiddleOutTrianglesAndCubics(
138 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter,
139 bool drawTrianglesAsIndirectCubicDraw) {
140 SkASSERT(!fTriangleBuffer);
141 SkASSERT(!fDoStencilTriangleBuffer);
142 SkASSERT(!fDoFillTriangleBuffer);
143 SkASSERT(!fCubicBuffer);
144 SkASSERT(!fStencilCubicsShader);
145 SkASSERT(!fIndirectDrawBuffer);
146
147 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
148 int maxInnerTriangles = fPath.countVerbs() - 1;
149 int maxCubics = fPath.countVerbs();
150
151 SkPoint* vertexData;
152 int vertexAdvancePerTriangle;
153 if (drawTrianglesAsIndirectCubicDraw) {
154 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
155 SkASSERT(resolveLevelCounter);
156 vertexAdvancePerTriangle = 4;
157 int baseTriangleInstance;
158 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
159 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
160 &baseTriangleInstance));
161 fBaseCubicVertex = baseTriangleInstance * 4;
162 } else {
163 // Allocate the triangles as normal 3-point instances in the triangle buffer.
164 vertexAdvancePerTriangle = 3;
165 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
166 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
167 }
168 if (!vertexData) {
169 return;
170 }
171
172 GrVectorXform xform(fViewMatrix);
173 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
174 fPath.countVerbs());
175 if (resolveLevelCounter) {
176 resolveLevelCounter->reset();
177 }
178 int numCountedCurves = 0;
179 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
180 switch (verb) {
181 case SkPathVerb::kMove:
182 middleOut.closeAndMove(pts[0]);
183 break;
184 case SkPathVerb::kLine:
185 middleOut.pushVertex(pts[1]);
186 break;
187 case SkPathVerb::kQuad:
188 middleOut.pushVertex(pts[2]);
189 if (resolveLevelCounter) {
190 // Quadratics get converted to cubics before rendering.
191 resolveLevelCounter->countCubic(GrWangsFormula::quadratic_log2(
192 kLinearizationIntolerance, pts, xform));
193 break;
194 }
195 ++numCountedCurves;
196 break;
197 case SkPathVerb::kCubic:
198 middleOut.pushVertex(pts[3]);
199 if (resolveLevelCounter) {
200 resolveLevelCounter->countCubic(GrWangsFormula::cubic_log2(
201 kLinearizationIntolerance, pts, xform));
202 break;
203 }
204 ++numCountedCurves;
205 break;
206 case SkPathVerb::kClose:
207 middleOut.close();
208 break;
209 case SkPathVerb::kConic:
210 SkUNREACHABLE;
211 }
212 }
213 int triangleCount = middleOut.close();
214 SkASSERT(triangleCount <= maxInnerTriangles);
215
216 if (drawTrianglesAsIndirectCubicDraw) {
217 SkASSERT(resolveLevelCounter);
218 int totalInstanceCount = triangleCount + resolveLevelCounter->totalCubicInstanceCount();
219 SkASSERT(vertexAdvancePerTriangle == 4);
220 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
221 sizeof(SkPoint) * 4);
222 if (totalInstanceCount) {
223 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
224 triangleCount);
225 }
226 } else {
227 SkASSERT(vertexAdvancePerTriangle == 3);
228 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
229 fTriangleVertexCount = triangleCount * 3;
230 if (fTriangleVertexCount) {
231 fDoStencilTriangleBuffer = true;
232 }
233 if (resolveLevelCounter) {
234 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
235 } else {
236 this->prepareTessellatedOuterCubics(target, numCountedCurves);
237 }
238 }
239}
240
241static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
242 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
243 return (b - a) * T + a;
244}
245
246static void line2cubic(const SkPoint& p0, const SkPoint& p1, SkPoint* out) {
247 out[0] = p0;
248 out[1] = lerp(p0, p1, 1/3.f);
249 out[2] = lerp(p0, p1, 2/3.f);
250 out[3] = p1;
251}
252
253static void quad2cubic(const SkPoint pts[], SkPoint* out) {
254 out[0] = pts[0];
255 out[1] = lerp(pts[0], pts[1], 2/3.f);
256 out[2] = lerp(pts[1], pts[2], 1/3.f);
257 out[3] = pts[2];
258}
259
260void GrPathTessellateOp::prepareIndirectOuterCubics(
261 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
262 SkASSERT(resolveLevelCounter.totalCubicInstanceCount() >= 0);
263 if (resolveLevelCounter.totalCubicInstanceCount() == 0) {
264 return;
265 }
266 // Allocate a buffer to store the cubic data.
267 SkPoint* cubicData;
268 int baseInstance;
269 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
270 sizeof(SkPoint) * 4, resolveLevelCounter.totalCubicInstanceCount(), &fCubicBuffer,
271 &baseInstance));
272 if (!cubicData) {
273 return;
274 }
275 fBaseCubicVertex = baseInstance * 4;
276 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
277 /*numTrianglesAtBeginningOfData=*/0);
278}
279
280void GrPathTessellateOp::prepareIndirectOuterCubicsAndTriangles(
281 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
282 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
283 SkASSERT(target->caps().drawInstancedSupport());
284 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalCubicInstanceCount() > 0);
285 SkASSERT(!fStencilCubicsShader);
286 SkASSERT(cubicData);
287
288 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
289 target->resourceProvider());
290 if (!fIndirectIndexBuffer) {
291 return;
292 }
293
294 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
295 // vertex on an instance boundary in order to accommodate this.
296 SkASSERT(fBaseCubicVertex % 4 == 0);
297 int baseInstance = fBaseCubicVertex >> 2;
298
299 // Start preparing the indirect draw buffer.
300 fIndirectDrawCount = resolveLevelCounter.totalCubicIndirectDrawCount();
301 if (numTrianglesAtBeginningOfData) {
302 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
303 }
304
305 // Allocate space for the GrDrawIndexedIndirectCommand structs.
306 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
307 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
308 if (!indirectData) {
309 SkASSERT(!fIndirectDrawBuffer);
310 return;
311 }
312
313 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
314 // location at each resolve level.
315 SkPoint* instanceLocations[kMaxResolveLevel + 1];
316 int indirectIdx = 0;
317 int runningInstanceCount = 0;
318 if (numTrianglesAtBeginningOfData) {
319 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
320 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
321 // emit the triangles [P0, P1, P2] from these 4-point instances.
322 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
323 numTrianglesAtBeginningOfData, baseInstance);
324 indirectIdx = 1;
325 runningInstanceCount = numTrianglesAtBeginningOfData;
326 }
327 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
328 int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel];
329 if (!instanceCountAtCurrLevel) {
330 SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
331 continue;
332 }
333 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
334 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
335 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
336 runningInstanceCount += instanceCountAtCurrLevel;
337 }
338
339#ifdef SK_DEBUG
340 SkASSERT(indirectIdx == fIndirectDrawCount);
341 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
342 resolveLevelCounter.totalCubicInstanceCount());
343 SkASSERT(fIndirectDrawCount > 0);
344
345 SkPoint* endLocations[kMaxResolveLevel + 1];
346 int lastResolveLevel = 0;
347 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
348 if (!instanceLocations[resolveLevel]) {
349 endLocations[resolveLevel] = nullptr;
350 continue;
351 }
352 endLocations[lastResolveLevel] = instanceLocations[resolveLevel];
353 lastResolveLevel = resolveLevel;
354 }
355 int totalInstanceCount = numTrianglesAtBeginningOfData +
356 resolveLevelCounter.totalCubicInstanceCount();
357 endLocations[lastResolveLevel] = cubicData + totalInstanceCount * 4;
358#endif
359
360 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
361
362 if (resolveLevelCounter.totalCubicInstanceCount()) {
363 GrVectorXform xform(fViewMatrix);
364 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
365 int level;
366 switch (verb) {
367 default:
368 continue;
369 case SkPathVerb::kQuad:
370 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
371 if (level == 0) {
372 continue;
373 }
374 level = std::min(level, kMaxResolveLevel);
375 quad2cubic(pts, instanceLocations[level]);
376 break;
377 case SkPathVerb::kCubic:
378 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
379 if (level == 0) {
380 continue;
381 }
382 level = std::min(level, kMaxResolveLevel);
383 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
384 break;
385 }
386 instanceLocations[level] += 4;
387 fCubicVertexCount += 4;
388 }
389 }
390
391#ifdef SK_DEBUG
392 for (int i = 1; i <= kMaxResolveLevel; ++i) {
393 SkASSERT(instanceLocations[i] == endLocations[i]);
394 }
395 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
396 resolveLevelCounter.totalCubicInstanceCount()) * 4);
397#endif
398
399 fStencilCubicsShader = target->allocator()->make<GrMiddleOutCubicShader>(fViewMatrix);
400}
401
402void GrPathTessellateOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
403 int numCountedCurves) {
404 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
405 SkASSERT(numCountedCurves >= 0);
406 SkASSERT(!fCubicBuffer);
407 SkASSERT(!fStencilCubicsShader);
408
409 if (numCountedCurves == 0) {
410 return;
411 }
412
413 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
414 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
415 if (!vertexData) {
416 return;
417 }
418 fCubicVertexCount = 0;
419
420 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
421 switch (verb) {
422 default:
423 continue;
424 case SkPathVerb::kQuad:
425 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
426 quad2cubic(pts, vertexData + fCubicVertexCount);
427 break;
428 case SkPathVerb::kCubic:
429 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
430 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
431 break;
432 }
433 fCubicVertexCount += 4;
434 }
435 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
436
437 fStencilCubicsShader = target->allocator()->make<GrCubicTessellateShader>(fViewMatrix);
438}
439
440void GrPathTessellateOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
441 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
442 SkASSERT(!fCubicBuffer);
443 SkASSERT(!fStencilCubicsShader);
444
445 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
446 // Each wedge has 5 vertices.
447 int maxVertices = (fPath.countVerbs() + 1) * 5;
448
449 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
450 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
451 if (!vertexData) {
452 return;
453 }
454 fCubicVertexCount = 0;
455
456 GrMidpointContourParser parser(fPath);
457 while (parser.parseNextContour()) {
458 SkPoint midpoint = parser.currentMidpoint();
459 SkPoint startPoint = {0, 0};
460 SkPoint lastPoint = startPoint;
461 for (auto [verb, pts, w] : parser.currentContour()) {
462 switch (verb) {
463 case SkPathVerb::kMove:
464 startPoint = lastPoint = pts[0];
465 continue;
466 case SkPathVerb::kClose:
467 continue; // Ignore. We can assume an implicit close at the end.
468 case SkPathVerb::kLine:
469 line2cubic(pts[0], pts[1], vertexData + fCubicVertexCount);
470 lastPoint = pts[1];
471 break;
472 case SkPathVerb::kQuad:
473 quad2cubic(pts, vertexData + fCubicVertexCount);
474 lastPoint = pts[2];
475 break;
476 case SkPathVerb::kCubic:
477 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
478 lastPoint = pts[3];
479 break;
480 case SkPathVerb::kConic:
481 SkUNREACHABLE;
482 }
483 vertexData[fCubicVertexCount + 4] = midpoint;
484 fCubicVertexCount += 5;
485 }
486 if (lastPoint != startPoint) {
487 line2cubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
488 vertexData[fCubicVertexCount + 4] = midpoint;
489 fCubicVertexCount += 5;
490 }
491 }
492
493 vertexAlloc.unlock(fCubicVertexCount);
494
495 if (fCubicVertexCount) {
496 fStencilCubicsShader = target->allocator()->make<GrWedgeTessellateShader>(fViewMatrix);
497 }
498}
499
500void GrPathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
501 this->drawStencilPass(flushState);
502 if (!(OpFlags::kStencilOnly & fOpFlags)) {
503 this->drawCoverPass(flushState);
504 }
505}
506
507void GrPathTessellateOp::drawStencilPass(GrOpFlushState* flushState) {
508 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
509 constexpr static GrUserStencilSettings kIncrDecrStencil(
510 GrUserStencilSettings::StaticInitSeparate<
511 0x0000, 0x0000,
512 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
513 0xffff, 0xffff,
514 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
515 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
516 0xffff, 0xffff>());
517
518 // Inverts the bottom stencil bit. Used for "even/odd" fill.
519 constexpr static GrUserStencilSettings kInvertStencil(
520 GrUserStencilSettings::StaticInit<
521 0x0000,
522 GrUserStencilTest::kAlwaysIfInClip,
523 0xffff,
524 GrUserStencilOp::kInvert,
525 GrUserStencilOp::kKeep,
526 0x0001>());
527
528 GrPipeline::InitArgs initArgs;
529 if (GrAAType::kNone != fAAType) {
530 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
531 }
532 if (flushState->caps().wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
533 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
534 }
535 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
536 SkPathFillType::kEvenOdd == fPath.getFillType());
537 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
538 &kIncrDecrStencil : &kInvertStencil;
539 initArgs.fCaps = &flushState->caps();
540 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
541 flushState->appliedHardClip());
542
543 if (fDoStencilTriangleBuffer) {
544 SkASSERT(fTriangleBuffer);
545 GrStencilTriangleShader stencilTriangleShader(fViewMatrix);
546 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
547 &stencilTriangleShader);
548 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
549 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
550 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
551 }
552
553 if (fStencilCubicsShader) {
554 SkASSERT(fCubicBuffer);
555 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
556 fStencilCubicsShader);
557 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
558 if (fIndirectDrawBuffer) {
559 SkASSERT(fIndirectIndexBuffer);
560 flushState->bindBuffers(fIndirectIndexBuffer, fCubicBuffer, nullptr);
561 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
562 fIndirectDrawCount);
563 } else {
564 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer);
565 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
566 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
567 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
568 }
569 }
570 }
571}
572
573void GrPathTessellateOp::drawCoverPass(GrOpFlushState* flushState) {
574 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
575 // to zero; discards immediately on stencil values of zero.
576 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
577 // samples already inside the clip.
578 constexpr static GrUserStencilSettings kTestAndResetStencil(
579 GrUserStencilSettings::StaticInit<
580 0x0000,
581 GrUserStencilTest::kNotEqual,
582 0xffff,
583 GrUserStencilOp::kZero,
584 GrUserStencilOp::kKeep,
585 0xffff>());
586
587 GrPipeline::InitArgs initArgs;
588 if (GrAAType::kNone != fAAType) {
589 if (flushState->proxy()->numSamples() == 1) {
590 // We are mixed sampled. We need to either enable conservative raster (preferred) or
591 // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
592 // the cover geometry, the stencil test is still multisampled and will still produce
593 // smooth results.)
594 SkASSERT(GrAAType::kCoverage == fAAType);
595 if (flushState->caps().conservativeRasterSupport()) {
596 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
597 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
598 }
599 } else {
600 // We are standard MSAA. Leave MSAA enabled for the cover geometry.
601 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
602 }
603 }
604 initArgs.fCaps = &flushState->caps();
605 initArgs.fDstProxyView = flushState->drawOpArgs().dstProxyView();
606 initArgs.fWriteSwizzle = flushState->drawOpArgs().writeSwizzle();
607 GrPipeline pipeline(initArgs, std::move(fProcessors), flushState->detachAppliedClip());
608
609 if (fDoFillTriangleBuffer) {
610 SkASSERT(fTriangleBuffer);
611
612 // These are a twist on the standard red book stencil settings that allow us to fill the
613 // inner polygon directly to the final render target. At this point, the curves are already
614 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
615 // not affected by any curves and we fill the path in directly. If the stencil value is
616 // nonzero, then we don't fill and instead continue the standard red book stencil process.
617 //
618 // NOTE: These settings are currently incompatible with a stencil clip.
619 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
620 GrUserStencilSettings::StaticInitSeparate<
621 0x0000, 0x0000,
622 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
623 0xffff, 0xffff,
624 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
625 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
626 0xffff, 0xffff>());
627
628 constexpr static GrUserStencilSettings kFillOrInvertStencil(
629 GrUserStencilSettings::StaticInit<
630 0x0000,
631 GrUserStencilTest::kEqual,
632 0xffff,
633 GrUserStencilOp::kKeep,
634 GrUserStencilOp::kZero,
635 0xffff>());
636
637 if (fDoStencilTriangleBuffer) {
638 // The path was already stencilled. Here we just need to do a cover pass.
639 pipeline.setUserStencil(&kTestAndResetStencil);
640 } else if (!fStencilCubicsShader) {
641 // There are no stencilled curves. We can ignore stencil and fill the path directly.
642 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
643 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
644 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
645 SkASSERT(!pipeline.hasStencilClip());
646 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
647 } else {
648 // Fill in the path pixels not touched by curves, invert stencil otherwise.
649 SkASSERT(!pipeline.hasStencilClip());
650 pipeline.setUserStencil(&kFillOrInvertStencil);
651 }
652
653 GrFillTriangleShader fillTriangleShader(fViewMatrix, fColor);
654 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
655 &fillTriangleShader);
656 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
657 flushState->bindTextures(fillTriangleShader, nullptr, pipeline);
658 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
659 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
660
661 if (fStencilCubicsShader) {
662 SkASSERT(fCubicBuffer);
663
664 // At this point, every pixel is filled in except the ones touched by curves. Issue a
665 // final cover pass over the curves by drawing their convex hulls. This will fill in any
666 // remaining samples and reset the stencil buffer.
667 pipeline.setUserStencil(&kTestAndResetStencil);
668 GrFillCubicHullShader fillCubicHullShader(fViewMatrix, fColor);
669 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
670 &fillCubicHullShader);
671 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
672 flushState->bindTextures(fillCubicHullShader, nullptr, pipeline);
673
674 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
675 // the base vertex on an instance boundary in order to accommodate this.
676 SkASSERT((fCubicVertexCount % 4) == 0);
677 SkASSERT((fBaseCubicVertex % 4) == 0);
678 flushState->bindBuffers(nullptr, fCubicBuffer, nullptr);
679 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
680 }
681 return;
682 }
683
684 // There are no triangles to fill. Just draw a bounding box.
685 pipeline.setUserStencil(&kTestAndResetStencil);
686 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
687 GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline,
688 &fillBoundingBoxShader);
689 flushState->bindPipelineAndScissorClip(programInfo, this->bounds());
690 flushState->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
691 flushState->bindBuffers(nullptr, nullptr, nullptr);
692 flushState->draw(4, 0);
693}
694