blob: 07dc51a7dc55d3b89d21058c0c4e6a71f1284042 [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 Dalton753c1b32020-11-02 12:00:19 -070015#include "src/gpu/geometry/GrPathUtils.h"
Chris Dalton5e1545f2020-09-25 16:24:03 -060016#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Chris Dalton4328e922020-01-29 13:16:14 -070017#include "src/gpu/tessellate/GrFillPathShader.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060018#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Dalton42915c22020-04-22 16:24:43 -060019#include "src/gpu/tessellate/GrMidpointContourParser.h"
Chris Daltonb5391d92020-05-24 14:55:54 -060020#include "src/gpu/tessellate/GrResolveLevelCounter.h"
Chris Daltonf9aea7f2020-01-21 11:19:26 -070021#include "src/gpu/tessellate/GrStencilPathShader.h"
Chris Daltonb96995d2020-06-04 16:44:29 -060022#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070023
Chris Daltonb96995d2020-06-04 16:44:29 -060024constexpr static float kLinearizationIntolerance =
25 GrTessellationPathRenderer::kLinearizationIntolerance;
26
27constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel;
28
29using OpFlags = GrTessellationPathRenderer::OpFlags;
Chris Daltonb5391d92020-05-24 14:55:54 -060030
Chris Dalton078f8752020-07-30 19:50:46 -060031GrPathTessellateOp::FixedFunctionFlags GrPathTessellateOp::fixedFunctionFlags() const {
Chris Daltonb832ce62020-01-06 19:49:37 -070032 auto flags = FixedFunctionFlags::kUsesStencil;
33 if (GrAAType::kNone != fAAType) {
34 flags |= FixedFunctionFlags::kUsesHWAA;
35 }
36 return flags;
37}
38
Chris Daltone74cebe2020-09-24 09:32:54 -060039namespace {
Robert Phillipsc655c3a2020-03-18 13:23:45 -040040
Chris Daltone74cebe2020-09-24 09:32:54 -060041class CpuTriangleAllocator : public GrEagerVertexAllocator {
42public:
43 CpuTriangleAllocator(SkArenaAlloc* arena, const SkPoint** data) : fArena(arena), fData(data) {}
44
45 void* lock(size_t stride, int eagerCount) override {
46 SkASSERT(!*fData);
47 SkASSERT(stride == sizeof(SkPoint));
48 SkPoint* data = fArena->makeArray<SkPoint>(eagerCount);
49 *fData = data;
50 return data;
51 }
52
53 void unlock(int actualCount) override { SkASSERT(*fData); }
54
55private:
56 SkArenaAlloc* const fArena;
57 const SkPoint** fData;
58};
59
60}
61
62void GrPathTessellateOp::onPrePrepare(GrRecordingContext* context,
Adlai Hollere2296f72020-11-19 13:41:26 -050063 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
Chris Daltone74cebe2020-09-24 09:32:54 -060064 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -050065 GrXferBarrierFlags renderPassXferBarriers,
66 GrLoadOp colorLoadOp) {
Chris Daltone74cebe2020-09-24 09:32:54 -060067 SkArenaAlloc* recordTimeAllocator = context->priv().recordTimeAllocator();
Chris Dalton5e1545f2020-09-25 16:24:03 -060068 GrAppliedHardClip hardClip = GrAppliedHardClip(
69 (clip) ? clip->hardClip() : GrAppliedHardClip::Disabled());
Chris Daltone74cebe2020-09-24 09:32:54 -060070 CpuTriangleAllocator cpuTriangleAllocator(recordTimeAllocator, &fOffThreadInnerTriangulation);
Chris Dalton5e1545f2020-09-25 16:24:03 -060071 PrePrepareArgs args{recordTimeAllocator, writeView, &hardClip, clip, &dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -050072 renderPassXferBarriers, colorLoadOp, context->priv().caps(),
73 &cpuTriangleAllocator};
Chris Dalton5e1545f2020-09-25 16:24:03 -060074
75 this->prePreparePrograms(args);
76
Chris Daltone74cebe2020-09-24 09:32:54 -060077 if (fStencilTrianglesProgram) {
78 context->priv().recordProgramInfo(fStencilTrianglesProgram);
79 }
80 if (fStencilCubicsProgram) {
81 context->priv().recordProgramInfo(fStencilCubicsProgram);
82 }
Chris Dalton5e1545f2020-09-25 16:24:03 -060083 if (fFillTrianglesProgram) {
84 context->priv().recordProgramInfo(fFillTrianglesProgram);
85 }
86 if (fFillPathProgram) {
87 context->priv().recordProgramInfo(fFillPathProgram);
88 }
Chris Daltone74cebe2020-09-24 09:32:54 -060089}
90
91void GrPathTessellateOp::prePreparePrograms(const PrePrepareArgs& args) {
Chris Dalton4328e922020-01-29 13:16:14 -070092 int numVerbs = fPath.countVerbs();
93 if (numVerbs <= 0) {
94 return;
95 }
Chris Daltonb5391d92020-05-24 14:55:54 -060096
97 // First check if the path is large and/or simple enough that we can actually triangulate the
98 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
99 // curves, and then fill the internal polygons directly to the final render target, thus drawing
100 // the majority of pixels in a single render pass.
101 SkScalar scales[2];
102 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
103 const SkRect& bounds = fPath.getBounds();
Chris Dalton4328e922020-01-29 13:16:14 -0700104 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
105 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
106 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700107 bool isLinear;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600108 // This will fail if the inner triangles do not form a simple polygon (e.g., self
109 // intersection, double winding).
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700110 if (this->prePrepareInnerPolygonTriangulation(args, &isLinear)) {
111 if (!isLinear) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600112 // Always use indirect draws for cubics instead of tessellation here. Our goal in
113 // this mode is to maximize GPU performance, and the middle-out topology used by our
114 // indirect draws is easier on the rasterizer than a tessellated fan. There also
115 // seems to be a small amount of fixed tessellation overhead that this avoids.
116 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600117 // We will need one final pass to cover the convex hulls of the cubics after
118 // drawing the inner triangles.
119 this->prePrepareFillCubicHullsProgram(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600120 }
Chris Dalton4328e922020-01-29 13:16:14 -0700121 return;
122 }
123 }
124
Chris Dalton5e1545f2020-09-25 16:24:03 -0600125 // If we didn't triangulate the inner fan then the fill program will be a simple bounding box.
126 this->prePrepareFillBoundingBoxProgram(args);
127
Chris Daltonb5391d92020-05-24 14:55:54 -0600128 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
129 // that contains both the inner triangles and the outer cubics, instead of using hardware
130 // tessellation. Also take this path if tessellation is not supported.
131 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
Chris Daltonb96995d2020-06-04 16:44:29 -0600132 if (drawTrianglesAsIndirectCubicDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600133 if (!drawTrianglesAsIndirectCubicDraw) {
134 this->prePrepareStencilTrianglesProgram(args);
135 }
136 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600137 return;
138 }
139
Chris Daltonb96995d2020-06-04 16:44:29 -0600140 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
Chris Daltone74cebe2020-09-24 09:32:54 -0600141 SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
Chris Daltonb96995d2020-06-04 16:44:29 -0600142
Chris Daltonb5391d92020-05-24 14:55:54 -0600143 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
144 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
145 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
146 // switches and draw calls.
Chris Dalton4328e922020-01-29 13:16:14 -0700147 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
148 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonb5391d92020-05-24 14:55:54 -0600149 if (rasterEdgeWork > 300 * 300) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600150 this->prePrepareStencilTrianglesProgram(args);
151 this->prePrepareStencilCubicsProgram<GrCubicTessellateShader>(args);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700152 return;
153 }
154
155 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltone74cebe2020-09-24 09:32:54 -0600156 this->prePrepareStencilCubicsProgram<GrWedgeTessellateShader>(args);
Chris Daltonb832ce62020-01-06 19:49:37 -0700157}
158
Chris Daltone74cebe2020-09-24 09:32:54 -0600159bool GrPathTessellateOp::prePrepareInnerPolygonTriangulation(const PrePrepareArgs& args,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700160 bool* isLinear) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600161 SkASSERT(!fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600162 SkASSERT(fTriangleVertexCount == 0);
163 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600164 SkASSERT(!fFillTrianglesProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600165
166 using GrTriangulator::Mode;
167
Chris Dalton04f9cda2020-04-23 10:04:25 -0600168 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
Chris Daltone74cebe2020-09-24 09:32:54 -0600169 args.fInnerTriangleAllocator,
170 Mode::kSimpleInnerPolygons,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700171 isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600172 if (fTriangleVertexCount == 0) {
173 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
174 // simple.
175 return false;
176 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600177 if ((fOpFlags & (OpFlags::kStencilOnly | OpFlags::kWireframe)) ||
Chris Daltonb96995d2020-06-04 16:44:29 -0600178 GrAAType::kCoverage == fAAType ||
Chris Daltone74cebe2020-09-24 09:32:54 -0600179 (args.fClip && args.fClip->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600180 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
181 // can't fill the inner polygon directly. Indicate that these triangles need to be
182 // stencilled.
Chris Daltone74cebe2020-09-24 09:32:54 -0600183 this->prePrepareStencilTrianglesProgram(args);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600184 }
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700185 this->prePrepareFillTrianglesProgram(args, *isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600186 return true;
187}
188
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600189// Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
190constexpr static GrUserStencilSettings kIncrDecrStencil(
191 GrUserStencilSettings::StaticInitSeparate<
192 0x0000, 0x0000,
193 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
194 0xffff, 0xffff,
195 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
196 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
197 0xffff, 0xffff>());
198
199// Inverts the bottom stencil bit. Used for "even/odd" fill.
200constexpr static GrUserStencilSettings kInvertStencil(
201 GrUserStencilSettings::StaticInit<
202 0x0000,
203 GrUserStencilTest::kAlwaysIfInClip,
204 0xffff,
205 GrUserStencilOp::kInvert,
206 GrUserStencilOp::kKeep,
207 0x0001>());
208
209constexpr static const GrUserStencilSettings* stencil_pass_settings(SkPathFillType fillType) {
210 return (fillType == SkPathFillType::kWinding) ? &kIncrDecrStencil : &kInvertStencil;
211}
212
Chris Daltone74cebe2020-09-24 09:32:54 -0600213void GrPathTessellateOp::prePrepareStencilTrianglesProgram(const PrePrepareArgs& args) {
214 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600215
216 this->prePreparePipelineForStencils(args);
217
Chris Daltone74cebe2020-09-24 09:32:54 -0600218 auto* shader = args.fArena->make<GrStencilTriangleShader>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600219 fStencilTrianglesProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600220 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500221 args.fXferBarrierFlags, args.fColorLoadOp, stencil_pass_settings(fPath.getFillType()),
222 *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600223}
224
225template<typename ShaderType>
226void GrPathTessellateOp::prePrepareStencilCubicsProgram(const PrePrepareArgs& args) {
227 SkASSERT(!fStencilCubicsProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600228
229 this->prePreparePipelineForStencils(args);
230
Chris Daltone74cebe2020-09-24 09:32:54 -0600231 auto* shader = args.fArena->make<ShaderType>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600232 fStencilCubicsProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600233 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500234 args.fXferBarrierFlags, args.fColorLoadOp, stencil_pass_settings(fPath.getFillType()),
235 *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600236}
237
Chris Dalton5e1545f2020-09-25 16:24:03 -0600238void GrPathTessellateOp::prePreparePipelineForStencils(const PrePrepareArgs& args) {
239 if (fPipelineForStencils) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600240 return;
241 }
242
Chris Daltone74cebe2020-09-24 09:32:54 -0600243 GrPipeline::InitArgs initArgs;
244 if (GrAAType::kNone != fAAType) {
245 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
246 }
247 if (args.fCaps->wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
248 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
249 }
250 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
251 SkPathFillType::kEvenOdd == fPath.getFillType());
Chris Daltone74cebe2020-09-24 09:32:54 -0600252 initArgs.fCaps = args.fCaps;
Chris Dalton5e1545f2020-09-25 16:24:03 -0600253 fPipelineForStencils = args.fArena->make<GrPipeline>(
254 initArgs, GrDisableColorXPFactory::MakeXferProcessor(), *args.fHardClip);
255}
256
257// Allows non-zero stencil values to pass and write a color, and resets the stencil value back to
258// zero; discards immediately on stencil values of zero.
259// NOTE: It's ok to not check the clip here because the previous stencil pass will have only written
260// to samples already inside the clip.
261constexpr static GrUserStencilSettings kTestAndResetStencil(
262 GrUserStencilSettings::StaticInit<
263 0x0000,
264 GrUserStencilTest::kNotEqual,
265 0xffff,
266 GrUserStencilOp::kZero,
267 GrUserStencilOp::kKeep,
268 0xffff>());
269
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700270void GrPathTessellateOp::prePrepareFillTrianglesProgram(const PrePrepareArgs& args, bool isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600271 SkASSERT(!fFillTrianglesProgram);
272
273 if (fOpFlags & OpFlags::kStencilOnly) {
274 return;
275 }
276
277 // These are a twist on the standard red book stencil settings that allow us to fill the inner
278 // polygon directly to the final render target. At this point, the curves are already stencilled
279 // in. So if the stencil value is zero, then it means the path at our sample is not affected by
280 // any curves and we fill the path in directly. If the stencil value is nonzero, then we don't
281 // fill and instead continue the standard red book stencil process.
282 //
283 // NOTE: These settings are currently incompatible with a stencil clip.
284 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
285 GrUserStencilSettings::StaticInitSeparate<
286 0x0000, 0x0000,
287 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
288 0xffff, 0xffff,
289 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
290 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
291 0xffff, 0xffff>());
292
293 constexpr static GrUserStencilSettings kFillOrInvertStencil(
294 GrUserStencilSettings::StaticInit<
295 0x0000,
296 GrUserStencilTest::kEqual,
297 0xffff,
298 GrUserStencilOp::kKeep,
299 GrUserStencilOp::kZero,
300 0xffff>());
301
302 this->prePreparePipelineForFills(args);
303
304 const GrUserStencilSettings* stencil;
305 if (fStencilTrianglesProgram) {
306 // The path was already stencilled. Here we just need to do a cover pass.
307 stencil = &kTestAndResetStencil;
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700308 } else if (isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600309 // There are no stencilled curves. We can ignore stencil and fill the path directly.
310 stencil = &GrUserStencilSettings::kUnused;
311 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
312 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
313 SkASSERT(!fPipelineForFills->hasStencilClip());
314 stencil = &kFillOrIncrDecrStencil;
315 } else {
316 // Fill in the path pixels not touched by curves, invert stencil otherwise.
317 SkASSERT(!fPipelineForFills->hasStencilClip());
318 stencil = &kFillOrInvertStencil;
319 }
320
321 auto* fillTriangleShader = args.fArena->make<GrFillTriangleShader>(fViewMatrix, fColor);
322 fFillTrianglesProgram = GrPathShader::MakeProgramInfo(
323 fillTriangleShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500324 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, stencil, *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600325}
326
327void GrPathTessellateOp::prePrepareFillCubicHullsProgram(const PrePrepareArgs& args) {
328 SkASSERT(!fFillPathProgram);
329
330 if (fOpFlags & OpFlags::kStencilOnly) {
331 return;
332 }
333
334 this->prePreparePipelineForFills(args);
335
336 auto* fillCubicHullsShader = args.fArena->make<GrFillCubicHullShader>(fViewMatrix, fColor);
337 fFillPathProgram = GrPathShader::MakeProgramInfo(
338 fillCubicHullsShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500339 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, &kTestAndResetStencil,
340 *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600341}
342
343void GrPathTessellateOp::prePrepareFillBoundingBoxProgram(const PrePrepareArgs& args) {
344 SkASSERT(!fFillPathProgram);
345
346 if (fOpFlags & OpFlags::kStencilOnly) {
347 return;
348 }
349
350 this->prePreparePipelineForFills(args);
351
352 auto* fillBoundingBoxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
353 fPath.getBounds());
354 fFillPathProgram = GrPathShader::MakeProgramInfo(
355 fillBoundingBoxShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500356 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, &kTestAndResetStencil,
357 *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600358}
359
360void GrPathTessellateOp::prePreparePipelineForFills(const PrePrepareArgs& args) {
361 SkASSERT(!(fOpFlags & OpFlags::kStencilOnly));
362
363 if (fPipelineForFills) {
364 return;
365 }
366
367 auto pipelineFlags = GrPipeline::InputFlags::kNone;
368 if (GrAAType::kNone != fAAType) {
Adlai Hollere2296f72020-11-19 13:41:26 -0500369 if (args.fWriteView.asRenderTargetProxy()->numSamples() == 1) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600370 // We are mixed sampled. We need to either enable conservative raster (preferred) or
371 // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
372 // the cover geometry, the stencil test is still multisampled and will still produce
373 // smooth results.)
374 SkASSERT(GrAAType::kCoverage == fAAType);
375 if (args.fCaps->conservativeRasterSupport()) {
376 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
377 pipelineFlags |= GrPipeline::InputFlags::kConservativeRaster;
378 }
379 } else {
380 // We are standard MSAA. Leave MSAA enabled for the cover geometry.
381 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
382 }
383 }
384
385 fPipelineForFills = GrSimpleMeshDrawOpHelper::CreatePipeline(
Adlai Hollere2296f72020-11-19 13:41:26 -0500386 args.fCaps, args.fArena, args.fWriteView.swizzle(), std::move(*args.fClip),
Chris Dalton5e1545f2020-09-25 16:24:03 -0600387 *args.fDstProxyView, std::move(fProcessors), pipelineFlags);
Chris Daltone74cebe2020-09-24 09:32:54 -0600388}
389
390void GrPathTessellateOp::onPrepare(GrOpFlushState* flushState) {
391 int numVerbs = fPath.countVerbs();
392 if (numVerbs <= 0) {
393 return;
394 }
395
Chris Dalton5e1545f2020-09-25 16:24:03 -0600396 if (!fPipelineForStencils && !fPipelineForFills) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600397 // Nothing has been prePrepared yet. Do it now.
398 GrEagerDynamicVertexAllocator innerTriangleAllocator(flushState, &fTriangleBuffer,
399 &fBaseTriangleVertex);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600400 GrAppliedHardClip hardClip = GrAppliedHardClip(flushState->appliedHardClip());
401 GrAppliedClip clip = flushState->detachAppliedClip();
402 PrePrepareArgs args{flushState->allocator(), flushState->writeView(), &hardClip,
403 &clip, &flushState->dstProxyView(),
Greg Daniel42dbca52020-11-20 10:22:43 -0500404 flushState->renderPassBarriers(), flushState->colorLoadOp(),
405 &flushState->caps(), &innerTriangleAllocator};
Chris Dalton5e1545f2020-09-25 16:24:03 -0600406 this->prePreparePrograms(args);
Chris Daltone74cebe2020-09-24 09:32:54 -0600407 }
408
409 if (fTriangleVertexCount != 0) {
410 // prePreparePrograms was able to generate an inner polygon triangulation. It will exist in
411 // either fOffThreadInnerTriangulation or fTriangleBuffer exclusively.
412 SkASSERT(SkToBool(fOffThreadInnerTriangulation) != SkToBool(fTriangleBuffer));
413 if (fOffThreadInnerTriangulation) {
414 // DDL generated the triangle buffer data off thread. Copy it to GPU.
415 void* data = flushState->makeVertexSpace(sizeof(SkPoint), fTriangleVertexCount,
416 &fTriangleBuffer, &fBaseTriangleVertex);
417 memcpy(data, fOffThreadInnerTriangulation, fTriangleVertexCount * sizeof(SkPoint));
418 }
419 if (fStencilCubicsProgram) {
420 // We always use indirect draws for inner-polygon-triangulation mode instead of
421 // tessellation.
422 SkASSERT(GrPrimitiveType::kPatches !=
423 fStencilCubicsProgram->primProc().cast<GrStencilPathShader>().primitiveType());
424 GrResolveLevelCounter resolveLevelCounter;
425 resolveLevelCounter.reset(fPath, fViewMatrix, kLinearizationIntolerance);
426 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
427 }
428 return;
429 }
430
431 SkASSERT(fStencilCubicsProgram);
432 const auto& stencilCubicsShader = fStencilCubicsProgram->primProc().cast<GrPathShader>();
433
434 if (stencilCubicsShader.primitiveType() != GrPrimitiveType::kPatches) {
435 // Outer cubics need indirect draws.
436 GrResolveLevelCounter resolveLevelCounter;
437 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter);
438 return;
439 }
440
441 if (stencilCubicsShader.tessellationPatchVertexCount() == 4) {
442 // Triangles and tessellated curves will be drawn separately.
443 this->prepareMiddleOutTrianglesAndCubics(flushState);
444 return;
445 }
446
447 // We are drawing tessellated wedges.
448 SkASSERT(stencilCubicsShader.tessellationPatchVertexCount() == 5);
449 this->prepareTessellatedCubicWedges(flushState);
450}
451
Chris Dalton078f8752020-07-30 19:50:46 -0600452void GrPathTessellateOp::prepareMiddleOutTrianglesAndCubics(
Chris Daltone74cebe2020-09-24 09:32:54 -0600453 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter) {
454 SkASSERT(fStencilCubicsProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600455 SkASSERT(!fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600456 SkASSERT(!fFillTrianglesProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600457 SkASSERT(!fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600458 SkASSERT(!fIndirectDrawBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600459 SkASSERT(fTriangleVertexCount == 0);
460 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600461
Chris Daltonf5132a02020-04-27 23:40:03 -0600462 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Daltonb5391d92020-05-24 14:55:54 -0600463 int maxInnerTriangles = fPath.countVerbs() - 1;
464 int maxCubics = fPath.countVerbs();
Chris Dalton42915c22020-04-22 16:24:43 -0600465
Chris Daltonb5391d92020-05-24 14:55:54 -0600466 SkPoint* vertexData;
467 int vertexAdvancePerTriangle;
Chris Daltone74cebe2020-09-24 09:32:54 -0600468 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600469 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
470 SkASSERT(resolveLevelCounter);
471 vertexAdvancePerTriangle = 4;
472 int baseTriangleInstance;
473 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
474 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
475 &baseTriangleInstance));
476 fBaseCubicVertex = baseTriangleInstance * 4;
477 } else {
478 // Allocate the triangles as normal 3-point instances in the triangle buffer.
479 vertexAdvancePerTriangle = 3;
480 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
481 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
482 }
Chris Dalton42915c22020-04-22 16:24:43 -0600483 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600484 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600485 }
Chris Dalton42915c22020-04-22 16:24:43 -0600486
Chris Daltonb5391d92020-05-24 14:55:54 -0600487 GrVectorXform xform(fViewMatrix);
488 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
489 fPath.countVerbs());
490 if (resolveLevelCounter) {
491 resolveLevelCounter->reset();
492 }
493 int numCountedCurves = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600494 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
495 switch (verb) {
496 case SkPathVerb::kMove:
Chris Daltonb5391d92020-05-24 14:55:54 -0600497 middleOut.closeAndMove(pts[0]);
Chris Daltonf5132a02020-04-27 23:40:03 -0600498 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600499 case SkPathVerb::kLine:
500 middleOut.pushVertex(pts[1]);
501 break;
502 case SkPathVerb::kQuad:
503 middleOut.pushVertex(pts[2]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600504 if (resolveLevelCounter) {
505 // Quadratics get converted to cubics before rendering.
506 resolveLevelCounter->countCubic(GrWangsFormula::quadratic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600507 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600508 break;
509 }
510 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600511 break;
512 case SkPathVerb::kCubic:
513 middleOut.pushVertex(pts[3]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600514 if (resolveLevelCounter) {
515 resolveLevelCounter->countCubic(GrWangsFormula::cubic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600516 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600517 break;
518 }
519 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600520 break;
521 case SkPathVerb::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600522 middleOut.close();
523 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600524 case SkPathVerb::kConic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600525 SkUNREACHABLE;
Chris Daltonf5132a02020-04-27 23:40:03 -0600526 }
Chris Dalton42915c22020-04-22 16:24:43 -0600527 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600528 int triangleCount = middleOut.close();
529 SkASSERT(triangleCount <= maxInnerTriangles);
Chris Dalton42915c22020-04-22 16:24:43 -0600530
Chris Daltone74cebe2020-09-24 09:32:54 -0600531 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600532 SkASSERT(resolveLevelCounter);
533 int totalInstanceCount = triangleCount + resolveLevelCounter->totalCubicInstanceCount();
534 SkASSERT(vertexAdvancePerTriangle == 4);
535 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
536 sizeof(SkPoint) * 4);
537 if (totalInstanceCount) {
538 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
539 triangleCount);
540 }
541 } else {
542 SkASSERT(vertexAdvancePerTriangle == 3);
543 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
544 fTriangleVertexCount = triangleCount * 3;
Chris Daltonb5391d92020-05-24 14:55:54 -0600545 if (resolveLevelCounter) {
546 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
547 } else {
548 this->prepareTessellatedOuterCubics(target, numCountedCurves);
549 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600550 }
Chris Dalton42915c22020-04-22 16:24:43 -0600551}
552
Chris Dalton078f8752020-07-30 19:50:46 -0600553void GrPathTessellateOp::prepareIndirectOuterCubics(
Chris Daltonb5391d92020-05-24 14:55:54 -0600554 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
555 SkASSERT(resolveLevelCounter.totalCubicInstanceCount() >= 0);
556 if (resolveLevelCounter.totalCubicInstanceCount() == 0) {
557 return;
558 }
559 // Allocate a buffer to store the cubic data.
560 SkPoint* cubicData;
561 int baseInstance;
562 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
563 sizeof(SkPoint) * 4, resolveLevelCounter.totalCubicInstanceCount(), &fCubicBuffer,
564 &baseInstance));
565 if (!cubicData) {
566 return;
567 }
568 fBaseCubicVertex = baseInstance * 4;
569 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
570 /*numTrianglesAtBeginningOfData=*/0);
571}
572
Chris Dalton078f8752020-07-30 19:50:46 -0600573void GrPathTessellateOp::prepareIndirectOuterCubicsAndTriangles(
Chris Daltonb5391d92020-05-24 14:55:54 -0600574 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
575 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600576 SkASSERT(target->caps().drawInstancedSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600577 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalCubicInstanceCount() > 0);
Chris Daltone74cebe2020-09-24 09:32:54 -0600578 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600579 SkASSERT(cubicData);
Chris Daltone74cebe2020-09-24 09:32:54 -0600580 SkASSERT(fCubicVertexCount == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600581
Chris Dalton1443c9d2020-05-27 09:43:34 -0600582 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
583 target->resourceProvider());
584 if (!fIndirectIndexBuffer) {
585 return;
586 }
587
Chris Daltonb5391d92020-05-24 14:55:54 -0600588 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
589 // vertex on an instance boundary in order to accommodate this.
590 SkASSERT(fBaseCubicVertex % 4 == 0);
591 int baseInstance = fBaseCubicVertex >> 2;
592
593 // Start preparing the indirect draw buffer.
594 fIndirectDrawCount = resolveLevelCounter.totalCubicIndirectDrawCount();
595 if (numTrianglesAtBeginningOfData) {
596 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
597 }
598
599 // Allocate space for the GrDrawIndexedIndirectCommand structs.
600 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
601 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
602 if (!indirectData) {
603 SkASSERT(!fIndirectDrawBuffer);
604 return;
605 }
606
607 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
608 // location at each resolve level.
609 SkPoint* instanceLocations[kMaxResolveLevel + 1];
610 int indirectIdx = 0;
611 int runningInstanceCount = 0;
612 if (numTrianglesAtBeginningOfData) {
613 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
614 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
615 // emit the triangles [P0, P1, P2] from these 4-point instances.
616 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
617 numTrianglesAtBeginningOfData, baseInstance);
618 indirectIdx = 1;
619 runningInstanceCount = numTrianglesAtBeginningOfData;
620 }
621 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
Chris Daltona6858ae2020-06-25 07:04:33 -0600622 int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel];
623 if (!instanceCountAtCurrLevel) {
624 SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
625 continue;
Chris Daltonb5391d92020-05-24 14:55:54 -0600626 }
Chris Daltona6858ae2020-06-25 07:04:33 -0600627 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
628 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
629 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
630 runningInstanceCount += instanceCountAtCurrLevel;
Chris Daltonb5391d92020-05-24 14:55:54 -0600631 }
632
633#ifdef SK_DEBUG
634 SkASSERT(indirectIdx == fIndirectDrawCount);
635 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
636 resolveLevelCounter.totalCubicInstanceCount());
637 SkASSERT(fIndirectDrawCount > 0);
638
639 SkPoint* endLocations[kMaxResolveLevel + 1];
Chris Daltona6858ae2020-06-25 07:04:33 -0600640 int lastResolveLevel = 0;
641 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
642 if (!instanceLocations[resolveLevel]) {
643 endLocations[resolveLevel] = nullptr;
644 continue;
645 }
646 endLocations[lastResolveLevel] = instanceLocations[resolveLevel];
647 lastResolveLevel = resolveLevel;
648 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600649 int totalInstanceCount = numTrianglesAtBeginningOfData +
650 resolveLevelCounter.totalCubicInstanceCount();
Chris Daltona6858ae2020-06-25 07:04:33 -0600651 endLocations[lastResolveLevel] = cubicData + totalInstanceCount * 4;
Chris Daltonb5391d92020-05-24 14:55:54 -0600652#endif
653
654 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
655
656 if (resolveLevelCounter.totalCubicInstanceCount()) {
657 GrVectorXform xform(fViewMatrix);
658 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
659 int level;
660 switch (verb) {
661 default:
662 continue;
663 case SkPathVerb::kQuad:
Chris Daltonb96995d2020-06-04 16:44:29 -0600664 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600665 if (level == 0) {
666 continue;
667 }
668 level = std::min(level, kMaxResolveLevel);
Chris Dalton753c1b32020-11-02 12:00:19 -0700669 GrPathUtils::convertQuadToCubic(pts, instanceLocations[level]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600670 break;
671 case SkPathVerb::kCubic:
Chris Daltonb96995d2020-06-04 16:44:29 -0600672 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600673 if (level == 0) {
674 continue;
675 }
676 level = std::min(level, kMaxResolveLevel);
677 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
678 break;
679 }
680 instanceLocations[level] += 4;
681 fCubicVertexCount += 4;
682 }
683 }
684
685#ifdef SK_DEBUG
686 for (int i = 1; i <= kMaxResolveLevel; ++i) {
687 SkASSERT(instanceLocations[i] == endLocations[i]);
688 }
689 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
690 resolveLevelCounter.totalCubicInstanceCount()) * 4);
691#endif
Chris Daltonb5391d92020-05-24 14:55:54 -0600692}
693
Chris Dalton078f8752020-07-30 19:50:46 -0600694void GrPathTessellateOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
Chris Daltonb5391d92020-05-24 14:55:54 -0600695 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600696 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600697 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600698 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600699 SkASSERT(fStencilCubicsProgram);
700 SkASSERT(fCubicVertexCount == 0);
Chris Dalton42915c22020-04-22 16:24:43 -0600701
702 if (numCountedCurves == 0) {
703 return;
704 }
705
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600706 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600707 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600708 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600709 return;
710 }
Chris Dalton42915c22020-04-22 16:24:43 -0600711
Chris Daltonf7a33072020-05-01 10:33:08 -0600712 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
713 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600714 default:
715 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600716 case SkPathVerb::kQuad:
717 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
Chris Dalton753c1b32020-11-02 12:00:19 -0700718 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600719 break;
720 case SkPathVerb::kCubic:
721 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
722 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600723 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600724 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600725 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600726 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600727 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
Chris Dalton42915c22020-04-22 16:24:43 -0600728}
729
Chris Dalton078f8752020-07-30 19:50:46 -0600730void GrPathTessellateOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600731 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600732 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600733 SkASSERT(fStencilCubicsProgram);
734 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600735
Chris Dalton42915c22020-04-22 16:24:43 -0600736 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
737 // Each wedge has 5 vertices.
738 int maxVertices = (fPath.countVerbs() + 1) * 5;
739
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600740 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600741 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
742 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600743 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600744 }
Chris Dalton42915c22020-04-22 16:24:43 -0600745
746 GrMidpointContourParser parser(fPath);
747 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600748 SkPoint midpoint = parser.currentMidpoint();
749 SkPoint startPoint = {0, 0};
750 SkPoint lastPoint = startPoint;
751 for (auto [verb, pts, w] : parser.currentContour()) {
752 switch (verb) {
753 case SkPathVerb::kMove:
754 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600755 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600756 case SkPathVerb::kClose:
757 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600758 case SkPathVerb::kLine:
Chris Dalton753c1b32020-11-02 12:00:19 -0700759 GrPathUtils::convertLineToCubic(pts[0], pts[1], vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600760 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600761 break;
762 case SkPathVerb::kQuad:
Chris Dalton753c1b32020-11-02 12:00:19 -0700763 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600764 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600765 break;
766 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600767 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
768 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600769 break;
770 case SkPathVerb::kConic:
771 SkUNREACHABLE;
772 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600773 vertexData[fCubicVertexCount + 4] = midpoint;
774 fCubicVertexCount += 5;
775 }
776 if (lastPoint != startPoint) {
Chris Dalton753c1b32020-11-02 12:00:19 -0700777 GrPathUtils::convertLineToCubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600778 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600779 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600780 }
781 }
782
Chris Dalton04f9cda2020-04-23 10:04:25 -0600783 vertexAlloc.unlock(fCubicVertexCount);
Chris Dalton42915c22020-04-22 16:24:43 -0600784}
785
Chris Dalton078f8752020-07-30 19:50:46 -0600786void GrPathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600787 this->drawStencilPass(flushState);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600788 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700789}
790
Chris Dalton078f8752020-07-30 19:50:46 -0600791void GrPathTessellateOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600792 if (fStencilTrianglesProgram && fTriangleVertexCount > 0) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600793 SkASSERT(fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600794 flushState->bindPipelineAndScissorClip(*fStencilTrianglesProgram, this->bounds());
Chris Dalton85138672020-07-24 08:47:03 -0600795 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600796 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700797 }
798
Chris Daltone74cebe2020-09-24 09:32:54 -0600799 if (fCubicVertexCount > 0) {
800 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600801 SkASSERT(fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600802 flushState->bindPipelineAndScissorClip(*fStencilCubicsProgram, this->bounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600803 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600804 SkASSERT(fIndirectIndexBuffer);
Greg Daniel426274b2020-07-20 11:37:38 -0400805 flushState->bindBuffers(fIndirectIndexBuffer, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600806 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
807 fIndirectDrawCount);
808 } else {
Greg Daniel426274b2020-07-20 11:37:38 -0400809 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600810 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
811 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
812 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
813 }
814 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700815 }
816}
817
Chris Dalton078f8752020-07-30 19:50:46 -0600818void GrPathTessellateOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600819 if (fFillTrianglesProgram) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600820 SkASSERT(fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600821 SkASSERT(fTriangleVertexCount > 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700822
Chris Dalton5e1545f2020-09-25 16:24:03 -0600823 // We have a triangulation of the path's inner polygon. This is the fast path. Fill those
824 // triangles directly to the screen.
825 flushState->bindPipelineAndScissorClip(*fFillTrianglesProgram, this->bounds());
826 flushState->bindTextures(fFillTrianglesProgram->primProc(), nullptr, *fPipelineForFills);
Greg Daniel426274b2020-07-20 11:37:38 -0400827 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600828 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600829
Chris Daltone74cebe2020-09-24 09:32:54 -0600830 if (fCubicVertexCount > 0) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600831 SkASSERT(fFillPathProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600832 SkASSERT(fCubicBuffer);
833
Chris Dalton5e1545f2020-09-25 16:24:03 -0600834 // At this point, every pixel is filled in except the ones touched by curves.
835 // fFillPathProgram will issue a final cover pass over the curves by drawing their
836 // convex hulls. This will fill in any remaining samples and reset the stencil buffer.
837 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
838 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600839
840 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
841 // the base vertex on an instance boundary in order to accommodate this.
842 SkASSERT((fCubicVertexCount % 4) == 0);
843 SkASSERT((fBaseCubicVertex % 4) == 0);
Greg Daniel426274b2020-07-20 11:37:38 -0400844 flushState->bindBuffers(nullptr, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600845 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700846 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600847 } else if (fFillPathProgram) {
848 // There are no triangles to fill. Just draw a bounding box.
849 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
850 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
851 flushState->bindBuffers(nullptr, nullptr, nullptr);
852 flushState->draw(4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700853 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700854}