blob: 0e38c17d51585577c2183dccd5f897be20059021 [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 Dalton078f8752020-07-30 19:50:46 -060032GrPathTessellateOp::FixedFunctionFlags GrPathTessellateOp::fixedFunctionFlags() const {
Chris Daltonb832ce62020-01-06 19:49:37 -070033 auto flags = FixedFunctionFlags::kUsesStencil;
34 if (GrAAType::kNone != fAAType) {
35 flags |= FixedFunctionFlags::kUsesHWAA;
36 }
37 return flags;
38}
39
Chris Daltone74cebe2020-09-24 09:32:54 -060040namespace {
Robert Phillipsc655c3a2020-03-18 13:23:45 -040041
Chris Daltone74cebe2020-09-24 09:32:54 -060042class CpuTriangleAllocator : public GrEagerVertexAllocator {
43public:
44 CpuTriangleAllocator(SkArenaAlloc* arena, const SkPoint** data) : fArena(arena), fData(data) {}
45
46 void* lock(size_t stride, int eagerCount) override {
47 SkASSERT(!*fData);
48 SkASSERT(stride == sizeof(SkPoint));
49 SkPoint* data = fArena->makeArray<SkPoint>(eagerCount);
50 *fData = data;
51 return data;
52 }
53
54 void unlock(int actualCount) override { SkASSERT(*fData); }
55
56private:
57 SkArenaAlloc* const fArena;
58 const SkPoint** fData;
59};
60
61}
62
63void GrPathTessellateOp::onPrePrepare(GrRecordingContext* context,
Adlai Hollere2296f72020-11-19 13:41:26 -050064 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
Chris Daltone74cebe2020-09-24 09:32:54 -060065 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -050066 GrXferBarrierFlags renderPassXferBarriers,
67 GrLoadOp colorLoadOp) {
Chris Daltone74cebe2020-09-24 09:32:54 -060068 SkArenaAlloc* recordTimeAllocator = context->priv().recordTimeAllocator();
Chris Dalton5e1545f2020-09-25 16:24:03 -060069 GrAppliedHardClip hardClip = GrAppliedHardClip(
70 (clip) ? clip->hardClip() : GrAppliedHardClip::Disabled());
Chris Daltone74cebe2020-09-24 09:32:54 -060071 CpuTriangleAllocator cpuTriangleAllocator(recordTimeAllocator, &fOffThreadInnerTriangulation);
Chris Dalton5e1545f2020-09-25 16:24:03 -060072 PrePrepareArgs args{recordTimeAllocator, writeView, &hardClip, clip, &dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -050073 renderPassXferBarriers, colorLoadOp, context->priv().caps(),
74 &cpuTriangleAllocator};
Chris Dalton5e1545f2020-09-25 16:24:03 -060075
76 this->prePreparePrograms(args);
77
Chris Daltone74cebe2020-09-24 09:32:54 -060078 if (fStencilTrianglesProgram) {
79 context->priv().recordProgramInfo(fStencilTrianglesProgram);
80 }
81 if (fStencilCubicsProgram) {
82 context->priv().recordProgramInfo(fStencilCubicsProgram);
83 }
Chris Dalton5e1545f2020-09-25 16:24:03 -060084 if (fFillTrianglesProgram) {
85 context->priv().recordProgramInfo(fFillTrianglesProgram);
86 }
87 if (fFillPathProgram) {
88 context->priv().recordProgramInfo(fFillPathProgram);
89 }
Chris Daltone74cebe2020-09-24 09:32:54 -060090}
91
92void GrPathTessellateOp::prePreparePrograms(const PrePrepareArgs& args) {
Chris Dalton4328e922020-01-29 13:16:14 -070093 int numVerbs = fPath.countVerbs();
94 if (numVerbs <= 0) {
95 return;
96 }
Chris Daltonb5391d92020-05-24 14:55:54 -060097
98 // First check if the path is large and/or simple enough that we can actually triangulate the
99 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
100 // curves, and then fill the internal polygons directly to the final render target, thus drawing
101 // the majority of pixels in a single render pass.
102 SkScalar scales[2];
103 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
104 const SkRect& bounds = fPath.getBounds();
Chris Dalton4328e922020-01-29 13:16:14 -0700105 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
106 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
107 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700108 bool isLinear;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600109 // This will fail if the inner triangles do not form a simple polygon (e.g., self
110 // intersection, double winding).
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700111 if (this->prePrepareInnerPolygonTriangulation(args, &isLinear)) {
112 if (!isLinear) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600113 // Always use indirect draws for cubics instead of tessellation here. Our goal in
114 // this mode is to maximize GPU performance, and the middle-out topology used by our
115 // indirect draws is easier on the rasterizer than a tessellated fan. There also
116 // seems to be a small amount of fixed tessellation overhead that this avoids.
117 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600118 // We will need one final pass to cover the convex hulls of the cubics after
119 // drawing the inner triangles.
120 this->prePrepareFillCubicHullsProgram(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600121 }
Chris Dalton4328e922020-01-29 13:16:14 -0700122 return;
123 }
124 }
125
Chris Dalton5e1545f2020-09-25 16:24:03 -0600126 // If we didn't triangulate the inner fan then the fill program will be a simple bounding box.
127 this->prePrepareFillBoundingBoxProgram(args);
128
Chris Daltonb5391d92020-05-24 14:55:54 -0600129 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
130 // that contains both the inner triangles and the outer cubics, instead of using hardware
131 // tessellation. Also take this path if tessellation is not supported.
132 bool drawTrianglesAsIndirectCubicDraw = (numVerbs < 50);
Chris Daltonb96995d2020-06-04 16:44:29 -0600133 if (drawTrianglesAsIndirectCubicDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600134 if (!drawTrianglesAsIndirectCubicDraw) {
135 this->prePrepareStencilTrianglesProgram(args);
136 }
137 this->prePrepareStencilCubicsProgram<GrMiddleOutCubicShader>(args);
Chris Daltonb5391d92020-05-24 14:55:54 -0600138 return;
139 }
140
Chris Daltonb96995d2020-06-04 16:44:29 -0600141 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
Chris Daltone74cebe2020-09-24 09:32:54 -0600142 SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
Chris Daltonb96995d2020-06-04 16:44:29 -0600143
Chris Daltonb5391d92020-05-24 14:55:54 -0600144 // Next see if we can split up the inner triangles and outer cubics into two draw calls. This
145 // allows for a more efficient inner triangle topology that can reduce the rasterizer load by a
146 // large margin on complex paths, but also causes greater CPU overhead due to the extra shader
147 // switches and draw calls.
Chris Dalton4328e922020-01-29 13:16:14 -0700148 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
149 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonb5391d92020-05-24 14:55:54 -0600150 if (rasterEdgeWork > 300 * 300) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600151 this->prePrepareStencilTrianglesProgram(args);
152 this->prePrepareStencilCubicsProgram<GrCubicTessellateShader>(args);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700153 return;
154 }
155
156 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltone74cebe2020-09-24 09:32:54 -0600157 this->prePrepareStencilCubicsProgram<GrWedgeTessellateShader>(args);
Chris Daltonb832ce62020-01-06 19:49:37 -0700158}
159
Chris Daltone74cebe2020-09-24 09:32:54 -0600160bool GrPathTessellateOp::prePrepareInnerPolygonTriangulation(const PrePrepareArgs& args,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700161 bool* isLinear) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600162 SkASSERT(!fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600163 SkASSERT(fTriangleVertexCount == 0);
164 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600165 SkASSERT(!fFillTrianglesProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600166
167 using GrTriangulator::Mode;
168
Chris Dalton04f9cda2020-04-23 10:04:25 -0600169 fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
Chris Daltone74cebe2020-09-24 09:32:54 -0600170 args.fInnerTriangleAllocator,
171 Mode::kSimpleInnerPolygons,
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700172 isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600173 if (fTriangleVertexCount == 0) {
174 // Mode::kSimpleInnerPolygons causes PathToTriangles to fail if the inner polygon(s) are not
175 // simple.
176 return false;
177 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600178 if ((fOpFlags & (OpFlags::kStencilOnly | OpFlags::kWireframe)) ||
Chris Daltonb96995d2020-06-04 16:44:29 -0600179 GrAAType::kCoverage == fAAType ||
Chris Daltone74cebe2020-09-24 09:32:54 -0600180 (args.fClip && args.fClip->hasStencilClip())) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600181 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
182 // can't fill the inner polygon directly. Indicate that these triangles need to be
183 // stencilled.
Chris Daltone74cebe2020-09-24 09:32:54 -0600184 this->prePrepareStencilTrianglesProgram(args);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600185 }
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700186 this->prePrepareFillTrianglesProgram(args, *isLinear);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600187 return true;
188}
189
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600190// Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
191constexpr static GrUserStencilSettings kIncrDecrStencil(
192 GrUserStencilSettings::StaticInitSeparate<
193 0x0000, 0x0000,
194 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
195 0xffff, 0xffff,
196 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
197 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
198 0xffff, 0xffff>());
199
200// Inverts the bottom stencil bit. Used for "even/odd" fill.
201constexpr static GrUserStencilSettings kInvertStencil(
202 GrUserStencilSettings::StaticInit<
203 0x0000,
204 GrUserStencilTest::kAlwaysIfInClip,
205 0xffff,
206 GrUserStencilOp::kInvert,
207 GrUserStencilOp::kKeep,
208 0x0001>());
209
210constexpr static const GrUserStencilSettings* stencil_pass_settings(SkPathFillType fillType) {
211 return (fillType == SkPathFillType::kWinding) ? &kIncrDecrStencil : &kInvertStencil;
212}
213
Chris Daltone74cebe2020-09-24 09:32:54 -0600214void GrPathTessellateOp::prePrepareStencilTrianglesProgram(const PrePrepareArgs& args) {
215 SkASSERT(!fStencilTrianglesProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600216
217 this->prePreparePipelineForStencils(args);
218
Chris Daltone74cebe2020-09-24 09:32:54 -0600219 auto* shader = args.fArena->make<GrStencilTriangleShader>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600220 fStencilTrianglesProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600221 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500222 args.fXferBarrierFlags, args.fColorLoadOp, stencil_pass_settings(fPath.getFillType()),
223 *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600224}
225
226template<typename ShaderType>
227void GrPathTessellateOp::prePrepareStencilCubicsProgram(const PrePrepareArgs& args) {
228 SkASSERT(!fStencilCubicsProgram);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600229
230 this->prePreparePipelineForStencils(args);
231
Chris Daltone74cebe2020-09-24 09:32:54 -0600232 auto* shader = args.fArena->make<ShaderType>(fViewMatrix);
Chris Dalton1b6a43c2020-09-25 12:21:18 -0600233 fStencilCubicsProgram = GrPathShader::MakeProgramInfo(
Chris Dalton5e1545f2020-09-25 16:24:03 -0600234 shader, args.fArena, args.fWriteView, fPipelineForStencils, *args.fDstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -0500235 args.fXferBarrierFlags, args.fColorLoadOp, stencil_pass_settings(fPath.getFillType()),
236 *args.fCaps);
Chris Daltone74cebe2020-09-24 09:32:54 -0600237}
238
Chris Dalton5e1545f2020-09-25 16:24:03 -0600239void GrPathTessellateOp::prePreparePipelineForStencils(const PrePrepareArgs& args) {
240 if (fPipelineForStencils) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600241 return;
242 }
243
Chris Daltone74cebe2020-09-24 09:32:54 -0600244 GrPipeline::InitArgs initArgs;
245 if (GrAAType::kNone != fAAType) {
246 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
247 }
248 if (args.fCaps->wireframeSupport() && (OpFlags::kWireframe & fOpFlags)) {
249 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
250 }
251 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
252 SkPathFillType::kEvenOdd == fPath.getFillType());
Chris Daltone74cebe2020-09-24 09:32:54 -0600253 initArgs.fCaps = args.fCaps;
Chris Dalton5e1545f2020-09-25 16:24:03 -0600254 fPipelineForStencils = args.fArena->make<GrPipeline>(
255 initArgs, GrDisableColorXPFactory::MakeXferProcessor(), *args.fHardClip);
256}
257
258// Allows non-zero stencil values to pass and write a color, and resets the stencil value back to
259// zero; discards immediately on stencil values of zero.
260// NOTE: It's ok to not check the clip here because the previous stencil pass will have only written
261// to samples already inside the clip.
262constexpr static GrUserStencilSettings kTestAndResetStencil(
263 GrUserStencilSettings::StaticInit<
264 0x0000,
265 GrUserStencilTest::kNotEqual,
266 0xffff,
267 GrUserStencilOp::kZero,
268 GrUserStencilOp::kKeep,
269 0xffff>());
270
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700271void GrPathTessellateOp::prePrepareFillTrianglesProgram(const PrePrepareArgs& args, bool isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600272 SkASSERT(!fFillTrianglesProgram);
273
274 if (fOpFlags & OpFlags::kStencilOnly) {
275 return;
276 }
277
278 // These are a twist on the standard red book stencil settings that allow us to fill the inner
279 // polygon directly to the final render target. At this point, the curves are already stencilled
280 // in. So if the stencil value is zero, then it means the path at our sample is not affected by
281 // any curves and we fill the path in directly. If the stencil value is nonzero, then we don't
282 // fill and instead continue the standard red book stencil process.
283 //
284 // NOTE: These settings are currently incompatible with a stencil clip.
285 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
286 GrUserStencilSettings::StaticInitSeparate<
287 0x0000, 0x0000,
288 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
289 0xffff, 0xffff,
290 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
291 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
292 0xffff, 0xffff>());
293
294 constexpr static GrUserStencilSettings kFillOrInvertStencil(
295 GrUserStencilSettings::StaticInit<
296 0x0000,
297 GrUserStencilTest::kEqual,
298 0xffff,
299 GrUserStencilOp::kKeep,
300 GrUserStencilOp::kZero,
301 0xffff>());
302
303 this->prePreparePipelineForFills(args);
304
305 const GrUserStencilSettings* stencil;
306 if (fStencilTrianglesProgram) {
307 // The path was already stencilled. Here we just need to do a cover pass.
308 stencil = &kTestAndResetStencil;
Chris Dalton86d4cfd2020-11-03 13:51:21 -0700309 } else if (isLinear) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600310 // There are no stencilled curves. We can ignore stencil and fill the path directly.
311 stencil = &GrUserStencilSettings::kUnused;
312 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
313 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
314 SkASSERT(!fPipelineForFills->hasStencilClip());
315 stencil = &kFillOrIncrDecrStencil;
316 } else {
317 // Fill in the path pixels not touched by curves, invert stencil otherwise.
318 SkASSERT(!fPipelineForFills->hasStencilClip());
319 stencil = &kFillOrInvertStencil;
320 }
321
322 auto* fillTriangleShader = args.fArena->make<GrFillTriangleShader>(fViewMatrix, fColor);
323 fFillTrianglesProgram = GrPathShader::MakeProgramInfo(
324 fillTriangleShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500325 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, stencil, *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600326}
327
328void GrPathTessellateOp::prePrepareFillCubicHullsProgram(const PrePrepareArgs& args) {
329 SkASSERT(!fFillPathProgram);
330
331 if (fOpFlags & OpFlags::kStencilOnly) {
332 return;
333 }
334
335 this->prePreparePipelineForFills(args);
336
337 auto* fillCubicHullsShader = args.fArena->make<GrFillCubicHullShader>(fViewMatrix, fColor);
338 fFillPathProgram = GrPathShader::MakeProgramInfo(
339 fillCubicHullsShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500340 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, &kTestAndResetStencil,
341 *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600342}
343
344void GrPathTessellateOp::prePrepareFillBoundingBoxProgram(const PrePrepareArgs& args) {
345 SkASSERT(!fFillPathProgram);
346
347 if (fOpFlags & OpFlags::kStencilOnly) {
348 return;
349 }
350
351 this->prePreparePipelineForFills(args);
352
353 auto* fillBoundingBoxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
354 fPath.getBounds());
355 fFillPathProgram = GrPathShader::MakeProgramInfo(
356 fillBoundingBoxShader, args.fArena, args.fWriteView, fPipelineForFills,
Greg Daniel42dbca52020-11-20 10:22:43 -0500357 *args.fDstProxyView, args.fXferBarrierFlags, args.fColorLoadOp, &kTestAndResetStencil,
358 *args.fCaps);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600359}
360
361void GrPathTessellateOp::prePreparePipelineForFills(const PrePrepareArgs& args) {
362 SkASSERT(!(fOpFlags & OpFlags::kStencilOnly));
363
364 if (fPipelineForFills) {
365 return;
366 }
367
368 auto pipelineFlags = GrPipeline::InputFlags::kNone;
369 if (GrAAType::kNone != fAAType) {
Adlai Hollere2296f72020-11-19 13:41:26 -0500370 if (args.fWriteView.asRenderTargetProxy()->numSamples() == 1) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600371 // We are mixed sampled. We need to either enable conservative raster (preferred) or
372 // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
373 // the cover geometry, the stencil test is still multisampled and will still produce
374 // smooth results.)
375 SkASSERT(GrAAType::kCoverage == fAAType);
376 if (args.fCaps->conservativeRasterSupport()) {
377 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
378 pipelineFlags |= GrPipeline::InputFlags::kConservativeRaster;
379 }
380 } else {
381 // We are standard MSAA. Leave MSAA enabled for the cover geometry.
382 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
383 }
384 }
385
386 fPipelineForFills = GrSimpleMeshDrawOpHelper::CreatePipeline(
Adlai Hollere2296f72020-11-19 13:41:26 -0500387 args.fCaps, args.fArena, args.fWriteView.swizzle(), std::move(*args.fClip),
Chris Dalton5e1545f2020-09-25 16:24:03 -0600388 *args.fDstProxyView, std::move(fProcessors), pipelineFlags);
Chris Daltone74cebe2020-09-24 09:32:54 -0600389}
390
391void GrPathTessellateOp::onPrepare(GrOpFlushState* flushState) {
392 int numVerbs = fPath.countVerbs();
393 if (numVerbs <= 0) {
394 return;
395 }
396
Chris Dalton5e1545f2020-09-25 16:24:03 -0600397 if (!fPipelineForStencils && !fPipelineForFills) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600398 // Nothing has been prePrepared yet. Do it now.
399 GrEagerDynamicVertexAllocator innerTriangleAllocator(flushState, &fTriangleBuffer,
400 &fBaseTriangleVertex);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600401 GrAppliedHardClip hardClip = GrAppliedHardClip(flushState->appliedHardClip());
402 GrAppliedClip clip = flushState->detachAppliedClip();
403 PrePrepareArgs args{flushState->allocator(), flushState->writeView(), &hardClip,
404 &clip, &flushState->dstProxyView(),
Greg Daniel42dbca52020-11-20 10:22:43 -0500405 flushState->renderPassBarriers(), flushState->colorLoadOp(),
406 &flushState->caps(), &innerTriangleAllocator};
Chris Dalton5e1545f2020-09-25 16:24:03 -0600407 this->prePreparePrograms(args);
Chris Daltone74cebe2020-09-24 09:32:54 -0600408 }
409
410 if (fTriangleVertexCount != 0) {
411 // prePreparePrograms was able to generate an inner polygon triangulation. It will exist in
412 // either fOffThreadInnerTriangulation or fTriangleBuffer exclusively.
413 SkASSERT(SkToBool(fOffThreadInnerTriangulation) != SkToBool(fTriangleBuffer));
414 if (fOffThreadInnerTriangulation) {
415 // DDL generated the triangle buffer data off thread. Copy it to GPU.
416 void* data = flushState->makeVertexSpace(sizeof(SkPoint), fTriangleVertexCount,
417 &fTriangleBuffer, &fBaseTriangleVertex);
418 memcpy(data, fOffThreadInnerTriangulation, fTriangleVertexCount * sizeof(SkPoint));
419 }
420 if (fStencilCubicsProgram) {
421 // We always use indirect draws for inner-polygon-triangulation mode instead of
422 // tessellation.
423 SkASSERT(GrPrimitiveType::kPatches !=
424 fStencilCubicsProgram->primProc().cast<GrStencilPathShader>().primitiveType());
425 GrResolveLevelCounter resolveLevelCounter;
426 resolveLevelCounter.reset(fPath, fViewMatrix, kLinearizationIntolerance);
427 this->prepareIndirectOuterCubics(flushState, resolveLevelCounter);
428 }
429 return;
430 }
431
432 SkASSERT(fStencilCubicsProgram);
433 const auto& stencilCubicsShader = fStencilCubicsProgram->primProc().cast<GrPathShader>();
434
435 if (stencilCubicsShader.primitiveType() != GrPrimitiveType::kPatches) {
436 // Outer cubics need indirect draws.
437 GrResolveLevelCounter resolveLevelCounter;
438 this->prepareMiddleOutTrianglesAndCubics(flushState, &resolveLevelCounter);
439 return;
440 }
441
442 if (stencilCubicsShader.tessellationPatchVertexCount() == 4) {
443 // Triangles and tessellated curves will be drawn separately.
444 this->prepareMiddleOutTrianglesAndCubics(flushState);
445 return;
446 }
447
448 // We are drawing tessellated wedges.
449 SkASSERT(stencilCubicsShader.tessellationPatchVertexCount() == 5);
450 this->prepareTessellatedCubicWedges(flushState);
451}
452
Chris Dalton078f8752020-07-30 19:50:46 -0600453void GrPathTessellateOp::prepareMiddleOutTrianglesAndCubics(
Chris Daltone74cebe2020-09-24 09:32:54 -0600454 GrMeshDrawOp::Target* target, GrResolveLevelCounter* resolveLevelCounter) {
455 SkASSERT(fStencilCubicsProgram);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600456 SkASSERT(!fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600457 SkASSERT(!fFillTrianglesProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600458 SkASSERT(!fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600459 SkASSERT(!fIndirectDrawBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600460 SkASSERT(fTriangleVertexCount == 0);
461 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600462
Chris Daltonf5132a02020-04-27 23:40:03 -0600463 // No initial moveTo, plus an implicit close at the end; n-2 triangles fill an n-gon.
Chris Daltonb5391d92020-05-24 14:55:54 -0600464 int maxInnerTriangles = fPath.countVerbs() - 1;
465 int maxCubics = fPath.countVerbs();
Chris Dalton42915c22020-04-22 16:24:43 -0600466
Chris Daltonb5391d92020-05-24 14:55:54 -0600467 SkPoint* vertexData;
468 int vertexAdvancePerTriangle;
Chris Daltone74cebe2020-09-24 09:32:54 -0600469 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600470 // Allocate the triangles as 4-point instances at the beginning of the cubic buffer.
471 SkASSERT(resolveLevelCounter);
472 vertexAdvancePerTriangle = 4;
473 int baseTriangleInstance;
474 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
475 sizeof(SkPoint) * 4, maxInnerTriangles + maxCubics, &fCubicBuffer,
476 &baseTriangleInstance));
477 fBaseCubicVertex = baseTriangleInstance * 4;
478 } else {
479 // Allocate the triangles as normal 3-point instances in the triangle buffer.
480 vertexAdvancePerTriangle = 3;
481 vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
482 sizeof(SkPoint), maxInnerTriangles * 3, &fTriangleBuffer, &fBaseTriangleVertex));
483 }
Chris Dalton42915c22020-04-22 16:24:43 -0600484 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600485 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600486 }
Chris Dalton42915c22020-04-22 16:24:43 -0600487
Chris Daltonb5391d92020-05-24 14:55:54 -0600488 GrVectorXform xform(fViewMatrix);
489 GrMiddleOutPolygonTriangulator middleOut(vertexData, vertexAdvancePerTriangle,
490 fPath.countVerbs());
491 if (resolveLevelCounter) {
492 resolveLevelCounter->reset();
493 }
494 int numCountedCurves = 0;
Chris Daltonf7a33072020-05-01 10:33:08 -0600495 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
496 switch (verb) {
497 case SkPathVerb::kMove:
Chris Daltonb5391d92020-05-24 14:55:54 -0600498 middleOut.closeAndMove(pts[0]);
Chris Daltonf5132a02020-04-27 23:40:03 -0600499 break;
Chris Daltonf7a33072020-05-01 10:33:08 -0600500 case SkPathVerb::kLine:
501 middleOut.pushVertex(pts[1]);
502 break;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700503 case SkPathVerb::kConic:
504 // We use the same quadratic formula for conics, ignoring w. This appears to be an
505 // upper bound on what the actual number of subdivisions would have been.
506 [[fallthrough]];
Chris Daltonf7a33072020-05-01 10:33:08 -0600507 case SkPathVerb::kQuad:
508 middleOut.pushVertex(pts[2]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600509 if (resolveLevelCounter) {
Chris Daltonb27f39c2020-11-23 09:30:24 -0700510 resolveLevelCounter->countInstance(GrWangsFormula::quadratic_log2(
Chris Daltonb96995d2020-06-04 16:44:29 -0600511 kLinearizationIntolerance, pts, xform));
Chris Daltonb5391d92020-05-24 14:55:54 -0600512 break;
513 }
514 ++numCountedCurves;
Chris Daltonf7a33072020-05-01 10:33:08 -0600515 break;
516 case SkPathVerb::kCubic:
517 middleOut.pushVertex(pts[3]);
Chris Daltonb5391d92020-05-24 14:55:54 -0600518 if (resolveLevelCounter) {
Chris Daltonb27f39c2020-11-23 09:30:24 -0700519 resolveLevelCounter->countInstance(GrWangsFormula::cubic_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::kClose:
Chris Daltonf5132a02020-04-27 23:40:03 -0600526 middleOut.close();
527 break;
Chris Daltonf5132a02020-04-27 23:40:03 -0600528 }
Chris Dalton42915c22020-04-22 16:24:43 -0600529 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600530 int triangleCount = middleOut.close();
531 SkASSERT(triangleCount <= maxInnerTriangles);
Chris Dalton42915c22020-04-22 16:24:43 -0600532
Chris Daltone74cebe2020-09-24 09:32:54 -0600533 if (!fStencilTrianglesProgram) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600534 SkASSERT(resolveLevelCounter);
Chris Daltonb27f39c2020-11-23 09:30:24 -0700535 int totalInstanceCount = triangleCount + resolveLevelCounter->totalInstanceCount();
Chris Daltonb5391d92020-05-24 14:55:54 -0600536 SkASSERT(vertexAdvancePerTriangle == 4);
537 target->putBackVertices(maxInnerTriangles + maxCubics - totalInstanceCount,
538 sizeof(SkPoint) * 4);
539 if (totalInstanceCount) {
540 this->prepareIndirectOuterCubicsAndTriangles(target, *resolveLevelCounter, vertexData,
541 triangleCount);
542 }
543 } else {
544 SkASSERT(vertexAdvancePerTriangle == 3);
545 target->putBackVertices(maxInnerTriangles - triangleCount, sizeof(SkPoint) * 3);
546 fTriangleVertexCount = triangleCount * 3;
Chris Daltonb5391d92020-05-24 14:55:54 -0600547 if (resolveLevelCounter) {
548 this->prepareIndirectOuterCubics(target, *resolveLevelCounter);
549 } else {
550 this->prepareTessellatedOuterCubics(target, numCountedCurves);
551 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600552 }
Chris Dalton42915c22020-04-22 16:24:43 -0600553}
554
Chris Dalton078f8752020-07-30 19:50:46 -0600555void GrPathTessellateOp::prepareIndirectOuterCubics(
Chris Daltonb5391d92020-05-24 14:55:54 -0600556 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter) {
Chris Daltonb27f39c2020-11-23 09:30:24 -0700557 SkASSERT(resolveLevelCounter.totalInstanceCount() >= 0);
558 if (resolveLevelCounter.totalInstanceCount() == 0) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600559 return;
560 }
561 // Allocate a buffer to store the cubic data.
562 SkPoint* cubicData;
563 int baseInstance;
564 cubicData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb27f39c2020-11-23 09:30:24 -0700565 sizeof(SkPoint) * 4, resolveLevelCounter.totalInstanceCount(), &fCubicBuffer,
Chris Daltonb5391d92020-05-24 14:55:54 -0600566 &baseInstance));
567 if (!cubicData) {
568 return;
569 }
570 fBaseCubicVertex = baseInstance * 4;
571 this->prepareIndirectOuterCubicsAndTriangles(target, resolveLevelCounter, cubicData,
572 /*numTrianglesAtBeginningOfData=*/0);
573}
574
Chris Dalton078f8752020-07-30 19:50:46 -0600575void GrPathTessellateOp::prepareIndirectOuterCubicsAndTriangles(
Chris Daltonb5391d92020-05-24 14:55:54 -0600576 GrMeshDrawOp::Target* target, const GrResolveLevelCounter& resolveLevelCounter,
577 SkPoint* cubicData, int numTrianglesAtBeginningOfData) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600578 SkASSERT(target->caps().drawInstancedSupport());
Chris Daltonb27f39c2020-11-23 09:30:24 -0700579 SkASSERT(numTrianglesAtBeginningOfData + resolveLevelCounter.totalInstanceCount() > 0);
Chris Daltone74cebe2020-09-24 09:32:54 -0600580 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600581 SkASSERT(cubicData);
Chris Daltone74cebe2020-09-24 09:32:54 -0600582 SkASSERT(fCubicVertexCount == 0);
Chris Daltonb5391d92020-05-24 14:55:54 -0600583
Chris Dalton1443c9d2020-05-27 09:43:34 -0600584 fIndirectIndexBuffer = GrMiddleOutCubicShader::FindOrMakeMiddleOutIndexBuffer(
585 target->resourceProvider());
586 if (!fIndirectIndexBuffer) {
587 return;
588 }
589
Chris Daltonb5391d92020-05-24 14:55:54 -0600590 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with the base
591 // vertex on an instance boundary in order to accommodate this.
592 SkASSERT(fBaseCubicVertex % 4 == 0);
593 int baseInstance = fBaseCubicVertex >> 2;
594
595 // Start preparing the indirect draw buffer.
Chris Daltonb27f39c2020-11-23 09:30:24 -0700596 fIndirectDrawCount = resolveLevelCounter.totalIndirectDrawCount();
Chris Daltonb5391d92020-05-24 14:55:54 -0600597 if (numTrianglesAtBeginningOfData) {
598 ++fIndirectDrawCount; // Add an indirect draw for the triangles at the beginning.
599 }
600
601 // Allocate space for the GrDrawIndexedIndirectCommand structs.
602 GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
603 fIndirectDrawCount, &fIndirectDrawBuffer, &fIndirectDrawOffset);
604 if (!indirectData) {
605 SkASSERT(!fIndirectDrawBuffer);
606 return;
607 }
608
609 // Fill out the GrDrawIndexedIndirectCommand structs and determine the starting instance data
610 // location at each resolve level.
611 SkPoint* instanceLocations[kMaxResolveLevel + 1];
612 int indirectIdx = 0;
613 int runningInstanceCount = 0;
614 if (numTrianglesAtBeginningOfData) {
615 // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
616 // at the beginning of the instance buffer. Add a special-case indirect draw here that will
617 // emit the triangles [P0, P1, P2] from these 4-point instances.
618 indirectData[0] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
619 numTrianglesAtBeginningOfData, baseInstance);
620 indirectIdx = 1;
621 runningInstanceCount = numTrianglesAtBeginningOfData;
622 }
623 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
Chris Daltona6858ae2020-06-25 07:04:33 -0600624 int instanceCountAtCurrLevel = resolveLevelCounter[resolveLevel];
625 if (!instanceCountAtCurrLevel) {
626 SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
627 continue;
Chris Daltonb5391d92020-05-24 14:55:54 -0600628 }
Chris Daltona6858ae2020-06-25 07:04:33 -0600629 instanceLocations[resolveLevel] = cubicData + runningInstanceCount * 4;
630 indirectData[indirectIdx++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
631 resolveLevel, instanceCountAtCurrLevel, baseInstance + runningInstanceCount);
632 runningInstanceCount += instanceCountAtCurrLevel;
Chris Daltonb5391d92020-05-24 14:55:54 -0600633 }
634
635#ifdef SK_DEBUG
636 SkASSERT(indirectIdx == fIndirectDrawCount);
637 SkASSERT(runningInstanceCount == numTrianglesAtBeginningOfData +
Chris Daltonb27f39c2020-11-23 09:30:24 -0700638 resolveLevelCounter.totalInstanceCount());
Chris Daltonb5391d92020-05-24 14:55:54 -0600639 SkASSERT(fIndirectDrawCount > 0);
640
641 SkPoint* endLocations[kMaxResolveLevel + 1];
Chris Daltona6858ae2020-06-25 07:04:33 -0600642 int lastResolveLevel = 0;
643 for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
644 if (!instanceLocations[resolveLevel]) {
645 endLocations[resolveLevel] = nullptr;
646 continue;
647 }
648 endLocations[lastResolveLevel] = instanceLocations[resolveLevel];
649 lastResolveLevel = resolveLevel;
650 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600651 int totalInstanceCount = numTrianglesAtBeginningOfData +
Chris Daltonb27f39c2020-11-23 09:30:24 -0700652 resolveLevelCounter.totalInstanceCount();
Chris Daltona6858ae2020-06-25 07:04:33 -0600653 endLocations[lastResolveLevel] = cubicData + totalInstanceCount * 4;
Chris Daltonb5391d92020-05-24 14:55:54 -0600654#endif
655
656 fCubicVertexCount = numTrianglesAtBeginningOfData * 4;
657
Chris Daltonb27f39c2020-11-23 09:30:24 -0700658 if (resolveLevelCounter.totalInstanceCount()) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600659 GrVectorXform xform(fViewMatrix);
660 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
661 int level;
662 switch (verb) {
663 default:
664 continue;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700665 case SkPathVerb::kConic:
666 // We use the same quadratic formula for conics, ignoring w. This appears to be
667 // an upper bound on what the actual number of subdivisions would have been.
668 [[fallthrough]];
Chris Daltonb5391d92020-05-24 14:55:54 -0600669 case SkPathVerb::kQuad:
Chris Daltonb96995d2020-06-04 16:44:29 -0600670 level = GrWangsFormula::quadratic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb5391d92020-05-24 14:55:54 -0600671 break;
672 case SkPathVerb::kCubic:
Chris Daltonb96995d2020-06-04 16:44:29 -0600673 level = GrWangsFormula::cubic_log2(kLinearizationIntolerance, pts, xform);
Chris Daltonb27f39c2020-11-23 09:30:24 -0700674 break;
675 }
676 if (level == 0) {
677 continue;
678 }
679 level = std::min(level, kMaxResolveLevel);
680 switch (verb) {
681 case SkPathVerb::kQuad:
682 GrPathUtils::convertQuadToCubic(pts, instanceLocations[level]);
683 break;
684 case SkPathVerb::kCubic:
Chris Daltonb5391d92020-05-24 14:55:54 -0600685 memcpy(instanceLocations[level], pts, sizeof(SkPoint) * 4);
686 break;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700687 case SkPathVerb::kConic:
688 GrPathShader::WriteConicPatch(pts, *w, instanceLocations[level]);
689 break;
690 default:
691 SkUNREACHABLE;
Chris Daltonb5391d92020-05-24 14:55:54 -0600692 }
693 instanceLocations[level] += 4;
694 fCubicVertexCount += 4;
695 }
696 }
697
698#ifdef SK_DEBUG
699 for (int i = 1; i <= kMaxResolveLevel; ++i) {
700 SkASSERT(instanceLocations[i] == endLocations[i]);
701 }
702 SkASSERT(fCubicVertexCount == (numTrianglesAtBeginningOfData +
Chris Daltonb27f39c2020-11-23 09:30:24 -0700703 resolveLevelCounter.totalInstanceCount()) * 4);
Chris Daltonb5391d92020-05-24 14:55:54 -0600704#endif
Chris Daltonb5391d92020-05-24 14:55:54 -0600705}
706
Chris Dalton078f8752020-07-30 19:50:46 -0600707void GrPathTessellateOp::prepareTessellatedOuterCubics(GrMeshDrawOp::Target* target,
Chris Daltonb5391d92020-05-24 14:55:54 -0600708 int numCountedCurves) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600709 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Daltonb5391d92020-05-24 14:55:54 -0600710 SkASSERT(numCountedCurves >= 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600711 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600712 SkASSERT(fStencilCubicsProgram);
713 SkASSERT(fCubicVertexCount == 0);
Chris Dalton42915c22020-04-22 16:24:43 -0600714
715 if (numCountedCurves == 0) {
716 return;
717 }
718
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600719 auto* vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600720 sizeof(SkPoint), numCountedCurves * 4, &fCubicBuffer, &fBaseCubicVertex));
Chris Dalton04f9cda2020-04-23 10:04:25 -0600721 if (!vertexData) {
Chris Dalton42915c22020-04-22 16:24:43 -0600722 return;
723 }
Chris Dalton42915c22020-04-22 16:24:43 -0600724
Chris Daltonf7a33072020-05-01 10:33:08 -0600725 for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
726 switch (verb) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600727 default:
728 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600729 case SkPathVerb::kQuad:
730 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
Chris Dalton753c1b32020-11-02 12:00:19 -0700731 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600732 break;
733 case SkPathVerb::kCubic:
734 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
735 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
Chris Daltonf7a33072020-05-01 10:33:08 -0600736 break;
Chris Daltonb27f39c2020-11-23 09:30:24 -0700737 case SkPathVerb::kConic:
738 SkASSERT(fCubicVertexCount < numCountedCurves * 4);
739 GrPathShader::WriteConicPatch(pts, *w, vertexData + fCubicVertexCount);
740 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600741 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600742 fCubicVertexCount += 4;
Chris Dalton42915c22020-04-22 16:24:43 -0600743 }
Chris Dalton04f9cda2020-04-23 10:04:25 -0600744 SkASSERT(fCubicVertexCount == numCountedCurves * 4);
Chris Dalton42915c22020-04-22 16:24:43 -0600745}
746
Chris Dalton078f8752020-07-30 19:50:46 -0600747void GrPathTessellateOp::prepareTessellatedCubicWedges(GrMeshDrawOp::Target* target) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600748 SkASSERT(target->caps().shaderCaps()->tessellationSupport());
Chris Dalton04f9cda2020-04-23 10:04:25 -0600749 SkASSERT(!fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600750 SkASSERT(fStencilCubicsProgram);
751 SkASSERT(fCubicVertexCount == 0);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600752
Chris Dalton42915c22020-04-22 16:24:43 -0600753 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
754 // Each wedge has 5 vertices.
755 int maxVertices = (fPath.countVerbs() + 1) * 5;
756
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600757 GrEagerDynamicVertexAllocator vertexAlloc(target, &fCubicBuffer, &fBaseCubicVertex);
Chris Dalton42915c22020-04-22 16:24:43 -0600758 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
759 if (!vertexData) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600760 return;
Chris Dalton42915c22020-04-22 16:24:43 -0600761 }
Chris Dalton42915c22020-04-22 16:24:43 -0600762
763 GrMidpointContourParser parser(fPath);
764 while (parser.parseNextContour()) {
Chris Daltonf7a33072020-05-01 10:33:08 -0600765 SkPoint midpoint = parser.currentMidpoint();
766 SkPoint startPoint = {0, 0};
767 SkPoint lastPoint = startPoint;
768 for (auto [verb, pts, w] : parser.currentContour()) {
769 switch (verb) {
770 case SkPathVerb::kMove:
771 startPoint = lastPoint = pts[0];
Chris Dalton42915c22020-04-22 16:24:43 -0600772 continue;
Chris Daltonf7a33072020-05-01 10:33:08 -0600773 case SkPathVerb::kClose:
774 continue; // Ignore. We can assume an implicit close at the end.
Chris Dalton42915c22020-04-22 16:24:43 -0600775 case SkPathVerb::kLine:
Chris Dalton753c1b32020-11-02 12:00:19 -0700776 GrPathUtils::convertLineToCubic(pts[0], pts[1], vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600777 lastPoint = pts[1];
Chris Dalton42915c22020-04-22 16:24:43 -0600778 break;
779 case SkPathVerb::kQuad:
Chris Dalton753c1b32020-11-02 12:00:19 -0700780 GrPathUtils::convertQuadToCubic(pts, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600781 lastPoint = pts[2];
Chris Dalton42915c22020-04-22 16:24:43 -0600782 break;
783 case SkPathVerb::kCubic:
Chris Daltonf7a33072020-05-01 10:33:08 -0600784 memcpy(vertexData + fCubicVertexCount, pts, sizeof(SkPoint) * 4);
785 lastPoint = pts[3];
Chris Dalton42915c22020-04-22 16:24:43 -0600786 break;
787 case SkPathVerb::kConic:
Chris Daltonb27f39c2020-11-23 09:30:24 -0700788 GrPathShader::WriteConicPatch(pts, *w, vertexData + fCubicVertexCount);
789 lastPoint = pts[2];
790 break;
Chris Dalton42915c22020-04-22 16:24:43 -0600791 }
Chris Daltonf7a33072020-05-01 10:33:08 -0600792 vertexData[fCubicVertexCount + 4] = midpoint;
793 fCubicVertexCount += 5;
794 }
795 if (lastPoint != startPoint) {
Chris Dalton753c1b32020-11-02 12:00:19 -0700796 GrPathUtils::convertLineToCubic(lastPoint, startPoint, vertexData + fCubicVertexCount);
Chris Daltonf7a33072020-05-01 10:33:08 -0600797 vertexData[fCubicVertexCount + 4] = midpoint;
Chris Dalton04f9cda2020-04-23 10:04:25 -0600798 fCubicVertexCount += 5;
Chris Dalton42915c22020-04-22 16:24:43 -0600799 }
800 }
801
Chris Dalton04f9cda2020-04-23 10:04:25 -0600802 vertexAlloc.unlock(fCubicVertexCount);
Chris Dalton42915c22020-04-22 16:24:43 -0600803}
804
Chris Dalton078f8752020-07-30 19:50:46 -0600805void GrPathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600806 this->drawStencilPass(flushState);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600807 this->drawCoverPass(flushState);
Chris Daltonb832ce62020-01-06 19:49:37 -0700808}
809
Chris Dalton078f8752020-07-30 19:50:46 -0600810void GrPathTessellateOp::drawStencilPass(GrOpFlushState* flushState) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600811 if (fStencilTrianglesProgram && fTriangleVertexCount > 0) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600812 SkASSERT(fTriangleBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600813 flushState->bindPipelineAndScissorClip(*fStencilTrianglesProgram, this->bounds());
Chris Dalton85138672020-07-24 08:47:03 -0600814 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600815 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700816 }
817
Chris Daltone74cebe2020-09-24 09:32:54 -0600818 if (fCubicVertexCount > 0) {
819 SkASSERT(fStencilCubicsProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600820 SkASSERT(fCubicBuffer);
Chris Daltone74cebe2020-09-24 09:32:54 -0600821 flushState->bindPipelineAndScissorClip(*fStencilCubicsProgram, this->bounds());
Chris Daltonb5391d92020-05-24 14:55:54 -0600822 if (fIndirectDrawBuffer) {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600823 SkASSERT(fIndirectIndexBuffer);
Greg Daniel426274b2020-07-20 11:37:38 -0400824 flushState->bindBuffers(fIndirectIndexBuffer, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600825 flushState->drawIndexedIndirect(fIndirectDrawBuffer.get(), fIndirectDrawOffset,
826 fIndirectDrawCount);
827 } else {
Greg Daniel426274b2020-07-20 11:37:38 -0400828 flushState->bindBuffers(nullptr, nullptr, fCubicBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600829 flushState->draw(fCubicVertexCount, fBaseCubicVertex);
830 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
831 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
832 }
833 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700834 }
835}
836
Chris Dalton078f8752020-07-30 19:50:46 -0600837void GrPathTessellateOp::drawCoverPass(GrOpFlushState* flushState) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600838 if (fFillTrianglesProgram) {
Chris Dalton04f9cda2020-04-23 10:04:25 -0600839 SkASSERT(fTriangleBuffer);
Chris Dalton5e1545f2020-09-25 16:24:03 -0600840 SkASSERT(fTriangleVertexCount > 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700841
Chris Dalton5e1545f2020-09-25 16:24:03 -0600842 // We have a triangulation of the path's inner polygon. This is the fast path. Fill those
843 // triangles directly to the screen.
844 flushState->bindPipelineAndScissorClip(*fFillTrianglesProgram, this->bounds());
845 flushState->bindTextures(fFillTrianglesProgram->primProc(), nullptr, *fPipelineForFills);
Greg Daniel426274b2020-07-20 11:37:38 -0400846 flushState->bindBuffers(nullptr, nullptr, fTriangleBuffer);
Chris Daltonb5391d92020-05-24 14:55:54 -0600847 flushState->draw(fTriangleVertexCount, fBaseTriangleVertex);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600848
Chris Daltone74cebe2020-09-24 09:32:54 -0600849 if (fCubicVertexCount > 0) {
Chris Dalton5e1545f2020-09-25 16:24:03 -0600850 SkASSERT(fFillPathProgram);
Chris Daltonb5391d92020-05-24 14:55:54 -0600851 SkASSERT(fCubicBuffer);
852
Chris Dalton5e1545f2020-09-25 16:24:03 -0600853 // At this point, every pixel is filled in except the ones touched by curves.
854 // fFillPathProgram will issue a final cover pass over the curves by drawing their
855 // convex hulls. This will fill in any remaining samples and reset the stencil buffer.
856 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
857 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
Chris Dalton04f9cda2020-04-23 10:04:25 -0600858
859 // Here we treat fCubicBuffer as an instance buffer. It should have been prepared with
860 // the base vertex on an instance boundary in order to accommodate this.
861 SkASSERT((fCubicVertexCount % 4) == 0);
862 SkASSERT((fBaseCubicVertex % 4) == 0);
Greg Daniel426274b2020-07-20 11:37:38 -0400863 flushState->bindBuffers(nullptr, fCubicBuffer, nullptr);
Chris Daltonb5391d92020-05-24 14:55:54 -0600864 flushState->drawInstanced(fCubicVertexCount >> 2, fBaseCubicVertex >> 2, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700865 }
Chris Dalton5e1545f2020-09-25 16:24:03 -0600866 } else if (fFillPathProgram) {
867 // There are no triangles to fill. Just draw a bounding box.
868 flushState->bindPipelineAndScissorClip(*fFillPathProgram, this->bounds());
869 flushState->bindTextures(fFillPathProgram->primProc(), nullptr, *fPipelineForFills);
870 flushState->bindBuffers(nullptr, nullptr, nullptr);
871 flushState->draw(4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700872 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700873}