blob: 12e89415dbd8956b4bdc94db77d16f087714c5d2 [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
8#include "src/gpu/tessellate/GrTessellatePathOp.h"
9
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 Dalton17dc4182020-03-25 16:18:16 -060013#include "src/gpu/GrTriangulator.h"
Chris Dalton4328e922020-01-29 13:16:14 -070014#include "src/gpu/tessellate/GrFillPathShader.h"
Chris Dalton42915c22020-04-22 16:24:43 -060015#include "src/gpu/tessellate/GrInnerPolygonContourParser.h"
16#include "src/gpu/tessellate/GrMidpointContourParser.h"
Chris Daltonf9aea7f2020-01-21 11:19:26 -070017#include "src/gpu/tessellate/GrStencilPathShader.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070018
19GrTessellatePathOp::FixedFunctionFlags GrTessellatePathOp::fixedFunctionFlags() const {
20 auto flags = FixedFunctionFlags::kUsesStencil;
21 if (GrAAType::kNone != fAAType) {
22 flags |= FixedFunctionFlags::kUsesHWAA;
23 }
24 return flags;
25}
26
Robert Phillipsc655c3a2020-03-18 13:23:45 -040027void GrTessellatePathOp::onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -040028 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -040029 GrAppliedClip*,
30 const GrXferProcessor::DstProxyView&) {
31}
32
Chris Daltonb832ce62020-01-06 19:49:37 -070033void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
Chris Dalton4328e922020-01-29 13:16:14 -070034 // First check if the path is large and/or simple enough that we can actually tessellate the
35 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
36 // curves, and then draw the internal polygons directly to the final render target, thus filling
37 // in the majority of pixels in a single render pass.
38 SkScalar scales[2];
39 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
40 const SkRect& bounds = fPath.getBounds();
41 int numVerbs = fPath.countVerbs();
42 if (numVerbs <= 0) {
43 return;
44 }
45 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
46 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
47 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
Chris Dalton42915c22020-04-22 16:24:43 -060048 GrEagerDynamicVertexAllocator pathVertexAllocator(state, &fPathVertexBuffer,
49 &fBasePathVertex);
Chris Dalton8e2b6942020-04-22 15:55:00 -060050 int numCountedCurves;
Chris Dalton4328e922020-01-29 13:16:14 -070051 // PathToTriangles(..kSimpleInnerPolygon..) will fail if the inner polygon is not simple.
Chris Dalton17dc4182020-03-25 16:18:16 -060052 if ((fPathVertexCount = GrTriangulator::PathToTriangles(
Chris Dalton4328e922020-01-29 13:16:14 -070053 fPath, 0, SkRect::MakeEmpty(), &pathVertexAllocator,
Chris Dalton8e2b6942020-04-22 15:55:00 -060054 GrTriangulator::Mode::kSimpleInnerPolygons, &numCountedCurves))) {
Chris Dalton4328e922020-01-29 13:16:14 -070055 if (((Flags::kStencilOnly | Flags::kWireframe) & fFlags) ||
56 GrAAType::kCoverage == fAAType ||
57 (state->appliedClip() && state->appliedClip()->hasStencilClip())) {
58 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
59 // can't fill the inner polygon directly. Create a stencil shader here to ensure we
60 // still stencil the entire path.
61 fStencilPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
62 }
63 if (!(Flags::kStencilOnly & fFlags)) {
64 fFillPathShader = state->allocator()->make<GrFillTriangleShader>(
65 fViewMatrix, fColor);
66 }
Chris Dalton42915c22020-04-22 16:24:43 -060067 this->prepareOuterCubics(state, numCountedCurves);
Chris Dalton4328e922020-01-29 13:16:14 -070068 return;
69 }
70 }
71
72 // Next see if we can split up inner polygon triangles and curves, and triangulate the inner
Chris Daltonf9aea7f2020-01-21 11:19:26 -070073 // polygon(s) more efficiently. This causes greater CPU overhead due to the extra shaders and
74 // draw calls, but the better triangulation can reduce the rasterizer load by a great deal on
75 // complex paths.
Chris Dalton4328e922020-01-29 13:16:14 -070076 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
77 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonf9aea7f2020-01-21 11:19:26 -070078 if (rasterEdgeWork > 1000 * 1000) {
Chris Dalton42915c22020-04-22 16:24:43 -060079 int numCountedCurves;
80 if (this->prepareInnerTriangles(state, &numCountedCurves)) {
Chris Dalton4328e922020-01-29 13:16:14 -070081 fStencilPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
82 }
Chris Dalton42915c22020-04-22 16:24:43 -060083 this->prepareOuterCubics(state, numCountedCurves);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070084 return;
85 }
86
87 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Dalton42915c22020-04-22 16:24:43 -060088 if (this->prepareCubicWedges(state)) {
Chris Dalton4328e922020-01-29 13:16:14 -070089 fStencilPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
90 }
Chris Daltonb832ce62020-01-06 19:49:37 -070091}
92
Chris Dalton42915c22020-04-22 16:24:43 -060093bool GrTessellatePathOp::prepareInnerTriangles(GrOpFlushState* flushState, int* numCountedCurves) {
94 // No initial moveTo, plus an implicit close at the end; n-2 trianles fill an n-gon.
95 // Each triangle has 3 vertices.
96 int maxVertices = (fPath.countVerbs() - 1) * 3;
97
98 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fPathVertexBuffer, &fBasePathVertex);
99 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
100 if (!vertexData) {
101 return false;
102 }
103 fPathVertexCount = 0;
104
105 GrInnerPolygonContourParser parser(fPath, maxVertices);
106 while (parser.parseNextContour()) {
107 fPathVertexCount += parser.emitInnerPolygon(vertexData + fPathVertexCount);
108 }
109 *numCountedCurves = parser.numCountedCurves();
110
111 vertexAlloc.unlock(fPathVertexCount);
112 return SkToBool(fPathVertexCount);
113}
114
115static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
116 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
117 return (b - a) * T + a;
118}
119
120static SkPoint write_line_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1) {
121 data[0] = p0;
122 data[1] = lerp(p0, p1, 1/3.f);
123 data[2] = lerp(p0, p1, 2/3.f);
124 data[3] = p1;
125 return data[3];
126}
127
128static SkPoint write_quadratic_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1,
129 const SkPoint& p2) {
130 data[0] = p0;
131 data[1] = lerp(p0, p1, 2/3.f);
132 data[2] = lerp(p1, p2, 1/3.f);
133 data[3] = p2;
134 return data[3];
135}
136
137static SkPoint write_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
138 const SkPoint& p3) {
139 data[0] = p0;
140 data[1] = p1;
141 data[2] = p2;
142 data[3] = p3;
143 return data[3];
144}
145
146void GrTessellatePathOp::prepareOuterCubics(GrOpFlushState* flushState, int numCountedCurves) {
147 SkASSERT(!fCubicInstanceBuffer);
148
149 if (numCountedCurves == 0) {
150 return;
151 }
152
153 auto* instanceData = static_cast<std::array<SkPoint, 4>*>(flushState->makeVertexSpace(
154 sizeof(SkPoint) * 4, numCountedCurves, &fCubicInstanceBuffer, &fBaseCubicInstance));
155 if (!instanceData) {
156 return;
157 }
158 fCubicInstanceCount = 0;
159
160 SkPath::Iter iter(fPath, false);
161 SkPath::Verb verb;
162 SkPoint pts[4];
163 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
164 if (SkPath::kQuad_Verb == verb) {
165 SkASSERT(fCubicInstanceCount < numCountedCurves);
166 write_quadratic_as_cubic(
167 instanceData[fCubicInstanceCount++].data(), pts[0], pts[1], pts[2]);
168 continue;
169 }
170 if (SkPath::kCubic_Verb == verb) {
171 SkASSERT(fCubicInstanceCount < numCountedCurves);
172 memcpy(instanceData[fCubicInstanceCount++].data(), pts, sizeof(SkPoint) * 4);
173 continue;
174 }
175 }
176 SkASSERT(fCubicInstanceCount == numCountedCurves);
177}
178
179bool GrTessellatePathOp::prepareCubicWedges(GrOpFlushState* flushState) {
180 // No initial moveTo, one wedge per verb, plus an implicit close at the end.
181 // Each wedge has 5 vertices.
182 int maxVertices = (fPath.countVerbs() + 1) * 5;
183
184 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fPathVertexBuffer, &fBasePathVertex);
185 auto* vertexData = vertexAlloc.lock<SkPoint>(maxVertices);
186 if (!vertexData) {
187 return false;
188 }
189 fPathVertexCount = 0;
190
191 GrMidpointContourParser parser(fPath);
192 while (parser.parseNextContour()) {
193 int ptsIdx = 0;
194 SkPoint lastPoint = parser.startPoint();
195 for (int i = 0; i < parser.countVerbs(); ++i) {
196 switch (parser.atVerb(i)) {
197 case SkPathVerb::kClose:
198 case SkPathVerb::kDone:
199 if (parser.startPoint() != lastPoint) {
200 lastPoint = write_line_as_cubic(
201 vertexData + fPathVertexCount, lastPoint, parser.startPoint());
202 break;
203 } // fallthru
204 default:
205 continue;
206
207 case SkPathVerb::kLine:
208 lastPoint = write_line_as_cubic(vertexData + fPathVertexCount, lastPoint,
209 parser.atPoint(ptsIdx));
210 ++ptsIdx;
211 break;
212 case SkPathVerb::kQuad:
213 lastPoint = write_quadratic_as_cubic(vertexData + fPathVertexCount, lastPoint,
214 parser.atPoint(ptsIdx),
215 parser.atPoint(ptsIdx + 1));
216 ptsIdx += 2;
217 break;
218 case SkPathVerb::kCubic:
219 lastPoint = write_cubic(vertexData + fPathVertexCount, lastPoint,
220 parser.atPoint(ptsIdx), parser.atPoint(ptsIdx + 1),
221 parser.atPoint(ptsIdx + 2));
222 ptsIdx += 3;
223 break;
224 case SkPathVerb::kConic:
225 SkUNREACHABLE;
226 }
227 vertexData[fPathVertexCount + 4] = parser.midpoint();
228 fPathVertexCount += 5;
229 }
230 }
231
232 vertexAlloc.unlock(fPathVertexCount);
233 return SkToBool(fPathVertexCount);
234}
235
Chris Daltonb832ce62020-01-06 19:49:37 -0700236void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600237 this->drawStencilPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700238 if (!(Flags::kStencilOnly & fFlags)) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600239 this->drawCoverPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -0700240 }
241}
242
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600243void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700244 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
245 constexpr static GrUserStencilSettings kIncrDecrStencil(
246 GrUserStencilSettings::StaticInitSeparate<
247 0x0000, 0x0000,
248 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
249 0xffff, 0xffff,
250 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
251 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
252 0xffff, 0xffff>());
253
254 // Inverts the bottom stencil bit. Used for "even/odd" fill.
255 constexpr static GrUserStencilSettings kInvertStencil(
256 GrUserStencilSettings::StaticInit<
257 0x0000,
258 GrUserStencilTest::kAlwaysIfInClip,
259 0xffff,
260 GrUserStencilOp::kInvert,
261 GrUserStencilOp::kKeep,
262 0x0001>());
263
264 GrPipeline::InitArgs initArgs;
265 if (GrAAType::kNone != fAAType) {
266 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
267 }
268 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
269 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
270 }
271 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
272 SkPathFillType::kEvenOdd == fPath.getFillType());
273 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
274 &kIncrDecrStencil : &kInvertStencil;
275 initArgs.fCaps = &state->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600276 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
277 state->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700278
Chris Dalton4328e922020-01-29 13:16:14 -0700279 if (fStencilPathShader) {
280 SkASSERT(fPathVertexBuffer);
Brian Salomon8afde5f2020-04-01 16:22:00 -0400281 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, fStencilPathShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600282 state->bindPipelineAndScissorClip(programInfo, this->bounds());
283 state->bindBuffers(nullptr, nullptr, fPathVertexBuffer.get());
284 state->draw(fPathVertexCount, fBasePathVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700285 }
286
287 if (fCubicInstanceBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700288 // Here we treat the cubic instance buffer as tessellation patches to stencil the curves.
Chris Dalton012f8492020-03-05 11:49:15 -0700289 GrStencilCubicShader shader(fViewMatrix);
Brian Salomon8afde5f2020-04-01 16:22:00 -0400290 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &shader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600291 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton012f8492020-03-05 11:49:15 -0700292 // Bind instancedBuff as vertex.
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600293 state->bindBuffers(nullptr, nullptr, fCubicInstanceBuffer.get());
294 state->draw(fCubicInstanceCount * 4, fBaseCubicInstance * 4);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700295 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700296
297 // http://skbug.com/9739
298 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
299 state->gpu()->insertManualFramebufferBarrier();
300 }
301}
302
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600303void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700304 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
305 // to zero; discards immediately on stencil values of zero.
306 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
307 // samples already inside the clip.
308 constexpr static GrUserStencilSettings kTestAndResetStencil(
309 GrUserStencilSettings::StaticInit<
310 0x0000,
311 GrUserStencilTest::kNotEqual,
312 0xffff,
313 GrUserStencilOp::kZero,
314 GrUserStencilOp::kKeep,
315 0xffff>());
316
317 GrPipeline::InitArgs initArgs;
318 if (GrAAType::kNone != fAAType) {
319 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
320 if (1 == state->proxy()->numSamples()) {
321 SkASSERT(GrAAType::kCoverage == fAAType);
322 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
323 // at every fragment. This way we will still get a double hit on shared edges, but
324 // whichever side comes first will cover every sample and will clear the stencil. The
325 // other side will then be discarded and not cause a double blend.
326 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
327 }
328 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700329 initArgs.fCaps = &state->caps();
330 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
Brian Salomon982f5462020-03-30 12:52:33 -0400331 initArgs.fWriteSwizzle = state->drawOpArgs().writeSwizzle();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600332 GrPipeline pipeline(initArgs, std::move(fProcessors), state->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700333
Chris Dalton4328e922020-01-29 13:16:14 -0700334 if (fFillPathShader) {
335 SkASSERT(fPathVertexBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700336
Chris Dalton4328e922020-01-29 13:16:14 -0700337 // These are a twist on the standard red book stencil settings that allow us to draw the
338 // inner polygon directly to the final render target. At this point, the curves are already
339 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
340 // not affected by any curves and we fill the path in directly. If the stencil value is
341 // nonzero, then we don't fill and instead continue the standard red book stencil process.
342 //
343 // NOTE: These settings are currently incompatible with a stencil clip.
344 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
345 GrUserStencilSettings::StaticInitSeparate<
346 0x0000, 0x0000,
347 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
348 0xffff, 0xffff,
349 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
350 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
351 0xffff, 0xffff>());
352
353 constexpr static GrUserStencilSettings kFillOrInvertStencil(
354 GrUserStencilSettings::StaticInit<
355 0x0000,
356 GrUserStencilTest::kEqual,
357 0xffff,
358 GrUserStencilOp::kKeep,
359 GrUserStencilOp::kZero,
360 0xffff>());
361
362 if (fStencilPathShader) {
363 // The path was already stencilled. Here we just need to do a cover pass.
364 pipeline.setUserStencil(&kTestAndResetStencil);
365 } else if (!fCubicInstanceBuffer) {
366 // There are no curves, so we can just ignore stencil and fill the path directly.
367 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
368 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
369 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
370 SkASSERT(!pipeline.hasStencilClip());
371 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
372 } else {
373 // Fill in the path pixels not touched by curves, invert stencil otherwise.
374 SkASSERT(!pipeline.hasStencilClip());
375 pipeline.setUserStencil(&kFillOrInvertStencil);
376 }
Brian Salomon8afde5f2020-04-01 16:22:00 -0400377 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, fFillPathShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600378 state->bindPipelineAndScissorClip(programInfo, this->bounds());
379 state->bindTextures(*fFillPathShader, nullptr, pipeline);
380 state->bindBuffers(nullptr, nullptr, fPathVertexBuffer.get());
381 state->draw(fPathVertexCount, fBasePathVertex);
Chris Dalton4328e922020-01-29 13:16:14 -0700382
383 if (fCubicInstanceBuffer) {
384 // At this point, every pixel is filled in except the ones touched by curves. Issue a
385 // final cover pass over the curves by drawing their convex hulls. This will fill in any
386 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700387 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton012f8492020-03-05 11:49:15 -0700388 GrFillCubicHullShader shader(fViewMatrix, fColor);
Brian Salomon8afde5f2020-04-01 16:22:00 -0400389 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &shader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600390 state->bindPipelineAndScissorClip(programInfo, this->bounds());
391 state->bindTextures(shader, nullptr, pipeline);
392 state->bindBuffers(nullptr, fCubicInstanceBuffer.get(), nullptr);
393 state->drawInstanced(fCubicInstanceCount, fBaseCubicInstance, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700394 }
Chris Dalton42915c22020-04-22 16:24:43 -0600395 return;
Chris Dalton4328e922020-01-29 13:16:14 -0700396 }
Chris Dalton42915c22020-04-22 16:24:43 -0600397
398 // There is not a fill shader for the path. Just draw a bounding box.
399 pipeline.setUserStencil(&kTestAndResetStencil);
400 GrFillBoundingBoxShader shader(fViewMatrix, fColor, fPath.getBounds());
401 GrPathShader::ProgramInfo programInfo(state->writeView(), &pipeline, &shader);
402 state->bindPipelineAndScissorClip(programInfo, this->bounds());
403 state->bindTextures(shader, nullptr, pipeline);
404 state->bindBuffers(nullptr, nullptr, nullptr);
405 state->draw(4, 0);
Chris Daltonb832ce62020-01-06 19:49:37 -0700406}