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