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