blob: cf79daa7a7bb8d0275baf5cd11f35275035d8384 [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 Dalton0e6d4f12020-05-24 20:48:56 +000033void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
34 // First check if the path is large and/or simple enough that we can actually triangulate the
35 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
36 // curves, and then fill the internal polygons directly to the final render target, thus filling
37 // 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();
Chris Dalton4328e922020-01-29 13:16:14 -070041 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 Dalton0e6d4f12020-05-24 20:48:56 +000048 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 Dalton0e6d4f12020-05-24 20:48:56 +000051 if (this->prepareNonOverlappingInnerTriangles(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
Chris Dalton0e6d4f12020-05-24 20:48:56 +000060 // Next see if we can split up inner polygon triangles and curves, and triangulate the inner
61 // 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 Dalton0e6d4f12020-05-24 20:48:56 +000066 if (rasterEdgeWork > 1000 * 1000) {
67 int numCountedCurves;
68 this->prepareMiddleOutInnerTriangles(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 Dalton0e6d4f12020-05-24 20:48:56 +000076 this->prepareCubicWedges(state);
Chris Daltonb832ce62020-01-06 19:49:37 -070077}
78
Chris Dalton2f2d81c2020-05-13 17:57:37 -060079bool GrTessellatePathOp::prepareNonOverlappingInnerTriangles(GrMeshDrawOp::Target* target,
Chris Daltonf5132a02020-04-27 23:40:03 -060080 int* numCountedCurves) {
Chris Dalton04f9cda2020-04-23 10:04:25 -060081 SkASSERT(!fTriangleBuffer);
82 SkASSERT(!fDoStencilTriangleBuffer);
83 SkASSERT(!fDoFillTriangleBuffer);
84
85 using GrTriangulator::Mode;
86
Chris Dalton2f2d81c2020-05-13 17:57:37 -060087 GrEagerDynamicVertexAllocator vertexAlloc(target, &fTriangleBuffer, &fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -060088 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 ||
Chris Dalton2f2d81c2020-05-13 17:57:37 -060097 (target->appliedClip() && target->appliedClip()->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -060098 // 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 Dalton0e6d4f12020-05-24 20:48:56 +0000109void GrTessellatePathOp::prepareMiddleOutInnerTriangles(GrMeshDrawOp::Target* target,
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 Dalton0e6d4f12020-05-24 20:48:56 +0000116 // Each triangle has 3 vertices.
117 int maxVertices = (fPath.countVerbs() - 1) * 3;
Chris Dalton42915c22020-04-22 16:24:43 -0600118
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000119 GrEagerDynamicVertexAllocator vertexAlloc(target, &fTriangleBuffer, &fBaseTriangleVertex);
120 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
Chris Dalton42915c22020-04-22 16:24:43 -0600121 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 Dalton0e6d4f12020-05-24 20:48:56 +0000125 constexpr static int kNumVerticesPerTriangle = 3;
126 GrMiddleOutPolygonTriangulator middleOut(vertexData, kNumVerticesPerTriangle, maxVertices);
127 int localCurveCount = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600128 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
129 switch (verb) {
130 case SkPathVerb::kMove:
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000131 middleOut.closeAndMove(*pts++);
Chris Daltonf5132a02020-04-27 23:40:03 -0600132 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600133 case SkPathVerb::kLine:
134 middleOut.pushVertex(pts[1]);
135 break;
136 case SkPathVerb::kQuad:
137 middleOut.pushVertex(pts[2]);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000138 ++localCurveCount;
Chris Daltonf7a33072020-05-01 10:33:08 -0600139 break;
140 case SkPathVerb::kCubic:
141 middleOut.pushVertex(pts[3]);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000142 ++localCurveCount;
Chris Daltonf7a33072020-05-01 10:33:08 -0600143 break;
144 case SkPathVerb::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600145 middleOut.close();
146 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600147 case SkPathVerb::kConic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600148 SkUNREACHABLE;
Chris Daltonf5132a02020-04-27 23:40:03 -0600149 }
Chris Dalton42915c22020-04-22 16:24:43 -0600150 }
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000151 fTriangleVertexCount = middleOut.close() * kNumVerticesPerTriangle;
152 *numCountedCurves = localCurveCount;
Chris Dalton42915c22020-04-22 16:24:43 -0600153
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000154 vertexAlloc.unlock(fTriangleVertexCount);
155
156 if (fTriangleVertexCount) {
157 fDoStencilTriangleBuffer = true;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600158 }
Chris Dalton42915c22020-04-22 16:24:43 -0600159}
160
161static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
162 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
163 return (b - a) * T + a;
164}
165
Chris Daltonf7a33072020-05-01 10:33:08 -0600166static void line2cubic(const SkPoint& p0, const SkPoint& p1, SkPoint* out) {
167 out[0] = p0;
168 out[1] = lerp(p0, p1, 1/3.f);
169 out[2] = lerp(p0, p1, 2/3.f);
170 out[3] = p1;
Chris Dalton42915c22020-04-22 16:24:43 -0600171}
172
Chris Daltonf7a33072020-05-01 10:33:08 -0600173static void quad2cubic(const SkPoint pts[], SkPoint* out) {
174 out[0] = pts[0];
175 out[1] = lerp(pts[0], pts[1], 2/3.f);
176 out[2] = lerp(pts[1], pts[2], 1/3.f);
177 out[3] = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600178}
179
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000180void GrTessellatePathOp::prepareOuterCubics(GrMeshDrawOp::Target* target, int numCountedCurves,
181 CubicDataAlignment alignment) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600182 SkASSERT(!fCubicBuffer);
183 SkASSERT(!fStencilCubicsShader);
Chris Dalton42915c22020-04-22 16:24:43 -0600184
185 if (numCountedCurves == 0) {
186 return;
187 }
188
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000189 bool instanceAligned = (alignment == CubicDataAlignment::kInstanceBoundary);
190 int instanceOrVertexStride = (instanceAligned) ? sizeof(SkPoint) * 4 : sizeof(SkPoint);
191 int instanceOrVertexCount = (instanceAligned) ? numCountedCurves : numCountedCurves * 4;
192 int baseInstanceOrVertex;
193
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600194 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000195 instanceOrVertexStride, instanceOrVertexCount, &fCubicBuffer, &baseInstanceOrVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600196 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600197 return;
198 }
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000199 fBaseCubicVertex = (instanceAligned) ? baseInstanceOrVertex * 4 : baseInstanceOrVertex;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600200 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600201
Chris Daltonf7a33072020-05-01 10:33:08 -0600202 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
203 switch (verb) {
204 case SkPathVerb::kQuad:
205 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
206 quad2cubic(pts, vertexData + fCubicVertexCount);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000207 fCubicVertexCount += 4;
Chris Daltonf7a33072020-05-01 10:33:08 -0600208 break;
209 case SkPathVerb::kCubic:
210 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
211 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000212 fCubicVertexCount += 4;
213 break;
214 default:
Chris Daltonf7a33072020-05-01 10:33:08 -0600215 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600216 }
217 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600218 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
219
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000220 fStencilCubicsShader = target->allocator()->make<GrStencilCubicShader>(fViewMatrix);
Chris Dalton42915c22020-04-22 16:24:43 -0600221}
222
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000223void GrTessellatePathOp::prepareCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600224 SkASSERT(!fCubicBuffer);
225 SkASSERT(!fStencilCubicsShader);
226
Chris Dalton42915c22020-04-22 16:24:43 -0600227 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
228 // Each wedge has 5 vertices.
229 int maxVertices = (fPath.countVerbs() + 1) * 5;
230
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600231 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600232 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
233 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600234 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600235 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600236 fCubicVertexCount = 0;
Chris Dalton42915c22020-04-22 16:24:43 -0600237
238 GrMidpointContourParser parser(fPath);
239 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600240 SkPoint midpoint = parser.currentMidpoint();
241 SkPoint startPoint = {0, 0};
242 SkPoint lastPoint = startPoint;
243 for (auto [verb, pts, w] : parser.currentContour()) {
244 switch (verb) {
245 case SkPathVerb::kMove:
246 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600247 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600248 case SkPathVerb::kClose:
249 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600250 case SkPathVerb::kLine:
Chris Daltonf7a33072020-05-01 10:33:08 -0600251 line2cubic(pts[0], pts[1], vertexData + fCubicVertexCount);
252 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600253 break;
254 case SkPathVerb::kQuad:
Chris Daltonf7a33072020-05-01 10:33:08 -0600255 quad2cubic(pts, vertexData + fCubicVertexCount);
256 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600257 break;
258 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600259 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
260 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600261 break;
262 case SkPathVerb::kConic:
263 SkUNREACHABLE;
264 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600265 vertexData[fCubicVertexCount + 4] = midpoint;
266 fCubicVertexCount += 5;
267 }
268 if (lastPoint != startPoint) {
269 line2cubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
270 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600271 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600272 }
273 }
274
Chris Dalton04f9cda2020-04-23 10:04:25 -0600275 vertexAlloc.unlock(fCubicVertexCount);
276
277 if (fCubicVertexCount) {
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000278 fStencilCubicsShader = target->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600279 }
Chris Dalton42915c22020-04-22 16:24:43 -0600280}
281
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000282void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
283 this->drawStencilPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700284 if (!(Flags::kStencilOnly & fFlags)) {
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000285 this->drawCoverPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700286 }
287}
288
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000289void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700290 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
291 constexpr static GrUserStencilSettings kIncrDecrStencil(
292 GrUserStencilSettings::StaticInitSeparate<
293 0x0000, 0x0000,
294 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
295 0xffff, 0xffff,
296 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
297 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
298 0xffff, 0xffff>());
299
300 // Inverts the bottom stencil bit. Used for "even/odd" fill.
301 constexpr static GrUserStencilSettings kInvertStencil(
302 GrUserStencilSettings::StaticInit<
303 0x0000,
304 GrUserStencilTest::kAlwaysIfInClip,
305 0xffff,
306 GrUserStencilOp::kInvert,
307 GrUserStencilOp::kKeep,
308 0x0001>());
309
310 GrPipeline::InitArgs initArgs;
311 if (GrAAType::kNone != fAAType) {
312 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
313 }
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000314 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700315 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
316 }
317 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
318 SkPathFillType::kEvenOdd == fPath.getFillType());
319 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
320 &kIncrDecrStencil : &kInvertStencil;
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000321 initArgs.fCaps = &state->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600322 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000323 state->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700324
Chris Dalton04f9cda2020-04-23 10:04:25 -0600325 if (fDoStencilTriangleBuffer) {
326 SkASSERT(fTriangleBuffer);
327 GrStencilTriangleShader stencilTriangleShader(fViewMatrix);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000328 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600329 &stencilTriangleShader);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000330 state->bindPipelineAndScissorClip(programInfo, this->bounds());
331 state->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
332 state->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700333 }
334
Chris Dalton04f9cda2020-04-23 10:04:25 -0600335 if (fStencilCubicsShader) {
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000336 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, fStencilCubicsShader);
337 state->bindPipelineAndScissorClip(programInfo, this->bounds());
338 state->bindBuffers(nullptr, nullptr, fCubicBuffer.get());
339 state->draw(fCubicVertexCount, fBaseCubicVertex);
340 }
341
342 // http://skbug.com/9739
343 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
344 state->gpu()->insertManualFramebufferBarrier();
Chris Daltonb832ce62020-01-06 19:49:37 -0700345 }
346}
347
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000348void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700349 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
350 // to zero; discards immediately on stencil values of zero.
351 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
352 // samples already inside the clip.
353 constexpr static GrUserStencilSettings kTestAndResetStencil(
354 GrUserStencilSettings::StaticInit<
355 0x0000,
356 GrUserStencilTest::kNotEqual,
357 0xffff,
358 GrUserStencilOp::kZero,
359 GrUserStencilOp::kKeep,
360 0xffff>());
361
362 GrPipeline::InitArgs initArgs;
363 if (GrAAType::kNone != fAAType) {
364 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000365 if (1 == state->proxy()->numSamples()) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700366 SkASSERT(GrAAType::kCoverage == fAAType);
367 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
368 // at every fragment. This way we will still get a double hit on shared edges, but
369 // whichever side comes first will cover every sample and will clear the stencil. The
370 // other side will then be discarded and not cause a double blend.
371 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
372 }
373 }
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000374 initArgs.fCaps = &state->caps();
375 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
376 initArgs.fWriteSwizzle = state->drawOpArgs().writeSwizzle();
377 GrPipeline pipeline(initArgs, std::move(fProcessors), state->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700378
Chris Dalton04f9cda2020-04-23 10:04:25 -0600379 if (fDoFillTriangleBuffer) {
380 SkASSERT(fTriangleBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700381
Chris Dalton04f9cda2020-04-23 10:04:25 -0600382 // These are a twist on the standard red book stencil settings that allow us to fill the
Chris Dalton4328e922020-01-29 13:16:14 -0700383 // inner polygon directly to the final render target. At this point, the curves are already
384 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
385 // not affected by any curves and we fill the path in directly. If the stencil value is
386 // nonzero, then we don't fill and instead continue the standard red book stencil process.
387 //
388 // NOTE: These settings are currently incompatible with a stencil clip.
389 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
390 GrUserStencilSettings::StaticInitSeparate<
391 0x0000, 0x0000,
392 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
393 0xffff, 0xffff,
394 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
395 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
396 0xffff, 0xffff>());
397
398 constexpr static GrUserStencilSettings kFillOrInvertStencil(
399 GrUserStencilSettings::StaticInit<
400 0x0000,
401 GrUserStencilTest::kEqual,
402 0xffff,
403 GrUserStencilOp::kKeep,
404 GrUserStencilOp::kZero,
405 0xffff>());
406
Chris Dalton04f9cda2020-04-23 10:04:25 -0600407 if (fDoStencilTriangleBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700408 // The path was already stencilled. Here we just need to do a cover pass.
409 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600410 } else if (!fStencilCubicsShader) {
411 // There are no stencilled curves. We can ignore stencil and fill the path directly.
Chris Dalton4328e922020-01-29 13:16:14 -0700412 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
413 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
414 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
415 SkASSERT(!pipeline.hasStencilClip());
416 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
417 } else {
418 // Fill in the path pixels not touched by curves, invert stencil otherwise.
419 SkASSERT(!pipeline.hasStencilClip());
420 pipeline.setUserStencil(&kFillOrInvertStencil);
421 }
Chris Dalton4328e922020-01-29 13:16:14 -0700422
Chris Dalton04f9cda2020-04-23 10:04:25 -0600423 GrFillTriangleShader fillTriangleShader(fViewMatrix, fColor);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000424 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &fillTriangleShader);
425 state->bindPipelineAndScissorClip(programInfo, this->bounds());
426 state->bindTextures(fillTriangleShader, nullptr, pipeline);
427 state->bindBuffers(nullptr, nullptr, fTriangleBuffer.get());
428 state->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600429
430 if (fStencilCubicsShader) {
Chris Dalton4328e922020-01-29 13:16:14 -0700431 // At this point, every pixel is filled in except the ones touched by curves. Issue a
432 // final cover pass over the curves by drawing their convex hulls. This will fill in any
433 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700434 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600435 GrFillCubicHullShader fillCubicHullShader(fViewMatrix, fColor);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000436 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600437 &fillCubicHullShader);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000438 state->bindPipelineAndScissorClip(programInfo, this->bounds());
439 state->bindTextures(fillCubicHullShader, nullptr, pipeline);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600440
441 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
442 // the base vertex on an instance boundary in order to accommodate this.
443 SkASSERT((fCubicVertexCount % 4) == 0);
444 SkASSERT((fBaseCubicVertex % 4) == 0);
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000445 state->bindBuffers(nullptr, fCubicBuffer.get(), nullptr);
446 state->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700447 }
Chris Dalton42915c22020-04-22 16:24:43 -0600448 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700449 }
Chris Dalton42915c22020-04-22 16:24:43 -0600450
Chris Dalton04f9cda2020-04-23 10:04:25 -0600451 // There are no triangles to fill. Just draw a bounding box.
Chris Dalton42915c22020-04-22 16:24:43 -0600452 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600453 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
Chris Dalton0e6d4f12020-05-24 20:48:56 +0000454 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &fillBoundingBoxShader);
455 state->bindPipelineAndScissorClip(programInfo, this->bounds());
456 state->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
457 state->bindBuffers(nullptr, nullptr, nullptr);
458 state->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700459}