blob: 7e4840845a6387c970c6957dc07c6e6ab3516193 [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 Dalton4328e922020-01-29 13:16:14 -070013#include "src/gpu/GrTessellator.h"
14#include "src/gpu/tessellate/GrFillPathShader.h"
Chris Dalton40a1cee2020-01-16 01:29:37 -070015#include "src/gpu/tessellate/GrPathParser.h"
Chris Daltonf9aea7f2020-01-21 11:19:26 -070016#include "src/gpu/tessellate/GrStencilPathShader.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070017
18GrTessellatePathOp::FixedFunctionFlags GrTessellatePathOp::fixedFunctionFlags() const {
19 auto flags = FixedFunctionFlags::kUsesStencil;
20 if (GrAAType::kNone != fAAType) {
21 flags |= FixedFunctionFlags::kUsesHWAA;
22 }
23 return flags;
24}
25
26void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
Chris Daltond081dce2020-01-23 12:09:04 -070027 GrEagerDynamicVertexAllocator pathVertexAllocator(state, &fPathVertexBuffer, &fBasePathVertex);
28 GrEagerDynamicVertexAllocator cubicInstanceAllocator(state, &fCubicInstanceBuffer,
29 &fBaseCubicInstance);
30
Chris Dalton4328e922020-01-29 13:16:14 -070031 // First check if the path is large and/or simple enough that we can actually tessellate the
32 // inner polygon(s) on the CPU. This is our fastest approach. It allows us to stencil only the
33 // curves, and then draw the internal polygons directly to the final render target, thus filling
34 // in the majority of pixels in a single render pass.
35 SkScalar scales[2];
36 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
37 const SkRect& bounds = fPath.getBounds();
38 int numVerbs = fPath.countVerbs();
39 if (numVerbs <= 0) {
40 return;
41 }
42 float gpuFragmentWork = bounds.height() * scales[0] * bounds.width() * scales[1];
43 float cpuTessellationWork = (float)numVerbs * SkNextLog2(numVerbs); // N log N.
44 if (cpuTessellationWork * 500 + (256 * 256) < gpuFragmentWork) { // Don't try below 256x256.
45 bool pathIsLinear;
46 // PathToTriangles(..kSimpleInnerPolygon..) will fail if the inner polygon is not simple.
47 if ((fPathVertexCount = GrTessellator::PathToTriangles(
48 fPath, 0, SkRect::MakeEmpty(), &pathVertexAllocator,
49 GrTessellator::Mode::kSimpleInnerPolygons, &pathIsLinear))) {
50 if (((Flags::kStencilOnly | Flags::kWireframe) & fFlags) ||
51 GrAAType::kCoverage == fAAType ||
52 (state->appliedClip() && state->appliedClip()->hasStencilClip())) {
53 // If we have certain flags, mixed samples, or a stencil clip then we unfortunately
54 // can't fill the inner polygon directly. Create a stencil shader here to ensure we
55 // still stencil the entire path.
56 fStencilPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
57 }
58 if (!(Flags::kStencilOnly & fFlags)) {
59 fFillPathShader = state->allocator()->make<GrFillTriangleShader>(
60 fViewMatrix, fColor);
61 }
62 if (!pathIsLinear) {
63 fCubicInstanceCount = GrPathParser::EmitCubicInstances(
64 fPath, &cubicInstanceAllocator);
65 SkASSERT(fCubicInstanceCount);
66 }
67 return;
68 }
69 }
70
71 // Next see if we can split up inner polygon triangles and curves, and triangulate the inner
Chris Daltonf9aea7f2020-01-21 11:19:26 -070072 // polygon(s) more efficiently. This causes greater CPU overhead due to the extra shaders and
73 // draw calls, but the better triangulation can reduce the rasterizer load by a great deal on
74 // complex paths.
Chris Dalton4328e922020-01-29 13:16:14 -070075 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of multiplying.
76 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
Chris Daltonf9aea7f2020-01-21 11:19:26 -070077 if (rasterEdgeWork > 1000 * 1000) {
Chris Dalton4328e922020-01-29 13:16:14 -070078 if ((fPathVertexCount =
79 GrPathParser::EmitInnerPolygonTriangles(fPath, &pathVertexAllocator))) {
80 fStencilPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
81 }
Chris Daltond081dce2020-01-23 12:09:04 -070082 fCubicInstanceCount = GrPathParser::EmitCubicInstances(fPath, &cubicInstanceAllocator);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070083 return;
84 }
85
86 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Dalton4328e922020-01-29 13:16:14 -070087 if ((fPathVertexCount = GrPathParser::EmitCenterWedgePatches(fPath, &pathVertexAllocator))) {
88 fStencilPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
89 }
Chris Daltonb832ce62020-01-06 19:49:37 -070090}
91
92void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -060093 this->drawStencilPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -070094 if (!(Flags::kStencilOnly & fFlags)) {
Chris Daltonaa0e45c2020-03-16 10:05:11 -060095 this->drawCoverPass(state);
Chris Daltonb832ce62020-01-06 19:49:37 -070096 }
97}
98
Chris Daltonaa0e45c2020-03-16 10:05:11 -060099void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700100 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
101 constexpr static GrUserStencilSettings kIncrDecrStencil(
102 GrUserStencilSettings::StaticInitSeparate<
103 0x0000, 0x0000,
104 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
105 0xffff, 0xffff,
106 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
107 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
108 0xffff, 0xffff>());
109
110 // Inverts the bottom stencil bit. Used for "even/odd" fill.
111 constexpr static GrUserStencilSettings kInvertStencil(
112 GrUserStencilSettings::StaticInit<
113 0x0000,
114 GrUserStencilTest::kAlwaysIfInClip,
115 0xffff,
116 GrUserStencilOp::kInvert,
117 GrUserStencilOp::kKeep,
118 0x0001>());
119
120 GrPipeline::InitArgs initArgs;
121 if (GrAAType::kNone != fAAType) {
122 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
123 }
124 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
125 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
126 }
127 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
128 SkPathFillType::kEvenOdd == fPath.getFillType());
129 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
130 &kIncrDecrStencil : &kInvertStencil;
131 initArgs.fCaps = &state->caps();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600132 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(),
133 state->appliedHardClip());
Chris Dalton012f8492020-03-05 11:49:15 -0700134
Chris Dalton4328e922020-01-29 13:16:14 -0700135 if (fStencilPathShader) {
136 SkASSERT(fPathVertexBuffer);
Chris Dalton012f8492020-03-05 11:49:15 -0700137 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, fStencilPathShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600138 state->bindPipelineAndScissorClip(programInfo, this->bounds());
139 state->bindBuffers(nullptr, nullptr, fPathVertexBuffer.get());
140 state->draw(fPathVertexCount, fBasePathVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700141 }
142
143 if (fCubicInstanceBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700144 // Here we treat the cubic instance buffer as tessellation patches to stencil the curves.
Chris Dalton012f8492020-03-05 11:49:15 -0700145 GrStencilCubicShader shader(fViewMatrix);
146 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, &shader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600147 state->bindPipelineAndScissorClip(programInfo, this->bounds());
Chris Dalton012f8492020-03-05 11:49:15 -0700148 // Bind instancedBuff as vertex.
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600149 state->bindBuffers(nullptr, nullptr, fCubicInstanceBuffer.get());
150 state->draw(fCubicInstanceCount * 4, fBaseCubicInstance * 4);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700151 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700152
153 // http://skbug.com/9739
154 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
155 state->gpu()->insertManualFramebufferBarrier();
156 }
157}
158
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600159void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700160 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
161 // to zero; discards immediately on stencil values of zero.
162 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
163 // samples already inside the clip.
164 constexpr static GrUserStencilSettings kTestAndResetStencil(
165 GrUserStencilSettings::StaticInit<
166 0x0000,
167 GrUserStencilTest::kNotEqual,
168 0xffff,
169 GrUserStencilOp::kZero,
170 GrUserStencilOp::kKeep,
171 0xffff>());
172
173 GrPipeline::InitArgs initArgs;
174 if (GrAAType::kNone != fAAType) {
175 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
176 if (1 == state->proxy()->numSamples()) {
177 SkASSERT(GrAAType::kCoverage == fAAType);
178 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
179 // at every fragment. This way we will still get a double hit on shared edges, but
180 // whichever side comes first will cover every sample and will clear the stencil. The
181 // other side will then be discarded and not cause a double blend.
182 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
183 }
184 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700185 initArgs.fCaps = &state->caps();
186 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
187 initArgs.fOutputSwizzle = state->drawOpArgs().outputSwizzle();
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600188 GrPipeline pipeline(initArgs, std::move(fProcessors), state->detachAppliedClip());
Chris Daltonb832ce62020-01-06 19:49:37 -0700189
Chris Dalton4328e922020-01-29 13:16:14 -0700190 if (fFillPathShader) {
191 SkASSERT(fPathVertexBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700192
Chris Dalton4328e922020-01-29 13:16:14 -0700193 // These are a twist on the standard red book stencil settings that allow us to draw the
194 // inner polygon directly to the final render target. At this point, the curves are already
195 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
196 // not affected by any curves and we fill the path in directly. If the stencil value is
197 // nonzero, then we don't fill and instead continue the standard red book stencil process.
198 //
199 // NOTE: These settings are currently incompatible with a stencil clip.
200 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
201 GrUserStencilSettings::StaticInitSeparate<
202 0x0000, 0x0000,
203 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
204 0xffff, 0xffff,
205 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
206 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
207 0xffff, 0xffff>());
208
209 constexpr static GrUserStencilSettings kFillOrInvertStencil(
210 GrUserStencilSettings::StaticInit<
211 0x0000,
212 GrUserStencilTest::kEqual,
213 0xffff,
214 GrUserStencilOp::kKeep,
215 GrUserStencilOp::kZero,
216 0xffff>());
217
218 if (fStencilPathShader) {
219 // The path was already stencilled. Here we just need to do a cover pass.
220 pipeline.setUserStencil(&kTestAndResetStencil);
221 } else if (!fCubicInstanceBuffer) {
222 // There are no curves, so we can just ignore stencil and fill the path directly.
223 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
224 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
225 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
226 SkASSERT(!pipeline.hasStencilClip());
227 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
228 } else {
229 // Fill in the path pixels not touched by curves, invert stencil otherwise.
230 SkASSERT(!pipeline.hasStencilClip());
231 pipeline.setUserStencil(&kFillOrInvertStencil);
232 }
Chris Dalton012f8492020-03-05 11:49:15 -0700233 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, fFillPathShader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600234 state->bindPipelineAndScissorClip(programInfo, this->bounds());
235 state->bindTextures(*fFillPathShader, nullptr, pipeline);
236 state->bindBuffers(nullptr, nullptr, fPathVertexBuffer.get());
237 state->draw(fPathVertexCount, fBasePathVertex);
Chris Dalton4328e922020-01-29 13:16:14 -0700238
239 if (fCubicInstanceBuffer) {
240 // At this point, every pixel is filled in except the ones touched by curves. Issue a
241 // final cover pass over the curves by drawing their convex hulls. This will fill in any
242 // remaining samples and reset the stencil buffer.
Chris Dalton4328e922020-01-29 13:16:14 -0700243 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton012f8492020-03-05 11:49:15 -0700244 GrFillCubicHullShader shader(fViewMatrix, fColor);
245 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, &shader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600246 state->bindPipelineAndScissorClip(programInfo, this->bounds());
247 state->bindTextures(shader, nullptr, pipeline);
248 state->bindBuffers(nullptr, fCubicInstanceBuffer.get(), nullptr);
249 state->drawInstanced(fCubicInstanceCount, fBaseCubicInstance, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700250 }
251 } else {
252 // There is not a fill shader for the path. Just draw a bounding box.
Chris Dalton4328e922020-01-29 13:16:14 -0700253 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton012f8492020-03-05 11:49:15 -0700254 GrFillBoundingBoxShader shader(fViewMatrix, fColor, fPath.getBounds());
255 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, &shader);
Chris Daltonaa0e45c2020-03-16 10:05:11 -0600256 state->bindPipelineAndScissorClip(programInfo, this->bounds());
257 state->bindTextures(shader, nullptr, pipeline);
258 state->bindBuffers(nullptr, nullptr, nullptr);
259 state->draw(4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700260 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700261}