• 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/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 
26 namespace {
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.
31 class BoundingBoxShader : public GrGeometryProcessor {
32 public:
BoundingBoxShader(SkPMColor4f color,const GrShaderCaps & shaderCaps)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 
49 private:
name() const50     const char* name() const final { return "tessellate_BoundingBoxShader"; }
addToKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const51     void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) 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->vertexIDSupport()) {
74                 // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
75                 // attrib.
76                 args.fVertBuilder->codeAppend(R"(
77                 float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
78             }
79             args.fVertBuilder->codeAppend(R"(
80             // Bloat the bounding box by 1/4px to be certain we will reset every stencil value.
81             float2x2 M_ = inverse(float2x2(matrix2d));
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) * localcoord + translate;)");
87             gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
88             gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
89 
90             // Fragment shader.
91             const char* color;
92             fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
93                                                              kHalf4_GrSLType, "color", &color);
94             args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
95             args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
96         }
97 
98         GrGLSLUniformHandler::UniformHandle fColorUniform;
99     };
100 
101     return std::make_unique<Impl>();
102 }
103 
104 }  // anonymous namespace
105 
106 namespace skgpu::v1 {
107 
visitProxies(const GrVisitProxyFunc & func) const108 void PathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
109     if (fCoverBBoxProgram) {
110         fCoverBBoxProgram->pipeline().visitProxies(func);
111     } else {
112         fProcessors.visitProxies(func);
113     }
114 }
115 
fixedFunctionFlags() const116 GrDrawOp::FixedFunctionFlags PathStencilCoverOp::fixedFunctionFlags() const {
117     auto flags = FixedFunctionFlags::kUsesStencil;
118     if (fAAType != GrAAType::kNone) {
119         flags |= FixedFunctionFlags::kUsesHWAA;
120     }
121     return flags;
122 }
123 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)124 GrProcessorSet::Analysis PathStencilCoverOp::finalize(const GrCaps& caps,
125                                                       const GrAppliedClip* clip,
126                                                       GrClampType clampType) {
127     return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
128                                 clampType, &fColor);
129 }
130 
prePreparePrograms(const GrTessellationShader::ProgramArgs & args,GrAppliedClip && appliedClip)131 void PathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
132                                             GrAppliedClip&& appliedClip) {
133     SkASSERT(!fTessellator);
134     SkASSERT(!fStencilFanProgram);
135     SkASSERT(!fStencilPathProgram);
136     SkASSERT(!fCoverBBoxProgram);
137 
138     // We transform paths on the CPU. This allows for better batching.
139     const SkMatrix& shaderMatrix = SkMatrix::I();
140     auto pipelineFlags = (fPathFlags & FillPathFlags::kWireframe)
141             ? GrPipeline::InputFlags::kWireframe
142             : GrPipeline::InputFlags::kNone;
143     const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
144             args, fAAType, appliedClip.hardClip(), pipelineFlags);
145     const GrUserStencilSettings* stencilSettings = GrPathTessellationShader::StencilPathSettings(
146                     GrFillRuleForPathFillType(this->pathFillType()));
147 
148     if (fTotalCombinedPathVerbCnt > 50 &&
149         this->bounds().height() * this->bounds().width() > 256 * 256) {
150         // Large complex paths do better with a dedicated triangle shader for the inner fan.
151         // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
152         // to make sure it has an efficient middle-out topology.
153         auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena,
154                                                                          shaderMatrix,
155                                                                          SK_PMColor4fTRANSPARENT);
156         fStencilFanProgram = GrTessellationShader::MakeProgram(args,
157                                                                shader,
158                                                                stencilPipeline,
159                                                                stencilSettings);
160         fTessellator = PathCurveTessellator::Make(args.fArena,
161                                                   args.fCaps->shaderCaps()->infinitySupport());
162     } else {
163         fTessellator = PathWedgeTessellator::Make(args.fArena,
164                                                   args.fCaps->shaderCaps()->infinitySupport());
165     }
166     auto* tessShader = GrPathTessellationShader::Make(args.fArena,
167                                                       shaderMatrix,
168                                                       SK_PMColor4fTRANSPARENT,
169                                                       fTotalCombinedPathVerbCnt,
170                                                       *stencilPipeline,
171                                                       fTessellator->patchAttribs(),
172                                                       *args.fCaps);
173     fStencilPathProgram = GrTessellationShader::MakeProgram(args,
174                                                             tessShader,
175                                                             stencilPipeline,
176                                                             stencilSettings);
177 
178     if (!(fPathFlags & FillPathFlags::kStencilOnly)) {
179         // Create a program that draws a bounding box over the path and fills its stencil coverage
180         // into the color buffer.
181         auto* bboxShader = args.fArena->make<BoundingBoxShader>(fColor, *args.fCaps->shaderCaps());
182         auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
183                                                                 std::move(appliedClip),
184                                                                 std::move(fProcessors));
185         auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings(
186                 SkPathFillType_IsInverse(this->pathFillType()));
187         fCoverBBoxProgram = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
188                 args.fCaps,
189                 args.fArena,
190                 bboxPipeline,
191                 args.fWriteView,
192                 args.fUsesMSAASurface,
193                 bboxShader,
194                 GrPrimitiveType::kTriangleStrip,
195                 args.fXferBarrierFlags,
196                 args.fColorLoadOp,
197                 bboxStencil);
198     }
199 }
200 
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)201 void PathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
202                                       const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
203                                       const GrDstProxyView& dstProxyView,
204                                       GrXferBarrierFlags renderPassXferBarriers,
205                                       GrLoadOp colorLoadOp) {
206     // DMSAA is not supported on DDL.
207     bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
208     this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
209                              &dstProxyView, renderPassXferBarriers, colorLoadOp,
210                              context->priv().caps()},
211                              (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
212     if (fStencilFanProgram) {
213         context->priv().recordProgramInfo(fStencilFanProgram);
214     }
215     if (fStencilPathProgram) {
216         context->priv().recordProgramInfo(fStencilPathProgram);
217     }
218     if (fCoverBBoxProgram) {
219         context->priv().recordProgramInfo(fCoverBBoxProgram);
220     }
221 }
222 
223 GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
224 
onPrepare(GrOpFlushState * flushState)225 void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
226     if (!fTessellator) {
227         this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
228                                  flushState->usesMSAASurface(), &flushState->dstProxyView(),
229                                  flushState->renderPassBarriers(), flushState->colorLoadOp(),
230                                  &flushState->caps()}, flushState->detachAppliedClip());
231         if (!fTessellator) {
232             return;
233         }
234     }
235 
236     if (fStencilFanProgram) {
237         // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
238         // middle-out topology.
239         GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
240         int maxCombinedFanEdges =
241                 PathTessellator::MaxCombinedFanEdgesInPathDrawList(fTotalCombinedPathVerbCnt);
242         // A single n-sided polygon is fanned by n-2 triangles. Multiple polygons with a combined
243         // edge count of n are fanned by strictly fewer triangles.
244         int maxTrianglesInFans = std::max(maxCombinedFanEdges - 2, 0);
245         int fanTriangleCount = 0;
246         if (VertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxTrianglesInFans * 3)) {
247             for (auto [pathMatrix, path, color] : *fPathDrawList) {
248                 AffineMatrix m(pathMatrix);
249                 for (PathMiddleOutFanIter it(path); !it.done();) {
250                     for (auto [p0, p1, p2] : it.nextStack()) {
251                         triangleVertexWriter << m.map2Points(p0, p1) << m.mapPoint(p2);
252                         ++fanTriangleCount;
253                     }
254                 }
255             }
256         }
257         SkASSERT(fanTriangleCount <= maxTrianglesInFans);
258         fFanVertexCount = fanTriangleCount * 3;
259         vertexAlloc.unlock(fFanVertexCount);
260     }
261 
262     auto tessShader = &fStencilPathProgram->geomProc().cast<GrPathTessellationShader>();
263     fTessellator->prepare(flushState,
264                           tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()),
265                           tessShader->viewMatrix(),
266                           *fPathDrawList,
267                           fTotalCombinedPathVerbCnt,
268                           tessShader->willUseTessellationShaders());
269 
270     if (fCoverBBoxProgram) {
271         size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();
272         VertexWriter vertexWriter = flushState->makeVertexSpace(instanceStride,
273                                                                 fPathCount,
274                                                                 &fBBoxBuffer,
275                                                                 &fBBoxBaseInstance);
276         SkDEBUGCODE(int pathCount = 0;)
277         for (auto [pathMatrix, path, color] : *fPathDrawList) {
278             SkDEBUGCODE(auto end = vertexWriter.makeOffset(instanceStride));
279             vertexWriter << pathMatrix.getScaleX()
280                          << pathMatrix.getSkewY()
281                          << pathMatrix.getSkewX()
282                          << pathMatrix.getScaleY()
283                          << pathMatrix.getTranslateX()
284                          << pathMatrix.getTranslateY();
285             if (path.isInverseFillType()) {
286                 // Fill the entire backing store to make sure we clear every stencil value back to
287                 // 0. If there is a scissor it will have already clipped the stencil draw.
288                 auto rtBounds =
289                         flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect();
290                 SkASSERT(rtBounds == fOriginalDrawBounds);
291                 SkRect pathSpaceRTBounds;
292                 if (SkMatrixPriv::InverseMapRect(pathMatrix, &pathSpaceRTBounds, rtBounds)) {
293                     vertexWriter << pathSpaceRTBounds;
294                 } else {
295                     vertexWriter << path.getBounds();
296                 }
297             } else {
298                 vertexWriter << path.getBounds();
299             }
300             SkASSERT(vertexWriter == end);
301             SkDEBUGCODE(++pathCount;)
302         }
303         SkASSERT(pathCount == fPathCount);
304     }
305 
306     if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
307         constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
308 
309         GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
310 
311         fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
312                 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
313     }
314 }
315 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)316 void PathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
317     if (!fTessellator) {
318         return;
319     }
320 
321     if (fCoverBBoxProgram &&
322         fCoverBBoxProgram->geomProc().hasVertexAttributes() &&
323         !fBBoxVertexBufferIfNoIDSupport) {
324         return;
325     }
326 
327     // Stencil the inner fan, if any.
328     if (fFanVertexCount > 0) {
329         SkASSERT(fStencilFanProgram);
330         SkASSERT(fFanBuffer);
331         flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
332         flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
333         flushState->draw(fFanVertexCount, fFanBaseVertex);
334     }
335 
336     // Stencil the rest of the path.
337     SkASSERT(fStencilPathProgram);
338     flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
339     fTessellator->draw(flushState, fStencilPathProgram->geomProc().willUseTessellationShaders());
340     if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
341         flushState->gpu()->insertManualFramebufferBarrier();  // http://skbug.com/9739
342     }
343 
344     // Fill in the bounding box (if not in stencil-only mode).
345     if (fCoverBBoxProgram) {
346         flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
347         flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
348                                  fCoverBBoxProgram->pipeline());
349         flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport);
350         flushState->drawInstanced(fPathCount, fBBoxBaseInstance, 4, 0);
351     }
352 }
353 
354 } // namespace skgpu::v1
355