blob: 53e83821b252091773524550daaf5a31881c4407 [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
88 if ((fPathVertexCount = GrPathParser::EmitCenterWedgePatches(fPath, &pathVertexAllocator))) {
89 fStencilPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
90 }
Chris Daltonb832ce62020-01-06 19:49:37 -070091}
92
93void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
Chris Daltonb832ce62020-01-06 19:49:37 -070094 GrAppliedClip clip = state->detachAppliedClip();
95 GrPipeline::FixedDynamicState fixedDynamicState;
96 if (clip.scissorState().enabled()) {
97 fixedDynamicState.fScissorRect = clip.scissorState().rect();
98 }
99
100 this->drawStencilPass(state, clip.hardClip(), &fixedDynamicState);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700101
Chris Daltonb832ce62020-01-06 19:49:37 -0700102 if (!(Flags::kStencilOnly & fFlags)) {
103 this->drawCoverPass(state, std::move(clip), &fixedDynamicState);
104 }
105}
106
107void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state, const GrAppliedHardClip& hardClip,
108 const GrPipeline::FixedDynamicState* fixedDynamicState) {
109 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
110 constexpr static GrUserStencilSettings kIncrDecrStencil(
111 GrUserStencilSettings::StaticInitSeparate<
112 0x0000, 0x0000,
113 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
114 0xffff, 0xffff,
115 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
116 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
117 0xffff, 0xffff>());
118
119 // Inverts the bottom stencil bit. Used for "even/odd" fill.
120 constexpr static GrUserStencilSettings kInvertStencil(
121 GrUserStencilSettings::StaticInit<
122 0x0000,
123 GrUserStencilTest::kAlwaysIfInClip,
124 0xffff,
125 GrUserStencilOp::kInvert,
126 GrUserStencilOp::kKeep,
127 0x0001>());
128
129 GrPipeline::InitArgs initArgs;
130 if (GrAAType::kNone != fAAType) {
131 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
132 }
133 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
134 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
135 }
136 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
137 SkPathFillType::kEvenOdd == fPath.getFillType());
138 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
139 &kIncrDecrStencil : &kInvertStencil;
140 initArgs.fCaps = &state->caps();
141
142 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(), hardClip);
Chris Daltonb832ce62020-01-06 19:49:37 -0700143
Chris Dalton4328e922020-01-29 13:16:14 -0700144 if (fStencilPathShader) {
145 SkASSERT(fPathVertexBuffer);
146 fStencilPathShader->issueDraw(state, &pipeline, fixedDynamicState, fPathVertexBuffer,
147 fPathVertexCount, fBasePathVertex, this->bounds());
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700148 }
149
150 if (fCubicInstanceBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700151 // Here we treat the cubic instance buffer as tessellation patches to stencil the curves.
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700152 GrMesh mesh(GrPrimitiveType::kPatches, 4);
153 mesh.setNonIndexedNonInstanced(fCubicInstanceCount * 4);
154 mesh.setVertexData(fCubicInstanceBuffer, fBaseCubicInstance * 4);
Chris Dalton4328e922020-01-29 13:16:14 -0700155 GrStencilCubicShader(fViewMatrix).issueDraw(
156 state, &pipeline, fixedDynamicState, mesh, this->bounds());
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700157 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700158
159 // http://skbug.com/9739
160 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
161 state->gpu()->insertManualFramebufferBarrier();
162 }
163}
164
165void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state, GrAppliedClip&& clip,
166 const GrPipeline::FixedDynamicState* fixedDynamicState) {
167 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
168 // to zero; discards immediately on stencil values of zero.
169 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
170 // samples already inside the clip.
171 constexpr static GrUserStencilSettings kTestAndResetStencil(
172 GrUserStencilSettings::StaticInit<
173 0x0000,
174 GrUserStencilTest::kNotEqual,
175 0xffff,
176 GrUserStencilOp::kZero,
177 GrUserStencilOp::kKeep,
178 0xffff>());
179
180 GrPipeline::InitArgs initArgs;
181 if (GrAAType::kNone != fAAType) {
182 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
183 if (1 == state->proxy()->numSamples()) {
184 SkASSERT(GrAAType::kCoverage == fAAType);
185 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
186 // at every fragment. This way we will still get a double hit on shared edges, but
187 // whichever side comes first will cover every sample and will clear the stencil. The
188 // other side will then be discarded and not cause a double blend.
189 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
190 }
191 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700192 initArgs.fCaps = &state->caps();
193 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
194 initArgs.fOutputSwizzle = state->drawOpArgs().outputSwizzle();
195
196 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Daltonb832ce62020-01-06 19:49:37 -0700197
Chris Dalton4328e922020-01-29 13:16:14 -0700198 if (fFillPathShader) {
199 SkASSERT(fPathVertexBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700200
Chris Dalton4328e922020-01-29 13:16:14 -0700201 // These are a twist on the standard red book stencil settings that allow us to draw the
202 // inner polygon directly to the final render target. At this point, the curves are already
203 // stencilled in. So if the stencil value is zero, then it means the path at our sample is
204 // not affected by any curves and we fill the path in directly. If the stencil value is
205 // nonzero, then we don't fill and instead continue the standard red book stencil process.
206 //
207 // NOTE: These settings are currently incompatible with a stencil clip.
208 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
209 GrUserStencilSettings::StaticInitSeparate<
210 0x0000, 0x0000,
211 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
212 0xffff, 0xffff,
213 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
214 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
215 0xffff, 0xffff>());
216
217 constexpr static GrUserStencilSettings kFillOrInvertStencil(
218 GrUserStencilSettings::StaticInit<
219 0x0000,
220 GrUserStencilTest::kEqual,
221 0xffff,
222 GrUserStencilOp::kKeep,
223 GrUserStencilOp::kZero,
224 0xffff>());
225
226 if (fStencilPathShader) {
227 // The path was already stencilled. Here we just need to do a cover pass.
228 pipeline.setUserStencil(&kTestAndResetStencil);
229 } else if (!fCubicInstanceBuffer) {
230 // There are no curves, so we can just ignore stencil and fill the path directly.
231 pipeline.setUserStencil(&GrUserStencilSettings::kUnused);
232 } else if (SkPathFillType::kWinding == fPath.getFillType()) {
233 // Fill in the path pixels not touched by curves, incr/decr stencil otherwise.
234 SkASSERT(!pipeline.hasStencilClip());
235 pipeline.setUserStencil(&kFillOrIncrDecrStencil);
236 } else {
237 // Fill in the path pixels not touched by curves, invert stencil otherwise.
238 SkASSERT(!pipeline.hasStencilClip());
239 pipeline.setUserStencil(&kFillOrInvertStencil);
240 }
241 fFillPathShader->issueDraw(state, &pipeline, fixedDynamicState, fPathVertexBuffer,
242 fPathVertexCount, fBasePathVertex, this->bounds());
243
244 if (fCubicInstanceBuffer) {
245 // At this point, every pixel is filled in except the ones touched by curves. Issue a
246 // final cover pass over the curves by drawing their convex hulls. This will fill in any
247 // remaining samples and reset the stencil buffer.
248 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
249 mesh.setInstanced(fCubicInstanceBuffer, fCubicInstanceCount, fBaseCubicInstance, 4);
250 pipeline.setUserStencil(&kTestAndResetStencil);
251 GrFillCubicHullShader(fViewMatrix, fColor).issueDraw(
252 state, &pipeline, fixedDynamicState, mesh, this->bounds());
253 }
254 } else {
255 // There is not a fill shader for the path. Just draw a bounding box.
256 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
257 mesh.setNonIndexedNonInstanced(4);
258 pipeline.setUserStencil(&kTestAndResetStencil);
259 GrFillBoundingBoxShader(fViewMatrix, fColor, fPath.getBounds()).issueDraw(
260 state, &pipeline, fixedDynamicState, mesh, this->bounds());
261 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700262}