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