blob: 4902e454b0e2d543d292607a8f6dae9c4a8eb272 [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 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).
Chris Daltonf5132a02020-04-27 23:40:03 -060051 if (this->prepareNonOverlappingInnerTriangles(state, &numCountedCurves)) {
Chris Dalton04f9cda2020-04-23 10:04:25 -060052 // 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 Daltonf5132a02020-04-27 23:40:03 -060068 this->prepareMiddleOutInnerTriangles(state, &numCountedCurves);
Chris Dalton04f9cda2020-04-23 10:04:25 -060069 // 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 Daltonf5132a02020-04-27 23:40:03 -060079bool GrTessellatePathOp::prepareNonOverlappingInnerTriangles(GrOpFlushState* flushState,
80 int* numCountedCurves) {
Chris Dalton04f9cda2020-04-23 10:04:25 -060081 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
Chris Daltonf5132a02020-04-27 23:40:03 -0600109void GrTessellatePathOp::prepareMiddleOutInnerTriangles(GrOpFlushState* flushState,
110 int* numCountedCurves) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600111 SkASSERT(!fTriangleBuffer);
112 SkASSERT(!fDoStencilTriangleBuffer);
113 SkASSERT(!fDoFillTriangleBuffer);
114
Chris Daltonf5132a02020-04-27 23:40:03 -0600115 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Dalton42915c22020-04-22 16:24:43 -0600116 // Each triangle has 3 vertices.
117 int maxVertices = (fPath.countVerbs() - 1) * 3;
118
Chris Dalton04f9cda2020-04-23 10:04:25 -0600119 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fTriangleBuffer, &fBaseTriangleVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600120 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
121 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600122 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600123 }
Chris Dalton42915c22020-04-22 16:24:43 -0600124
Chris Daltonf5132a02020-04-27 23:40:03 -0600125 GrMiddleOutPolygonTriangulator middleOut(vertexData, maxVertices);
126 int localCurveCount = 0;
127 const SkPoint* pts = SkPathPriv::PointData(fPath);
128 for (SkPath::Verb verb : SkPathPriv::Verbs(fPath)) {
129 switch ((uint8_t)verb) {
130 case SkPath::kMove_Verb:
131 middleOut.closeAndMove(*pts++);
132 break;
133 case SkPath::kClose_Verb:
134 middleOut.close();
135 break;
136 static_assert(SkPath::kLine_Verb == 1); case 1:
137 static_assert(SkPath::kQuad_Verb == 2); case 2:
138 static_assert(SkPath::kConic_Verb == 3); case 3:
139 static_assert(SkPath::kCubic_Verb == 4); case 4:
140 constexpr static int kPtsAdvance[] = {0, 1, 2, 2, 3};
141 constexpr static int kCurveCountAdvance[] = {0, 0, 1, 1, 1};
142 pts += kPtsAdvance[verb];
143 middleOut.pushVertex(pts[-1]);
144 localCurveCount += kCurveCountAdvance[verb];
145 break;
146 }
Chris Dalton42915c22020-04-22 16:24:43 -0600147 }
Chris Daltonf5132a02020-04-27 23:40:03 -0600148 fTriangleVertexCount = middleOut.close();
149 *numCountedCurves = localCurveCount;
Chris Dalton42915c22020-04-22 16:24:43 -0600150
Chris Dalton04f9cda2020-04-23 10:04:25 -0600151 vertexAlloc.unlock(fTriangleVertexCount);
152
153 if (fTriangleVertexCount) {
154 fDoStencilTriangleBuffer = true;
155 }
Chris Dalton42915c22020-04-22 16:24:43 -0600156}
157
158static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
159 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
160 return (b - a) * T + a;
161}
162
163static SkPoint write_line_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1) {
164 data[0] = p0;
165 data[1] = lerp(p0, p1, 1/3.f);
166 data[2] = lerp(p0, p1, 2/3.f);
167 data[3] = p1;
168 return data[3];
169}
170
171static SkPoint write_quadratic_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1,
172 const SkPoint& p2) {
173 data[0] = p0;
174 data[1] = lerp(p0, p1, 2/3.f);
175 data[2] = lerp(p1, p2, 1/3.f);
176 data[3] = p2;
177 return data[3];
178}
179
180static SkPoint write_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
181 const SkPoint& p3) {
182 data[0] = p0;
183 data[1] = p1;
184 data[2] = p2;
185 data[3] = p3;
186 return data[3];
187}
188
Chris Dalton04f9cda2020-04-23 10:04:25 -0600189void GrTessellatePathOp::prepareOuterCubics(GrOpFlushState* flushState, int numCountedCurves,
190 CubicDataAlignment alignment) {
191 SkASSERT(!fCubicBuffer);
192 SkASSERT(!fStencilCubicsShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600193
194 if (numCountedCurves == 0) {
195 return;
196 }
197
Chris Dalton04f9cda2020-04-23 10:04:25 -0600198 bool instanceAligned = (alignment == CubicDataAlignment::kInstanceBoundary);
199 int instanceOrVertexStride = (instanceAligned) ? sizeof(SkPoint) * 4 : sizeof(SkPoint);
200 int instanceOrVertexCount = (instanceAligned) ? numCountedCurves : numCountedCurves * 4;
201 int baseInstanceOrVertex;
202
203 auto* vertexData = static_cast<SkPoint*>(flushState->makeVertexSpace(
204 instanceOrVertexStride, instanceOrVertexCount, &fCubicBuffer, &baseInstanceOrVertex));
205 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600206 return;
207 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600208 fBaseCubicVertex = (instanceAligned) ? baseInstanceOrVertex * 4 : baseInstanceOrVertex;
209 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600210
211 SkPath::Iter iter(fPath, false);
212 SkPath::Verb verb;
213 SkPoint pts[4];
214 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
215 if (SkPath::kQuad_Verb == verb) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600216 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
217 write_quadratic_as_cubic(vertexData + fCubicVertexCount, pts[0], pts[1], pts[2]);
218 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600219 continue;
220 }
221 if (SkPath::kCubic_Verb == verb) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600222 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
223 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
224 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600225 continue;
226 }
227 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600228 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
229
230 fStencilCubicsShader = flushState->allocator()->make<GrStencilCubicShader>(fViewMatrix);
Chris Dalton42915c22020-04-22 16:24:43 -0600231}
232
Chris Dalton04f9cda2020-04-23 10:04:25 -0600233void GrTessellatePathOp::prepareCubicWedges(GrOpFlushState* flushState) {
234 SkASSERT(!fCubicBuffer);
235 SkASSERT(!fStencilCubicsShader);
236
Chris Dalton42915c22020-04-22 16:24:43 -0600237 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
238 // Each wedge has 5 vertices.
239 int maxVertices = (fPath.countVerbs() + 1) * 5;
240
Chris Dalton04f9cda2020-04-23 10:04:25 -0600241 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600242 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
243 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600244 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600245 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600246 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600247
248 GrMidpointContourParser parser(fPath);
249 while (parser.parseNextContour()) {
250 int ptsIdx = 0;
251 SkPoint lastPoint = parser.startPoint();
252 for (int i = 0; i < parser.countVerbs(); ++i) {
253 switch (parser.atVerb(i)) {
254 case SkPathVerb::kClose:
255 case SkPathVerb::kDone:
256 if (parser.startPoint() != lastPoint) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600257 lastPoint = write_line_as_cubic(vertexData + fCubicVertexCount,
258 lastPoint, parser.startPoint());
Chris Dalton42915c22020-04-22 16:24:43 -0600259 break;
260 } // fallthru
261 default:
262 continue;
263
264 case SkPathVerb::kLine:
Chris Dalton04f9cda2020-04-23 10:04:25 -0600265 lastPoint = write_line_as_cubic(vertexData + fCubicVertexCount,
266 lastPoint, parser.atPoint(ptsIdx));
Chris Dalton42915c22020-04-22 16:24:43 -0600267 ++ptsIdx;
268 break;
269 case SkPathVerb::kQuad:
Chris Dalton04f9cda2020-04-23 10:04:25 -0600270 lastPoint = write_quadratic_as_cubic(vertexData + fCubicVertexCount,
271 lastPoint, parser.atPoint(ptsIdx),
Chris Dalton42915c22020-04-22 16:24:43 -0600272 parser.atPoint(ptsIdx + 1));
273 ptsIdx += 2;
274 break;
275 case SkPathVerb::kCubic:
Chris Dalton04f9cda2020-04-23 10:04:25 -0600276 lastPoint = write_cubic(vertexData + fCubicVertexCount, lastPoint,
Chris Dalton42915c22020-04-22 16:24:43 -0600277 parser.atPoint(ptsIdx), parser.atPoint(ptsIdx + 1),
278 parser.atPoint(ptsIdx + 2));
279 ptsIdx += 3;
280 break;
281 case SkPathVerb::kConic:
282 SkUNREACHABLE;
283 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600284 vertexData[fCubicVertexCount + 4] = parser.midpoint();
285 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600286 }
287 }
288
Chris Dalton04f9cda2020-04-23 10:04:25 -0600289 vertexAlloc.unlock(fCubicVertexCount);
290
291 if (fCubicVertexCount) {
292 fStencilCubicsShader = flushState->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
293 }
Chris Dalton42915c22020-04-22 16:24:43 -0600294}
295
Chris Daltonb832ce62020-01-06 19:49:37 -0700296void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600297 this->drawStencilPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700298 if (!(Flags::kStencilOnly & fFlags)) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600299 this->drawCoverPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700300 }
301}
302
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600303void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700304 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
305 constexpr static GrUserStencilSettings kIncrDecrStencil(
306 GrUserStencilSettings::StaticInitSeparate<
307 0x0000, 0x0000,
308 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
309 0xffff, 0xffff,
310 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
311 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
312 0xffff, 0xffff>());
313
314 // Inverts the bottom stencil bit. Used for "even/odd" fill.
315 constexpr static GrUserStencilSettings kInvertStencil(
316 GrUserStencilSettings::StaticInit<
317 0x0000,
318 GrUserStencilTest::kAlwaysIfInClip,
319 0xffff,
320 GrUserStencilOp::kInvert,
321 GrUserStencilOp::kKeep,
322 0x0001>());
323
324 GrPipeline::InitArgs initArgs;
325 if (GrAAType::kNone != fAAType) {
326 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
327 }
328 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
329 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
330 }
331 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
332 SkPathFillType::kEvenOdd == fPath.getFillType());
333 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
334 &kIncrDecrStencil : &kInvertStencil;
335 initArgs.fCaps = &state->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600336 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
337 state->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700338
Chris Dalton04f9cda2020-04-23 10:04:25 -0600339 if (fDoStencilTriangleBuffer) {
340 SkASSERT(fTriangleBuffer);
341 GrStencilTriangleShader stencilTriangleShader(fViewMatrix);
342 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline,
343 &stencilTriangleShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600344 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600345 state->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
346 state->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700347 }
348
Chris Dalton04f9cda2020-04-23 10:04:25 -0600349 if (fStencilCubicsShader) {
350 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, fStencilCubicsShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600351 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600352 state->bindBuffers(nullptr, nullptr, fCubicBuffer.get());
353 state->draw(fCubicVertexCount, fBaseCubicVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700354 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700355
356 // http://skbug.com/9739
357 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
358 state->gpu()->insertManualFramebufferBarrier();
359 }
360}
361
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600362void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700363 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
364 // to zero; discards immediately on stencil values of zero.
365 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
366 // samples already inside the clip.
367 constexpr static GrUserStencilSettings kTestAndResetStencil(
368 GrUserStencilSettings::StaticInit<
369 0x0000,
370 GrUserStencilTest::kNotEqual,
371 0xffff,
372 GrUserStencilOp::kZero,
373 GrUserStencilOp::kKeep,
374 0xffff>());
375
376 GrPipeline::InitArgs initArgs;
377 if (GrAAType::kNone != fAAType) {
378 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
379 if (1 == state->proxy()->numSamples()) {
380 SkASSERT(GrAAType::kCoverage == fAAType);
381 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
382 // at every fragment. This way we will still get a double hit on shared edges, but
383 // whichever side comes first will cover every sample and will clear the stencil. The
384 // other side will then be discarded and not cause a double blend.
385 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
386 }
387 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700388 initArgs.fCaps = &state->caps();
389 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
Brian Salomon982f5462020-03-30 12:52:33 -0400390 initArgs.fWriteSwizzle = state->drawOpArgs().writeSwizzle();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600391 GrPipeline pipeline(initArgs, std::move(fProcessors), state->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700392
Chris Dalton04f9cda2020-04-23 10:04:25 -0600393 if (fDoFillTriangleBuffer) {
394 SkASSERT(fTriangleBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700395
Chris Dalton04f9cda2020-04-23 10:04:25 -0600396 // These are a twist on the standard red book stencil settings that allow us to fill the
Chris Dalton4328e922020-01-29 13:16:14 -0700397 // inner polygon directly to the final render target. At this point, the curves are already
398 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
399 // not affected by any curves and we fill the path in directly. If the stencil value is
400 // nonzero, then we don't fill and instead continue the standard red book stencil process.
401 //
402 // NOTE: These settings are currently incompatible with a stencil clip.
403 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
404 GrUserStencilSettings::StaticInitSeparate<
405 0x0000, 0x0000,
406 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
407 0xffff, 0xffff,
408 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
409 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
410 0xffff, 0xffff>());
411
412 constexpr static GrUserStencilSettings kFillOrInvertStencil(
413 GrUserStencilSettings::StaticInit<
414 0x0000,
415 GrUserStencilTest::kEqual,
416 0xffff,
417 GrUserStencilOp::kKeep,
418 GrUserStencilOp::kZero,
419 0xffff>());
420
Chris Dalton04f9cda2020-04-23 10:04:25 -0600421 if (fDoStencilTriangleBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700422 // The path was already stencilled. Here we just need to do a cover pass.
423 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600424 } else if (!fStencilCubicsShader) {
425 // There are no stencilled curves. We can ignore stencil and fill the path directly.
Chris Dalton4328e922020-01-29 13:16:14 -0700426 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
427 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
428 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
429 SkASSERT(!pipeline.hasStencilClip());
430 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
431 } else {
432 // Fill in the path pixels not touched by curves, invert stencil otherwise.
433 SkASSERT(!pipeline.hasStencilClip());
434 pipeline.setUserStencil(&kFillOrInvertStencil);
435 }
Chris Dalton4328e922020-01-29 13:16:14 -0700436
Chris Dalton04f9cda2020-04-23 10:04:25 -0600437 GrFillTriangleShader fillTriangleShader(fViewMatrix, fColor);
438 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &fillTriangleShader);
439 state->bindPipelineAndScissorClip(programInfo, this->bounds());
440 state->bindTextures(fillTriangleShader, nullptr, pipeline);
441 state->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
442 state->draw(fTriangleVertexCount, fBaseTriangleVertex);
443
444 if (fStencilCubicsShader) {
Chris Dalton4328e922020-01-29 13:16:14 -0700445 // At this point, every pixel is filled in except the ones touched by curves. Issue a
446 // final cover pass over the curves by drawing their convex hulls. This will fill in any
447 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700448 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600449 GrFillCubicHullShader fillCubicHullShader(fViewMatrix, fColor);
450 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline,
451 &fillCubicHullShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600452 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600453 state->bindTextures(fillCubicHullShader, nullptr, pipeline);
454
455 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
456 // the base vertex on an instance boundary in order to accommodate this.
457 SkASSERT((fCubicVertexCount % 4) == 0);
458 SkASSERT((fBaseCubicVertex % 4) == 0);
459 state->bindBuffers(nullptr, fCubicBuffer.get(), nullptr);
460 state->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700461 }
Chris Dalton42915c22020-04-22 16:24:43 -0600462 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700463 }
Chris Dalton42915c22020-04-22 16:24:43 -0600464
Chris Dalton04f9cda2020-04-23 10:04:25 -0600465 // There are no triangles to fill. Just draw a bounding box.
Chris Dalton42915c22020-04-22 16:24:43 -0600466 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600467 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
468 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &fillBoundingBoxShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600469 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600470 state->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
Chris Dalton42915c22020-04-22 16:24:43 -0600471 state->bindBuffers(nullptr, nullptr, nullptr);
472 state->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700473}