• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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/PathStencilCoverOp.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/GrGLSLFragmentShaderBuilder.h"
16 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
17 #include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
18 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
19 #include "src/gpu/ganesh/tessellate/GrPathTessellationShader.h"
20 #include "src/gpu/tessellate/AffineMatrix.h"
21 #include "src/gpu/tessellate/FixedCountBufferUtils.h"
22 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
23 #include "src/gpu/tessellate/Tessellation.h"
24 
25 namespace {
26 
27 // Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
28 // edges of the path.
29 // NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
30 class BoundingBoxShader : public GrGeometryProcessor {
31 public:
BoundingBoxShader(SkPMColor4f color,const GrShaderCaps & shaderCaps)32     BoundingBoxShader(SkPMColor4f color, const GrShaderCaps& shaderCaps)
33             : GrGeometryProcessor(kTessellate_BoundingBoxShader_ClassID)
34             , fColor(color) {
35         if (!shaderCaps.fVertexIDSupport) {
36             constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType,
37                                                         SkSLType::kFloat2);
38             this->setVertexAttributesWithImplicitOffsets(&kUnitCoordAttrib, 1);
39         }
40         constexpr static Attribute kInstanceAttribs[] = {
41             {"matrix2d", kFloat4_GrVertexAttribType, SkSLType::kFloat4},
42             {"translate", kFloat2_GrVertexAttribType, SkSLType::kFloat2},
43             {"pathBounds", kFloat4_GrVertexAttribType, SkSLType::kFloat4}
44         };
45         this->setInstanceAttributesWithImplicitOffsets(kInstanceAttribs,
46                                                        std::size(kInstanceAttribs));
47     }
48 
49 private:
name() const50     const char* name() const final { return "tessellate_BoundingBoxShader"; }
addToKey(const GrShaderCaps &,skgpu::KeyBuilder *) const51     void addToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const final {}
52     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
53 
54     const SkPMColor4f fColor;
55 };
56 
makeProgramImpl(const GrShaderCaps &) const57 std::unique_ptr<GrGeometryProcessor::ProgramImpl> BoundingBoxShader::makeProgramImpl(
58         const GrShaderCaps&) const {
59     class Impl : public ProgramImpl {
60     public:
61         void setData(const GrGLSLProgramDataManager& pdman,
62                      const GrShaderCaps&,
63                      const GrGeometryProcessor& gp) override {
64             const SkPMColor4f& color = gp.cast<BoundingBoxShader>().fColor;
65             pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
66         }
67 
68     private:
69         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
70             args.fVaryingHandler->emitAttributes(args.fGeomProc);
71 
72             // Vertex shader.
73             if (args.fShaderCaps->fVertexIDSupport) {
74                 // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
75                 // attrib.
76                 args.fVertBuilder->codeAppend(
77                 "float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);");
78             }
79             args.fVertBuilder->codeAppend(
80             // Bloat the bounding box by 1/4px to be certain we will reset every stencil value.
81             "float2x2 M_ = inverse(float2x2(matrix2d.xy, matrix2d.zw));"
82             "float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;"
83 
84             // Find the vertex position.
85             "float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, unitCoord);"
86             "float2 vertexpos = float2x2(matrix2d.xy, matrix2d.zw) * localcoord + translate;"
87             );
88             gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord");
89             gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertexpos");
90 
91             // Fragment shader.
92             const char* color;
93             fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
94                                                              SkSLType::kHalf4, "color", &color);
95             args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
96             args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
97         }
98 
99         GrGLSLUniformHandler::UniformHandle fColorUniform;
100     };
101 
102     return std::make_unique<Impl>();
103 }
104 
105 }  // anonymous namespace
106 
107 namespace skgpu::v1 {
108 
visitProxies(const GrVisitProxyFunc & func) const109 void PathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
110     if (fCoverBBoxProgram) {
111         fCoverBBoxProgram->pipeline().visitProxies(func);
112     } else {
113         fProcessors.visitProxies(func);
114     }
115 }
116 
fixedFunctionFlags() const117 GrDrawOp::FixedFunctionFlags PathStencilCoverOp::fixedFunctionFlags() const {
118     auto flags = FixedFunctionFlags::kUsesStencil;
119     if (fAAType != GrAAType::kNone) {
120         flags |= FixedFunctionFlags::kUsesHWAA;
121     }
122     return flags;
123 }
124 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)125 GrProcessorSet::Analysis PathStencilCoverOp::finalize(const GrCaps& caps,
126                                                       const GrAppliedClip* clip,
127                                                       GrClampType clampType) {
128     return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
129                                 clampType, &fColor);
130 }
131 
prePreparePrograms(const GrTessellationShader::ProgramArgs & args,GrAppliedClip && appliedClip)132 void PathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
133                                             GrAppliedClip&& appliedClip) {
134     SkASSERT(!fTessellator);
135     SkASSERT(!fStencilFanProgram);
136     SkASSERT(!fStencilPathProgram);
137     SkASSERT(!fCoverBBoxProgram);
138 
139     // We transform paths on the CPU. This allows for better batching.
140     const SkMatrix& shaderMatrix = SkMatrix::I();
141     auto pipelineFlags = (fPathFlags & FillPathFlags::kWireframe)
142             ? GrPipeline::InputFlags::kWireframe
143             : GrPipeline::InputFlags::kNone;
144     const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
145             args, fAAType, appliedClip.hardClip(), pipelineFlags);
146     const GrUserStencilSettings* stencilSettings = GrPathTessellationShader::StencilPathSettings(
147                     GrFillRuleForPathFillType(this->pathFillType()));
148 
149     if (fTotalCombinedPathVerbCnt > 50 &&
150         this->bounds().height() * this->bounds().width() > 256 * 256) {
151         // Large complex paths do better with a dedicated triangle shader for the inner fan.
152         // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
153         // to make sure it has an efficient middle-out topology.
154         auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena,
155                                                                          shaderMatrix,
156                                                                          SK_PMColor4fTRANSPARENT);
157         fStencilFanProgram = GrTessellationShader::MakeProgram(args,
158                                                                shader,
159                                                                stencilPipeline,
160                                                                stencilSettings);
161         fTessellator = PathCurveTessellator::Make(args.fArena,
162                                                   args.fCaps->shaderCaps()->fInfinitySupport);
163     } else {
164         fTessellator = PathWedgeTessellator::Make(args.fArena,
165                                                   args.fCaps->shaderCaps()->fInfinitySupport);
166     }
167     auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(),
168                                                       args.fArena,
169                                                       shaderMatrix,
170                                                       SK_PMColor4fTRANSPARENT,
171                                                       fTessellator->patchAttribs());
172     fStencilPathProgram = GrTessellationShader::MakeProgram(args,
173                                                             tessShader,
174                                                             stencilPipeline,
175                                                             stencilSettings);
176 
177     if (!(fPathFlags & FillPathFlags::kStencilOnly)) {
178         // Create a program that draws a bounding box over the path and fills its stencil coverage
179         // into the color buffer.
180         auto* bboxShader = args.fArena->make<BoundingBoxShader>(fColor, *args.fCaps->shaderCaps());
181         auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
182                                                                 std::move(appliedClip),
183                                                                 std::move(fProcessors));
184         auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings(
185                 SkPathFillType_IsInverse(this->pathFillType()));
186         fCoverBBoxProgram = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
187                 args.fCaps,
188                 args.fArena,
189                 bboxPipeline,
190                 args.fWriteView,
191                 args.fUsesMSAASurface,
192                 bboxShader,
193                 GrPrimitiveType::kTriangleStrip,
194                 args.fXferBarrierFlags,
195                 args.fColorLoadOp,
196                 bboxStencil);
197     }
198 }
199 
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)200 void PathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
201                                       const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
202                                       const GrDstProxyView& dstProxyView,
203                                       GrXferBarrierFlags renderPassXferBarriers,
204                                       GrLoadOp colorLoadOp) {
205     // DMSAA is not supported on DDL.
206     bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
207     this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
208                              &dstProxyView, renderPassXferBarriers, colorLoadOp,
209                              context->priv().caps()},
210                              (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
211     if (fStencilFanProgram) {
212         context->priv().recordProgramInfo(fStencilFanProgram);
213     }
214     if (fStencilPathProgram) {
215         context->priv().recordProgramInfo(fStencilPathProgram);
216     }
217     if (fCoverBBoxProgram) {
218         context->priv().recordProgramInfo(fCoverBBoxProgram);
219     }
220 }
221 
222 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
223 
onPrepare(GrOpFlushState * flushState)224 void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
225     if (!fTessellator) {
226         this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
227                                  flushState->usesMSAASurface(), &flushState->dstProxyView(),
228                                  flushState->renderPassBarriers(), flushState->colorLoadOp(),
229                                  &flushState->caps()}, flushState->detachAppliedClip());
230         if (!fTessellator) {
231             return;
232         }
233     }
234 
235     if (fStencilFanProgram) {
236         // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
237         // middle-out topology.
238         GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
239         // Path fans might have an extra edge from an implicit kClose at the end, but they also
240         // always begin with kMove. So the max possible number of edges in a single path is equal to
241         // the number of verbs. Therefore, the max number of combined fan edges in a path list is
242         // the number of combined verbs from the paths in the list.
243         // A single n-sided polygon is fanned by n-2 triangles. Multiple polygons with a combined
244         // edge count of n are fanned by strictly fewer triangles.
245         int maxTrianglesInFans = std::max(fTotalCombinedPathVerbCnt - 2, 0);
246         int fanTriangleCount = 0;
247         if (VertexWriter triangleVertexWriter =
248                     vertexAlloc.lockWriter(sizeof(SkPoint), maxTrianglesInFans * 3)) {
249             for (auto [pathMatrix, path, color] : *fPathDrawList) {
250                 tess::AffineMatrix m(pathMatrix);
251                 for (tess::PathMiddleOutFanIter it(path); !it.done();) {
252                     for (auto [p0, p1, p2] : it.nextStack()) {
253                         triangleVertexWriter << m.map2Points(p0, p1) << m.mapPoint(p2);
254                         ++fanTriangleCount;
255                     }
256                 }
257             }
258 
259 
260             SkASSERT(fanTriangleCount <= maxTrianglesInFans);
261             fFanVertexCount = fanTriangleCount * 3;
262             vertexAlloc.unlock(fFanVertexCount);
263         }
264     }
265 
266     auto tessShader = &fStencilPathProgram->geomProc().cast<GrPathTessellationShader>();
267     fTessellator->prepare(flushState,
268                           tessShader->viewMatrix(),
269                           *fPathDrawList,
270                           fTotalCombinedPathVerbCnt);
271 
272     if (fCoverBBoxProgram) {
273         size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();
274         VertexWriter vertexWriter = flushState->makeVertexWriter(instanceStride,
275                                                                  fPathCount,
276                                                                  &fBBoxBuffer,
277                                                                  &fBBoxBaseInstance);
278         SkDEBUGCODE(int pathCount = 0;)
279         for (auto [pathMatrix, path, color] : *fPathDrawList) {
280             SkDEBUGCODE(auto end = vertexWriter.mark(instanceStride));
281             vertexWriter << pathMatrix.getScaleX()
282                          << pathMatrix.getSkewY()
283                          << pathMatrix.getSkewX()
284                          << pathMatrix.getScaleY()
285                          << pathMatrix.getTranslateX()
286                          << pathMatrix.getTranslateY();
287             if (path.isInverseFillType()) {
288                 // Fill the entire backing store to make sure we clear every stencil value back to
289                 // 0. If there is a scissor it will have already clipped the stencil draw.
290                 auto rtBounds =
291                         flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect();
292                 SkASSERT(rtBounds == fOriginalDrawBounds);
293                 SkRect pathSpaceRTBounds;
294                 if (SkMatrixPriv::InverseMapRect(pathMatrix, &pathSpaceRTBounds, rtBounds)) {
295                     vertexWriter << pathSpaceRTBounds;
296                 } else {
297                     vertexWriter << path.getBounds();
298                 }
299             } else {
300                 vertexWriter << path.getBounds();
301             }
302             SkASSERT(vertexWriter.mark() == end);
303             SkDEBUGCODE(++pathCount;)
304         }
305         SkASSERT(pathCount == fPathCount);
306     }
307 
308     if (!flushState->caps().shaderCaps()->fVertexIDSupport) {
309         constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
310 
311         SKGPU_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
312 
313         fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
314                 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
315     }
316 }
317 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)318 void PathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
319     if (!fTessellator) {
320         return;
321     }
322 
323     if (fCoverBBoxProgram &&
324         fCoverBBoxProgram->geomProc().hasVertexAttributes() &&
325         !fBBoxVertexBufferIfNoIDSupport) {
326         return;
327     }
328 
329     // Stencil the inner fan, if any.
330     if (fFanVertexCount > 0) {
331         SkASSERT(fStencilFanProgram);
332         SkASSERT(fFanBuffer);
333         flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
334         flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
335         flushState->draw(fFanVertexCount, fFanBaseVertex);
336     }
337 
338     // Stencil the rest of the path.
339     SkASSERT(fStencilPathProgram);
340     flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
341     fTessellator->draw(flushState);
342 
343     // Fill in the bounding box (if not in stencil-only mode).
344     if (fCoverBBoxProgram) {
345         flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
346         flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
347                                  fCoverBBoxProgram->pipeline());
348         flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport);
349         flushState->drawInstanced(fPathCount, fBBoxBaseInstance, 4, 0);
350     }
351 }
352 
353 } // namespace skgpu::v1
354