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