Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
PathStencilCoverOp.cpp
Go to the documentation of this file.
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
9
24
25namespace {
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.
30class BoundingBoxShader : public GrGeometryProcessor {
31public:
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,
38 this->setVertexAttributesWithImplicitOffsets(&kUnitCoordAttrib, 1);
39 }
40 constexpr static Attribute kInstanceAttribs[] = {
44 };
45 this->setInstanceAttributesWithImplicitOffsets(kInstanceAttribs,
46 std::size(kInstanceAttribs));
47 }
48
49private:
50 const char* name() const final { return "tessellate_BoundingBoxShader"; }
51 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
57std::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
100 };
101
102 return std::make_unique<Impl>();
103}
104
105} // anonymous namespace
106
107namespace skgpu::ganesh {
108
110 if (fCoverBBoxProgram) {
111 fCoverBBoxProgram->pipeline().visitProxies(func);
112 } else {
113 fProcessors.visitProxies(func);
114 }
115}
116
124
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)
143 : GrPipeline::InputFlags::kNone;
145 args, fAAType, appliedClip.hardClip(), pipelineFlags);
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.
155 shaderMatrix,
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,
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));
185 SkPathFillType_IsInverse(this->pathFillType()));
187 args.fCaps,
188 args.fArena,
189 bboxPipeline,
190 args.fWriteView,
191 args.fUsesMSAASurface,
192 bboxShader,
194 args.fXferBarrierFlags,
195 args.fColorLoadOp,
196 bboxStencil);
197 }
198}
199
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
222SKGPU_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
223
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 =
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
318void 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::ganesh
GrClampType
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
@ kFragment_GrShaderFlag
GrFillRule GrFillRuleForPathFillType(SkPathFillType fillType)
GrLoadOp
@ kFloat2_GrVertexAttribType
@ kFloat4_GrVertexAttribType
GrXferBarrierFlags
SkColor4f color
#define SKGPU_DECLARE_STATIC_UNIQUE_KEY(name)
#define SKGPU_DEFINE_STATIC_UNIQUE_KEY(name)
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkPMColor4f SK_PMColor4fTRANSPARENT
static bool SkPathFillType_IsInverse(SkPathFillType ft)
Definition SkPathTypes.h:26
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
static GrAppliedClip Disabled()
const GrCaps * caps() const
const GrShaderCaps * shaderCaps() const
Definition GrCaps.h:63
FixedFunctionFlags
Definition GrDrawOp.h:104
void unlock(int actualCount) final
skgpu::VertexWriter lockWriter(size_t stride, int eagerCount)
void setData(const GrGLSLProgramDataManager &pdman, const GrFragmentProcessor &processor)
virtual void set4f(UniformHandle, float, float, float, float) const =0
GrGLSLProgramDataManager::UniformHandle UniformHandle
void setInstanceAttributesWithImplicitOffsets(const Attribute *attrs, int attrCount)
virtual std::unique_ptr< ProgramImpl > makeProgramImpl(const GrShaderCaps &) const =0
virtual void addToKey(const GrShaderCaps &, skgpu::KeyBuilder *) const =0
size_t instanceStride() const
void setVertexAttributesWithImplicitOffsets(const Attribute *attrs, int attrCount)
skgpu::VertexWriter makeVertexWriter(size_t vertexSize, int vertexCount, sk_sp< const GrBuffer > *, int *startVertex)
GrLoadOp colorLoadOp() const final
const GrDstProxyView & dstProxyView() const final
GrXferBarrierFlags renderPassBarriers() const final
SkArenaAlloc * allocator() override
void bindPipelineAndScissorClip(const GrProgramInfo &programInfo, const SkRect &drawBounds)
void bindBuffers(sk_sp< const GrBuffer > indexBuffer, sk_sp< const GrBuffer > instanceBuffer, sk_sp< const GrBuffer > vertexBuffer, GrPrimitiveRestart primitiveRestart=GrPrimitiveRestart::kNo)
const GrSurfaceProxyView & writeView() const final
void drawInstanced(int instanceCount, int baseInstance, int vertexCount, int baseVertex)
GrResourceProvider * resourceProvider() const final
GrAppliedClip detachAppliedClip() final
const GrCaps & caps() const final
void bindTextures(const GrGeometryProcessor &geomProc, const GrSurfaceProxy &singleGeomProcTexture, const GrPipeline &pipeline)
void draw(int vertexCount, int baseVertex)
bool usesMSAASurface() const final
const SkRect & bounds() const
Definition GrOp.h:122
static const GrUserStencilSettings * StencilPathSettings(GrFillRule fillRule)
static GrPathTessellationShader * MakeSimpleTriangleShader(SkArenaAlloc *, const SkMatrix &viewMatrix, const SkPMColor4f &)
static const GrPipeline * MakeStencilOnlyPipeline(const ProgramArgs &, GrAAType, const GrAppliedHardClip &, GrPipeline::InputFlags=GrPipeline::InputFlags::kNone)
static GrPathTessellationShader * Make(const GrShaderCaps &, SkArenaAlloc *, const SkMatrix &viewMatrix, const SkPMColor4f &, PatchAttribs)
static const GrUserStencilSettings * TestAndResetStencilSettings(bool isInverseFill=false)
void visitProxies(const GrVisitProxyFunc &) const
void visitProxies(const GrVisitProxyFunc &) const
Analysis finalize(const GrProcessorAnalysisColor &, const GrProcessorAnalysisCoverage, const GrAppliedClip *, const GrUserStencilSettings *, const GrCaps &, GrClampType, SkPMColor4f *inputColorOverride)
const T & cast() const
virtual const char * name() const =0
const GrPipeline & pipeline() const
const GrGeometryProcessor & geomProc() const
void recordProgramInfo(const GrProgramInfo *programInfo)
GrRecordingContextPriv priv()
sk_sp< const GrGpuBuffer > findOrMakeStaticBuffer(GrGpuBufferType intendedType, size_t size, const skgpu::UniqueKey &key, InitializeBufferFn)
static GrProgramInfo * CreateProgramInfo(const GrCaps *, SkArenaAlloc *, const GrPipeline *, const GrSurfaceProxyView &writeView, bool usesMSAASurface, GrGeometryProcessor *, GrPrimitiveType, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp, const GrUserStencilSettings *=&GrUserStencilSettings::kUnused)
GrRenderTargetProxy * asRenderTargetProxy() const
SkRect backingStoreBoundsRect() const
static const GrPipeline * MakePipeline(const ProgramArgs &, GrAAType, GrAppliedClip &&, GrProcessorSet &&)
static GrProgramInfo * MakeProgram(const ProgramArgs &args, const GrTessellationShader *shader, const GrPipeline *pipeline, const GrUserStencilSettings *stencil)
static bool InverseMapRect(const SkMatrix &mx, SkRect *dst, const SkRect &src)
static const SkMatrix & I()
static PathCurveTessellator * Make(SkArenaAlloc *arena, bool infinitySupport, PatchAttribs attribs=PatchAttribs::kNone)
void onPrepare(GrOpFlushState *) override
GrProcessorSet::Analysis finalize(const GrCaps &, const GrAppliedClip *, GrClampType) override
void onExecute(GrOpFlushState *, const SkRect &chainBounds) override
FixedFunctionFlags fixedFunctionFlags() const override
void onPrePrepare(GrRecordingContext *, const GrSurfaceProxyView &, GrAppliedClip *, const GrDstProxyView &, GrXferBarrierFlags, GrLoadOp colorLoadOp) override
void visitProxies(const GrVisitProxyFunc &) const override
static PathWedgeTessellator * Make(SkArenaAlloc *arena, bool infinitySupport, PatchAttribs attribs=PatchAttribs::kNone)
FlutterSemanticsFlag flags
glong glong end
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
int32_t height
int32_t width
bool fVertexIDSupport
Mark mark(size_t offset=0) const