blob: 43fa30330308eea036ed65622a7664efb73f5e1c [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 Daltonb832ce62020-01-06 19:49:37 -070093 GrAppliedClip clip = state->detachAppliedClip();
Chris Dalton012f8492020-03-05 11:49:15 -070094 this->drawStencilPass(state, clip.hardClip());
Chris Daltonb832ce62020-01-06 19:49:37 -070095 if (!(Flags::kStencilOnly & fFlags)) {
Chris Dalton012f8492020-03-05 11:49:15 -070096 this->drawCoverPass(state, std::move(clip));
Chris Daltonb832ce62020-01-06 19:49:37 -070097 }
98}
99
Chris Dalton012f8492020-03-05 11:49:15 -0700100void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state, const GrAppliedHardClip& hardClip) {
Chris Daltonb832ce62020-01-06 19:49:37 -0700101 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
102 constexpr static GrUserStencilSettings kIncrDecrStencil(
103 GrUserStencilSettings::StaticInitSeparate<
104 0x0000, 0x0000,
105 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
106 0xffff, 0xffff,
107 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
108 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
109 0xffff, 0xffff>());
110
111 // Inverts the bottom stencil bit. Used for "even/odd" fill.
112 constexpr static GrUserStencilSettings kInvertStencil(
113 GrUserStencilSettings::StaticInit<
114 0x0000,
115 GrUserStencilTest::kAlwaysIfInClip,
116 0xffff,
117 GrUserStencilOp::kInvert,
118 GrUserStencilOp::kKeep,
119 0x0001>());
120
121 GrPipeline::InitArgs initArgs;
122 if (GrAAType::kNone != fAAType) {
123 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
124 }
125 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
126 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
127 }
128 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
129 SkPathFillType::kEvenOdd == fPath.getFillType());
130 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
131 &kIncrDecrStencil : &kInvertStencil;
132 initArgs.fCaps = &state->caps();
133
134 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(), hardClip);
Chris Daltonb832ce62020-01-06 19:49:37 -0700135
Chris Dalton012f8492020-03-05 11:49:15 -0700136 GrOpsRenderPass* renderPass = state->opsRenderPass();
137
Chris Dalton4328e922020-01-29 13:16:14 -0700138 if (fStencilPathShader) {
139 SkASSERT(fPathVertexBuffer);
Chris Dalton012f8492020-03-05 11:49:15 -0700140 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, fStencilPathShader);
141 renderPass->bindPipeline(programInfo, this->bounds(), hardClip.scissorRectIfEnabled());
142 renderPass->bindBuffers(nullptr, nullptr, fPathVertexBuffer.get());
143 renderPass->draw(fPathVertexCount, fBasePathVertex);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700144 }
145
146 if (fCubicInstanceBuffer) {
Chris Dalton4328e922020-01-29 13:16:14 -0700147 // Here we treat the cubic instance buffer as tessellation patches to stencil the curves.
Chris Dalton012f8492020-03-05 11:49:15 -0700148 GrStencilCubicShader shader(fViewMatrix);
149 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, &shader);
150 renderPass->bindPipeline(programInfo, this->bounds(), hardClip.scissorRectIfEnabled());
151 // Bind instancedBuff as vertex.
152 renderPass->bindBuffers(nullptr, nullptr, fCubicInstanceBuffer.get());
153 renderPass->draw(fCubicInstanceCount * 4, fBaseCubicInstance * 4);
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700154 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700155
156 // http://skbug.com/9739
157 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
158 state->gpu()->insertManualFramebufferBarrier();
159 }
160}
161
Chris Dalton012f8492020-03-05 11:49:15 -0700162void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state, GrAppliedClip&& clip) {
163 GrOpsRenderPass* renderPass = state->opsRenderPass();
164 const SkIRect* scissorRectIfEnabled = clip.scissorRectIfEnabled();
165
Chris Daltonb832ce62020-01-06 19:49:37 -0700166 // 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 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700191 initArgs.fCaps = &state->caps();
192 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
193 initArgs.fOutputSwizzle = state->drawOpArgs().outputSwizzle();
Chris Daltonb832ce62020-01-06 19:49:37 -0700194 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
Chris Daltonb832ce62020-01-06 19:49:37 -0700195
Chris Dalton4328e922020-01-29 13:16:14 -0700196 if (fFillPathShader) {
197 SkASSERT(fPathVertexBuffer);
Chris Daltonb832ce62020-01-06 19:49:37 -0700198
Chris Dalton4328e922020-01-29 13:16:14 -0700199 // 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 }
Chris Dalton012f8492020-03-05 11:49:15 -0700239 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, fFillPathShader);
240 renderPass->bindPipeline(programInfo, this->bounds(), scissorRectIfEnabled);
241 renderPass->bindTextures(*fFillPathShader, nullptr, pipeline);
242 renderPass->bindBuffers(nullptr, nullptr, fPathVertexBuffer.get());
243 renderPass->draw(fPathVertexCount, fBasePathVertex);
Chris Dalton4328e922020-01-29 13:16:14 -0700244
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.
Chris Dalton4328e922020-01-29 13:16:14 -0700249 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton012f8492020-03-05 11:49:15 -0700250 GrFillCubicHullShader shader(fViewMatrix, fColor);
251 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, &shader);
252 renderPass->bindPipeline(programInfo, this->bounds(), scissorRectIfEnabled);
253 renderPass->bindTextures(shader, nullptr, pipeline);
254 renderPass->bindBuffers(nullptr, fCubicInstanceBuffer.get(), nullptr);
255 renderPass->drawInstanced(fCubicInstanceCount, fBaseCubicInstance, 4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700256 }
257 } else {
258 // There is not a fill shader for the path. Just draw a bounding box.
Chris Dalton4328e922020-01-29 13:16:14 -0700259 pipeline.setUserStencil(&kTestAndResetStencil);
Chris Dalton012f8492020-03-05 11:49:15 -0700260 GrFillBoundingBoxShader shader(fViewMatrix, fColor, fPath.getBounds());
261 GrPathShader::ProgramInfo programInfo(state->outputView(), &pipeline, &shader);
262 renderPass->bindPipeline(programInfo, this->bounds(), scissorRectIfEnabled);
263 renderPass->bindTextures(shader, nullptr, pipeline);
264 renderPass->bindBuffers(nullptr, nullptr, nullptr);
265 renderPass->draw(4, 0);
Chris Dalton4328e922020-01-29 13:16:14 -0700266 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700267}