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