blob: 62e307a3cbf5718d59a71dcbb5b66af0a4098d6c [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 Daltonb27f39c2020-11-23 09:30:24 -070023#include "src/gpu/tessellate/GrWangsFormula.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070024
Chris Daltonb96995d2020-06-04 16:44:29 -060025constexpr static float kLinearizationIntolerance =
26 GrTessellationPathRenderer::kLinearizationIntolerance;
27
28constexpr static int kMaxResolveLevel = GrTessellationPathRenderer::kMaxResolveLevel;
29
30using OpFlags = GrTessellationPathRenderer::OpFlags;
Chris Daltonb5391d92020-05-24 14:55:54 -060031
Chris Daltonb0643342020-12-15 01:04:12 -070032void GrPathTessellateOp::visitProxies(const VisitProxyFunc& fn) const {
33 if (fPipelineForFills) {
34 fPipelineForFills->visitProxies(fn);
35 } else {
36 fProcessors.visitProxies(fn);
37 }
38}
39
Chris Dalton078f8752020-07-30 19:50:46 -060040GrPathTessellateOp::FixedFunctionFlags GrPathTessellateOp::fixedFunctionFlags() const {
Chris Daltonb832ce62020-01-06 19:49:37 -070041 auto flags = FixedFunctionFlags::kUsesStencil;
42 if (GrAAType::kNone != fAAType) {
43 flags |= FixedFunctionFlags::kUsesHWAA;
44 }
45 return flags;
46}
47
Chris Daltone74cebe2020-09-24 09:32:54 -060048namespace {
Robert Phillipsc655c3a2020-03-18 13:23:45 -040049
Chris Daltone74cebe2020-09-24 09:32:54 -060050class CpuTriangleAllocator : public GrEagerVertexAllocator {
51public:
52 CpuTriangleAllocator(SkArenaAlloc* arena, const SkPoint** data) : fArena(arena), fData(data) {}
53
54 void* lock(size_t stride, int eagerCount) override {
55 SkASSERT(!*fData);
56 SkASSERT(stride == sizeof(SkPoint));
57 SkPoint* data = fArena->makeArray<SkPoint>(eagerCount);
58 *fData = data;
59 return data;
60 }
61
62 void unlock(int actualCount) override { SkASSERT(*fData); }
63
64private:
65 SkArenaAlloc* const fArena;
66 const SkPoint** fData;
67};
68
69}
70
71void GrPathTessellateOp::onPrePrepare(GrRecordingContext* context,
Adlai Hollere2296f72020-11-19 13:41:26 -050072 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
Chris Daltone74cebe2020-09-24 09:32:54 -060073 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -050074 GrXferBarrierFlags renderPassXferBarriers,
75 GrLoadOp colorLoadOp) {
Chris Daltone74cebe2020-09-24 09:32:54 -060076 SkArenaAlloc* recordTimeAllocator = context->priv().recordTimeAllocator();
Chris Dalton5e1545f2020-09-25 16:24:03 -060077 GrAppliedHardClip hardClip = GrAppliedHardClip(
78 (clip) ? clip->hardClip() : GrAppliedHardClip::Disabled());
Chris Daltone74cebe2020-09-24 09:32:54 -060079 CpuTriangleAllocator cpuTriangleAllocator(recordTimeAllocator, &fOffThreadInnerTriangulation);
Chris Dalton5e1545f2020-09-25 16:24:03 -060080 PrePrepareArgs args{recordTimeAllocator, writeView, &hardClip, clip, &dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -050081 renderPassXferBarriers, colorLoadOp, context->priv().caps(),
82 &cpuTriangleAllocator};
Chris Dalton5e1545f2020-09-25 16:24:03 -060083
84 this->prePreparePrograms(args);
85
Chris Daltone74cebe2020-09-24 09:32:54 -060086 if (fStencilTrianglesProgram) {
87 context->priv().recordProgramInfo(fStencilTrianglesProgram);
88 }
89 if (fStencilCubicsProgram) {
90 context->priv().recordProgramInfo(fStencilCubicsProgram);
91 }
Chris Dalton5e1545f2020-09-25 16:24:03 -060092 if (fFillTrianglesProgram) {
93 context->priv().recordProgramInfo(fFillTrianglesProgram);
94 }
95 if (fFillPathProgram) {
96 context->priv().recordProgramInfo(fFillPathProgram);
97 }
Chris Daltone74cebe2020-09-24 09:32:54 -060098}
99
100void GrPathTessellateOp::prePreparePrograms(const PrePrepareArgs& args) {
Chris Dalton4328e922020-01-29 13:16:14 -0700101 int numVerbs = fPath.countVerbs();
102 if (numVerbs <= 0) {
103 return;
104 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600105
106 // First check if the path is large and/or simple enough that we can actually triangulate the
107 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
108 // curves, and then fill the internal polygons directly to the final render target, thus drawing
109 // the majority of pixels in a single render pass.
110 SkScalar scales[2];
111 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
112 const SkRect& bounds = fPath.getBounds();
Chris Dalton4328e922020-01-29 13:16:14 -0700113 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
114 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
115 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700116 bool isLinear;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600117 // This will fail if the inner triangles do not form a simple polygon (e.g., self
118 // intersection, double winding).
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700119 if (this->prePrepareInnerPolygonTriangulation(args, &isLinear)) {
120 if (!isLinear) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600121 // Always use indirect draws for cubics instead of tessellation here. Our goal in
122 // this mode is to maximize GPU performance, and the middle-out topology used by our
123 // indirect draws is easier on the rasterizer than a tessellated fan. There also
124 // seems to be a small amount of fixed tessellation overhead that this avoids.
125 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600126 // We will need one final pass to cover the convex hulls of the cubics after
127 // drawing the inner triangles.
128 this->prePrepareFillCubicHullsProgram(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600129 }
Chris Dalton4328e922020-01-29 13:16:14 -0700130 return;
131 }
132 }
133
Chris Dalton5e1545f2020-09-25 16:24:03 -0600134 // If we didn't triangulate the inner fan then the fill program will be a simple bounding box.
135 this->prePrepareFillBoundingBoxProgram(args);
136
Chris Daltonb5391d92020-05-24 14:55:54 -0600137 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
138 // that contains both the inner triangles and the outer cubics, instead of using hardware
139 // tessellation. Also take this path if tessellation is not supported.
140 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
Chris Daltonb96995d2020-06-04 16:44:29 -0600141 if (drawTrianglesAsIndirectCubicDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600142 if (!drawTrianglesAsIndirectCubicDraw) {
143 this->prePrepareStencilTrianglesProgram(args);
144 }
145 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600146 return;
147 }
148
Chris Daltonb96995d2020-06-04 16:44:29 -0600149 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
Chris Daltone74cebe2020-09-24 09:32:54 -0600150 SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
Chris Daltonb96995d2020-06-04 16:44:29 -0600151
Chris Daltonb5391d92020-05-24 14:55:54 -0600152 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
153 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
154 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
155 // switches and draw calls.
Chris Dalton4328e922020-01-29 13:16:14 -0700156 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
157 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonb5391d92020-05-24 14:55:54 -0600158 if (rasterEdgeWork > 300 * 300) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600159 this->prePrepareStencilTrianglesProgram(args);
160 this->prePrepareStencilCubicsProgram<GrCubicTessellateShader>(args);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700161 return;
162 }
163
164 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltone74cebe2020-09-24 09:32:54 -0600165 this->prePrepareStencilCubicsProgram<GrWedgeTessellateShader>(args);
Chris Daltonb832ce62020-01-06 19:49:37 -0700166}
167
Chris Daltone74cebe2020-09-24 09:32:54 -0600168bool GrPathTessellateOp::prePrepareInnerPolygonTriangulation(const PrePrepareArgs& args,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700169 bool* isLinear) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600170 SkASSERT(!fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600171 SkASSERT(fTriangleVertexCount == 0);
172 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600173 SkASSERT(!fFillTrianglesProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600174
175 using GrTriangulator::Mode;
176
Chris Dalton04f9cda2020-04-23 10:04:25 -0600177 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
Chris Daltone74cebe2020-09-24 09:32:54 -0600178 args.fInnerTriangleAllocator,
179 Mode::kSimpleInnerPolygons,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700180 isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600181 if (fTriangleVertexCount == 0) {
182 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
183 // simple.
184 return false;
185 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600186 if ((fOpFlags & (OpFlags::kStencilOnly | OpFlags::kWireframe)) ||
Chris Daltonb96995d2020-06-04 16:44:29 -0600187 GrAAType::kCoverage == fAAType ||
Chris Daltone74cebe2020-09-24 09:32:54 -0600188 (args.fClip && args.fClip->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600189 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
190 // can't fill the inner polygon directly. Indicate that these triangles need to be
191 // stencilled.
Chris Daltone74cebe2020-09-24 09:32:54 -0600192 this->prePrepareStencilTrianglesProgram(args);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600193 }
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700194 this->prePrepareFillTrianglesProgram(args, *isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600195 return true;
196}
197
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600198// Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
199constexpr static GrUserStencilSettings kIncrDecrStencil(
200 GrUserStencilSettings::StaticInitSeparate<
201 0x0000, 0x0000,
202 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
203 0xffff, 0xffff,
204 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
205 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
206 0xffff, 0xffff>());
207
208// Inverts the bottom stencil bit. Used for "even/odd" fill.
209constexpr static GrUserStencilSettings kInvertStencil(
210 GrUserStencilSettings::StaticInit<
211 0x0000,
212 GrUserStencilTest::kAlwaysIfInClip,
213 0xffff,
214 GrUserStencilOp::kInvert,
215 GrUserStencilOp::kKeep,
216 0x0001>());
217
218constexpr static const GrUserStencilSettings* stencil_pass_settings(SkPathFillType fillType) {
219 return (fillType == SkPathFillType::kWinding) ? &kIncrDecrStencil : &kInvertStencil;
220}
221
Chris Daltone74cebe2020-09-24 09:32:54 -0600222void GrPathTessellateOp::prePrepareStencilTrianglesProgram(const PrePrepareArgs& args) {
223 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600224
225 this->prePreparePipelineForStencils(args);
226
Chris Daltone74cebe2020-09-24 09:32:54 -0600227 auto* shader = args.fArena->make<GrStencilTriangleShader>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600228 fStencilTrianglesProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600229 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500230 args.fXferBarrierFlags, args.fColorLoadOp, stencil_pass_settings(fPath.getFillType()),
231 *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600232}
233
234template<typename ShaderType>
235void GrPathTessellateOp::prePrepareStencilCubicsProgram(const PrePrepareArgs& args) {
236 SkASSERT(!fStencilCubicsProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600237
238 this->prePreparePipelineForStencils(args);
239
Chris Daltone74cebe2020-09-24 09:32:54 -0600240 auto* shader = args.fArena->make<ShaderType>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600241 fStencilCubicsProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600242 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500243 args.fXferBarrierFlags, args.fColorLoadOp, stencil_pass_settings(fPath.getFillType()),
244 *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600245}
246
Chris Dalton5e1545f2020-09-25 16:24:03 -0600247void GrPathTessellateOp::prePreparePipelineForStencils(const PrePrepareArgs& args) {
248 if (fPipelineForStencils) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600249 return;
250 }
251
Chris Daltone74cebe2020-09-24 09:32:54 -0600252 GrPipeline::InitArgs initArgs;
253 if (GrAAType::kNone != fAAType) {
254 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
255 }
256 if (args.fCaps->wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
257 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
258 }
259 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
260 SkPathFillType::kEvenOdd == fPath.getFillType());
Chris Daltone74cebe2020-09-24 09:32:54 -0600261 initArgs.fCaps = args.fCaps;
Chris Dalton5e1545f2020-09-25 16:24:03 -0600262 fPipelineForStencils = args.fArena->make<GrPipeline>(
263 initArgs, GrDisableColorXPFactory::MakeXferProcessor(), *args.fHardClip);
264}
265
266// Allows non-zero stencil values to pass and write a color, and resets the stencil value back to
267// zero; discards immediately on stencil values of zero.
268// NOTE: It's ok to not check the clip here because the previous stencil pass will have only written
269// to samples already inside the clip.
270constexpr static GrUserStencilSettings kTestAndResetStencil(
271 GrUserStencilSettings::StaticInit<
272 0x0000,
273 GrUserStencilTest::kNotEqual,
274 0xffff,
275 GrUserStencilOp::kZero,
276 GrUserStencilOp::kKeep,
277 0xffff>());
278
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700279void GrPathTessellateOp::prePrepareFillTrianglesProgram(const PrePrepareArgs& args, bool isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600280 SkASSERT(!fFillTrianglesProgram);
281
282 if (fOpFlags & OpFlags::kStencilOnly) {
283 return;
284 }
285
286 // These are a twist on the standard red book stencil settings that allow us to fill the inner
287 // polygon directly to the final render target. At this point, the curves are already stencilled
288 // in. So if the stencil value is zero, then it means the path at our sample is not affected by
289 // any curves and we fill the path in directly. If the stencil value is nonzero, then we don't
290 // fill and instead continue the standard red book stencil process.
291 //
292 // NOTE: These settings are currently incompatible with a stencil clip.
293 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
294 GrUserStencilSettings::StaticInitSeparate<
295 0x0000, 0x0000,
296 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
297 0xffff, 0xffff,
298 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
299 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
300 0xffff, 0xffff>());
301
302 constexpr static GrUserStencilSettings kFillOrInvertStencil(
303 GrUserStencilSettings::StaticInit<
304 0x0000,
305 GrUserStencilTest::kEqual,
306 0xffff,
307 GrUserStencilOp::kKeep,
308 GrUserStencilOp::kZero,
309 0xffff>());
310
311 this->prePreparePipelineForFills(args);
312
313 const GrUserStencilSettings* stencil;
314 if (fStencilTrianglesProgram) {
315 // The path was already stencilled. Here we just need to do a cover pass.
316 stencil = &kTestAndResetStencil;
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700317 } else if (isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600318 // There are no stencilled curves. We can ignore stencil and fill the path directly.
319 stencil = &GrUserStencilSettings::kUnused;
320 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
321 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
322 SkASSERT(!fPipelineForFills->hasStencilClip());
323 stencil = &kFillOrIncrDecrStencil;
324 } else {
325 // Fill in the path pixels not touched by curves, invert stencil otherwise.
326 SkASSERT(!fPipelineForFills->hasStencilClip());
327 stencil = &kFillOrInvertStencil;
328 }
329
330 auto* fillTriangleShader = args.fArena->make<GrFillTriangleShader>(fViewMatrix, fColor);
331 fFillTrianglesProgram = GrPathShader::MakeProgramInfo(
332 fillTriangleShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500333 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, stencil, *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600334}
335
336void GrPathTessellateOp::prePrepareFillCubicHullsProgram(const PrePrepareArgs& args) {
337 SkASSERT(!fFillPathProgram);
338
339 if (fOpFlags & OpFlags::kStencilOnly) {
340 return;
341 }
342
343 this->prePreparePipelineForFills(args);
344
345 auto* fillCubicHullsShader = args.fArena->make<GrFillCubicHullShader>(fViewMatrix, fColor);
346 fFillPathProgram = GrPathShader::MakeProgramInfo(
347 fillCubicHullsShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500348 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, &kTestAndResetStencil,
349 *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600350}
351
352void GrPathTessellateOp::prePrepareFillBoundingBoxProgram(const PrePrepareArgs& args) {
353 SkASSERT(!fFillPathProgram);
354
355 if (fOpFlags & OpFlags::kStencilOnly) {
356 return;
357 }
358
359 this->prePreparePipelineForFills(args);
360
361 auto* fillBoundingBoxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
362 fPath.getBounds());
363 fFillPathProgram = GrPathShader::MakeProgramInfo(
364 fillBoundingBoxShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500365 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, &kTestAndResetStencil,
366 *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600367}
368
369void GrPathTessellateOp::prePreparePipelineForFills(const PrePrepareArgs& args) {
370 SkASSERT(!(fOpFlags & OpFlags::kStencilOnly));
371
372 if (fPipelineForFills) {
373 return;
374 }
375
376 auto pipelineFlags = GrPipeline::InputFlags::kNone;
377 if (GrAAType::kNone != fAAType) {
Adlai Hollere2296f72020-11-19 13:41:26 -0500378 if (args.fWriteView.asRenderTargetProxy()->numSamples() == 1) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600379 // We are mixed sampled. We need to either enable conservative raster (preferred) or
380 // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
381 // the cover geometry, the stencil test is still multisampled and will still produce
382 // smooth results.)
383 SkASSERT(GrAAType::kCoverage == fAAType);
384 if (args.fCaps->conservativeRasterSupport()) {
385 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
386 pipelineFlags |= GrPipeline::InputFlags::kConservativeRaster;
387 }
388 } else {
389 // We are standard MSAA. Leave MSAA enabled for the cover geometry.
390 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
391 }
392 }
393
394 fPipelineForFills = GrSimpleMeshDrawOpHelper::CreatePipeline(
Chris Daltonb0643342020-12-15 01:04:12 -0700395 args.fCaps, args.fArena, args.fWriteView.swizzle(),
396 (args.fClip) ? std::move(*args.fClip) : GrAppliedClip::Disabled(), *args.fDstProxyView,
397 std::move(fProcessors), pipelineFlags);
Chris Daltone74cebe2020-09-24 09:32:54 -0600398}
399
400void GrPathTessellateOp::onPrepare(GrOpFlushState* flushState) {
401 int numVerbs = fPath.countVerbs();
402 if (numVerbs <= 0) {
403 return;
404 }
405
Chris Dalton5e1545f2020-09-25 16:24:03 -0600406 if (!fPipelineForStencils && !fPipelineForFills) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600407 // Nothing has been prePrepared yet. Do it now.
408 GrEagerDynamicVertexAllocator innerTriangleAllocator(flushState, &fTriangleBuffer,
409 &fBaseTriangleVertex);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600410 GrAppliedHardClip hardClip = GrAppliedHardClip(flushState->appliedHardClip());
411 GrAppliedClip clip = flushState->detachAppliedClip();
412 PrePrepareArgs args{flushState->allocator(), flushState->writeView(), &hardClip,
413 &clip, &flushState->dstProxyView(),
Greg Daniel42dbca52020-11-20 10:22:43 -0500414 flushState->renderPassBarriers(), flushState->colorLoadOp(),
415 &flushState->caps(), &innerTriangleAllocator};
Chris Dalton5e1545f2020-09-25 16:24:03 -0600416 this->prePreparePrograms(args);
Chris Daltone74cebe2020-09-24 09:32:54 -0600417 }
418
419 if (fTriangleVertexCount != 0) {
420 // prePreparePrograms was able to generate an inner polygon triangulation. It will exist in
421 // either fOffThreadInnerTriangulation or fTriangleBuffer exclusively.
422 SkASSERT(SkToBool(fOffThreadInnerTriangulation) != SkToBool(fTriangleBuffer));
423 if (fOffThreadInnerTriangulation) {
424 // DDL generated the triangle buffer data off thread. Copy it to GPU.
425 void* data = flushState->makeVertexSpace(sizeof(SkPoint), fTriangleVertexCount,
426 &fTriangleBuffer, &fBaseTriangleVertex);
427 memcpy(data, fOffThreadInnerTriangulation, fTriangleVertexCount * sizeof(SkPoint));
428 }
429 if (fStencilCubicsProgram) {
430 // We always use indirect draws for inner-polygon-triangulation mode instead of
431 // tessellation.
432 SkASSERT(GrPrimitiveType::kPatches !=
433 fStencilCubicsProgram->primProc().cast<GrStencilPathShader>().primitiveType());
434 GrResolveLevelCounter resolveLevelCounter;
435 resolveLevelCounter.reset(fPath, fViewMatrix, kLinearizationIntolerance);
436 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
437 }
438 return;
439 }
440
441 SkASSERT(fStencilCubicsProgram);
442 const auto& stencilCubicsShader = fStencilCubicsProgram->primProc().cast<GrPathShader>();
443
444 if (stencilCubicsShader.primitiveType() != GrPrimitiveType::kPatches) {
445 // Outer cubics need indirect draws.
446 GrResolveLevelCounter resolveLevelCounter;
447 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter);
448 return;
449 }
450
451 if (stencilCubicsShader.tessellationPatchVertexCount() == 4) {
452 // Triangles and tessellated curves will be drawn separately.
453 this->prepareMiddleOutTrianglesAndCubics(flushState);
454 return;
455 }
456
457 // We are drawing tessellated wedges.
458 SkASSERT(stencilCubicsShader.tessellationPatchVertexCount() == 5);
459 this->prepareTessellatedCubicWedges(flushState);
460}
461
Chris Dalton078f8752020-07-30 19:50:46 -0600462void GrPathTessellateOp::prepareMiddleOutTrianglesAndCubics(
Chris Daltone74cebe2020-09-24 09:32:54 -0600463 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter) {
464 SkASSERT(fStencilCubicsProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600465 SkASSERT(!fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600466 SkASSERT(!fFillTrianglesProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600467 SkASSERT(!fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600468 SkASSERT(!fIndirectDrawBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600469 SkASSERT(fTriangleVertexCount == 0);
470 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600471
Chris Daltonf5132a02020-04-27 23:40:03 -0600472 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Daltonb5391d92020-05-24 14:55:54 -0600473 int maxInnerTriangles = fPath.countVerbs() - 1;
474 int maxCubics = fPath.countVerbs();
Chris Dalton42915c22020-04-22 16:24:43 -0600475
Chris Daltonb5391d92020-05-24 14:55:54 -0600476 SkPoint* vertexData;
477 int vertexAdvancePerTriangle;
Chris Daltone74cebe2020-09-24 09:32:54 -0600478 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600479 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
480 SkASSERT(resolveLevelCounter);
481 vertexAdvancePerTriangle = 4;
482 int baseTriangleInstance;
483 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
484 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
485 &baseTriangleInstance));
486 fBaseCubicVertex = baseTriangleInstance * 4;
487 } else {
488 // Allocate the triangles as normal 3-point instances in the triangle buffer.
489 vertexAdvancePerTriangle = 3;
490 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
491 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
492 }
Chris Dalton42915c22020-04-22 16:24:43 -0600493 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600494 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600495 }
Chris Dalton42915c22020-04-22 16:24:43 -0600496
Chris Daltonb5391d92020-05-24 14:55:54 -0600497 GrVectorXform xform(fViewMatrix);
498 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
499 fPath.countVerbs());
500 if (resolveLevelCounter) {
501 resolveLevelCounter->reset();
502 }
503 int numCountedCurves = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600504 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
505 switch (verb) {
506 case SkPathVerb::kMove:
Chris Daltonb5391d92020-05-24 14:55:54 -0600507 middleOut.closeAndMove(pts[0]);
Chris Daltonf5132a02020-04-27 23:40:03 -0600508 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600509 case SkPathVerb::kLine:
510 middleOut.pushVertex(pts[1]);
511 break;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700512 case SkPathVerb::kConic:
513 // We use the same quadratic formula for conics, ignoring w. This appears to be an
514 // upper bound on what the actual number of subdivisions would have been.
515 [[fallthrough]];
Chris Daltonf7a33072020-05-01 10:33:08 -0600516 case SkPathVerb::kQuad:
517 middleOut.pushVertex(pts[2]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600518 if (resolveLevelCounter) {
Chris Daltonb27f39c2020-11-23 09:30:24 -0700519 resolveLevelCounter->countInstance(GrWangsFormula::quadratic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600520 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600521 break;
522 }
523 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600524 break;
525 case SkPathVerb::kCubic:
526 middleOut.pushVertex(pts[3]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600527 if (resolveLevelCounter) {
Chris Daltonb27f39c2020-11-23 09:30:24 -0700528 resolveLevelCounter->countInstance(GrWangsFormula::cubic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600529 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600530 break;
531 }
532 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600533 break;
534 case SkPathVerb::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600535 middleOut.close();
536 break;
Chris Daltonf5132a02020-04-27 23:40:03 -0600537 }
Chris Dalton42915c22020-04-22 16:24:43 -0600538 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600539 int triangleCount = middleOut.close();
540 SkASSERT(triangleCount <= maxInnerTriangles);
Chris Dalton42915c22020-04-22 16:24:43 -0600541
Chris Daltone74cebe2020-09-24 09:32:54 -0600542 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600543 SkASSERT(resolveLevelCounter);
Chris Daltonb27f39c2020-11-23 09:30:24 -0700544 int totalInstanceCount = triangleCount + resolveLevelCounter->totalInstanceCount();
Chris Daltonb5391d92020-05-24 14:55:54 -0600545 SkASSERT(vertexAdvancePerTriangle == 4);
546 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
547 sizeof(SkPoint) * 4);
548 if (totalInstanceCount) {
549 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
550 triangleCount);
551 }
552 } else {
553 SkASSERT(vertexAdvancePerTriangle == 3);
554 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
555 fTriangleVertexCount = triangleCount * 3;
Chris Daltonb5391d92020-05-24 14:55:54 -0600556 if (resolveLevelCounter) {
557 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
558 } else {
559 this->prepareTessellatedOuterCubics(target, numCountedCurves);
560 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600561 }
Chris Dalton42915c22020-04-22 16:24:43 -0600562}
563
Chris Dalton078f8752020-07-30 19:50:46 -0600564void GrPathTessellateOp::prepareIndirectOuterCubics(
Chris Daltonb5391d92020-05-24 14:55:54 -0600565 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
Chris Daltonb27f39c2020-11-23 09:30:24 -0700566 SkASSERT(resolveLevelCounter.totalInstanceCount() >= 0);
567 if (resolveLevelCounter.totalInstanceCount() == 0) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600568 return;
569 }
570 // Allocate a buffer to store the cubic data.
571 SkPoint* cubicData;
572 int baseInstance;
573 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb27f39c2020-11-23 09:30:24 -0700574 sizeof(SkPoint) * 4, resolveLevelCounter.totalInstanceCount(), &fCubicBuffer,
Chris Daltonb5391d92020-05-24 14:55:54 -0600575 &baseInstance));
576 if (!cubicData) {
577 return;
578 }
579 fBaseCubicVertex = baseInstance * 4;
580 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
581 /*numTrianglesAtBeginningOfData=*/0);
582}
583
Chris Dalton078f8752020-07-30 19:50:46 -0600584void GrPathTessellateOp::prepareIndirectOuterCubicsAndTriangles(
Chris Daltonb5391d92020-05-24 14:55:54 -0600585 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
586 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600587 SkASSERT(target->caps().drawInstancedSupport());
Chris Daltonb27f39c2020-11-23 09:30:24 -0700588 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalInstanceCount() > 0);
Chris Daltone74cebe2020-09-24 09:32:54 -0600589 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600590 SkASSERT(cubicData);
Chris Daltone74cebe2020-09-24 09:32:54 -0600591 SkASSERT(fCubicVertexCount == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600592
Chris Dalton1443c9d2020-05-27 09:43:34 -0600593 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
594 target->resourceProvider());
595 if (!fIndirectIndexBuffer) {
596 return;
597 }
598
Chris Daltonb5391d92020-05-24 14:55:54 -0600599 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
600 // vertex on an instance boundary in order to accommodate this.
601 SkASSERT(fBaseCubicVertex % 4 == 0);
602 int baseInstance = fBaseCubicVertex >> 2;
603
604 // Start preparing the indirect draw buffer.
Chris Daltonb27f39c2020-11-23 09:30:24 -0700605 fIndirectDrawCount = resolveLevelCounter.totalIndirectDrawCount();
Chris Daltonb5391d92020-05-24 14:55:54 -0600606 if (numTrianglesAtBeginningOfData) {
607 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
608 }
609
610 // Allocate space for the GrDrawIndexedIndirectCommand structs.
611 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
612 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
613 if (!indirectData) {
614 SkASSERT(!fIndirectDrawBuffer);
615 return;
616 }
617
618 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
619 // location at each resolve level.
620 SkPoint* instanceLocations[kMaxResolveLevel + 1];
621 int indirectIdx = 0;
622 int runningInstanceCount = 0;
623 if (numTrianglesAtBeginningOfData) {
624 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
625 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
626 // emit the triangles [P0, P1, P2] from these 4-point instances.
627 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
628 numTrianglesAtBeginningOfData, baseInstance);
629 indirectIdx = 1;
630 runningInstanceCount = numTrianglesAtBeginningOfData;
631 }
632 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
Chris Daltona6858ae2020-06-25 07:04:33 -0600633 int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel];
634 if (!instanceCountAtCurrLevel) {
635 SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
636 continue;
Chris Daltonb5391d92020-05-24 14:55:54 -0600637 }
Chris Daltona6858ae2020-06-25 07:04:33 -0600638 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
639 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
640 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
641 runningInstanceCount += instanceCountAtCurrLevel;
Chris Daltonb5391d92020-05-24 14:55:54 -0600642 }
643
644#ifdef SK_DEBUG
645 SkASSERT(indirectIdx == fIndirectDrawCount);
646 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
Chris Daltonb27f39c2020-11-23 09:30:24 -0700647 resolveLevelCounter.totalInstanceCount());
Chris Daltonb5391d92020-05-24 14:55:54 -0600648 SkASSERT(fIndirectDrawCount > 0);
649
650 SkPoint* endLocations[kMaxResolveLevel + 1];
Chris Daltona6858ae2020-06-25 07:04:33 -0600651 int lastResolveLevel = 0;
652 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
653 if (!instanceLocations[resolveLevel]) {
654 endLocations[resolveLevel] = nullptr;
655 continue;
656 }
657 endLocations[lastResolveLevel] = instanceLocations[resolveLevel];
658 lastResolveLevel = resolveLevel;
659 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600660 int totalInstanceCount = numTrianglesAtBeginningOfData +
Chris Daltonb27f39c2020-11-23 09:30:24 -0700661 resolveLevelCounter.totalInstanceCount();
Chris Daltona6858ae2020-06-25 07:04:33 -0600662 endLocations[lastResolveLevel] = cubicData + totalInstanceCount * 4;
Chris Daltonb5391d92020-05-24 14:55:54 -0600663#endif
664
665 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
666
Chris Daltonb27f39c2020-11-23 09:30:24 -0700667 if (resolveLevelCounter.totalInstanceCount()) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600668 GrVectorXform xform(fViewMatrix);
669 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
670 int level;
671 switch (verb) {
672 default:
673 continue;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700674 case SkPathVerb::kConic:
675 // We use the same quadratic formula for conics, ignoring w. This appears to be
676 // an upper bound on what the actual number of subdivisions would have been.
677 [[fallthrough]];
Chris Daltonb5391d92020-05-24 14:55:54 -0600678 case SkPathVerb::kQuad:
Chris Daltonb96995d2020-06-04 16:44:29 -0600679 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600680 break;
681 case SkPathVerb::kCubic:
Chris Daltonb96995d2020-06-04 16:44:29 -0600682 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb27f39c2020-11-23 09:30:24 -0700683 break;
684 }
685 if (level == 0) {
686 continue;
687 }
688 level = std::min(level, kMaxResolveLevel);
689 switch (verb) {
690 case SkPathVerb::kQuad:
691 GrPathUtils::convertQuadToCubic(pts, instanceLocations[level]);
692 break;
693 case SkPathVerb::kCubic:
Chris Daltonb5391d92020-05-24 14:55:54 -0600694 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
695 break;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700696 case SkPathVerb::kConic:
697 GrPathShader::WriteConicPatch(pts, *w, instanceLocations[level]);
698 break;
699 default:
700 SkUNREACHABLE;
Chris Daltonb5391d92020-05-24 14:55:54 -0600701 }
702 instanceLocations[level] += 4;
703 fCubicVertexCount += 4;
704 }
705 }
706
707#ifdef SK_DEBUG
708 for (int i = 1; i <= kMaxResolveLevel; ++i) {
709 SkASSERT(instanceLocations[i] == endLocations[i]);
710 }
711 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
Chris Daltonb27f39c2020-11-23 09:30:24 -0700712 resolveLevelCounter.totalInstanceCount()) * 4);
Chris Daltonb5391d92020-05-24 14:55:54 -0600713#endif
Chris Daltonb5391d92020-05-24 14:55:54 -0600714}
715
Chris Dalton078f8752020-07-30 19:50:46 -0600716void GrPathTessellateOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
Chris Daltonb5391d92020-05-24 14:55:54 -0600717 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600718 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600719 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600720 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600721 SkASSERT(fStencilCubicsProgram);
722 SkASSERT(fCubicVertexCount == 0);
Chris Dalton42915c22020-04-22 16:24:43 -0600723
724 if (numCountedCurves == 0) {
725 return;
726 }
727
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600728 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600729 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600730 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600731 return;
732 }
Chris Dalton42915c22020-04-22 16:24:43 -0600733
Chris Daltonf7a33072020-05-01 10:33:08 -0600734 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
735 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600736 default:
737 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600738 case SkPathVerb::kQuad:
739 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
Chris Dalton753c1b32020-11-02 12:00:19 -0700740 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600741 break;
742 case SkPathVerb::kCubic:
743 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
744 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600745 break;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700746 case SkPathVerb::kConic:
747 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
748 GrPathShader::WriteConicPatch(pts, *w, vertexData + fCubicVertexCount);
749 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600750 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600751 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600752 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600753 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
Chris Dalton42915c22020-04-22 16:24:43 -0600754}
755
Chris Dalton078f8752020-07-30 19:50:46 -0600756void GrPathTessellateOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600757 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600758 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600759 SkASSERT(fStencilCubicsProgram);
760 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600761
Chris Dalton42915c22020-04-22 16:24:43 -0600762 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
763 // Each wedge has 5 vertices.
764 int maxVertices = (fPath.countVerbs() + 1) * 5;
765
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600766 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600767 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
768 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600769 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600770 }
Chris Dalton42915c22020-04-22 16:24:43 -0600771
772 GrMidpointContourParser parser(fPath);
773 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600774 SkPoint midpoint = parser.currentMidpoint();
775 SkPoint startPoint = {0, 0};
776 SkPoint lastPoint = startPoint;
777 for (auto [verb, pts, w] : parser.currentContour()) {
778 switch (verb) {
779 case SkPathVerb::kMove:
780 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600781 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600782 case SkPathVerb::kClose:
783 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600784 case SkPathVerb::kLine:
Chris Dalton753c1b32020-11-02 12:00:19 -0700785 GrPathUtils::convertLineToCubic(pts[0], pts[1], vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600786 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600787 break;
788 case SkPathVerb::kQuad:
Chris Dalton753c1b32020-11-02 12:00:19 -0700789 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600790 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600791 break;
792 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600793 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
794 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600795 break;
796 case SkPathVerb::kConic:
Chris Daltonb27f39c2020-11-23 09:30:24 -0700797 GrPathShader::WriteConicPatch(pts, *w, vertexData + fCubicVertexCount);
798 lastPoint = pts[2];
799 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600800 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600801 vertexData[fCubicVertexCount + 4] = midpoint;
802 fCubicVertexCount += 5;
803 }
804 if (lastPoint != startPoint) {
Chris Dalton753c1b32020-11-02 12:00:19 -0700805 GrPathUtils::convertLineToCubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600806 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600807 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600808 }
809 }
810
Chris Dalton04f9cda2020-04-23 10:04:25 -0600811 vertexAlloc.unlock(fCubicVertexCount);
Chris Dalton42915c22020-04-22 16:24:43 -0600812}
813
Chris Dalton078f8752020-07-30 19:50:46 -0600814void GrPathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600815 this->drawStencilPass(flushState);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600816 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700817}
818
Chris Dalton078f8752020-07-30 19:50:46 -0600819void GrPathTessellateOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600820 if (fStencilTrianglesProgram && fTriangleVertexCount > 0) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600821 SkASSERT(fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600822 flushState->bindPipelineAndScissorClip(*fStencilTrianglesProgram, this->bounds());
Chris Dalton85138672020-07-24 08:47:03 -0600823 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600824 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700825 }
826
Chris Daltone74cebe2020-09-24 09:32:54 -0600827 if (fCubicVertexCount > 0) {
828 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600829 SkASSERT(fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600830 flushState->bindPipelineAndScissorClip(*fStencilCubicsProgram, this->bounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600831 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600832 SkASSERT(fIndirectIndexBuffer);
Greg Daniel426274b2020-07-20 11:37:38 -0400833 flushState->bindBuffers(fIndirectIndexBuffer, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600834 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
835 fIndirectDrawCount);
836 } else {
Greg Daniel426274b2020-07-20 11:37:38 -0400837 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600838 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
839 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
840 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
841 }
842 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700843 }
844}
845
Chris Dalton078f8752020-07-30 19:50:46 -0600846void GrPathTessellateOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600847 if (fFillTrianglesProgram) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600848 SkASSERT(fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600849 SkASSERT(fTriangleVertexCount > 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700850
Chris Dalton5e1545f2020-09-25 16:24:03 -0600851 // We have a triangulation of the path's inner polygon. This is the fast path. Fill those
852 // triangles directly to the screen.
853 flushState->bindPipelineAndScissorClip(*fFillTrianglesProgram, this->bounds());
854 flushState->bindTextures(fFillTrianglesProgram->primProc(), nullptr, *fPipelineForFills);
Greg Daniel426274b2020-07-20 11:37:38 -0400855 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600856 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600857
Chris Daltone74cebe2020-09-24 09:32:54 -0600858 if (fCubicVertexCount > 0) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600859 SkASSERT(fFillPathProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600860 SkASSERT(fCubicBuffer);
861
Chris Dalton5e1545f2020-09-25 16:24:03 -0600862 // At this point, every pixel is filled in except the ones touched by curves.
863 // fFillPathProgram will issue a final cover pass over the curves by drawing their
864 // convex hulls. This will fill in any remaining samples and reset the stencil buffer.
865 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
866 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600867
868 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
869 // the base vertex on an instance boundary in order to accommodate this.
870 SkASSERT((fCubicVertexCount % 4) == 0);
871 SkASSERT((fBaseCubicVertex % 4) == 0);
Greg Daniel426274b2020-07-20 11:37:38 -0400872 flushState->bindBuffers(nullptr, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600873 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700874 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600875 } else if (fFillPathProgram) {
876 // There are no triangles to fill. Just draw a bounding box.
877 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
878 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
879 flushState->bindBuffers(nullptr, nullptr, nullptr);
880 flushState->draw(4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700881 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700882}