blob: 29c5ae5241010001cac50ed3e7a421d611498a3a [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
Chris Dalton078f8752020-07-30 19:50:46 -06008#include "src/gpu/tessellate/GrPathTessellateOp.h"
Chris Daltonb832ce62020-01-06 19:49:37 -07009
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 Daltone74cebe2020-09-24 09:32:54 -060013#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton17dc4182020-03-25 16:18:16 -060014#include "src/gpu/GrTriangulator.h"
Chris Dalton4328e922020-01-29 13:16:14 -070015#include "src/gpu/tessellate/GrFillPathShader.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060016#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Dalton42915c22020-04-22 16:24:43 -060017#include "src/gpu/tessellate/GrMidpointContourParser.h"
Chris Daltonb5391d92020-05-24 14:55:54 -060018#include "src/gpu/tessellate/GrResolveLevelCounter.h"
Chris Daltonf9aea7f2020-01-21 11:19:26 -070019#include "src/gpu/tessellate/GrStencilPathShader.h"
Chris Daltonb96995d2020-06-04 16:44:29 -060020#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070021
Chris Daltonb96995d2020-06-04 16:44:29 -060022constexpr static float kLinearizationIntolerance =
23 GrTessellationPathRenderer::kLinearizationIntolerance;
24
25constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel;
26
27using OpFlags = GrTessellationPathRenderer::OpFlags;
Chris Daltonb5391d92020-05-24 14:55:54 -060028
Chris Dalton078f8752020-07-30 19:50:46 -060029GrPathTessellateOp::FixedFunctionFlags GrPathTessellateOp::fixedFunctionFlags() const {
Chris Daltonb832ce62020-01-06 19:49:37 -070030 auto flags = FixedFunctionFlags::kUsesStencil;
31 if (GrAAType::kNone != fAAType) {
32 flags |= FixedFunctionFlags::kUsesHWAA;
33 }
34 return flags;
35}
36
Chris Daltone74cebe2020-09-24 09:32:54 -060037namespace {
Robert Phillipsc655c3a2020-03-18 13:23:45 -040038
Chris Daltone74cebe2020-09-24 09:32:54 -060039class CpuTriangleAllocator : public GrEagerVertexAllocator {
40public:
41 CpuTriangleAllocator(SkArenaAlloc* arena, const SkPoint** data) : fArena(arena), fData(data) {}
42
43 void* lock(size_t stride, int eagerCount) override {
44 SkASSERT(!*fData);
45 SkASSERT(stride == sizeof(SkPoint));
46 SkPoint* data = fArena->makeArray<SkPoint>(eagerCount);
47 *fData = data;
48 return data;
49 }
50
51 void unlock(int actualCount) override { SkASSERT(*fData); }
52
53private:
54 SkArenaAlloc* const fArena;
55 const SkPoint** fData;
56};
57
58}
59
60void GrPathTessellateOp::onPrePrepare(GrRecordingContext* context,
61 const GrSurfaceProxyView* writeView, GrAppliedClip* clip,
62 const GrXferProcessor::DstProxyView& dstProxyView,
63 GrXferBarrierFlags renderPassXferBarriers) {
64 SkArenaAlloc* recordTimeAllocator = context->priv().recordTimeAllocator();
65 CpuTriangleAllocator cpuTriangleAllocator(recordTimeAllocator, &fOffThreadInnerTriangulation);
66 this->prePreparePrograms({recordTimeAllocator, writeView, clip, &dstProxyView,
67 renderPassXferBarriers, context->priv().caps(),
68 &cpuTriangleAllocator});
69 if (fStencilTrianglesProgram) {
70 context->priv().recordProgramInfo(fStencilTrianglesProgram);
71 }
72 if (fStencilCubicsProgram) {
73 context->priv().recordProgramInfo(fStencilCubicsProgram);
74 }
75 // TODO: Record the fill programs as well once they are getting prePrepared.
76}
77
78void GrPathTessellateOp::prePreparePrograms(const PrePrepareArgs& args) {
Chris Dalton4328e922020-01-29 13:16:14 -070079 int numVerbs = fPath.countVerbs();
80 if (numVerbs <= 0) {
81 return;
82 }
Chris Daltonb5391d92020-05-24 14:55:54 -060083
84 // First check if the path is large and/or simple enough that we can actually triangulate the
85 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
86 // curves, and then fill the internal polygons directly to the final render target, thus drawing
87 // the majority of pixels in a single render pass.
88 SkScalar scales[2];
89 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
90 const SkRect& bounds = fPath.getBounds();
Chris Dalton4328e922020-01-29 13:16:14 -070091 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
92 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
93 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Daltonb5391d92020-05-24 14:55:54 -060094 int numCountedCubics;
Chris Dalton04f9cda2020-04-23 10:04:25 -060095 // This will fail if the inner triangles do not form a simple polygon (e.g., self
96 // intersection, double winding).
Chris Daltone74cebe2020-09-24 09:32:54 -060097 if (this->prePrepareInnerPolygonTriangulation(args, &numCountedCubics)) {
98 if (numCountedCubics) {
99 // Always use indirect draws for cubics instead of tessellation here. Our goal in
100 // this mode is to maximize GPU performance, and the middle-out topology used by our
101 // indirect draws is easier on the rasterizer than a tessellated fan. There also
102 // seems to be a small amount of fixed tessellation overhead that this avoids.
103 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600104 }
Chris Dalton4328e922020-01-29 13:16:14 -0700105 return;
106 }
107 }
108
Chris Daltonb5391d92020-05-24 14:55:54 -0600109 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
110 // that contains both the inner triangles and the outer cubics, instead of using hardware
111 // tessellation. Also take this path if tessellation is not supported.
112 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
Chris Daltonb96995d2020-06-04 16:44:29 -0600113 if (drawTrianglesAsIndirectCubicDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600114 if (!drawTrianglesAsIndirectCubicDraw) {
115 this->prePrepareStencilTrianglesProgram(args);
116 }
117 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600118 return;
119 }
120
Chris Daltonb96995d2020-06-04 16:44:29 -0600121 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
Chris Daltone74cebe2020-09-24 09:32:54 -0600122 SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
Chris Daltonb96995d2020-06-04 16:44:29 -0600123
Chris Daltonb5391d92020-05-24 14:55:54 -0600124 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
125 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
126 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
127 // switches and draw calls.
Chris Dalton4328e922020-01-29 13:16:14 -0700128 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
129 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonb5391d92020-05-24 14:55:54 -0600130 if (rasterEdgeWork > 300 * 300) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600131 this->prePrepareStencilTrianglesProgram(args);
132 this->prePrepareStencilCubicsProgram<GrCubicTessellateShader>(args);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700133 return;
134 }
135
136 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltone74cebe2020-09-24 09:32:54 -0600137 this->prePrepareStencilCubicsProgram<GrWedgeTessellateShader>(args);
Chris Daltonb832ce62020-01-06 19:49:37 -0700138}
139
Chris Daltone74cebe2020-09-24 09:32:54 -0600140bool GrPathTessellateOp::prePrepareInnerPolygonTriangulation(const PrePrepareArgs& args,
Chris Daltonf5132a02020-04-27 23:40:03 -0600141 int* numCountedCurves) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600142 SkASSERT(!fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600143 SkASSERT(fTriangleVertexCount == 0);
144 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600145 SkASSERT(!fDoFillTriangleBuffer);
146
147 using GrTriangulator::Mode;
148
Chris Dalton04f9cda2020-04-23 10:04:25 -0600149 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
Chris Daltone74cebe2020-09-24 09:32:54 -0600150 args.fInnerTriangleAllocator,
151 Mode::kSimpleInnerPolygons,
Chris Dalton04f9cda2020-04-23 10:04:25 -0600152 numCountedCurves);
153 if (fTriangleVertexCount == 0) {
154 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
155 // simple.
156 return false;
157 }
Chris Daltonb96995d2020-06-04 16:44:29 -0600158 if (((OpFlags::kStencilOnly | OpFlags::kWireframe) & fOpFlags) ||
159 GrAAType::kCoverage == fAAType ||
Chris Daltone74cebe2020-09-24 09:32:54 -0600160 (args.fClip && args.fClip->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600161 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
162 // can't fill the inner polygon directly. Indicate that these triangles need to be
163 // stencilled.
Chris Daltone74cebe2020-09-24 09:32:54 -0600164 this->prePrepareStencilTrianglesProgram(args);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600165 }
Chris Daltonb96995d2020-06-04 16:44:29 -0600166 if (!(OpFlags::kStencilOnly & fOpFlags)) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600167 fDoFillTriangleBuffer = true;
168 }
169 return true;
170}
171
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600172// Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
173constexpr static GrUserStencilSettings kIncrDecrStencil(
174 GrUserStencilSettings::StaticInitSeparate<
175 0x0000, 0x0000,
176 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
177 0xffff, 0xffff,
178 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
179 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
180 0xffff, 0xffff>());
181
182// Inverts the bottom stencil bit. Used for "even/odd" fill.
183constexpr static GrUserStencilSettings kInvertStencil(
184 GrUserStencilSettings::StaticInit<
185 0x0000,
186 GrUserStencilTest::kAlwaysIfInClip,
187 0xffff,
188 GrUserStencilOp::kInvert,
189 GrUserStencilOp::kKeep,
190 0x0001>());
191
192constexpr static const GrUserStencilSettings* stencil_pass_settings(SkPathFillType fillType) {
193 return (fillType == SkPathFillType::kWinding) ? &kIncrDecrStencil : &kInvertStencil;
194}
195
Chris Daltone74cebe2020-09-24 09:32:54 -0600196void GrPathTessellateOp::prePrepareStencilTrianglesProgram(const PrePrepareArgs& args) {
197 SkASSERT(!fStencilTrianglesProgram);
198 auto* shader = args.fArena->make<GrStencilTriangleShader>(fViewMatrix);
199 this->prePrepareSharedStencilPipeline(args);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600200 fStencilTrianglesProgram = GrPathShader::MakeProgramInfo(
201 shader, args.fArena, args.fWriteView, fSharedStencilPipeline, *args.fDstProxfView,
202 args.fXferBarrierFlags, stencil_pass_settings(fPath.getFillType()), *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600203}
204
205template<typename ShaderType>
206void GrPathTessellateOp::prePrepareStencilCubicsProgram(const PrePrepareArgs& args) {
207 SkASSERT(!fStencilCubicsProgram);
208 auto* shader = args.fArena->make<ShaderType>(fViewMatrix);
209 this->prePrepareSharedStencilPipeline(args);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600210 fStencilCubicsProgram = GrPathShader::MakeProgramInfo(
211 shader, args.fArena, args.fWriteView, fSharedStencilPipeline, *args.fDstProxfView,
212 args.fXferBarrierFlags, stencil_pass_settings(fPath.getFillType()), *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600213}
214
215void GrPathTessellateOp::prePrepareSharedStencilPipeline(const PrePrepareArgs& args) {
216 if (fSharedStencilPipeline) {
217 return;
218 }
219
Chris Daltone74cebe2020-09-24 09:32:54 -0600220 GrPipeline::InitArgs initArgs;
221 if (GrAAType::kNone != fAAType) {
222 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
223 }
224 if (args.fCaps->wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
225 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
226 }
227 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
228 SkPathFillType::kEvenOdd == fPath.getFillType());
Chris Daltone74cebe2020-09-24 09:32:54 -0600229 initArgs.fCaps = args.fCaps;
230 const auto& hardClip = (args.fClip) ? args.fClip->hardClip() : GrAppliedHardClip::Disabled();
231 fSharedStencilPipeline = args.fArena->make<GrPipeline>(
232 initArgs, GrDisableColorXPFactory::MakeXferProcessor(), hardClip);
233}
234
235void GrPathTessellateOp::onPrepare(GrOpFlushState* flushState) {
236 int numVerbs = fPath.countVerbs();
237 if (numVerbs <= 0) {
238 return;
239 }
240
241 if (!fSharedStencilPipeline) {
242 // Nothing has been prePrepared yet. Do it now.
243 GrEagerDynamicVertexAllocator innerTriangleAllocator(flushState, &fTriangleBuffer,
244 &fBaseTriangleVertex);
245 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
246 flushState->appliedClip(), &flushState->dstProxyView(),
247 flushState->renderPassBarriers(), &flushState->caps(),
248 &innerTriangleAllocator});
249 }
250
251 if (fTriangleVertexCount != 0) {
252 // prePreparePrograms was able to generate an inner polygon triangulation. It will exist in
253 // either fOffThreadInnerTriangulation or fTriangleBuffer exclusively.
254 SkASSERT(SkToBool(fOffThreadInnerTriangulation) != SkToBool(fTriangleBuffer));
255 if (fOffThreadInnerTriangulation) {
256 // DDL generated the triangle buffer data off thread. Copy it to GPU.
257 void* data = flushState->makeVertexSpace(sizeof(SkPoint), fTriangleVertexCount,
258 &fTriangleBuffer, &fBaseTriangleVertex);
259 memcpy(data, fOffThreadInnerTriangulation, fTriangleVertexCount * sizeof(SkPoint));
260 }
261 if (fStencilCubicsProgram) {
262 // We always use indirect draws for inner-polygon-triangulation mode instead of
263 // tessellation.
264 SkASSERT(GrPrimitiveType::kPatches !=
265 fStencilCubicsProgram->primProc().cast<GrStencilPathShader>().primitiveType());
266 GrResolveLevelCounter resolveLevelCounter;
267 resolveLevelCounter.reset(fPath, fViewMatrix, kLinearizationIntolerance);
268 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
269 }
270 return;
271 }
272
273 SkASSERT(fStencilCubicsProgram);
274 const auto& stencilCubicsShader = fStencilCubicsProgram->primProc().cast<GrPathShader>();
275
276 if (stencilCubicsShader.primitiveType() != GrPrimitiveType::kPatches) {
277 // Outer cubics need indirect draws.
278 GrResolveLevelCounter resolveLevelCounter;
279 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter);
280 return;
281 }
282
283 if (stencilCubicsShader.tessellationPatchVertexCount() == 4) {
284 // Triangles and tessellated curves will be drawn separately.
285 this->prepareMiddleOutTrianglesAndCubics(flushState);
286 return;
287 }
288
289 // We are drawing tessellated wedges.
290 SkASSERT(stencilCubicsShader.tessellationPatchVertexCount() == 5);
291 this->prepareTessellatedCubicWedges(flushState);
292}
293
Chris Dalton078f8752020-07-30 19:50:46 -0600294void GrPathTessellateOp::prepareMiddleOutTrianglesAndCubics(
Chris Daltone74cebe2020-09-24 09:32:54 -0600295 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter) {
296 SkASSERT(fStencilCubicsProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600297 SkASSERT(!fTriangleBuffer);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600298 SkASSERT(!fDoFillTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600299 SkASSERT(!fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600300 SkASSERT(!fIndirectDrawBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600301 SkASSERT(fTriangleVertexCount == 0);
302 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600303
Chris Daltonf5132a02020-04-27 23:40:03 -0600304 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Daltonb5391d92020-05-24 14:55:54 -0600305 int maxInnerTriangles = fPath.countVerbs() - 1;
306 int maxCubics = fPath.countVerbs();
Chris Dalton42915c22020-04-22 16:24:43 -0600307
Chris Daltonb5391d92020-05-24 14:55:54 -0600308 SkPoint* vertexData;
309 int vertexAdvancePerTriangle;
Chris Daltone74cebe2020-09-24 09:32:54 -0600310 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600311 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
312 SkASSERT(resolveLevelCounter);
313 vertexAdvancePerTriangle = 4;
314 int baseTriangleInstance;
315 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
316 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
317 &baseTriangleInstance));
318 fBaseCubicVertex = baseTriangleInstance * 4;
319 } else {
320 // Allocate the triangles as normal 3-point instances in the triangle buffer.
321 vertexAdvancePerTriangle = 3;
322 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
323 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
324 }
Chris Dalton42915c22020-04-22 16:24:43 -0600325 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600326 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600327 }
Chris Dalton42915c22020-04-22 16:24:43 -0600328
Chris Daltonb5391d92020-05-24 14:55:54 -0600329 GrVectorXform xform(fViewMatrix);
330 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
331 fPath.countVerbs());
332 if (resolveLevelCounter) {
333 resolveLevelCounter->reset();
334 }
335 int numCountedCurves = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600336 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
337 switch (verb) {
338 case SkPathVerb::kMove:
Chris Daltonb5391d92020-05-24 14:55:54 -0600339 middleOut.closeAndMove(pts[0]);
Chris Daltonf5132a02020-04-27 23:40:03 -0600340 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600341 case SkPathVerb::kLine:
342 middleOut.pushVertex(pts[1]);
343 break;
344 case SkPathVerb::kQuad:
345 middleOut.pushVertex(pts[2]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600346 if (resolveLevelCounter) {
347 // Quadratics get converted to cubics before rendering.
348 resolveLevelCounter->countCubic(GrWangsFormula::quadratic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600349 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600350 break;
351 }
352 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600353 break;
354 case SkPathVerb::kCubic:
355 middleOut.pushVertex(pts[3]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600356 if (resolveLevelCounter) {
357 resolveLevelCounter->countCubic(GrWangsFormula::cubic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600358 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600359 break;
360 }
361 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600362 break;
363 case SkPathVerb::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600364 middleOut.close();
365 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600366 case SkPathVerb::kConic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600367 SkUNREACHABLE;
Chris Daltonf5132a02020-04-27 23:40:03 -0600368 }
Chris Dalton42915c22020-04-22 16:24:43 -0600369 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600370 int triangleCount = middleOut.close();
371 SkASSERT(triangleCount <= maxInnerTriangles);
Chris Dalton42915c22020-04-22 16:24:43 -0600372
Chris Daltone74cebe2020-09-24 09:32:54 -0600373 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600374 SkASSERT(resolveLevelCounter);
375 int totalInstanceCount = triangleCount + resolveLevelCounter->totalCubicInstanceCount();
376 SkASSERT(vertexAdvancePerTriangle == 4);
377 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
378 sizeof(SkPoint) * 4);
379 if (totalInstanceCount) {
380 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
381 triangleCount);
382 }
383 } else {
384 SkASSERT(vertexAdvancePerTriangle == 3);
385 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
386 fTriangleVertexCount = triangleCount * 3;
Chris Daltonb5391d92020-05-24 14:55:54 -0600387 if (resolveLevelCounter) {
388 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
389 } else {
390 this->prepareTessellatedOuterCubics(target, numCountedCurves);
391 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600392 }
Chris Dalton42915c22020-04-22 16:24:43 -0600393}
394
395static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
396 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
397 return (b - a) * T + a;
398}
399
Chris Daltonf7a33072020-05-01 10:33:08 -0600400static void line2cubic(const SkPoint& p0, const SkPoint& p1, SkPoint* out) {
401 out[0] = p0;
402 out[1] = lerp(p0, p1, 1/3.f);
403 out[2] = lerp(p0, p1, 2/3.f);
404 out[3] = p1;
Chris Dalton42915c22020-04-22 16:24:43 -0600405}
406
Chris Daltonf7a33072020-05-01 10:33:08 -0600407static void quad2cubic(const SkPoint pts[], SkPoint* out) {
408 out[0] = pts[0];
409 out[1] = lerp(pts[0], pts[1], 2/3.f);
410 out[2] = lerp(pts[1], pts[2], 1/3.f);
411 out[3] = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600412}
413
Chris Dalton078f8752020-07-30 19:50:46 -0600414void GrPathTessellateOp::prepareIndirectOuterCubics(
Chris Daltonb5391d92020-05-24 14:55:54 -0600415 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
416 SkASSERT(resolveLevelCounter.totalCubicInstanceCount() >= 0);
417 if (resolveLevelCounter.totalCubicInstanceCount() == 0) {
418 return;
419 }
420 // Allocate a buffer to store the cubic data.
421 SkPoint* cubicData;
422 int baseInstance;
423 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
424 sizeof(SkPoint) * 4, resolveLevelCounter.totalCubicInstanceCount(), &fCubicBuffer,
425 &baseInstance));
426 if (!cubicData) {
427 return;
428 }
429 fBaseCubicVertex = baseInstance * 4;
430 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
431 /*numTrianglesAtBeginningOfData=*/0);
432}
433
Chris Dalton078f8752020-07-30 19:50:46 -0600434void GrPathTessellateOp::prepareIndirectOuterCubicsAndTriangles(
Chris Daltonb5391d92020-05-24 14:55:54 -0600435 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
436 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600437 SkASSERT(target->caps().drawInstancedSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600438 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalCubicInstanceCount() > 0);
Chris Daltone74cebe2020-09-24 09:32:54 -0600439 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600440 SkASSERT(cubicData);
Chris Daltone74cebe2020-09-24 09:32:54 -0600441 SkASSERT(fCubicVertexCount == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600442
Chris Dalton1443c9d2020-05-27 09:43:34 -0600443 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
444 target->resourceProvider());
445 if (!fIndirectIndexBuffer) {
446 return;
447 }
448
Chris Daltonb5391d92020-05-24 14:55:54 -0600449 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
450 // vertex on an instance boundary in order to accommodate this.
451 SkASSERT(fBaseCubicVertex % 4 == 0);
452 int baseInstance = fBaseCubicVertex >> 2;
453
454 // Start preparing the indirect draw buffer.
455 fIndirectDrawCount = resolveLevelCounter.totalCubicIndirectDrawCount();
456 if (numTrianglesAtBeginningOfData) {
457 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
458 }
459
460 // Allocate space for the GrDrawIndexedIndirectCommand structs.
461 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
462 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
463 if (!indirectData) {
464 SkASSERT(!fIndirectDrawBuffer);
465 return;
466 }
467
468 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
469 // location at each resolve level.
470 SkPoint* instanceLocations[kMaxResolveLevel + 1];
471 int indirectIdx = 0;
472 int runningInstanceCount = 0;
473 if (numTrianglesAtBeginningOfData) {
474 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
475 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
476 // emit the triangles [P0, P1, P2] from these 4-point instances.
477 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
478 numTrianglesAtBeginningOfData, baseInstance);
479 indirectIdx = 1;
480 runningInstanceCount = numTrianglesAtBeginningOfData;
481 }
482 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
Chris Daltona6858ae2020-06-25 07:04:33 -0600483 int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel];
484 if (!instanceCountAtCurrLevel) {
485 SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
486 continue;
Chris Daltonb5391d92020-05-24 14:55:54 -0600487 }
Chris Daltona6858ae2020-06-25 07:04:33 -0600488 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
489 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
490 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
491 runningInstanceCount += instanceCountAtCurrLevel;
Chris Daltonb5391d92020-05-24 14:55:54 -0600492 }
493
494#ifdef SK_DEBUG
495 SkASSERT(indirectIdx == fIndirectDrawCount);
496 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
497 resolveLevelCounter.totalCubicInstanceCount());
498 SkASSERT(fIndirectDrawCount > 0);
499
500 SkPoint* endLocations[kMaxResolveLevel + 1];
Chris Daltona6858ae2020-06-25 07:04:33 -0600501 int lastResolveLevel = 0;
502 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
503 if (!instanceLocations[resolveLevel]) {
504 endLocations[resolveLevel] = nullptr;
505 continue;
506 }
507 endLocations[lastResolveLevel] = instanceLocations[resolveLevel];
508 lastResolveLevel = resolveLevel;
509 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600510 int totalInstanceCount = numTrianglesAtBeginningOfData +
511 resolveLevelCounter.totalCubicInstanceCount();
Chris Daltona6858ae2020-06-25 07:04:33 -0600512 endLocations[lastResolveLevel] = cubicData + totalInstanceCount * 4;
Chris Daltonb5391d92020-05-24 14:55:54 -0600513#endif
514
515 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
516
517 if (resolveLevelCounter.totalCubicInstanceCount()) {
518 GrVectorXform xform(fViewMatrix);
519 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
520 int level;
521 switch (verb) {
522 default:
523 continue;
524 case SkPathVerb::kQuad:
Chris Daltonb96995d2020-06-04 16:44:29 -0600525 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600526 if (level == 0) {
527 continue;
528 }
529 level = std::min(level, kMaxResolveLevel);
530 quad2cubic(pts, instanceLocations[level]);
531 break;
532 case SkPathVerb::kCubic:
Chris Daltonb96995d2020-06-04 16:44:29 -0600533 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600534 if (level == 0) {
535 continue;
536 }
537 level = std::min(level, kMaxResolveLevel);
538 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
539 break;
540 }
541 instanceLocations[level] += 4;
542 fCubicVertexCount += 4;
543 }
544 }
545
546#ifdef SK_DEBUG
547 for (int i = 1; i <= kMaxResolveLevel; ++i) {
548 SkASSERT(instanceLocations[i] == endLocations[i]);
549 }
550 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
551 resolveLevelCounter.totalCubicInstanceCount()) * 4);
552#endif
Chris Daltonb5391d92020-05-24 14:55:54 -0600553}
554
Chris Dalton078f8752020-07-30 19:50:46 -0600555void GrPathTessellateOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
Chris Daltonb5391d92020-05-24 14:55:54 -0600556 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600557 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600558 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600559 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600560 SkASSERT(fStencilCubicsProgram);
561 SkASSERT(fCubicVertexCount == 0);
Chris Dalton42915c22020-04-22 16:24:43 -0600562
563 if (numCountedCurves == 0) {
564 return;
565 }
566
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600567 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600568 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600569 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600570 return;
571 }
Chris Dalton42915c22020-04-22 16:24:43 -0600572
Chris Daltonf7a33072020-05-01 10:33:08 -0600573 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
574 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600575 default:
576 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600577 case SkPathVerb::kQuad:
578 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
579 quad2cubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600580 break;
581 case SkPathVerb::kCubic:
582 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
583 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600584 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600585 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600586 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600587 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600588 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
Chris Dalton42915c22020-04-22 16:24:43 -0600589}
590
Chris Dalton078f8752020-07-30 19:50:46 -0600591void GrPathTessellateOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600592 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600593 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600594 SkASSERT(fStencilCubicsProgram);
595 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600596
Chris Dalton42915c22020-04-22 16:24:43 -0600597 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
598 // Each wedge has 5 vertices.
599 int maxVertices = (fPath.countVerbs() + 1) * 5;
600
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600601 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600602 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
603 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600604 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600605 }
Chris Dalton42915c22020-04-22 16:24:43 -0600606
607 GrMidpointContourParser parser(fPath);
608 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600609 SkPoint midpoint = parser.currentMidpoint();
610 SkPoint startPoint = {0, 0};
611 SkPoint lastPoint = startPoint;
612 for (auto [verb, pts, w] : parser.currentContour()) {
613 switch (verb) {
614 case SkPathVerb::kMove:
615 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600616 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600617 case SkPathVerb::kClose:
618 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600619 case SkPathVerb::kLine:
Chris Daltonf7a33072020-05-01 10:33:08 -0600620 line2cubic(pts[0], pts[1], vertexData + fCubicVertexCount);
621 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600622 break;
623 case SkPathVerb::kQuad:
Chris Daltonf7a33072020-05-01 10:33:08 -0600624 quad2cubic(pts, vertexData + fCubicVertexCount);
625 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600626 break;
627 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600628 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
629 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600630 break;
631 case SkPathVerb::kConic:
632 SkUNREACHABLE;
633 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600634 vertexData[fCubicVertexCount + 4] = midpoint;
635 fCubicVertexCount += 5;
636 }
637 if (lastPoint != startPoint) {
638 line2cubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
639 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600640 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600641 }
642 }
643
Chris Dalton04f9cda2020-04-23 10:04:25 -0600644 vertexAlloc.unlock(fCubicVertexCount);
Chris Dalton42915c22020-04-22 16:24:43 -0600645}
646
Chris Dalton078f8752020-07-30 19:50:46 -0600647void GrPathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600648 this->drawStencilPass(flushState);
Chris Daltonb96995d2020-06-04 16:44:29 -0600649 if (!(OpFlags::kStencilOnly & fOpFlags)) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600650 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700651 }
652}
653
Chris Dalton078f8752020-07-30 19:50:46 -0600654void GrPathTessellateOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600655 if (fStencilTrianglesProgram && fTriangleVertexCount > 0) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600656 SkASSERT(fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600657 flushState->bindPipelineAndScissorClip(*fStencilTrianglesProgram, this->bounds());
Chris Dalton85138672020-07-24 08:47:03 -0600658 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600659 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700660 }
661
Chris Daltone74cebe2020-09-24 09:32:54 -0600662 if (fCubicVertexCount > 0) {
663 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600664 SkASSERT(fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600665 flushState->bindPipelineAndScissorClip(*fStencilCubicsProgram, this->bounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600666 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600667 SkASSERT(fIndirectIndexBuffer);
Greg Daniel426274b2020-07-20 11:37:38 -0400668 flushState->bindBuffers(fIndirectIndexBuffer, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600669 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
670 fIndirectDrawCount);
671 } else {
Greg Daniel426274b2020-07-20 11:37:38 -0400672 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600673 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
674 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
675 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
676 }
677 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700678 }
679}
680
Chris Dalton078f8752020-07-30 19:50:46 -0600681void GrPathTessellateOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700682 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
683 // to zero; discards immediately on stencil values of zero.
684 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
685 // samples already inside the clip.
686 constexpr static GrUserStencilSettings kTestAndResetStencil(
687 GrUserStencilSettings::StaticInit<
688 0x0000,
689 GrUserStencilTest::kNotEqual,
690 0xffff,
691 GrUserStencilOp::kZero,
692 GrUserStencilOp::kKeep,
693 0xffff>());
694
695 GrPipeline::InitArgs initArgs;
696 if (GrAAType::kNone != fAAType) {
Chris Daltond72cb4c2020-07-16 17:50:17 -0600697 if (flushState->proxy()->numSamples() == 1) {
698 // We are mixed sampled. We need to either enable conservative raster (preferred) or
699 // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
700 // the cover geometry, the stencil test is still multisampled and will still produce
701 // smooth results.)
Chris Daltonb832ce62020-01-06 19:49:37 -0700702 SkASSERT(GrAAType::kCoverage == fAAType);
Chris Daltond72cb4c2020-07-16 17:50:17 -0600703 if (flushState->caps().conservativeRasterSupport()) {
704 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
705 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
706 }
707 } else {
708 // We are standard MSAA. Leave MSAA enabled for the cover geometry.
709 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
Chris Daltonb832ce62020-01-06 19:49:37 -0700710 }
711 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600712 initArgs.fCaps = &flushState->caps();
713 initArgs.fDstProxyView = flushState->drawOpArgs().dstProxyView();
714 initArgs.fWriteSwizzle = flushState->drawOpArgs().writeSwizzle();
715 GrPipeline pipeline(initArgs, std::move(fProcessors), flushState->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700716
Chris Dalton04f9cda2020-04-23 10:04:25 -0600717 if (fDoFillTriangleBuffer) {
718 SkASSERT(fTriangleBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700719
Chris Dalton04f9cda2020-04-23 10:04:25 -0600720 // These are a twist on the standard red book stencil settings that allow us to fill the
Chris Dalton4328e922020-01-29 13:16:14 -0700721 // inner polygon directly to the final render target. At this point, the curves are already
722 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
723 // not affected by any curves and we fill the path in directly. If the stencil value is
724 // nonzero, then we don't fill and instead continue the standard red book stencil process.
725 //
726 // NOTE: These settings are currently incompatible with a stencil clip.
727 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
728 GrUserStencilSettings::StaticInitSeparate<
729 0x0000, 0x0000,
730 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
731 0xffff, 0xffff,
732 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
733 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
734 0xffff, 0xffff>());
735
736 constexpr static GrUserStencilSettings kFillOrInvertStencil(
737 GrUserStencilSettings::StaticInit<
738 0x0000,
739 GrUserStencilTest::kEqual,
740 0xffff,
741 GrUserStencilOp::kKeep,
742 GrUserStencilOp::kZero,
743 0xffff>());
744
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600745 const GrUserStencilSettings* stencil;
Chris Daltone74cebe2020-09-24 09:32:54 -0600746 if (fStencilTrianglesProgram) {
Chris Dalton4328e922020-01-29 13:16:14 -0700747 // The path was already stencilled. Here we just need to do a cover pass.
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600748 stencil = &kTestAndResetStencil;
Chris Daltone74cebe2020-09-24 09:32:54 -0600749 } else if (fCubicVertexCount == 0) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600750 // There are no stencilled curves. We can ignore stencil and fill the path directly.
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600751 stencil = &GrUserStencilSettings::kUnused;
Chris Dalton4328e922020-01-29 13:16:14 -0700752 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
753 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
754 SkASSERT(!pipeline.hasStencilClip());
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600755 stencil = &kFillOrIncrDecrStencil;
Chris Dalton4328e922020-01-29 13:16:14 -0700756 } else {
757 // Fill in the path pixels not touched by curves, invert stencil otherwise.
758 SkASSERT(!pipeline.hasStencilClip());
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600759 stencil = &kFillOrInvertStencil;
Chris Dalton4328e922020-01-29 13:16:14 -0700760 }
Chris Dalton4328e922020-01-29 13:16:14 -0700761
Chris Daltone74cebe2020-09-24 09:32:54 -0600762 GrFillTriangleShader fillTrianglesShader(fViewMatrix, fColor);
763 auto* fillTrianglesProgram = GrPathShader::MakeProgramInfo(
764 &fillTrianglesShader, flushState->allocator(), flushState->writeView(), &pipeline,
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600765 flushState->dstProxyView(), flushState->renderPassBarriers(), stencil,
766 flushState->caps());
Chris Daltone74cebe2020-09-24 09:32:54 -0600767 flushState->bindPipelineAndScissorClip(*fillTrianglesProgram, this->bounds());
768 flushState->bindTextures(fillTrianglesShader, nullptr, pipeline);
Greg Daniel426274b2020-07-20 11:37:38 -0400769 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600770 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600771
Chris Daltone74cebe2020-09-24 09:32:54 -0600772 if (fCubicVertexCount > 0) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600773 SkASSERT(fCubicBuffer);
774
Chris Dalton4328e922020-01-29 13:16:14 -0700775 // At this point, every pixel is filled in except the ones touched by curves. Issue a
776 // final cover pass over the curves by drawing their convex hulls. This will fill in any
777 // remaining samples and reset the stencil buffer.
Chris Daltone74cebe2020-09-24 09:32:54 -0600778 GrFillCubicHullShader fillCubicHullsShader(fViewMatrix, fColor);
779 auto* fillCubicHullsProgram = GrPathShader::MakeProgramInfo(
780 &fillCubicHullsShader, flushState->allocator(), flushState->writeView(),
781 &pipeline, flushState->dstProxyView(), flushState->renderPassBarriers(),
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600782 &kTestAndResetStencil, flushState->caps());
Chris Daltone74cebe2020-09-24 09:32:54 -0600783 flushState->bindPipelineAndScissorClip(*fillCubicHullsProgram, this->bounds());
784 flushState->bindTextures(fillCubicHullsShader, nullptr, pipeline);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600785
786 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
787 // the base vertex on an instance boundary in order to accommodate this.
788 SkASSERT((fCubicVertexCount % 4) == 0);
789 SkASSERT((fBaseCubicVertex % 4) == 0);
Greg Daniel426274b2020-07-20 11:37:38 -0400790 flushState->bindBuffers(nullptr, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600791 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700792 }
Chris Dalton42915c22020-04-22 16:24:43 -0600793 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700794 }
Chris Dalton42915c22020-04-22 16:24:43 -0600795
Chris Dalton04f9cda2020-04-23 10:04:25 -0600796 // There are no triangles to fill. Just draw a bounding box.
Chris Dalton04f9cda2020-04-23 10:04:25 -0600797 GrFillBoundingBoxShader fillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds());
Chris Daltone74cebe2020-09-24 09:32:54 -0600798 auto* fillBoundingBoxProgram = GrPathShader::MakeProgramInfo(
799 &fillBoundingBoxShader, flushState->allocator(), flushState->writeView(),
800 &pipeline, flushState->dstProxyView(), flushState->renderPassBarriers(),
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600801 &kTestAndResetStencil, flushState->caps());
Chris Daltone74cebe2020-09-24 09:32:54 -0600802 flushState->bindPipelineAndScissorClip(*fillBoundingBoxProgram, this->bounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600803 flushState->bindTextures(fillBoundingBoxShader, nullptr, pipeline);
804 flushState->bindBuffers(nullptr, nullptr, nullptr);
805 flushState->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700806}