• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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