blob: 4aea49f00ac1f02b5cf3482eef211772fd7bea53 [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 Dalton42915c22020-04-22 16:24:43 -060015#include "src/gpu/tessellate/GrInnerPolygonContourParser.h"
16#include "src/gpu/tessellate/GrMidpointContourParser.h"
Chris Daltonf9aea7f2020-01-21 11:19:26 -070017#include "src/gpu/tessellate/GrStencilPathShader.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070018
19GrTessellatePathOp::FixedFunctionFlags GrTessellatePathOp::fixedFunctionFlags() const {
20 auto flags = FixedFunctionFlags::kUsesStencil;
21 if (GrAAType::kNone != fAAType) {
22 flags |= FixedFunctionFlags::kUsesHWAA;
23 }
24 return flags;
25}
26
Robert Phillipsc655c3a2020-03-18 13:23:45 -040027void GrTessellatePathOp::onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -040028 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -040029 GrAppliedClip*,
30 const GrXferProcessor::DstProxyView&) {
31}
32
Chris Daltonb832ce62020-01-06 19:49:37 -070033void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
Chris Dalton04f9cda2020-04-23 10:04:25 -060034 // First check if the path is large and/or simple enough that we can actually triangulate the
Chris Dalton4328e922020-01-29 13:16:14 -070035 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
Chris Dalton04f9cda2020-04-23 10:04:25 -060036 // curves, and then fill the internal polygons directly to the final render target, thus filling
Chris Dalton4328e922020-01-29 13:16:14 -070037 // in the majority of pixels in a single render pass.
38 SkScalar scales[2];
39 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
40 const SkRect& bounds = fPath.getBounds();
41 int numVerbs = fPath.countVerbs();
42 if (numVerbs <= 0) {
43 return;
44 }
45 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
46 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
47 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Dalton8e2b6942020-04-22 15:55:00 -060048 int numCountedCurves;
Chris Dalton04f9cda2020-04-23 10:04:25 -060049 // This will fail if the inner triangles do not form a simple polygon (e.g., self
50 // intersection, double winding).
51 if (this->prepareSimpleInnerPolygonTriangulation(state, &numCountedCurves)) {
52 // Prepare cubics on an instance boundary so we can use the buffer to fill local convex
53 // hulls as well.
54 this->prepareOuterCubics(state, numCountedCurves,
55 CubicDataAlignment::kInstanceBoundary);
Chris Dalton4328e922020-01-29 13:16:14 -070056 return;
57 }
58 }
59
60 // Next see if we can split up inner polygon triangles and curves, and triangulate the inner
Chris Daltonf9aea7f2020-01-21 11:19:26 -070061 // polygon(s) more efficiently. This causes greater CPU overhead due to the extra shaders and
62 // draw calls, but the better triangulation can reduce the rasterizer load by a great deal on
63 // complex paths.
Chris Dalton4328e922020-01-29 13:16:14 -070064 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
65 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonf9aea7f2020-01-21 11:19:26 -070066 if (rasterEdgeWork > 1000 * 1000) {
Chris Dalton42915c22020-04-22 16:24:43 -060067 int numCountedCurves;
Chris Dalton04f9cda2020-04-23 10:04:25 -060068 this->prepareInnerTriangles(state, &numCountedCurves);
69 // We will fill the path with a bounding box instead local cubic convex hulls, so there is
70 // no need to prepare the cubics on an instance boundary.
71 this->prepareOuterCubics(state, numCountedCurves, CubicDataAlignment::kVertexBoundary);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070072 return;
73 }
74
75 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Dalton04f9cda2020-04-23 10:04:25 -060076 this->prepareCubicWedges(state);
Chris Daltonb832ce62020-01-06 19:49:37 -070077}
78
Chris Dalton04f9cda2020-04-23 10:04:25 -060079bool GrTessellatePathOp::prepareSimpleInnerPolygonTriangulation(GrOpFlushState* flushState,
80 int* numCountedCurves) {
81 SkASSERT(!fTriangleBuffer);
82 SkASSERT(!fDoStencilTriangleBuffer);
83 SkASSERT(!fDoFillTriangleBuffer);
84
85 using GrTriangulator::Mode;
86
87 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fTriangleBuffer, &fBaseTriangleVertex);
88 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
89 &vertexAlloc, Mode::kSimpleInnerPolygons,
90 numCountedCurves);
91 if (fTriangleVertexCount == 0) {
92 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
93 // simple.
94 return false;
95 }
96 if (((Flags::kStencilOnly | Flags::kWireframe) & fFlags) || GrAAType::kCoverage == fAAType ||
97 (flushState->appliedClip() && flushState->appliedClip()->hasStencilClip())) {
98 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
99 // can't fill the inner polygon directly. Indicate that these triangles need to be
100 // stencilled.
101 fDoStencilTriangleBuffer = true;
102 }
103 if (!(Flags::kStencilOnly & fFlags)) {
104 fDoFillTriangleBuffer = true;
105 }
106 return true;
107}
108
109void GrTessellatePathOp::prepareInnerTriangles(GrOpFlushState* flushState, int* numCountedCurves) {
110 SkASSERT(!fTriangleBuffer);
111 SkASSERT(!fDoStencilTriangleBuffer);
112 SkASSERT(!fDoFillTriangleBuffer);
113
Chris Dalton42915c22020-04-22 16:24:43 -0600114 // No initial moveTo, plus an implicit close at the end; n-2 trianles fill an n-gon.
115 // Each triangle has 3 vertices.
116 int maxVertices = (fPath.countVerbs() - 1) * 3;
117
Chris Dalton04f9cda2020-04-23 10:04:25 -0600118 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fTriangleBuffer, &fBaseTriangleVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600119 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
120 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600121 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600122 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600123 fTriangleVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600124
125 GrInnerPolygonContourParser parser(fPath, maxVertices);
126 while (parser.parseNextContour()) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600127 fTriangleVertexCount += parser.emitInnerPolygon(vertexData + fTriangleVertexCount);
Chris Dalton42915c22020-04-22 16:24:43 -0600128 }
129 *numCountedCurves = parser.numCountedCurves();
130
Chris Dalton04f9cda2020-04-23 10:04:25 -0600131 vertexAlloc.unlock(fTriangleVertexCount);
132
133 if (fTriangleVertexCount) {
134 fDoStencilTriangleBuffer = true;
135 }
Chris Dalton42915c22020-04-22 16:24:43 -0600136}
137
138static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
139 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
140 return (b - a) * T + a;
141}
142
143static SkPoint write_line_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1) {
144 data[0] = p0;
145 data[1] = lerp(p0, p1, 1/3.f);
146 data[2] = lerp(p0, p1, 2/3.f);
147 data[3] = p1;
148 return data[3];
149}
150
151static SkPoint write_quadratic_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1,
152 const SkPoint& p2) {
153 data[0] = p0;
154 data[1] = lerp(p0, p1, 2/3.f);
155 data[2] = lerp(p1, p2, 1/3.f);
156 data[3] = p2;
157 return data[3];
158}
159
160static SkPoint write_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
161 const SkPoint& p3) {
162 data[0] = p0;
163 data[1] = p1;
164 data[2] = p2;
165 data[3] = p3;
166 return data[3];
167}
168
Chris Dalton04f9cda2020-04-23 10:04:25 -0600169void GrTessellatePathOp::prepareOuterCubics(GrOpFlushState* flushState, int numCountedCurves,
170 CubicDataAlignment alignment) {
171 SkASSERT(!fCubicBuffer);
172 SkASSERT(!fStencilCubicsShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600173
174 if (numCountedCurves == 0) {
175 return;
176 }
177
Chris Dalton04f9cda2020-04-23 10:04:25 -0600178 bool instanceAligned = (alignment == CubicDataAlignment::kInstanceBoundary);
179 int instanceOrVertexStride = (instanceAligned) ? sizeof(SkPoint) * 4 : sizeof(SkPoint);
180 int instanceOrVertexCount = (instanceAligned) ? numCountedCurves : numCountedCurves * 4;
181 int baseInstanceOrVertex;
182
183 auto* vertexData = static_cast<SkPoint*>(flushState->makeVertexSpace(
184 instanceOrVertexStride, instanceOrVertexCount, &fCubicBuffer, &baseInstanceOrVertex));
185 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600186 return;
187 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600188 fBaseCubicVertex = (instanceAligned) ? baseInstanceOrVertex * 4 : baseInstanceOrVertex;
189 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600190
191 SkPath::Iter iter(fPath, false);
192 SkPath::Verb verb;
193 SkPoint pts[4];
194 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
195 if (SkPath::kQuad_Verb == verb) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600196 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
197 write_quadratic_as_cubic(vertexData + fCubicVertexCount, pts[0], pts[1], pts[2]);
198 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600199 continue;
200 }
201 if (SkPath::kCubic_Verb == verb) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600202 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
203 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
204 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600205 continue;
206 }
207 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600208 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
209
210 fStencilCubicsShader = flushState->allocator()->make<GrStencilCubicShader>(fViewMatrix);
Chris Dalton42915c22020-04-22 16:24:43 -0600211}
212
Chris Dalton04f9cda2020-04-23 10:04:25 -0600213void GrTessellatePathOp::prepareCubicWedges(GrOpFlushState* flushState) {
214 SkASSERT(!fCubicBuffer);
215 SkASSERT(!fStencilCubicsShader);
216
Chris Dalton42915c22020-04-22 16:24:43 -0600217 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
218 // Each wedge has 5 vertices.
219 int maxVertices = (fPath.countVerbs() + 1) * 5;
220
Chris Dalton04f9cda2020-04-23 10:04:25 -0600221 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600222 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
223 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600224 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600225 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600226 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600227
228 GrMidpointContourParser parser(fPath);
229 while (parser.parseNextContour()) {
230 int ptsIdx = 0;
231 SkPoint lastPoint = parser.startPoint();
232 for (int i = 0; i < parser.countVerbs(); ++i) {
233 switch (parser.atVerb(i)) {
234 case SkPathVerb::kClose:
235 case SkPathVerb::kDone:
236 if (parser.startPoint() != lastPoint) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600237 lastPoint = write_line_as_cubic(vertexData + fCubicVertexCount,
238 lastPoint, parser.startPoint());
Chris Dalton42915c22020-04-22 16:24:43 -0600239 break;
240 } // fallthru
241 default:
242 continue;
243
244 case SkPathVerb::kLine:
Chris Dalton04f9cda2020-04-23 10:04:25 -0600245 lastPoint = write_line_as_cubic(vertexData + fCubicVertexCount,
246 lastPoint, parser.atPoint(ptsIdx));
Chris Dalton42915c22020-04-22 16:24:43 -0600247 ++ptsIdx;
248 break;
249 case SkPathVerb::kQuad:
Chris Dalton04f9cda2020-04-23 10:04:25 -0600250 lastPoint = write_quadratic_as_cubic(vertexData + fCubicVertexCount,
251 lastPoint, parser.atPoint(ptsIdx),
Chris Dalton42915c22020-04-22 16:24:43 -0600252 parser.atPoint(ptsIdx + 1));
253 ptsIdx += 2;
254 break;
255 case SkPathVerb::kCubic:
Chris Dalton04f9cda2020-04-23 10:04:25 -0600256 lastPoint = write_cubic(vertexData + fCubicVertexCount, lastPoint,
Chris Dalton42915c22020-04-22 16:24:43 -0600257 parser.atPoint(ptsIdx), parser.atPoint(ptsIdx + 1),
258 parser.atPoint(ptsIdx + 2));
259 ptsIdx += 3;
260 break;
261 case SkPathVerb::kConic:
262 SkUNREACHABLE;
263 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600264 vertexData[fCubicVertexCount + 4] = parser.midpoint();
265 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600266 }
267 }
268
Chris Dalton04f9cda2020-04-23 10:04:25 -0600269 vertexAlloc.unlock(fCubicVertexCount);
270
271 if (fCubicVertexCount) {
272 fStencilCubicsShader = flushState->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
273 }
Chris Dalton42915c22020-04-22 16:24:43 -0600274}
275
Chris Daltonb832ce62020-01-06 19:49:37 -0700276void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600277 this->drawStencilPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700278 if (!(Flags::kStencilOnly & fFlags)) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600279 this->drawCoverPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700280 }
281}
282
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600283void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700284 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
285 constexpr static GrUserStencilSettings kIncrDecrStencil(
286 GrUserStencilSettings::StaticInitSeparate<
287 0x0000, 0x0000,
288 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
289 0xffff, 0xffff,
290 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
291 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
292 0xffff, 0xffff>());
293
294 // Inverts the bottom stencil bit. Used for "even/odd" fill.
295 constexpr static GrUserStencilSettings kInvertStencil(
296 GrUserStencilSettings::StaticInit<
297 0x0000,
298 GrUserStencilTest::kAlwaysIfInClip,
299 0xffff,
300 GrUserStencilOp::kInvert,
301 GrUserStencilOp::kKeep,
302 0x0001>());
303
304 GrPipeline::InitArgs initArgs;
305 if (GrAAType::kNone != fAAType) {
306 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
307 }
308 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
309 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
310 }
311 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
312 SkPathFillType::kEvenOdd == fPath.getFillType());
313 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
314 &kIncrDecrStencil : &kInvertStencil;
315 initArgs.fCaps = &state->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600316 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
317 state->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700318
Chris Dalton04f9cda2020-04-23 10:04:25 -0600319 if (fDoStencilTriangleBuffer) {
320 SkASSERT(fTriangleBuffer);
321 GrStencilTriangleShader stencilTriangleShader(fViewMatrix);
322 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline,
323 &stencilTriangleShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600324 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600325 state->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
326 state->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700327 }
328
Chris Dalton04f9cda2020-04-23 10:04:25 -0600329 if (fStencilCubicsShader) {
330 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, fStencilCubicsShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600331 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600332 state->bindBuffers(nullptr, nullptr, fCubicBuffer.get());
333 state->draw(fCubicVertexCount, fBaseCubicVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700334 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700335
336 // http://skbug.com/9739
337 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
338 state->gpu()->insertManualFramebufferBarrier();
339 }
340}
341
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600342void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700343 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
344 // to zero; discards immediately on stencil values of zero.
345 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
346 // samples already inside the clip.
347 constexpr static GrUserStencilSettings kTestAndResetStencil(
348 GrUserStencilSettings::StaticInit<
349 0x0000,
350 GrUserStencilTest::kNotEqual,
351 0xffff,
352 GrUserStencilOp::kZero,
353 GrUserStencilOp::kKeep,
354 0xffff>());
355
356 GrPipeline::InitArgs initArgs;
357 if (GrAAType::kNone != fAAType) {
358 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
359 if (1 == state->proxy()->numSamples()) {
360 SkASSERT(GrAAType::kCoverage == fAAType);
361 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
362 // at every fragment. This way we will still get a double hit on shared edges, but
363 // whichever side comes first will cover every sample and will clear the stencil. The
364 // other side will then be discarded and not cause a double blend.
365 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
366 }
367 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700368 initArgs.fCaps = &state->caps();
369 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
Brian Salomon982f5462020-03-30 12:52:33 -0400370 initArgs.fWriteSwizzle = state->drawOpArgs().writeSwizzle();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600371 GrPipeline pipeline(initArgs, std::move(fProcessors), state->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700372
Chris Dalton04f9cda2020-04-23 10:04:25 -0600373 if (fDoFillTriangleBuffer) {
374 SkASSERT(fTriangleBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700375
Chris Dalton04f9cda2020-04-23 10:04:25 -0600376 // These are a twist on the standard red book stencil settings that allow us to fill the
Chris Dalton4328e922020-01-29 13:16:14 -0700377 // inner polygon directly to the final render target. At this point, the curves are already
378 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
379 // not affected by any curves and we fill the path in directly. If the stencil value is
380 // nonzero, then we don't fill and instead continue the standard red book stencil process.
381 //
382 // NOTE: These settings are currently incompatible with a stencil clip.
383 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
384 GrUserStencilSettings::StaticInitSeparate<
385 0x0000, 0x0000,
386 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
387 0xffff, 0xffff,
388 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
389 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
390 0xffff, 0xffff>());
391
392 constexpr static GrUserStencilSettings kFillOrInvertStencil(
393 GrUserStencilSettings::StaticInit<
394 0x0000,
395 GrUserStencilTest::kEqual,
396 0xffff,
397 GrUserStencilOp::kKeep,
398 GrUserStencilOp::kZero,
399 0xffff>());
400
Chris Dalton04f9cda2020-04-23 10:04:25 -0600401 if (fDoStencilTriangleBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700402 // The path was already stencilled. Here we just need to do a cover pass.
403 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600404 } else if (!fStencilCubicsShader) {
405 // There are no stencilled curves. We can ignore stencil and fill the path directly.
Chris Dalton4328e922020-01-29 13:16:14 -0700406 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
407 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
408 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
409 SkASSERT(!pipeline.hasStencilClip());
410 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
411 } else {
412 // Fill in the path pixels not touched by curves, invert stencil otherwise.
413 SkASSERT(!pipeline.hasStencilClip());
414 pipeline.setUserStencil(&kFillOrInvertStencil);
415 }
Chris Dalton4328e922020-01-29 13:16:14 -0700416
Chris Dalton04f9cda2020-04-23 10:04:25 -0600417 GrFillTriangleShader fillTriangleShader(fViewMatrix, fColor);
418 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &fillTriangleShader);
419 state->bindPipelineAndScissorClip(programInfo, this->bounds());
420 state->bindTextures(fillTriangleShader, nullptr, pipeline);
421 state->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
422 state->draw(fTriangleVertexCount, fBaseTriangleVertex);
423
424 if (fStencilCubicsShader) {
Chris Dalton4328e922020-01-29 13:16:14 -0700425 // At this point, every pixel is filled in except the ones touched by curves. Issue a
426 // final cover pass over the curves by drawing their convex hulls. This will fill in any
427 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700428 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600429 GrFillCubicHullShader fillCubicHullShader(fViewMatrix, fColor);
430 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline,
431 &fillCubicHullShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600432 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600433 state->bindTextures(fillCubicHullShader, nullptr, pipeline);
434
435 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
436 // the base vertex on an instance boundary in order to accommodate this.
437 SkASSERT((fCubicVertexCount % 4) == 0);
438 SkASSERT((fBaseCubicVertex % 4) == 0);
439 state->bindBuffers(nullptr, fCubicBuffer.get(), nullptr);
440 state->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700441 }
Chris Dalton42915c22020-04-22 16:24:43 -0600442 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700443 }
Chris Dalton42915c22020-04-22 16:24:43 -0600444
Chris Dalton04f9cda2020-04-23 10:04:25 -0600445 // There are no triangles to fill. Just draw a bounding box.
Chris Dalton42915c22020-04-22 16:24:43 -0600446 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600447 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
448 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &fillBoundingBoxShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600449 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600450 state->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
Chris Dalton42915c22020-04-22 16:24:43 -0600451 state->bindBuffers(nullptr, nullptr, nullptr);
452 state->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700453}