• 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/ganesh/ops/PathInnerTriangulateOp.h"
9 
10 #include "src/gpu/ganesh/GrEagerVertexAllocator.h"
11 #include "src/gpu/ganesh/GrGpu.h"
12 #include "src/gpu/ganesh/GrOpFlushState.h"
13 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
14 #include "src/gpu/ganesh/GrResourceProvider.h"
15 #include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
16 #include "src/gpu/ganesh/tessellate/GrPathTessellationShader.h"
17 #include "src/gpu/ganesh/tessellate/PathTessellator.h"
18 
19 #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
20 
21 namespace skgpu::v1 {
22 
23 namespace {
24 
25 // Fills an array of convex hulls surrounding 4-point cubic or conic instances. This shader is used
26 // for the "cover" pass after the curves have been fully stencilled.
27 class HullShader : public GrPathTessellationShader {
28 public:
HullShader(const SkMatrix & viewMatrix,SkPMColor4f color,const GrShaderCaps & shaderCaps)29     HullShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
30             : GrPathTessellationShader(kTessellate_HullShader_ClassID,
31                                        GrPrimitiveType::kTriangleStrip,
32                                        viewMatrix,
33                                        color,
34                                        PatchAttribs::kNone) {
35         fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
36         fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
37         if (!shaderCaps.fInfinitySupport) {
38             // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
39             // infinity can't detect this. On these platforms we also write out an extra float with
40             // each patch that explicitly tells the shader what type of curve it is.
41             fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, SkSLType::kFloat);
42         }
43         this->setInstanceAttributesWithImplicitOffsets(fInstanceAttribs.data(),
44                                                        fInstanceAttribs.size());
45         SkASSERT(fInstanceAttribs.size() <= kMaxInstanceAttribCount);
46 
47         if (!shaderCaps.fVertexIDSupport) {
48             constexpr static Attribute kVertexIdxAttrib("vertexidx", kFloat_GrVertexAttribType,
49                                                         SkSLType::kFloat);
50             this->setVertexAttributesWithImplicitOffsets(&kVertexIdxAttrib, 1);
51         }
52     }
53 
54 private:
name() const55     const char* name() const final { return "tessellate_HullShader"; }
addToKey(const GrShaderCaps &,KeyBuilder *) const56     void addToKey(const GrShaderCaps&, KeyBuilder*) const final {}
57     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
58 
59     constexpr static int kMaxInstanceAttribCount = 3;
60     SkSTArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs;
61 };
62 
makeProgramImpl(const GrShaderCaps &) const63 std::unique_ptr<GrGeometryProcessor::ProgramImpl> HullShader::makeProgramImpl(
64         const GrShaderCaps&) const {
65     class Impl : public GrPathTessellationShader::Impl {
66         void emitVertexCode(const GrShaderCaps& shaderCaps,
67                             const GrPathTessellationShader&,
68                             GrGLSLVertexBuilder* v,
69                             GrGLSLVaryingHandler*,
70                             GrGPArgs* gpArgs) override {
71             if (shaderCaps.fInfinitySupport) {
72                 v->insertFunction(
73                 "bool is_conic_curve() { return isinf(p23.w); }"
74                 "bool is_non_triangular_conic_curve() {"
75                     // We consider a conic non-triangular as long as its weight isn't infinity.
76                     // NOTE: "isinf == false" works on Mac Radeon GLSL; "!isinf" can get the wrong
77                     // answer.
78                     "return isinf(p23.z) == false;"
79                 "}"
80                 );
81             } else {
82                 v->insertFunction(SkStringPrintf(
83                 "bool is_conic_curve() { return curveType != %g; }",
84                         tess::kCubicCurveType).c_str());
85                 v->insertFunction(SkStringPrintf(
86                 "bool is_non_triangular_conic_curve() {"
87                     "return curveType == %g;"
88                 "}", tess::kConicCurveType).c_str());
89             }
90             v->codeAppend(
91             "float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;"
92             "if (is_conic_curve()) {"
93                 // Conics are 3 points, with the weight in p3.
94                 "float w = p3.x;"
95                 "p3 = p2;"  // Duplicate the endpoint for shared code that also runs on cubics.
96                 "if (is_non_triangular_conic_curve()) {"
97                     // Convert the points to a trapeziodal hull that circumcscribes the conic.
98                     "float2 p1w = p1 * w;"
99                     "float T = .51;"  // Bias outward a bit to ensure we cover the outermost samples.
100                     "float2 c1 = mix(p0, p1w, T);"
101                     "float2 c2 = mix(p2, p1w, T);"
102                     "float iw = 1 / mix(1, w, T);"
103                     "p2 = c2 * iw;"
104                     "p1 = c1 * iw;"
105                 "}"
106             "}"
107 
108             // Translate the points to v0..3 where v0=0.
109             "float2 v1 = p1 - p0;"
110             "float2 v2 = p2 - p0;"
111             "float2 v3 = p3 - p0;"
112 
113             // Reorder the points so v2 bisects v1 and v3.
114             "if (sign(cross_length_2d(v2, v1)) == sign(cross_length_2d(v2, v3))) {"
115                 "float2 tmp = p2;"
116                 "if (sign(cross_length_2d(v1, v2)) != sign(cross_length_2d(v1, v3))) {"
117                     "p2 = p1;"  // swap(p2, p1)
118                     "p1 = tmp;"
119                 "} else {"
120                     "p2 = p3;"  // swap(p2, p3)
121                     "p3 = tmp;"
122                 "}"
123             "}"
124             );
125 
126             if (shaderCaps.fVertexIDSupport) {
127                 // If we don't have sk_VertexID support then "vertexidx" already came in as a
128                 // vertex attrib.
129                 v->codeAppend(
130                 // sk_VertexID comes in fan order. Convert to strip order.
131                 "int vertexidx = sk_VertexID;"
132                 "vertexidx ^= vertexidx >> 1;");
133             }
134 
135             v->codeAppend(
136             // Find the "turn direction" of each corner and net turn direction.
137             "float vertexdir = 0;"
138             "float netdir = 0;"
139             "float2 prev, next;"
140             "float dir;"
141             "float2 localcoord;"
142             "float2 nextcoord;"
143             );
144 
145             for (int i = 0; i < 4; ++i) {
146                 v->codeAppendf(
147                 "prev = p%i - p%i;", i, (i + 3) % 4);
148                 v->codeAppendf(
149                 "next = p%i - p%i;", (i + 1) % 4, i);
150                 v->codeAppendf(
151                 "dir = sign(cross_length_2d(prev, next));"
152                 "if (vertexidx == %i) {"
153                     "vertexdir = dir;"
154                     "localcoord = p%i;"
155                     "nextcoord = p%i;"
156                 "}"
157                 "netdir += dir;", i, i, (i + 1) % 4);
158             }
159 
160             v->codeAppend(
161             // Remove the non-convex vertex, if any.
162             "if (vertexdir != sign(netdir)) {"
163                 "localcoord = nextcoord;"
164             "}"
165 
166             "float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;");
167             gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord");
168             gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertexpos");
169         }
170     };
171     return std::make_unique<Impl>();
172 }
173 
174 }  // anonymous namespace
175 
visitProxies(const GrVisitProxyFunc & func) const176 void PathInnerTriangulateOp::visitProxies(const GrVisitProxyFunc& func) const {
177     if (fPipelineForFills) {
178         fPipelineForFills->visitProxies(func);
179     } else {
180         fProcessors.visitProxies(func);
181     }
182 }
183 
fixedFunctionFlags() const184 GrDrawOp::FixedFunctionFlags PathInnerTriangulateOp::fixedFunctionFlags() const {
185     auto flags = FixedFunctionFlags::kUsesStencil;
186     if (GrAAType::kNone != fAAType) {
187         flags |= FixedFunctionFlags::kUsesHWAA;
188     }
189     return flags;
190 }
191 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)192 GrProcessorSet::Analysis PathInnerTriangulateOp::finalize(const GrCaps& caps,
193                                                           const GrAppliedClip* clip,
194                                                           GrClampType clampType) {
195     return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
196                                 clampType, &fColor);
197 }
198 
pushFanStencilProgram(const GrTessellationShader::ProgramArgs & args,const GrPipeline * pipelineForStencils,const GrUserStencilSettings * stencil)199 void PathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args,
200                                                    const GrPipeline* pipelineForStencils,
201                                                    const GrUserStencilSettings* stencil) {
202     SkASSERT(pipelineForStencils);
203     auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
204                                                                      SK_PMColor4fTRANSPARENT);
205     fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils,
206                                                              stencil)); }
207 
pushFanFillProgram(const GrTessellationShader::ProgramArgs & args,const GrUserStencilSettings * stencil)208 void PathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args,
209                                                 const GrUserStencilSettings* stencil) {
210     SkASSERT(fPipelineForFills);
211     auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
212                                                                      fColor);
213     fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills,
214                                                              stencil));
215 }
216 
prePreparePrograms(const GrTessellationShader::ProgramArgs & args,GrAppliedClip && appliedClip)217 void PathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
218                                                 GrAppliedClip&& appliedClip) {
219     SkASSERT(!fFanTriangulator);
220     SkASSERT(!fFanPolys);
221     SkASSERT(!fPipelineForFills);
222     SkASSERT(!fTessellator);
223     SkASSERT(!fStencilCurvesProgram);
224     SkASSERT(fFanPrograms.empty());
225     SkASSERT(!fCoverHullsProgram);
226 
227     if (fPath.countVerbs() <= 0) {
228         return;
229     }
230 
231     // If using wireframe, we have to fall back on a standard Redbook "stencil then cover" algorithm
232     // instead of bypassing the stencil buffer to fill the fan directly.
233     bool forceRedbookStencilPass =
234             (fPathFlags & (FillPathFlags::kStencilOnly | FillPathFlags::kWireframe));
235     bool doFill = !(fPathFlags & FillPathFlags::kStencilOnly);
236 
237     bool isLinear;
238     fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena);
239     fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear);
240 
241     // Create a pipeline for stencil passes if needed.
242     const GrPipeline* pipelineForStencils = nullptr;
243     if (forceRedbookStencilPass || !isLinear) {  // Curves always get stencilled.
244         auto pipelineFlags = (fPathFlags & FillPathFlags::kWireframe)
245                 ? GrPipeline::InputFlags::kWireframe
246                 : GrPipeline::InputFlags::kNone;
247         pipelineForStencils = GrPathTessellationShader::MakeStencilOnlyPipeline(
248                 args, fAAType, appliedClip.hardClip(), pipelineFlags);
249     }
250 
251     // Create a pipeline for fill passes if needed.
252     if (doFill) {
253         fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType,
254                                                                std::move(appliedClip),
255                                                                std::move(fProcessors));
256     }
257 
258     // Pass 1: Tessellate the outer curves into the stencil buffer.
259     if (!isLinear) {
260         fTessellator = PathCurveTessellator::Make(args.fArena,
261                                                   args.fCaps->shaderCaps()->fInfinitySupport);
262         auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(),
263                                                           args.fArena,
264                                                           fViewMatrix,
265                                                           SK_PMColor4fTRANSPARENT,
266                                                           fTessellator->patchAttribs());
267         const GrUserStencilSettings* stencilPathSettings =
268                 GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
269         fStencilCurvesProgram = GrTessellationShader::MakeProgram(args,
270                                                                   tessShader,
271                                                                   pipelineForStencils,
272                                                                   stencilPathSettings);
273     }
274 
275     // Pass 2: Fill the path's inner fan with a stencil test against the curves.
276     if (fFanPolys) {
277         if (forceRedbookStencilPass) {
278             // Use a standard Redbook "stencil then cover" algorithm instead of bypassing the
279             // stencil buffer to fill the fan directly.
280             const GrUserStencilSettings* stencilPathSettings =
281                     GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
282             this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings);
283             if (doFill) {
284                 this->pushFanFillProgram(args,
285                                          GrPathTessellationShader::TestAndResetStencilSettings());
286             }
287         } else if (isLinear) {
288             // There are no outer curves! Ignore stencil and fill the path directly.
289             SkASSERT(!pipelineForStencils);
290             this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
291         } else if (!fPipelineForFills->hasStencilClip()) {
292             // These are a twist on the standard Redbook stencil settings that allow us to fill the
293             // inner polygon directly to the final render target. By the time these programs
294             // execute, the outer curves will already be stencilled in. So if the stencil value is
295             // zero, then it means the sample in question is not affected by any curves and we can
296             // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
297             // continue the standard Redbook counting process.
298             constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
299                 GrUserStencilSettings::StaticInitSeparate<
300                     0x0000,                       0x0000,
301                     GrUserStencilTest::kEqual,    GrUserStencilTest::kEqual,
302                     0xffff,                       0xffff,
303                     GrUserStencilOp::kKeep,       GrUserStencilOp::kKeep,
304                     GrUserStencilOp::kIncWrap,    GrUserStencilOp::kDecWrap,
305                     0xffff,                       0xffff>());
306 
307             constexpr static GrUserStencilSettings kFillOrInvertStencil(
308                 GrUserStencilSettings::StaticInit<
309                     0x0000,
310                     GrUserStencilTest::kEqual,
311                     0xffff,
312                     GrUserStencilOp::kKeep,
313                     // "Zero" instead of "Invert" because the fan only touches any given pixel once.
314                     GrUserStencilOp::kZero,
315                     0xffff>());
316 
317             auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
318                     ? &kFillOrIncrDecrStencil
319                     : &kFillOrInvertStencil;
320             this->pushFanFillProgram(args, stencil);
321         } else {
322             // This is the same idea as above, but we use two passes instead of one because there is
323             // a stencil clip. The stencil test isn't expressive enough to do the above tests and
324             // also check the clip bit in a single pass.
325             constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
326                 GrUserStencilSettings::StaticInit<
327                     0x0000,
328                     GrUserStencilTest::kEqualIfInClip,
329                     0xffff,
330                     GrUserStencilOp::kKeep,
331                     GrUserStencilOp::kKeep,
332                     0xffff>());
333 
334             constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
335                 GrUserStencilSettings::StaticInitSeparate<
336                     0x0000,                         0x0000,
337                     // No need to check the clip because the previous stencil pass will have only
338                     // written to samples already inside the clip.
339                     GrUserStencilTest::kNotEqual,   GrUserStencilTest::kNotEqual,
340                     0xffff,                         0xffff,
341                     GrUserStencilOp::kIncWrap,      GrUserStencilOp::kDecWrap,
342                     GrUserStencilOp::kKeep,         GrUserStencilOp::kKeep,
343                     0xffff,                         0xffff>());
344 
345             constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
346                 GrUserStencilSettings::StaticInit<
347                     0x0000,
348                     // No need to check the clip because the previous stencil pass will have only
349                     // written to samples already inside the clip.
350                     GrUserStencilTest::kNotEqual,
351                     0xffff,
352                     // "Zero" instead of "Invert" because the fan only touches any given pixel once.
353                     GrUserStencilOp::kZero,
354                     GrUserStencilOp::kKeep,
355                     0xffff>());
356 
357             // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
358             this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
359 
360             // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
361             auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
362                     ? &kIncrDecrStencilIfNonzero
363                     : &kInvertStencilIfNonZero;
364             this->pushFanStencilProgram(args, pipelineForStencils, stencil);
365         }
366     }
367 
368     // Pass 3: Draw convex hulls around each curve.
369     if (doFill && !isLinear) {
370         // By the time this program executes, every pixel will be filled in except the ones touched
371         // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
372         // This will fill in any remaining samples and reset the stencil values back to zero.
373         SkASSERT(fTessellator);
374         auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor,
375                                                          *args.fCaps->shaderCaps());
376         fCoverHullsProgram = GrTessellationShader::MakeProgram(
377                 args, hullShader, fPipelineForFills,
378                 GrPathTessellationShader::TestAndResetStencilSettings());
379     }
380 }
381 
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)382 void PathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
383                                           const GrSurfaceProxyView& writeView,
384                                           GrAppliedClip* clip,
385                                           const GrDstProxyView& dstProxyView,
386                                           GrXferBarrierFlags renderPassXferBarriers,
387                                           GrLoadOp colorLoadOp) {
388     // DMSAA is not supported on DDL.
389     bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
390     this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
391                              &dstProxyView, renderPassXferBarriers, colorLoadOp,
392                              context->priv().caps()},
393                              (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
394     if (fStencilCurvesProgram) {
395         context->priv().recordProgramInfo(fStencilCurvesProgram);
396     }
397     for (const GrProgramInfo* fanProgram : fFanPrograms) {
398         context->priv().recordProgramInfo(fanProgram);
399     }
400     if (fCoverHullsProgram) {
401         context->priv().recordProgramInfo(fCoverHullsProgram);
402     }
403 }
404 
405 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gHullVertexBufferKey);
406 
onPrepare(GrOpFlushState * flushState)407 void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
408     const GrCaps& caps = flushState->caps();
409 
410     if (!fFanTriangulator) {
411         this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
412                                  flushState->usesMSAASurface(), &flushState->dstProxyView(),
413                                  flushState->renderPassBarriers(), flushState->colorLoadOp(),
414                                  &caps}, flushState->detachAppliedClip());
415         if (!fFanTriangulator) {
416             return;
417         }
418     }
419 
420     if (fFanPolys) {
421         GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
422         fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
423     }
424 
425     if (fTessellator) {
426         auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
427         fTessellator->prepareWithTriangles(flushState,
428                                            tessShader->viewMatrix(),
429                                            &fFanBreadcrumbs,
430                                            {SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT},
431                                            fPath.countVerbs());
432     }
433 
434     if (!caps.shaderCaps()->fVertexIDSupport) {
435         constexpr static float kStripOrderIDs[4] = {0, 1, 3, 2};
436 
437         SKGPU_DEFINE_STATIC_UNIQUE_KEY(gHullVertexBufferKey);
438 
439         fHullVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
440                 GrGpuBufferType::kVertex, sizeof(kStripOrderIDs), kStripOrderIDs,
441                 gHullVertexBufferKey);
442     }
443 }
444 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)445 void PathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
446     if (fCoverHullsProgram &&
447         fCoverHullsProgram->geomProc().hasVertexAttributes() &&
448         !fHullVertexBufferIfNoIDSupport) {
449         return;
450     }
451 
452     if (fStencilCurvesProgram) {
453         SkASSERT(fTessellator);
454         flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
455         fTessellator->draw(flushState);
456     }
457 
458     // Allocation of the fan vertex buffer may have failed but we already pushed back fan programs.
459     if (fFanBuffer) {
460         for (const GrProgramInfo* fanProgram : fFanPrograms) {
461             flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
462             flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
463             flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
464             flushState->draw(fFanVertexCount, fBaseFanVertex);
465         }
466     }
467 
468     if (fCoverHullsProgram) {
469         SkASSERT(fTessellator);
470         flushState->bindPipelineAndScissorClip(*fCoverHullsProgram, this->bounds());
471         flushState->bindTextures(fCoverHullsProgram->geomProc(), nullptr, *fPipelineForFills);
472         fTessellator->drawHullInstances(flushState, fHullVertexBufferIfNoIDSupport);
473     }
474 }
475 
476 } // namespace skgpu::v1
477 
478 #endif // SK_ENABLE_OPTIMIZE_SIZE
479