blob: 8f4a60040648b3cb98542b0b6293eb0d046ed494 [file] [log] [blame]
Chris Daltonebb37e72021-01-27 17:59:45 -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/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
19using OpFlags = GrTessellationPathRenderer::OpFlags;
20
21void GrPathInnerTriangulateOp::visitProxies(const VisitProxyFunc& fn) const {
22 if (fPipelineForFills) {
23 fPipelineForFills->visitProxies(fn);
24 } else {
25 fProcessors.visitProxies(fn);
26 }
27}
28
29GrDrawOp::FixedFunctionFlags GrPathInnerTriangulateOp::fixedFunctionFlags() const {
30 auto flags = FixedFunctionFlags::kUsesStencil;
31 if (GrAAType::kNone != fAAType) {
32 flags |= FixedFunctionFlags::kUsesHWAA;
33 }
34 return flags;
35}
36
37GrProcessorSet::Analysis GrPathInnerTriangulateOp::finalize(const GrCaps& caps,
38 const GrAppliedClip* clip,
Chris Daltonebb37e72021-01-27 17:59:45 -070039 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060040 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
41 clampType, &fColor);
Chris Daltonebb37e72021-01-27 17:59:45 -070042}
43
44void 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
52void 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
59void 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
Chris Dalton57ab06c2021-04-22 12:57:28 -060073 // 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));
Chris Daltonebb37e72021-01-27 17:59:45 -070076 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.
Chris Dalton2758a312021-05-20 10:46:25 -0600102 fTessellator = args.fArena->make<GrPathIndirectTessellator>(
103 fViewMatrix, fPath, GrPathTessellator::DrawInnerFan::kNo);
Chris Daltona9f759d2021-05-18 12:37:08 -0600104 fStencilCurvesProgram = GrStencilPathShader::MakeStencilProgram<GrCurveMiddleOutShader>(
Chris Daltonebb37e72021-01-27 17:59:45 -0700105 args, fViewMatrix, pipelineForStencils, fPath.getFillType());
106 }
107
108 // Pass 2: Fill the path's inner fan with a stencil test against the curves.
109 if (fFanPolys) {
110 if (forceRedbookStencilPass) {
111 // Use a standard Redbook "stencil then fill" algorithm instead of bypassing the stencil
112 // buffer to fill the fan directly.
113 this->pushFanStencilProgram(
114 args, pipelineForStencils,
115 GrStencilPathShader::StencilPassSettings(fPath.getFillType()));
116 if (doFill) {
117 this->pushFanFillProgram(args, GrFillPathShader::TestAndResetStencilSettings());
118 }
119 } else if (isLinear) {
120 // There are no outer curves! Ignore stencil and fill the path directly.
121 SkASSERT(!pipelineForStencils);
122 this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
123 } else if (!fPipelineForFills->hasStencilClip()) {
124 // These are a twist on the standard Redbook stencil settings that allow us to fill the
125 // inner polygon directly to the final render target. By the time these programs
126 // execute, the outer curves will already be stencilled in. So if the stencil value is
127 // zero, then it means the sample in question is not affected by any curves and we can
128 // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
129 // continue the standard Redbook counting process.
130 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
131 GrUserStencilSettings::StaticInitSeparate<
132 0x0000, 0x0000,
133 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
134 0xffff, 0xffff,
135 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
136 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
137 0xffff, 0xffff>());
138
139 constexpr static GrUserStencilSettings kFillOrInvertStencil(
140 GrUserStencilSettings::StaticInit<
141 0x0000,
142 GrUserStencilTest::kEqual,
143 0xffff,
144 GrUserStencilOp::kKeep,
145 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
146 GrUserStencilOp::kZero,
147 0xffff>());
148
149 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
150 ? &kFillOrIncrDecrStencil
151 : &kFillOrInvertStencil;
152 this->pushFanFillProgram(args, stencil);
153 } else {
154 // This is the same idea as above, but we use two passes instead of one because there is
155 // a stencil clip. The stencil test isn't expressive enough to do the above tests and
156 // also check the clip bit in a single pass.
157 constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
158 GrUserStencilSettings::StaticInit<
159 0x0000,
160 GrUserStencilTest::kEqualIfInClip,
161 0xffff,
162 GrUserStencilOp::kKeep,
163 GrUserStencilOp::kKeep,
164 0xffff>());
165
166 constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
167 GrUserStencilSettings::StaticInitSeparate<
168 0x0000, 0x0000,
169 // No need to check the clip because the previous stencil pass will have only
170 // written to samples already inside the clip.
171 GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual,
172 0xffff, 0xffff,
173 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
174 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
175 0xffff, 0xffff>());
176
177 constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
178 GrUserStencilSettings::StaticInit<
179 0x0000,
180 // No need to check the clip because the previous stencil pass will have only
181 // written to samples already inside the clip.
182 GrUserStencilTest::kNotEqual,
183 0xffff,
184 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
185 GrUserStencilOp::kZero,
186 GrUserStencilOp::kKeep,
187 0xffff>());
188
189 // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
190 this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
191
192 // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
193 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
194 ? &kIncrDecrStencilIfNonzero
195 : &kInvertStencilIfNonZero;
196 this->pushFanStencilProgram(args, pipelineForStencils, stencil);
197 }
198 }
199
200 // Pass 3: Draw convex hulls around each curve.
201 if (doFill && !isLinear) {
202 // By the time this program executes, every pixel will be filled in except the ones touched
203 // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
204 // This will fill in any remaining samples and reset the stencil values back to zero.
205 SkASSERT(fTessellator);
206 auto* hullShader = args.fArena->make<GrFillCubicHullShader>(fViewMatrix, fColor);
207 fFillHullsProgram = GrPathShader::MakeProgram(
208 args, hullShader, fPipelineForFills,
209 GrFillPathShader::TestAndResetStencilSettings());
210 }
211}
212
213void GrPathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
214 const GrSurfaceProxyView& writeView,
215 GrAppliedClip* clip,
216 const GrXferProcessor::DstProxyView& dstProxyView,
217 GrXferBarrierFlags renderPassXferBarriers,
218 GrLoadOp colorLoadOp) {
219 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
220 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
221 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
222 if (fStencilCurvesProgram) {
223 context->priv().recordProgramInfo(fStencilCurvesProgram);
224 }
225 for (const GrProgramInfo* fanProgram : fFanPrograms) {
226 context->priv().recordProgramInfo(fanProgram);
227 }
228 if (fFillHullsProgram) {
229 context->priv().recordProgramInfo(fFillHullsProgram);
230 }
231}
232
233void GrPathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
234 if (!fFanTriangulator) {
235 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
236 &flushState->dstProxyView(), flushState->renderPassBarriers(),
237 flushState->colorLoadOp(), &flushState->caps()},
238 flushState->detachAppliedClip());
239 if (!fFanTriangulator) {
240 return;
241 }
242 }
243
244 if (fFanPolys) {
245 GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
246 fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
247 }
248
249 if (fTessellator) {
250 // Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
Chris Dalton8447f132021-05-21 15:54:23 -0600251 fTessellator->prepare(flushState, this->bounds(), fViewMatrix, fPath, &fFanBreadcrumbs);
Chris Daltonebb37e72021-01-27 17:59:45 -0700252 }
253}
254
255void GrPathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
256 if (fStencilCurvesProgram) {
257 SkASSERT(fTessellator);
258 flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
259 fTessellator->draw(flushState);
260 }
261
262 for (const GrProgramInfo* fanProgram : fFanPrograms) {
263 SkASSERT(fFanBuffer);
264 flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400265 flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
Chris Daltonebb37e72021-01-27 17:59:45 -0700266 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
267 flushState->draw(fFanVertexCount, fBaseFanVertex);
268 }
269
270 if (fFillHullsProgram) {
271 SkASSERT(fTessellator);
272 flushState->bindPipelineAndScissorClip(*fFillHullsProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400273 flushState->bindTextures(fFillHullsProgram->geomProc(), nullptr, *fPipelineForFills);
Chris Daltonebb37e72021-01-27 17:59:45 -0700274 fTessellator->drawHullInstances(flushState);
275 }
276}