Flutter Engine
The Flutter Engine
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
109void PathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
110 if (fCoverBBoxProgram) {
111 fCoverBBoxProgram->pipeline().visitProxies(func);
112 } else {
113 fProcessors.visitProxies(func);
114 }
115}
116
117GrDrawOp::FixedFunctionFlags PathStencilCoverOp::fixedFunctionFlags() const {
119 if (fAAType != GrAAType::kNone) {
121 }
122 return flags;
123}
124
125GrProcessorSet::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
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)
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
200void 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
222SKGPU_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
223
224void 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 =
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
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
@ kFragment_GrShaderFlag
Definition: GrTypesPriv.h:287
GrFillRule GrFillRuleForPathFillType(SkPathFillType fillType)
Definition: GrTypesPriv.h:183
GrLoadOp
Definition: GrTypesPriv.h:155
@ kFloat2_GrVertexAttribType
Definition: GrTypesPriv.h:314
@ kFloat4_GrVertexAttribType
Definition: GrTypesPriv.h:316
GrXferBarrierFlags
#define SKGPU_DEFINE_STATIC_UNIQUE_KEY(name)
Definition: ResourceKey.h:324
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr SkPMColor4f SK_PMColor4fTRANSPARENT
Definition: SkColorData.h:378
static bool SkPathFillType_IsInverse(SkPathFillType ft)
Definition: SkPathTypes.h:26
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
static GrAppliedClip Disabled()
Definition: GrAppliedClip.h:96
const GrCaps * caps() const
Definition: GrCaps.h:57
const GrShaderCaps * shaderCaps() const
Definition: GrCaps.h:63
FixedFunctionFlags
Definition: GrDrawOp.h:104
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
bool hasVertexAttributes() 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
Definition: GrPipeline.cpp:105
void visitProxies(const GrVisitProxyFunc &) const
Analysis finalize(const GrProcessorAnalysisColor &, const GrProcessorAnalysisCoverage, const GrAppliedClip *, const GrUserStencilSettings *, const GrCaps &, GrClampType, SkPMColor4f *inputColorOverride)
const T & cast() const
Definition: GrProcessor.h:127
virtual const char * name() const =0
const GrPipeline & pipeline() const
Definition: GrProgramInfo.h:39
const GrGeometryProcessor & geomProc() const
Definition: GrProgramInfo.h:40
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)
Definition: SkMatrixPriv.h:54
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
static PathCurveTessellator * Make(SkArenaAlloc *arena, bool infinitySupport, PatchAttribs attribs=PatchAttribs::kNone)
static PathWedgeTessellator * Make(SkArenaAlloc *arena, bool infinitySupport, PatchAttribs attribs=PatchAttribs::kNone)
DlColor color
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static float max(float r, float g, float b)
Definition: hsl.cpp:49
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SKGPU_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey)
int32_t height
int32_t width
bool fVertexIDSupport
Definition: GrShaderCaps.h:36