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/ops/PathStencilCoverOp.h"
9
10#include "src/gpu/GrEagerVertexAllocator.h"
11#include "src/gpu/GrGpu.h"
12#include "src/gpu/GrOpFlushState.h"
13#include "src/gpu/GrRecordingContextPriv.h"
14#include "src/gpu/GrResourceProvider.h"
15#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
16#include "src/gpu/glsl/GrGLSLVarying.h"
17#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
18#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
19#include "src/gpu/tessellate/AffineMatrix.h"
20#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
21#include "src/gpu/tessellate/PathCurveTessellator.h"
22#include "src/gpu/tessellate/PathWedgeTessellator.h"
23#include "src/gpu/tessellate/Tessellation.h"
24#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
25
26namespace {
27
28// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
29// edges of the path.
30// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
31class BoundingBoxShader : public GrGeometryProcessor {
32public:
33    BoundingBoxShader(SkPMColor4f color, const GrShaderCaps& shaderCaps)
34            : GrGeometryProcessor(kTessellate_BoundingBoxShader_ClassID)
35            , fColor(color) {
36        if (!shaderCaps.vertexIDSupport()) {
37            constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType,
38                                                        kFloat2_GrSLType);
39            this->setVertexAttributes(&kUnitCoordAttrib, 1);
40        }
41        constexpr static Attribute kInstanceAttribs[] = {
42            {"matrix2d", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
43            {"translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType},
44            {"pathBounds", kFloat4_GrVertexAttribType, kFloat4_GrSLType}
45        };
46        this->setInstanceAttributes(kInstanceAttribs, SK_ARRAY_COUNT(kInstanceAttribs));
47    }
48
49private:
50    const char* name() const final { return "tessellate_BoundingBoxShader"; }
51    SkString getShaderDfxInfo() const override { return SkString("ShaderDfx_BoundingBoxShader"); }
52    void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
53    std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
54
55    const SkPMColor4f fColor;
56};
57
58std::unique_ptr<GrGeometryProcessor::ProgramImpl> BoundingBoxShader::makeProgramImpl(
59        const GrShaderCaps&) const {
60    class Impl : public ProgramImpl {
61    public:
62        void setData(const GrGLSLProgramDataManager& pdman,
63                     const GrShaderCaps&,
64                     const GrGeometryProcessor& gp) override {
65            const SkPMColor4f& color = gp.cast<BoundingBoxShader>().fColor;
66            pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
67        }
68
69    private:
70        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
71            args.fVaryingHandler->emitAttributes(args.fGeomProc);
72
73            // Vertex shader.
74            if (args.fShaderCaps->vertexIDSupport()) {
75                // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
76                // attrib.
77                args.fVertBuilder->codeAppend(R"(
78                float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
79            }
80            args.fVertBuilder->codeAppend(R"(
81            // Bloat the bounding box by 1/4px to be certain we will reset every stencil value.
82            float2x2 M_ = inverse(float2x2(matrix2d));
83            float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
84
85            // Find the vertex position.
86            float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, unitCoord);
87            float2 vertexpos = float2x2(matrix2d) * localcoord + translate;)");
88            gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
89            gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
90
91            // Fragment shader.
92            const char* color;
93            fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
94                                                             kHalf4_GrSLType, "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
107namespace skgpu::v1 {
108
109void PathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
110    if (fCoverBBoxProgram) {
111        fCoverBBoxProgram->pipeline().visitProxies(func);
112    } else {
113        fProcessors.visitProxies(func);
114    }
115}
116
117GrDrawOp::FixedFunctionFlags PathStencilCoverOp::fixedFunctionFlags() const {
118    auto flags = FixedFunctionFlags::kUsesStencil;
119    if (fAAType != GrAAType::kNone) {
120        flags |= FixedFunctionFlags::kUsesHWAA;
121    }
122    return flags;
123}
124
125GrProcessorSet::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
132void 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()->infinitySupport());
163    } else {
164        fTessellator = PathWedgeTessellator::Make(args.fArena,
165                                                  args.fCaps->shaderCaps()->infinitySupport());
166    }
167    auto* tessShader = GrPathTessellationShader::Make(args.fArena,
168                                                      shaderMatrix,
169                                                      SK_PMColor4fTRANSPARENT,
170                                                      fTotalCombinedPathVerbCnt,
171                                                      *stencilPipeline,
172                                                      fTessellator->patchAttribs(),
173                                                      *args.fCaps);
174    fStencilPathProgram = GrTessellationShader::MakeProgram(args,
175                                                            tessShader,
176                                                            stencilPipeline,
177                                                            stencilSettings);
178
179    if (!(fPathFlags & FillPathFlags::kStencilOnly)) {
180        // Create a program that draws a bounding box over the path and fills its stencil coverage
181        // into the color buffer.
182        auto* bboxShader = args.fArena->make<BoundingBoxShader>(fColor, *args.fCaps->shaderCaps());
183        auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
184                                                                std::move(appliedClip),
185                                                                std::move(fProcessors));
186        auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings(
187                SkPathFillType_IsInverse(this->pathFillType()));
188        fCoverBBoxProgram = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
189                args.fCaps,
190                args.fArena,
191                bboxPipeline,
192                args.fWriteView,
193                args.fUsesMSAASurface,
194                bboxShader,
195                GrPrimitiveType::kTriangleStrip,
196                args.fXferBarrierFlags,
197                args.fColorLoadOp,
198                bboxStencil);
199    }
200}
201
202void PathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
203                                      const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
204                                      const GrDstProxyView& dstProxyView,
205                                      GrXferBarrierFlags renderPassXferBarriers,
206                                      GrLoadOp colorLoadOp) {
207    // DMSAA is not supported on DDL.
208    bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
209    this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
210                             &dstProxyView, renderPassXferBarriers, colorLoadOp,
211                             context->priv().caps()},
212                             (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
213    if (fStencilFanProgram) {
214        context->priv().recordProgramInfo(fStencilFanProgram);
215    }
216    if (fStencilPathProgram) {
217        context->priv().recordProgramInfo(fStencilPathProgram);
218    }
219    if (fCoverBBoxProgram) {
220        context->priv().recordProgramInfo(fCoverBBoxProgram);
221    }
222}
223
224GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
225
226void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
227    if (!fTessellator) {
228        this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
229                                 flushState->usesMSAASurface(), &flushState->dstProxyView(),
230                                 flushState->renderPassBarriers(), flushState->colorLoadOp(),
231                                 &flushState->caps()}, flushState->detachAppliedClip());
232        if (!fTessellator) {
233            return;
234        }
235    }
236
237    if (fStencilFanProgram) {
238        // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
239        // middle-out topology.
240        GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
241        int maxCombinedFanEdges =
242                PathTessellator::MaxCombinedFanEdgesInPathDrawList(fTotalCombinedPathVerbCnt);
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(maxCombinedFanEdges - 2, 0);
246        int fanTriangleCount = 0;
247        if (VertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxTrianglesInFans * 3)) {
248            for (auto [pathMatrix, path, color] : *fPathDrawList) {
249                AffineMatrix m(pathMatrix);
250                for (PathMiddleOutFanIter it(path); !it.done();) {
251                    for (auto [p0, p1, p2] : it.nextStack()) {
252                        triangleVertexWriter << m.map2Points(p0, p1) << m.mapPoint(p2);
253                        ++fanTriangleCount;
254                    }
255                }
256            }
257        }
258        SkASSERT(fanTriangleCount <= maxTrianglesInFans);
259        fFanVertexCount = fanTriangleCount * 3;
260        vertexAlloc.unlock(fFanVertexCount);
261    }
262
263    auto tessShader = &fStencilPathProgram->geomProc().cast<GrPathTessellationShader>();
264    fTessellator->prepare(flushState,
265                          tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
266                          tessShader->viewMatrix(),
267                          *fPathDrawList,
268                          fTotalCombinedPathVerbCnt,
269                          tessShader->willUseTessellationShaders());
270
271    if (fCoverBBoxProgram) {
272        size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();
273        VertexWriter vertexWriter = flushState->makeVertexSpace(instanceStride,
274                                                                fPathCount,
275                                                                &fBBoxBuffer,
276                                                                &fBBoxBaseInstance);
277        SkDEBUGCODE(int pathCount = 0;)
278        for (auto [pathMatrix, path, color] : *fPathDrawList) {
279            SkDEBUGCODE(auto end = vertexWriter.makeOffset(instanceStride));
280            vertexWriter << pathMatrix.getScaleX()
281                         << pathMatrix.getSkewY()
282                         << pathMatrix.getSkewX()
283                         << pathMatrix.getScaleY()
284                         << pathMatrix.getTranslateX()
285                         << pathMatrix.getTranslateY();
286            if (path.isInverseFillType()) {
287                // Fill the entire backing store to make sure we clear every stencil value back to
288                // 0. If there is a scissor it will have already clipped the stencil draw.
289                auto rtBounds =
290                        flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect();
291                SkASSERT(rtBounds == fOriginalDrawBounds);
292                SkRect pathSpaceRTBounds;
293                if (SkMatrixPriv::InverseMapRect(pathMatrix, &pathSpaceRTBounds, rtBounds)) {
294                    vertexWriter << pathSpaceRTBounds;
295                } else {
296                    vertexWriter << path.getBounds();
297                }
298            } else {
299                vertexWriter << path.getBounds();
300            }
301            SkASSERT(vertexWriter == end);
302            SkDEBUGCODE(++pathCount;)
303        }
304        SkASSERT(pathCount == fPathCount);
305    }
306
307    if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
308        constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
309
310        GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
311
312        fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
313                GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
314    }
315}
316
317void PathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
318    if (!fTessellator) {
319        return;
320    }
321
322    if (fCoverBBoxProgram &&
323        fCoverBBoxProgram->geomProc().hasVertexAttributes() &&
324        !fBBoxVertexBufferIfNoIDSupport) {
325        return;
326    }
327
328    // Stencil the inner fan, if any.
329    if (fFanVertexCount > 0) {
330        SkASSERT(fStencilFanProgram);
331        SkASSERT(fFanBuffer);
332        flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
333        flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
334        flushState->draw(fFanVertexCount, fFanBaseVertex);
335    }
336
337    // Stencil the rest of the path.
338    SkASSERT(fStencilPathProgram);
339    flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
340    fTessellator->draw(flushState, fStencilPathProgram->geomProc().willUseTessellationShaders());
341    if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
342        flushState->gpu()->insertManualFramebufferBarrier();  // http://skbug.com/9739
343    }
344
345    // Fill in the bounding box (if not in stencil-only mode).
346    if (fCoverBBoxProgram) {
347        flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
348        flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
349                                 fCoverBBoxProgram->pipeline());
350        flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport);
351        flushState->drawInstanced(fPathCount, fBBoxBaseInstance, 4, 0);
352    }
353}
354
355} // namespace skgpu::v1
356