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