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/GrPathInnerTriangulateOp.h"
9
10 #include "src/gpu/GrEagerVertexAllocator.h"
11 #include "src/gpu/GrInnerFanTriangulator.h"
12 #include "src/gpu/GrOpFlushState.h"
13 #include "src/gpu/GrRecordingContextPriv.h"
14 #include "src/gpu/tessellate/GrFillPathShader.h"
15 #include "src/gpu/tessellate/GrPathTessellator.h"
16 #include "src/gpu/tessellate/GrStencilPathShader.h"
17 #include "src/gpu/tessellate/GrTessellationPathRenderer.h"
18
19 using OpFlags = GrTessellationPathRenderer::OpFlags;
20
visitProxies(const VisitProxyFunc & fn) const21 void GrPathInnerTriangulateOp::visitProxies(const VisitProxyFunc& fn) const {
22 if (fPipelineForFills) {
23 fPipelineForFills->visitProxies(fn);
24 } else {
25 fProcessors.visitProxies(fn);
26 }
27 }
28
fixedFunctionFlags() const29 GrDrawOp::FixedFunctionFlags GrPathInnerTriangulateOp::fixedFunctionFlags() const {
30 auto flags = FixedFunctionFlags::kUsesStencil;
31 if (GrAAType::kNone != fAAType) {
32 flags |= FixedFunctionFlags::kUsesHWAA;
33 }
34 return flags;
35 }
36
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)37 GrProcessorSet::Analysis GrPathInnerTriangulateOp::finalize(const GrCaps& caps,
38 const GrAppliedClip* clip,
39 GrClampType clampType) {
40 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
41 clampType, &fColor);
42 }
43
pushFanStencilProgram(const GrPathShader::ProgramArgs & args,const GrPipeline * pipelineForStencils,const GrUserStencilSettings * stencil)44 void GrPathInnerTriangulateOp::pushFanStencilProgram(const GrPathShader::ProgramArgs& args,
45 const GrPipeline* pipelineForStencils,
46 const GrUserStencilSettings* stencil) {
47 SkASSERT(pipelineForStencils);
48 fFanPrograms.push_back(GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
49 args, fViewMatrix, pipelineForStencils, stencil));
50 }
51
pushFanFillProgram(const GrPathShader::ProgramArgs & args,const GrUserStencilSettings * stencil)52 void GrPathInnerTriangulateOp::pushFanFillProgram(const GrPathShader::ProgramArgs& args,
53 const GrUserStencilSettings* stencil) {
54 SkASSERT(fPipelineForFills);
55 auto* shader = args.fArena->make<GrFillTriangleShader>(fViewMatrix, fColor);
56 fFanPrograms.push_back(GrPathShader::MakeProgram(args, shader, fPipelineForFills, stencil));
57 }
58
prePreparePrograms(const GrPathShader::ProgramArgs & args,GrAppliedClip && appliedClip)59 void GrPathInnerTriangulateOp::prePreparePrograms(const GrPathShader::ProgramArgs& args,
60 GrAppliedClip&& appliedClip) {
61 SkASSERT(!fFanTriangulator);
62 SkASSERT(!fFanPolys);
63 SkASSERT(!fPipelineForFills);
64 SkASSERT(!fTessellator);
65 SkASSERT(!fStencilCurvesProgram);
66 SkASSERT(fFanPrograms.empty());
67 SkASSERT(!fFillHullsProgram);
68
69 if (fPath.countVerbs() <= 0) {
70 return;
71 }
72
73 // If using wireframe, we have to fall back on a standard Redbook "stencil then fill" algorithm
74 // instead of bypassing the stencil buffer to fill the fan directly.
75 bool forceRedbookStencilPass = (fOpFlags & (OpFlags::kStencilOnly | OpFlags::kWireframe));
76 bool doFill = !(fOpFlags & OpFlags::kStencilOnly);
77
78 bool isLinear;
79 fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena);
80 fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear);
81
82 // Create a pipeline for stencil passes if needed.
83 const GrPipeline* pipelineForStencils = nullptr;
84 if (forceRedbookStencilPass || !isLinear) { // Curves always get stencilled.
85 pipelineForStencils = GrStencilPathShader::MakeStencilPassPipeline(args, fAAType, fOpFlags,
86 appliedClip.hardClip());
87 }
88
89 // Create a pipeline for fill passes if needed.
90 if (doFill) {
91 fPipelineForFills = GrFillPathShader::MakeFillPassPipeline(args, fAAType,
92 std::move(appliedClip),
93 std::move(fProcessors));
94 }
95
96 // Pass 1: Tessellate the outer curves into the stencil buffer.
97 if (!isLinear) {
98 // Always use indirect draws for now. Our goal in this op is to maximize GPU performance,
99 // and the middle-out topology used by indirect draws is easier on the rasterizer than what
100 // we can do with hw tessellation. So far we haven't found any platforms where trying to use
101 // hw tessellation here is worth it.
102 using DrawInnerFan = GrPathIndirectTessellator::DrawInnerFan;
103 fTessellator = args.fArena->make<GrPathIndirectTessellator>(fViewMatrix, fPath,
104 DrawInnerFan::kNo);
105 fStencilCurvesProgram = GrStencilPathShader::MakeStencilProgram<GrMiddleOutCubicShader>(
106 args, fViewMatrix, pipelineForStencils, fPath.getFillType());
107 }
108
109 // Pass 2: Fill the path's inner fan with a stencil test against the curves.
110 if (fFanPolys) {
111 if (forceRedbookStencilPass) {
112 // Use a standard Redbook "stencil then fill" algorithm instead of bypassing the stencil
113 // buffer to fill the fan directly.
114 this->pushFanStencilProgram(
115 args, pipelineForStencils,
116 GrStencilPathShader::StencilPassSettings(fPath.getFillType()));
117 if (doFill) {
118 this->pushFanFillProgram(args, GrFillPathShader::TestAndResetStencilSettings());
119 }
120 } else if (isLinear) {
121 // There are no outer curves! Ignore stencil and fill the path directly.
122 SkASSERT(!pipelineForStencils);
123 this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
124 } else if (!fPipelineForFills->hasStencilClip()) {
125 // These are a twist on the standard Redbook stencil settings that allow us to fill the
126 // inner polygon directly to the final render target. By the time these programs
127 // execute, the outer curves will already be stencilled in. So if the stencil value is
128 // zero, then it means the sample in question is not affected by any curves and we can
129 // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
130 // continue the standard Redbook counting process.
131 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
132 GrUserStencilSettings::StaticInitSeparate<
133 0x0000, 0x0000,
134 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
135 0xffff, 0xffff,
136 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
137 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
138 0xffff, 0xffff>());
139
140 constexpr static GrUserStencilSettings kFillOrInvertStencil(
141 GrUserStencilSettings::StaticInit<
142 0x0000,
143 GrUserStencilTest::kEqual,
144 0xffff,
145 GrUserStencilOp::kKeep,
146 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
147 GrUserStencilOp::kZero,
148 0xffff>());
149
150 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
151 ? &kFillOrIncrDecrStencil
152 : &kFillOrInvertStencil;
153 this->pushFanFillProgram(args, stencil);
154 } else {
155 // This is the same idea as above, but we use two passes instead of one because there is
156 // a stencil clip. The stencil test isn't expressive enough to do the above tests and
157 // also check the clip bit in a single pass.
158 constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
159 GrUserStencilSettings::StaticInit<
160 0x0000,
161 GrUserStencilTest::kEqualIfInClip,
162 0xffff,
163 GrUserStencilOp::kKeep,
164 GrUserStencilOp::kKeep,
165 0xffff>());
166
167 constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
168 GrUserStencilSettings::StaticInitSeparate<
169 0x0000, 0x0000,
170 // No need to check the clip because the previous stencil pass will have only
171 // written to samples already inside the clip.
172 GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual,
173 0xffff, 0xffff,
174 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
175 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
176 0xffff, 0xffff>());
177
178 constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
179 GrUserStencilSettings::StaticInit<
180 0x0000,
181 // No need to check the clip because the previous stencil pass will have only
182 // written to samples already inside the clip.
183 GrUserStencilTest::kNotEqual,
184 0xffff,
185 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
186 GrUserStencilOp::kZero,
187 GrUserStencilOp::kKeep,
188 0xffff>());
189
190 // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
191 this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
192
193 // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
194 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
195 ? &kIncrDecrStencilIfNonzero
196 : &kInvertStencilIfNonZero;
197 this->pushFanStencilProgram(args, pipelineForStencils, stencil);
198 }
199 }
200
201 // Pass 3: Draw convex hulls around each curve.
202 if (doFill && !isLinear) {
203 // By the time this program executes, every pixel will be filled in except the ones touched
204 // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
205 // This will fill in any remaining samples and reset the stencil values back to zero.
206 SkASSERT(fTessellator);
207 auto* hullShader = args.fArena->make<GrFillCubicHullShader>(fViewMatrix, fColor);
208 fFillHullsProgram = GrPathShader::MakeProgram(
209 args, hullShader, fPipelineForFills,
210 GrFillPathShader::TestAndResetStencilSettings());
211 }
212 }
213
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrXferProcessor::DstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)214 void GrPathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
215 const GrSurfaceProxyView& writeView,
216 GrAppliedClip* clip,
217 const GrXferProcessor::DstProxyView& dstProxyView,
218 GrXferBarrierFlags renderPassXferBarriers,
219 GrLoadOp colorLoadOp) {
220 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
221 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
222 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
223 if (fStencilCurvesProgram) {
224 context->priv().recordProgramInfo(fStencilCurvesProgram);
225 }
226 for (const GrProgramInfo* fanProgram : fFanPrograms) {
227 context->priv().recordProgramInfo(fanProgram);
228 }
229 if (fFillHullsProgram) {
230 context->priv().recordProgramInfo(fFillHullsProgram);
231 }
232 }
233
onPrepare(GrOpFlushState * flushState)234 void GrPathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
235 if (!fFanTriangulator) {
236 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
237 &flushState->dstProxyView(), flushState->renderPassBarriers(),
238 flushState->colorLoadOp(), &flushState->caps()},
239 flushState->detachAppliedClip());
240 if (!fFanTriangulator) {
241 return;
242 }
243 }
244
245 if (fFanPolys) {
246 GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
247 fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
248 }
249
250 if (fTessellator) {
251 // Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
252 fTessellator->prepare(flushState, fViewMatrix, fPath, &fFanBreadcrumbs);
253 }
254 }
255
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)256 void GrPathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
257 if (fStencilCurvesProgram) {
258 SkASSERT(fTessellator);
259 flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
260 fTessellator->draw(flushState);
261 }
262
263 for (const GrProgramInfo* fanProgram : fFanPrograms) {
264 SkASSERT(fFanBuffer);
265 flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
266 flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
267 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
268 flushState->draw(fFanVertexCount, fBaseFanVertex);
269 }
270
271 if (fFillHullsProgram) {
272 SkASSERT(fTessellator);
273 flushState->bindPipelineAndScissorClip(*fFillHullsProgram, this->bounds());
274 flushState->bindTextures(fFillHullsProgram->geomProc(), nullptr, *fPipelineForFills);
275 fTessellator->drawHullInstances(flushState);
276 }
277 }
278