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