blob: 6aa42d643a546c6aa1a94f083dc8c51235b45982 [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,
63 const GrSurfaceProxyView* writeView, GrAppliedClip* clip,
64 const GrXferProcessor::DstProxyView& dstProxyView,
65 GrXferBarrierFlags renderPassXferBarriers) {
66 SkArenaAlloc* recordTimeAllocator = context->priv().recordTimeAllocator();
Chris Dalton5e1545f2020-09-25 16:24:03 -060067 GrAppliedHardClip hardClip = GrAppliedHardClip(
68 (clip) ? clip->hardClip() : GrAppliedHardClip::Disabled());
Chris Daltone74cebe2020-09-24 09:32:54 -060069 CpuTriangleAllocator cpuTriangleAllocator(recordTimeAllocator, &fOffThreadInnerTriangulation);
Chris Dalton5e1545f2020-09-25 16:24:03 -060070 PrePrepareArgs args{recordTimeAllocator, writeView, &hardClip, clip, &dstProxyView,
71 renderPassXferBarriers, context->priv().caps(), &cpuTriangleAllocator};
72
73 this->prePreparePrograms(args);
74
Chris Daltone74cebe2020-09-24 09:32:54 -060075 if (fStencilTrianglesProgram) {
76 context->priv().recordProgramInfo(fStencilTrianglesProgram);
77 }
78 if (fStencilCubicsProgram) {
79 context->priv().recordProgramInfo(fStencilCubicsProgram);
80 }
Chris Dalton5e1545f2020-09-25 16:24:03 -060081 if (fFillTrianglesProgram) {
82 context->priv().recordProgramInfo(fFillTrianglesProgram);
83 }
84 if (fFillPathProgram) {
85 context->priv().recordProgramInfo(fFillPathProgram);
86 }
Chris Daltone74cebe2020-09-24 09:32:54 -060087}
88
89void GrPathTessellateOp::prePreparePrograms(const PrePrepareArgs& args) {
Chris Dalton4328e922020-01-29 13:16:14 -070090 int numVerbs = fPath.countVerbs();
91 if (numVerbs <= 0) {
92 return;
93 }
Chris Daltonb5391d92020-05-24 14:55:54 -060094
95 // First check if the path is large and/or simple enough that we can actually triangulate the
96 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
97 // curves, and then fill the internal polygons directly to the final render target, thus drawing
98 // the majority of pixels in a single render pass.
99 SkScalar scales[2];
100 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
101 const SkRect& bounds = fPath.getBounds();
Chris Dalton4328e922020-01-29 13:16:14 -0700102 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
103 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
104 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700105 bool isLinear;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600106 // This will fail if the inner triangles do not form a simple polygon (e.g., self
107 // intersection, double winding).
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700108 if (this->prePrepareInnerPolygonTriangulation(args, &isLinear)) {
109 if (!isLinear) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600110 // Always use indirect draws for cubics instead of tessellation here. Our goal in
111 // this mode is to maximize GPU performance, and the middle-out topology used by our
112 // indirect draws is easier on the rasterizer than a tessellated fan. There also
113 // seems to be a small amount of fixed tessellation overhead that this avoids.
114 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600115 // We will need one final pass to cover the convex hulls of the cubics after
116 // drawing the inner triangles.
117 this->prePrepareFillCubicHullsProgram(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600118 }
Chris Dalton4328e922020-01-29 13:16:14 -0700119 return;
120 }
121 }
122
Chris Dalton5e1545f2020-09-25 16:24:03 -0600123 // If we didn't triangulate the inner fan then the fill program will be a simple bounding box.
124 this->prePrepareFillBoundingBoxProgram(args);
125
Chris Daltonb5391d92020-05-24 14:55:54 -0600126 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
127 // that contains both the inner triangles and the outer cubics, instead of using hardware
128 // tessellation. Also take this path if tessellation is not supported.
129 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
Chris Daltonb96995d2020-06-04 16:44:29 -0600130 if (drawTrianglesAsIndirectCubicDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600131 if (!drawTrianglesAsIndirectCubicDraw) {
132 this->prePrepareStencilTrianglesProgram(args);
133 }
134 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600135 return;
136 }
137
Chris Daltonb96995d2020-06-04 16:44:29 -0600138 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
Chris Daltone74cebe2020-09-24 09:32:54 -0600139 SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
Chris Daltonb96995d2020-06-04 16:44:29 -0600140
Chris Daltonb5391d92020-05-24 14:55:54 -0600141 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
142 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
143 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
144 // switches and draw calls.
Chris Dalton4328e922020-01-29 13:16:14 -0700145 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
146 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonb5391d92020-05-24 14:55:54 -0600147 if (rasterEdgeWork > 300 * 300) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600148 this->prePrepareStencilTrianglesProgram(args);
149 this->prePrepareStencilCubicsProgram<GrCubicTessellateShader>(args);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700150 return;
151 }
152
153 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltone74cebe2020-09-24 09:32:54 -0600154 this->prePrepareStencilCubicsProgram<GrWedgeTessellateShader>(args);
Chris Daltonb832ce62020-01-06 19:49:37 -0700155}
156
Chris Daltone74cebe2020-09-24 09:32:54 -0600157bool GrPathTessellateOp::prePrepareInnerPolygonTriangulation(const PrePrepareArgs& args,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700158 bool* isLinear) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600159 SkASSERT(!fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600160 SkASSERT(fTriangleVertexCount == 0);
161 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600162 SkASSERT(!fFillTrianglesProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600163
164 using GrTriangulator::Mode;
165
Chris Dalton04f9cda2020-04-23 10:04:25 -0600166 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
Chris Daltone74cebe2020-09-24 09:32:54 -0600167 args.fInnerTriangleAllocator,
168 Mode::kSimpleInnerPolygons,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700169 isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600170 if (fTriangleVertexCount == 0) {
171 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
172 // simple.
173 return false;
174 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600175 if ((fOpFlags & (OpFlags::kStencilOnly | OpFlags::kWireframe)) ||
Chris Daltonb96995d2020-06-04 16:44:29 -0600176 GrAAType::kCoverage == fAAType ||
Chris Daltone74cebe2020-09-24 09:32:54 -0600177 (args.fClip && args.fClip->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600178 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
179 // can't fill the inner polygon directly. Indicate that these triangles need to be
180 // stencilled.
Chris Daltone74cebe2020-09-24 09:32:54 -0600181 this->prePrepareStencilTrianglesProgram(args);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600182 }
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700183 this->prePrepareFillTrianglesProgram(args, *isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600184 return true;
185}
186
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600187// Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
188constexpr static GrUserStencilSettings kIncrDecrStencil(
189 GrUserStencilSettings::StaticInitSeparate<
190 0x0000, 0x0000,
191 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
192 0xffff, 0xffff,
193 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
194 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
195 0xffff, 0xffff>());
196
197// Inverts the bottom stencil bit. Used for "even/odd" fill.
198constexpr static GrUserStencilSettings kInvertStencil(
199 GrUserStencilSettings::StaticInit<
200 0x0000,
201 GrUserStencilTest::kAlwaysIfInClip,
202 0xffff,
203 GrUserStencilOp::kInvert,
204 GrUserStencilOp::kKeep,
205 0x0001>());
206
207constexpr static const GrUserStencilSettings* stencil_pass_settings(SkPathFillType fillType) {
208 return (fillType == SkPathFillType::kWinding) ? &kIncrDecrStencil : &kInvertStencil;
209}
210
Chris Daltone74cebe2020-09-24 09:32:54 -0600211void GrPathTessellateOp::prePrepareStencilTrianglesProgram(const PrePrepareArgs& args) {
212 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600213
214 this->prePreparePipelineForStencils(args);
215
Chris Daltone74cebe2020-09-24 09:32:54 -0600216 auto* shader = args.fArena->make<GrStencilTriangleShader>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600217 fStencilTrianglesProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600218 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600219 args.fXferBarrierFlags, stencil_pass_settings(fPath.getFillType()), *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600220}
221
222template<typename ShaderType>
223void GrPathTessellateOp::prePrepareStencilCubicsProgram(const PrePrepareArgs& args) {
224 SkASSERT(!fStencilCubicsProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600225
226 this->prePreparePipelineForStencils(args);
227
Chris Daltone74cebe2020-09-24 09:32:54 -0600228 auto* shader = args.fArena->make<ShaderType>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600229 fStencilCubicsProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600230 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600231 args.fXferBarrierFlags, stencil_pass_settings(fPath.getFillType()), *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600232}
233
Chris Dalton5e1545f2020-09-25 16:24:03 -0600234void GrPathTessellateOp::prePreparePipelineForStencils(const PrePrepareArgs& args) {
235 if (fPipelineForStencils) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600236 return;
237 }
238
Chris Daltone74cebe2020-09-24 09:32:54 -0600239 GrPipeline::InitArgs initArgs;
240 if (GrAAType::kNone != fAAType) {
241 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
242 }
243 if (args.fCaps->wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
244 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
245 }
246 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
247 SkPathFillType::kEvenOdd == fPath.getFillType());
Chris Daltone74cebe2020-09-24 09:32:54 -0600248 initArgs.fCaps = args.fCaps;
Chris Dalton5e1545f2020-09-25 16:24:03 -0600249 fPipelineForStencils = args.fArena->make<GrPipeline>(
250 initArgs, GrDisableColorXPFactory::MakeXferProcessor(), *args.fHardClip);
251}
252
253// Allows non-zero stencil values to pass and write a color, and resets the stencil value back to
254// zero; discards immediately on stencil values of zero.
255// NOTE: It's ok to not check the clip here because the previous stencil pass will have only written
256// to samples already inside the clip.
257constexpr static GrUserStencilSettings kTestAndResetStencil(
258 GrUserStencilSettings::StaticInit<
259 0x0000,
260 GrUserStencilTest::kNotEqual,
261 0xffff,
262 GrUserStencilOp::kZero,
263 GrUserStencilOp::kKeep,
264 0xffff>());
265
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700266void GrPathTessellateOp::prePrepareFillTrianglesProgram(const PrePrepareArgs& args, bool isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600267 SkASSERT(!fFillTrianglesProgram);
268
269 if (fOpFlags & OpFlags::kStencilOnly) {
270 return;
271 }
272
273 // These are a twist on the standard red book stencil settings that allow us to fill the inner
274 // polygon directly to the final render target. At this point, the curves are already stencilled
275 // in. So if the stencil value is zero, then it means the path at our sample is not affected by
276 // any curves and we fill the path in directly. If the stencil value is nonzero, then we don't
277 // fill and instead continue the standard red book stencil process.
278 //
279 // NOTE: These settings are currently incompatible with a stencil clip.
280 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
281 GrUserStencilSettings::StaticInitSeparate<
282 0x0000, 0x0000,
283 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
284 0xffff, 0xffff,
285 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
286 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
287 0xffff, 0xffff>());
288
289 constexpr static GrUserStencilSettings kFillOrInvertStencil(
290 GrUserStencilSettings::StaticInit<
291 0x0000,
292 GrUserStencilTest::kEqual,
293 0xffff,
294 GrUserStencilOp::kKeep,
295 GrUserStencilOp::kZero,
296 0xffff>());
297
298 this->prePreparePipelineForFills(args);
299
300 const GrUserStencilSettings* stencil;
301 if (fStencilTrianglesProgram) {
302 // The path was already stencilled. Here we just need to do a cover pass.
303 stencil = &kTestAndResetStencil;
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700304 } else if (isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600305 // There are no stencilled curves. We can ignore stencil and fill the path directly.
306 stencil = &GrUserStencilSettings::kUnused;
307 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
308 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
309 SkASSERT(!fPipelineForFills->hasStencilClip());
310 stencil = &kFillOrIncrDecrStencil;
311 } else {
312 // Fill in the path pixels not touched by curves, invert stencil otherwise.
313 SkASSERT(!fPipelineForFills->hasStencilClip());
314 stencil = &kFillOrInvertStencil;
315 }
316
317 auto* fillTriangleShader = args.fArena->make<GrFillTriangleShader>(fViewMatrix, fColor);
318 fFillTrianglesProgram = GrPathShader::MakeProgramInfo(
319 fillTriangleShader, args.fArena, args.fWriteView, fPipelineForFills,
320 *args.fDstProxyView, args.fXferBarrierFlags, stencil, *args.fCaps);
321}
322
323void GrPathTessellateOp::prePrepareFillCubicHullsProgram(const PrePrepareArgs& args) {
324 SkASSERT(!fFillPathProgram);
325
326 if (fOpFlags & OpFlags::kStencilOnly) {
327 return;
328 }
329
330 this->prePreparePipelineForFills(args);
331
332 auto* fillCubicHullsShader = args.fArena->make<GrFillCubicHullShader>(fViewMatrix, fColor);
333 fFillPathProgram = GrPathShader::MakeProgramInfo(
334 fillCubicHullsShader, args.fArena, args.fWriteView, fPipelineForFills,
335 *args.fDstProxyView, args.fXferBarrierFlags, &kTestAndResetStencil, *args.fCaps);
336}
337
338void GrPathTessellateOp::prePrepareFillBoundingBoxProgram(const PrePrepareArgs& args) {
339 SkASSERT(!fFillPathProgram);
340
341 if (fOpFlags & OpFlags::kStencilOnly) {
342 return;
343 }
344
345 this->prePreparePipelineForFills(args);
346
347 auto* fillBoundingBoxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
348 fPath.getBounds());
349 fFillPathProgram = GrPathShader::MakeProgramInfo(
350 fillBoundingBoxShader, args.fArena, args.fWriteView, fPipelineForFills,
351 *args.fDstProxyView, args.fXferBarrierFlags, &kTestAndResetStencil, *args.fCaps);
352}
353
354void GrPathTessellateOp::prePreparePipelineForFills(const PrePrepareArgs& args) {
355 SkASSERT(!(fOpFlags & OpFlags::kStencilOnly));
356
357 if (fPipelineForFills) {
358 return;
359 }
360
361 auto pipelineFlags = GrPipeline::InputFlags::kNone;
362 if (GrAAType::kNone != fAAType) {
363 if (args.fWriteView->asRenderTargetProxy()->numSamples() == 1) {
364 // We are mixed sampled. We need to either enable conservative raster (preferred) or
365 // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
366 // the cover geometry, the stencil test is still multisampled and will still produce
367 // smooth results.)
368 SkASSERT(GrAAType::kCoverage == fAAType);
369 if (args.fCaps->conservativeRasterSupport()) {
370 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
371 pipelineFlags |= GrPipeline::InputFlags::kConservativeRaster;
372 }
373 } else {
374 // We are standard MSAA. Leave MSAA enabled for the cover geometry.
375 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
376 }
377 }
378
379 fPipelineForFills = GrSimpleMeshDrawOpHelper::CreatePipeline(
380 args.fCaps, args.fArena, args.fWriteView->swizzle(), std::move(*args.fClip),
381 *args.fDstProxyView, std::move(fProcessors), pipelineFlags);
Chris Daltone74cebe2020-09-24 09:32:54 -0600382}
383
384void GrPathTessellateOp::onPrepare(GrOpFlushState* flushState) {
385 int numVerbs = fPath.countVerbs();
386 if (numVerbs <= 0) {
387 return;
388 }
389
Chris Dalton5e1545f2020-09-25 16:24:03 -0600390 if (!fPipelineForStencils && !fPipelineForFills) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600391 // Nothing has been prePrepared yet. Do it now.
392 GrEagerDynamicVertexAllocator innerTriangleAllocator(flushState, &fTriangleBuffer,
393 &fBaseTriangleVertex);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600394 GrAppliedHardClip hardClip = GrAppliedHardClip(flushState->appliedHardClip());
395 GrAppliedClip clip = flushState->detachAppliedClip();
396 PrePrepareArgs args{flushState->allocator(), flushState->writeView(), &hardClip,
397 &clip, &flushState->dstProxyView(),
398 flushState->renderPassBarriers(), &flushState->caps(),
399 &innerTriangleAllocator};
400 this->prePreparePrograms(args);
Chris Daltone74cebe2020-09-24 09:32:54 -0600401 }
402
403 if (fTriangleVertexCount != 0) {
404 // prePreparePrograms was able to generate an inner polygon triangulation. It will exist in
405 // either fOffThreadInnerTriangulation or fTriangleBuffer exclusively.
406 SkASSERT(SkToBool(fOffThreadInnerTriangulation) != SkToBool(fTriangleBuffer));
407 if (fOffThreadInnerTriangulation) {
408 // DDL generated the triangle buffer data off thread. Copy it to GPU.
409 void* data = flushState->makeVertexSpace(sizeof(SkPoint), fTriangleVertexCount,
410 &fTriangleBuffer, &fBaseTriangleVertex);
411 memcpy(data, fOffThreadInnerTriangulation, fTriangleVertexCount * sizeof(SkPoint));
412 }
413 if (fStencilCubicsProgram) {
414 // We always use indirect draws for inner-polygon-triangulation mode instead of
415 // tessellation.
416 SkASSERT(GrPrimitiveType::kPatches !=
417 fStencilCubicsProgram->primProc().cast<GrStencilPathShader>().primitiveType());
418 GrResolveLevelCounter resolveLevelCounter;
419 resolveLevelCounter.reset(fPath, fViewMatrix, kLinearizationIntolerance);
420 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
421 }
422 return;
423 }
424
425 SkASSERT(fStencilCubicsProgram);
426 const auto& stencilCubicsShader = fStencilCubicsProgram->primProc().cast<GrPathShader>();
427
428 if (stencilCubicsShader.primitiveType() != GrPrimitiveType::kPatches) {
429 // Outer cubics need indirect draws.
430 GrResolveLevelCounter resolveLevelCounter;
431 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter);
432 return;
433 }
434
435 if (stencilCubicsShader.tessellationPatchVertexCount() == 4) {
436 // Triangles and tessellated curves will be drawn separately.
437 this->prepareMiddleOutTrianglesAndCubics(flushState);
438 return;
439 }
440
441 // We are drawing tessellated wedges.
442 SkASSERT(stencilCubicsShader.tessellationPatchVertexCount() == 5);
443 this->prepareTessellatedCubicWedges(flushState);
444}
445
Chris Dalton078f8752020-07-30 19:50:46 -0600446void GrPathTessellateOp::prepareMiddleOutTrianglesAndCubics(
Chris Daltone74cebe2020-09-24 09:32:54 -0600447 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter) {
448 SkASSERT(fStencilCubicsProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600449 SkASSERT(!fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600450 SkASSERT(!fFillTrianglesProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600451 SkASSERT(!fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600452 SkASSERT(!fIndirectDrawBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600453 SkASSERT(fTriangleVertexCount == 0);
454 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600455
Chris Daltonf5132a02020-04-27 23:40:03 -0600456 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Daltonb5391d92020-05-24 14:55:54 -0600457 int maxInnerTriangles = fPath.countVerbs() - 1;
458 int maxCubics = fPath.countVerbs();
Chris Dalton42915c22020-04-22 16:24:43 -0600459
Chris Daltonb5391d92020-05-24 14:55:54 -0600460 SkPoint* vertexData;
461 int vertexAdvancePerTriangle;
Chris Daltone74cebe2020-09-24 09:32:54 -0600462 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600463 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
464 SkASSERT(resolveLevelCounter);
465 vertexAdvancePerTriangle = 4;
466 int baseTriangleInstance;
467 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
468 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
469 &baseTriangleInstance));
470 fBaseCubicVertex = baseTriangleInstance * 4;
471 } else {
472 // Allocate the triangles as normal 3-point instances in the triangle buffer.
473 vertexAdvancePerTriangle = 3;
474 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
475 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
476 }
Chris Dalton42915c22020-04-22 16:24:43 -0600477 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600478 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600479 }
Chris Dalton42915c22020-04-22 16:24:43 -0600480
Chris Daltonb5391d92020-05-24 14:55:54 -0600481 GrVectorXform xform(fViewMatrix);
482 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
483 fPath.countVerbs());
484 if (resolveLevelCounter) {
485 resolveLevelCounter->reset();
486 }
487 int numCountedCurves = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600488 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
489 switch (verb) {
490 case SkPathVerb::kMove:
Chris Daltonb5391d92020-05-24 14:55:54 -0600491 middleOut.closeAndMove(pts[0]);
Chris Daltonf5132a02020-04-27 23:40:03 -0600492 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600493 case SkPathVerb::kLine:
494 middleOut.pushVertex(pts[1]);
495 break;
496 case SkPathVerb::kQuad:
497 middleOut.pushVertex(pts[2]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600498 if (resolveLevelCounter) {
499 // Quadratics get converted to cubics before rendering.
500 resolveLevelCounter->countCubic(GrWangsFormula::quadratic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600501 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600502 break;
503 }
504 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600505 break;
506 case SkPathVerb::kCubic:
507 middleOut.pushVertex(pts[3]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600508 if (resolveLevelCounter) {
509 resolveLevelCounter->countCubic(GrWangsFormula::cubic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600510 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600511 break;
512 }
513 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600514 break;
515 case SkPathVerb::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600516 middleOut.close();
517 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600518 case SkPathVerb::kConic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600519 SkUNREACHABLE;
Chris Daltonf5132a02020-04-27 23:40:03 -0600520 }
Chris Dalton42915c22020-04-22 16:24:43 -0600521 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600522 int triangleCount = middleOut.close();
523 SkASSERT(triangleCount <= maxInnerTriangles);
Chris Dalton42915c22020-04-22 16:24:43 -0600524
Chris Daltone74cebe2020-09-24 09:32:54 -0600525 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600526 SkASSERT(resolveLevelCounter);
527 int totalInstanceCount = triangleCount + resolveLevelCounter->totalCubicInstanceCount();
528 SkASSERT(vertexAdvancePerTriangle == 4);
529 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
530 sizeof(SkPoint) * 4);
531 if (totalInstanceCount) {
532 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
533 triangleCount);
534 }
535 } else {
536 SkASSERT(vertexAdvancePerTriangle == 3);
537 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
538 fTriangleVertexCount = triangleCount * 3;
Chris Daltonb5391d92020-05-24 14:55:54 -0600539 if (resolveLevelCounter) {
540 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
541 } else {
542 this->prepareTessellatedOuterCubics(target, numCountedCurves);
543 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600544 }
Chris Dalton42915c22020-04-22 16:24:43 -0600545}
546
Chris Dalton078f8752020-07-30 19:50:46 -0600547void GrPathTessellateOp::prepareIndirectOuterCubics(
Chris Daltonb5391d92020-05-24 14:55:54 -0600548 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
549 SkASSERT(resolveLevelCounter.totalCubicInstanceCount() >= 0);
550 if (resolveLevelCounter.totalCubicInstanceCount() == 0) {
551 return;
552 }
553 // Allocate a buffer to store the cubic data.
554 SkPoint* cubicData;
555 int baseInstance;
556 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
557 sizeof(SkPoint) * 4, resolveLevelCounter.totalCubicInstanceCount(), &fCubicBuffer,
558 &baseInstance));
559 if (!cubicData) {
560 return;
561 }
562 fBaseCubicVertex = baseInstance * 4;
563 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
564 /*numTrianglesAtBeginningOfData=*/0);
565}
566
Chris Dalton078f8752020-07-30 19:50:46 -0600567void GrPathTessellateOp::prepareIndirectOuterCubicsAndTriangles(
Chris Daltonb5391d92020-05-24 14:55:54 -0600568 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
569 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600570 SkASSERT(target->caps().drawInstancedSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600571 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalCubicInstanceCount() > 0);
Chris Daltone74cebe2020-09-24 09:32:54 -0600572 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600573 SkASSERT(cubicData);
Chris Daltone74cebe2020-09-24 09:32:54 -0600574 SkASSERT(fCubicVertexCount == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600575
Chris Dalton1443c9d2020-05-27 09:43:34 -0600576 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
577 target->resourceProvider());
578 if (!fIndirectIndexBuffer) {
579 return;
580 }
581
Chris Daltonb5391d92020-05-24 14:55:54 -0600582 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
583 // vertex on an instance boundary in order to accommodate this.
584 SkASSERT(fBaseCubicVertex % 4 == 0);
585 int baseInstance = fBaseCubicVertex >> 2;
586
587 // Start preparing the indirect draw buffer.
588 fIndirectDrawCount = resolveLevelCounter.totalCubicIndirectDrawCount();
589 if (numTrianglesAtBeginningOfData) {
590 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
591 }
592
593 // Allocate space for the GrDrawIndexedIndirectCommand structs.
594 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
595 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
596 if (!indirectData) {
597 SkASSERT(!fIndirectDrawBuffer);
598 return;
599 }
600
601 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
602 // location at each resolve level.
603 SkPoint* instanceLocations[kMaxResolveLevel + 1];
604 int indirectIdx = 0;
605 int runningInstanceCount = 0;
606 if (numTrianglesAtBeginningOfData) {
607 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
608 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
609 // emit the triangles [P0, P1, P2] from these 4-point instances.
610 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
611 numTrianglesAtBeginningOfData, baseInstance);
612 indirectIdx = 1;
613 runningInstanceCount = numTrianglesAtBeginningOfData;
614 }
615 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
Chris Daltona6858ae2020-06-25 07:04:33 -0600616 int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel];
617 if (!instanceCountAtCurrLevel) {
618 SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
619 continue;
Chris Daltonb5391d92020-05-24 14:55:54 -0600620 }
Chris Daltona6858ae2020-06-25 07:04:33 -0600621 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
622 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
623 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
624 runningInstanceCount += instanceCountAtCurrLevel;
Chris Daltonb5391d92020-05-24 14:55:54 -0600625 }
626
627#ifdef SK_DEBUG
628 SkASSERT(indirectIdx == fIndirectDrawCount);
629 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
630 resolveLevelCounter.totalCubicInstanceCount());
631 SkASSERT(fIndirectDrawCount > 0);
632
633 SkPoint* endLocations[kMaxResolveLevel + 1];
Chris Daltona6858ae2020-06-25 07:04:33 -0600634 int lastResolveLevel = 0;
635 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
636 if (!instanceLocations[resolveLevel]) {
637 endLocations[resolveLevel] = nullptr;
638 continue;
639 }
640 endLocations[lastResolveLevel] = instanceLocations[resolveLevel];
641 lastResolveLevel = resolveLevel;
642 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600643 int totalInstanceCount = numTrianglesAtBeginningOfData +
644 resolveLevelCounter.totalCubicInstanceCount();
Chris Daltona6858ae2020-06-25 07:04:33 -0600645 endLocations[lastResolveLevel] = cubicData + totalInstanceCount * 4;
Chris Daltonb5391d92020-05-24 14:55:54 -0600646#endif
647
648 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
649
650 if (resolveLevelCounter.totalCubicInstanceCount()) {
651 GrVectorXform xform(fViewMatrix);
652 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
653 int level;
654 switch (verb) {
655 default:
656 continue;
657 case SkPathVerb::kQuad:
Chris Daltonb96995d2020-06-04 16:44:29 -0600658 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600659 if (level == 0) {
660 continue;
661 }
662 level = std::min(level, kMaxResolveLevel);
Chris Dalton753c1b32020-11-02 12:00:19 -0700663 GrPathUtils::convertQuadToCubic(pts, instanceLocations[level]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600664 break;
665 case SkPathVerb::kCubic:
Chris Daltonb96995d2020-06-04 16:44:29 -0600666 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600667 if (level == 0) {
668 continue;
669 }
670 level = std::min(level, kMaxResolveLevel);
671 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
672 break;
673 }
674 instanceLocations[level] += 4;
675 fCubicVertexCount += 4;
676 }
677 }
678
679#ifdef SK_DEBUG
680 for (int i = 1; i <= kMaxResolveLevel; ++i) {
681 SkASSERT(instanceLocations[i] == endLocations[i]);
682 }
683 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
684 resolveLevelCounter.totalCubicInstanceCount()) * 4);
685#endif
Chris Daltonb5391d92020-05-24 14:55:54 -0600686}
687
Chris Dalton078f8752020-07-30 19:50:46 -0600688void GrPathTessellateOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
Chris Daltonb5391d92020-05-24 14:55:54 -0600689 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600690 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600691 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600692 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600693 SkASSERT(fStencilCubicsProgram);
694 SkASSERT(fCubicVertexCount == 0);
Chris Dalton42915c22020-04-22 16:24:43 -0600695
696 if (numCountedCurves == 0) {
697 return;
698 }
699
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600700 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600701 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600702 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600703 return;
704 }
Chris Dalton42915c22020-04-22 16:24:43 -0600705
Chris Daltonf7a33072020-05-01 10:33:08 -0600706 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
707 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600708 default:
709 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600710 case SkPathVerb::kQuad:
711 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
Chris Dalton753c1b32020-11-02 12:00:19 -0700712 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600713 break;
714 case SkPathVerb::kCubic:
715 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
716 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600717 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600718 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600719 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600720 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600721 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
Chris Dalton42915c22020-04-22 16:24:43 -0600722}
723
Chris Dalton078f8752020-07-30 19:50:46 -0600724void GrPathTessellateOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600725 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600726 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600727 SkASSERT(fStencilCubicsProgram);
728 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600729
Chris Dalton42915c22020-04-22 16:24:43 -0600730 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
731 // Each wedge has 5 vertices.
732 int maxVertices = (fPath.countVerbs() + 1) * 5;
733
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600734 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600735 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
736 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600737 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600738 }
Chris Dalton42915c22020-04-22 16:24:43 -0600739
740 GrMidpointContourParser parser(fPath);
741 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600742 SkPoint midpoint = parser.currentMidpoint();
743 SkPoint startPoint = {0, 0};
744 SkPoint lastPoint = startPoint;
745 for (auto [verb, pts, w] : parser.currentContour()) {
746 switch (verb) {
747 case SkPathVerb::kMove:
748 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600749 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600750 case SkPathVerb::kClose:
751 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600752 case SkPathVerb::kLine:
Chris Dalton753c1b32020-11-02 12:00:19 -0700753 GrPathUtils::convertLineToCubic(pts[0], pts[1], vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600754 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600755 break;
756 case SkPathVerb::kQuad:
Chris Dalton753c1b32020-11-02 12:00:19 -0700757 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600758 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600759 break;
760 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600761 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
762 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600763 break;
764 case SkPathVerb::kConic:
765 SkUNREACHABLE;
766 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600767 vertexData[fCubicVertexCount + 4] = midpoint;
768 fCubicVertexCount += 5;
769 }
770 if (lastPoint != startPoint) {
Chris Dalton753c1b32020-11-02 12:00:19 -0700771 GrPathUtils::convertLineToCubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600772 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600773 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600774 }
775 }
776
Chris Dalton04f9cda2020-04-23 10:04:25 -0600777 vertexAlloc.unlock(fCubicVertexCount);
Chris Dalton42915c22020-04-22 16:24:43 -0600778}
779
Chris Dalton078f8752020-07-30 19:50:46 -0600780void GrPathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600781 this->drawStencilPass(flushState);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600782 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700783}
784
Chris Dalton078f8752020-07-30 19:50:46 -0600785void GrPathTessellateOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600786 if (fStencilTrianglesProgram && fTriangleVertexCount > 0) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600787 SkASSERT(fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600788 flushState->bindPipelineAndScissorClip(*fStencilTrianglesProgram, this->bounds());
Chris Dalton85138672020-07-24 08:47:03 -0600789 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600790 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700791 }
792
Chris Daltone74cebe2020-09-24 09:32:54 -0600793 if (fCubicVertexCount > 0) {
794 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600795 SkASSERT(fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600796 flushState->bindPipelineAndScissorClip(*fStencilCubicsProgram, this->bounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600797 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600798 SkASSERT(fIndirectIndexBuffer);
Greg Daniel426274b2020-07-20 11:37:38 -0400799 flushState->bindBuffers(fIndirectIndexBuffer, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600800 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
801 fIndirectDrawCount);
802 } else {
Greg Daniel426274b2020-07-20 11:37:38 -0400803 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600804 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
805 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
806 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
807 }
808 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700809 }
810}
811
Chris Dalton078f8752020-07-30 19:50:46 -0600812void GrPathTessellateOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600813 if (fFillTrianglesProgram) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600814 SkASSERT(fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600815 SkASSERT(fTriangleVertexCount > 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700816
Chris Dalton5e1545f2020-09-25 16:24:03 -0600817 // We have a triangulation of the path's inner polygon. This is the fast path. Fill those
818 // triangles directly to the screen.
819 flushState->bindPipelineAndScissorClip(*fFillTrianglesProgram, this->bounds());
820 flushState->bindTextures(fFillTrianglesProgram->primProc(), nullptr, *fPipelineForFills);
Greg Daniel426274b2020-07-20 11:37:38 -0400821 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600822 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600823
Chris Daltone74cebe2020-09-24 09:32:54 -0600824 if (fCubicVertexCount > 0) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600825 SkASSERT(fFillPathProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600826 SkASSERT(fCubicBuffer);
827
Chris Dalton5e1545f2020-09-25 16:24:03 -0600828 // At this point, every pixel is filled in except the ones touched by curves.
829 // fFillPathProgram will issue a final cover pass over the curves by drawing their
830 // convex hulls. This will fill in any remaining samples and reset the stencil buffer.
831 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
832 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600833
834 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
835 // the base vertex on an instance boundary in order to accommodate this.
836 SkASSERT((fCubicVertexCount % 4) == 0);
837 SkASSERT((fBaseCubicVertex % 4) == 0);
Greg Daniel426274b2020-07-20 11:37:38 -0400838 flushState->bindBuffers(nullptr, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600839 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700840 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600841 } else if (fFillPathProgram) {
842 // There are no triangles to fill. Just draw a bounding box.
843 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
844 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
845 flushState->bindBuffers(nullptr, nullptr, nullptr);
846 flushState->draw(4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700847 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700848}