blob: a4ce3f11bd44dc710be66cf1b99a4914c4ce29e1 [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"
13#include "src/gpu/GrOpsRenderPass.h"
14#include "src/gpu/GrProgramInfo.h"
Chris Daltonb832ce62020-01-06 19:49:37 -070015#include "src/gpu/tessellate/GrCoverShader.h"
Chris Dalton40a1cee2020-01-16 01:29:37 -070016#include "src/gpu/tessellate/GrPathParser.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
27void GrTessellatePathOp::onPrepare(GrOpFlushState* state) {
Chris Daltond081dce2020-01-23 12:09:04 -070028 GrEagerDynamicVertexAllocator pathVertexAllocator(state, &fPathVertexBuffer, &fBasePathVertex);
29 GrEagerDynamicVertexAllocator cubicInstanceAllocator(state, &fCubicInstanceBuffer,
30 &fBaseCubicInstance);
31
Chris Daltonf9aea7f2020-01-21 11:19:26 -070032 // First see if we should split up inner polygon triangles and curves, and triangulate the inner
33 // polygon(s) more efficiently. This causes greater CPU overhead due to the extra shaders and
34 // draw calls, but the better triangulation can reduce the rasterizer load by a great deal on
35 // complex paths.
36 const SkRect& bounds = fPath.getBounds();
37 float scale = fViewMatrix.getMaxScale();
38 // Raster-edge work is 1-dimensional, so we sum height and width rather than multiplying them.
39 float rasterEdgeWork = (bounds.height() + bounds.width()) * scale * fPath.countVerbs();
40 if (rasterEdgeWork > 1000 * 1000) {
Chris Daltond081dce2020-01-23 12:09:04 -070041 fPathShader = state->allocator()->make<GrStencilTriangleShader>(fViewMatrix);
42 fPathVertexCount = GrPathParser::EmitInnerPolygonTriangles(fPath, &pathVertexAllocator);
43 fCubicInstanceCount = GrPathParser::EmitCubicInstances(fPath, &cubicInstanceAllocator);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070044 return;
45 }
46
47 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
Chris Daltond081dce2020-01-23 12:09:04 -070048 fPathShader = state->allocator()->make<GrStencilWedgeShader>(fViewMatrix);
49 fPathVertexCount = GrPathParser::EmitCenterWedgePatches(fPath, &pathVertexAllocator);
Chris Daltonb832ce62020-01-06 19:49:37 -070050}
51
52void GrTessellatePathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
Chris Daltonb832ce62020-01-06 19:49:37 -070053 GrAppliedClip clip = state->detachAppliedClip();
54 GrPipeline::FixedDynamicState fixedDynamicState;
55 if (clip.scissorState().enabled()) {
56 fixedDynamicState.fScissorRect = clip.scissorState().rect();
57 }
58
59 this->drawStencilPass(state, clip.hardClip(), &fixedDynamicState);
Chris Daltonf9aea7f2020-01-21 11:19:26 -070060
Chris Daltonb832ce62020-01-06 19:49:37 -070061 if (!(Flags::kStencilOnly & fFlags)) {
62 this->drawCoverPass(state, std::move(clip), &fixedDynamicState);
63 }
64}
65
66void GrTessellatePathOp::drawStencilPass(GrOpFlushState* state, const GrAppliedHardClip& hardClip,
67 const GrPipeline::FixedDynamicState* fixedDynamicState) {
68 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
69 constexpr static GrUserStencilSettings kIncrDecrStencil(
70 GrUserStencilSettings::StaticInitSeparate<
71 0x0000, 0x0000,
72 GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip,
73 0xffff, 0xffff,
74 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
75 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
76 0xffff, 0xffff>());
77
78 // Inverts the bottom stencil bit. Used for "even/odd" fill.
79 constexpr static GrUserStencilSettings kInvertStencil(
80 GrUserStencilSettings::StaticInit<
81 0x0000,
82 GrUserStencilTest::kAlwaysIfInClip,
83 0xffff,
84 GrUserStencilOp::kInvert,
85 GrUserStencilOp::kKeep,
86 0x0001>());
87
88 GrPipeline::InitArgs initArgs;
89 if (GrAAType::kNone != fAAType) {
90 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
91 }
92 if (state->caps().wireframeSupport() && (Flags::kWireframe & fFlags)) {
93 initArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe;
94 }
95 SkASSERT(SkPathFillType::kWinding == fPath.getFillType() ||
96 SkPathFillType::kEvenOdd == fPath.getFillType());
97 initArgs.fUserStencil = (SkPathFillType::kWinding == fPath.getFillType()) ?
98 &kIncrDecrStencil : &kInvertStencil;
99 initArgs.fCaps = &state->caps();
100
101 GrPipeline pipeline(initArgs, GrDisableColorXPFactory::MakeXferProcessor(), hardClip);
Chris Daltonb832ce62020-01-06 19:49:37 -0700102
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700103 if (fPathVertexBuffer) {
104 GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
105 state->proxy()->backendFormat(), state->view()->origin(),
106 &pipeline, fPathShader, fixedDynamicState, nullptr, 0,
107 fPathShader->primitiveType(),
108 fPathShader->tessellationPatchVertexCount());
Chris Daltonb832ce62020-01-06 19:49:37 -0700109
Chris Daltonf9aea7f2020-01-21 11:19:26 -0700110 GrMesh mesh(fPathShader->primitiveType(), fPathShader->tessellationPatchVertexCount());
111 mesh.setNonIndexedNonInstanced(fPathVertexCount);
112 mesh.setVertexData(fPathVertexBuffer, fBasePathVertex);
113
114 state->opsRenderPass()->draw(programInfo, &mesh, 1, this->bounds());
115 }
116
117 if (fCubicInstanceBuffer) {
118 // Here we treat the cubic instance buffer as tessellation patches.
119 GrStencilCubicShader shader(fViewMatrix);
120 GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
121 state->proxy()->backendFormat(), state->view()->origin(),
122 &pipeline, &shader, fixedDynamicState, nullptr, 0,
123 GrPrimitiveType::kPatches, 4);
124
125 GrMesh mesh(GrPrimitiveType::kPatches, 4);
126 mesh.setNonIndexedNonInstanced(fCubicInstanceCount * 4);
127 mesh.setVertexData(fCubicInstanceBuffer, fBaseCubicInstance * 4);
128
129 state->opsRenderPass()->draw(programInfo, &mesh, 1, this->bounds());
130 }
Chris Daltonb832ce62020-01-06 19:49:37 -0700131
132 // http://skbug.com/9739
133 if (state->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
134 state->gpu()->insertManualFramebufferBarrier();
135 }
136}
137
138void GrTessellatePathOp::drawCoverPass(GrOpFlushState* state, GrAppliedClip&& clip,
139 const GrPipeline::FixedDynamicState* fixedDynamicState) {
140 // Allows non-zero stencil values to pass and write a color, and resets the stencil value back
141 // to zero; discards immediately on stencil values of zero.
142 // NOTE: It's ok to not check the clip here because the previous stencil pass only wrote to
143 // samples already inside the clip.
144 constexpr static GrUserStencilSettings kTestAndResetStencil(
145 GrUserStencilSettings::StaticInit<
146 0x0000,
147 GrUserStencilTest::kNotEqual,
148 0xffff,
149 GrUserStencilOp::kZero,
150 GrUserStencilOp::kKeep,
151 0xffff>());
152
153 GrPipeline::InitArgs initArgs;
154 if (GrAAType::kNone != fAAType) {
155 initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
156 if (1 == state->proxy()->numSamples()) {
157 SkASSERT(GrAAType::kCoverage == fAAType);
158 // We are mixed sampled. Use conservative raster to make the sample coverage mask 100%
159 // at every fragment. This way we will still get a double hit on shared edges, but
160 // whichever side comes first will cover every sample and will clear the stencil. The
161 // other side will then be discarded and not cause a double blend.
162 initArgs.fInputFlags |= GrPipeline::InputFlags::kConservativeRaster;
163 }
164 }
165 initArgs.fUserStencil = &kTestAndResetStencil;
166 initArgs.fCaps = &state->caps();
167 initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
168 initArgs.fOutputSwizzle = state->drawOpArgs().outputSwizzle();
169
170 GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
171 GrCoverShader shader(fViewMatrix, fPath.getBounds(), fColor);
172 GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
173 state->proxy()->backendFormat(), state->view()->origin(), &pipeline,
174 &shader, fixedDynamicState, nullptr, 0,
175 GrPrimitiveType::kTriangleStrip);
176
177 GrMesh mesh(GrPrimitiveType::kTriangleStrip);
178 mesh.setNonIndexedNonInstanced(4);
179
180 state->opsRenderPass()->draw(programInfo, &mesh, 1, this->bounds());
181}