Flutter Engine
The Flutter Engine
PathInnerTriangulateOp.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2019 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
18
19using namespace skia_private;
20
21#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
22
23namespace skgpu::ganesh {
24
25namespace {
26
27// Fills an array of convex hulls surrounding 4-point cubic or conic instances. This shader is used
28// for the "cover" pass after the curves have been fully stencilled.
29class HullShader : public GrPathTessellationShader {
30public:
31 HullShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
32 : GrPathTessellationShader(kTessellate_HullShader_ClassID,
34 viewMatrix,
35 color,
37 fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
38 fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, SkSLType::kFloat4);
39 if (!shaderCaps.fInfinitySupport) {
40 // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
41 // infinity can't detect this. On these platforms we also write out an extra float with
42 // each patch that explicitly tells the shader what type of curve it is.
43 fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, SkSLType::kFloat);
44 }
45 this->setInstanceAttributesWithImplicitOffsets(fInstanceAttribs.data(),
46 fInstanceAttribs.size());
47 SkASSERT(fInstanceAttribs.size() <= kMaxInstanceAttribCount);
48
49 if (!shaderCaps.fVertexIDSupport) {
50 constexpr static Attribute kVertexIdxAttrib("vertexidx", kFloat_GrVertexAttribType,
52 this->setVertexAttributesWithImplicitOffsets(&kVertexIdxAttrib, 1);
53 }
54 }
55
56private:
57 const char* name() const final { return "tessellate_HullShader"; }
58 void addToKey(const GrShaderCaps&, KeyBuilder*) const final {}
59 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
60
61 constexpr static int kMaxInstanceAttribCount = 3;
63};
64
65std::unique_ptr<GrGeometryProcessor::ProgramImpl> HullShader::makeProgramImpl(
66 const GrShaderCaps&) const {
67 class Impl : public GrPathTessellationShader::Impl {
68 void emitVertexCode(const GrShaderCaps& shaderCaps,
72 GrGPArgs* gpArgs) override {
73 if (shaderCaps.fInfinitySupport) {
75 "bool is_conic_curve() { return isinf(p23.w); }"
76 "bool is_non_triangular_conic_curve() {"
77 // We consider a conic non-triangular as long as its weight isn't infinity.
78 // NOTE: "isinf == false" works on Mac Radeon GLSL; "!isinf" can get the wrong
79 // answer.
80 "return isinf(p23.z) == false;"
81 "}"
82 );
83 } else {
85 "bool is_conic_curve() { return curveType != %g; }",
86 tess::kCubicCurveType).c_str());
88 "bool is_non_triangular_conic_curve() {"
89 "return curveType == %g;"
90 "}", tess::kConicCurveType).c_str());
91 }
92 v->codeAppend(
93 "float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;"
94 "if (is_conic_curve()) {"
95 // Conics are 3 points, with the weight in p3.
96 "float w = p3.x;"
97 "p3 = p2;" // Duplicate the endpoint for shared code that also runs on cubics.
98 "if (is_non_triangular_conic_curve()) {"
99 // Convert the points to a trapeziodal hull that circumcscribes the conic.
100 "float2 p1w = p1 * w;"
101 "float T = .51;" // Bias outward a bit to ensure we cover the outermost samples.
102 "float2 c1 = mix(p0, p1w, T);"
103 "float2 c2 = mix(p2, p1w, T);"
104 "float iw = 1 / mix(1, w, T);"
105 "p2 = c2 * iw;"
106 "p1 = c1 * iw;"
107 "}"
108 "}"
109
110 // Translate the points to v0..3 where v0=0.
111 "float2 v1 = p1 - p0;"
112 "float2 v2 = p2 - p0;"
113 "float2 v3 = p3 - p0;"
114
115 // Reorder the points so v2 bisects v1 and v3.
116 "if (sign(cross_length_2d(v2, v1)) == sign(cross_length_2d(v2, v3))) {"
117 "float2 tmp = p2;"
118 "if (sign(cross_length_2d(v1, v2)) != sign(cross_length_2d(v1, v3))) {"
119 "p2 = p1;" // swap(p2, p1)
120 "p1 = tmp;"
121 "} else {"
122 "p2 = p3;" // swap(p2, p3)
123 "p3 = tmp;"
124 "}"
125 "}"
126 );
127
128 if (shaderCaps.fVertexIDSupport) {
129 // If we don't have sk_VertexID support then "vertexidx" already came in as a
130 // vertex attrib.
131 v->codeAppend(
132 // sk_VertexID comes in fan order. Convert to strip order.
133 "int vertexidx = sk_VertexID;"
134 "vertexidx ^= vertexidx >> 1;");
135 }
136
137 v->codeAppend(
138 // Find the "turn direction" of each corner and net turn direction.
139 "float vertexdir = 0;"
140 "float netdir = 0;"
141 "float2 prev, next;"
142 "float dir;"
143 "float2 localcoord;"
144 "float2 nextcoord;"
145 );
146
147 for (int i = 0; i < 4; ++i) {
148 v->codeAppendf(
149 "prev = p%i - p%i;", i, (i + 3) % 4);
150 v->codeAppendf(
151 "next = p%i - p%i;", (i + 1) % 4, i);
152 v->codeAppendf(
153 "dir = sign(cross_length_2d(prev, next));"
154 "if (vertexidx == %i) {"
155 "vertexdir = dir;"
156 "localcoord = p%i;"
157 "nextcoord = p%i;"
158 "}"
159 "netdir += dir;", i, i, (i + 1) % 4);
160 }
161
162 v->codeAppend(
163 // Remove the non-convex vertex, if any.
164 "if (vertexdir != sign(netdir)) {"
165 "localcoord = nextcoord;"
166 "}"
167
168 "float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;");
169 gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord");
170 gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertexpos");
171 }
172 };
173 return std::make_unique<Impl>();
174}
175
176} // anonymous namespace
177
178void PathInnerTriangulateOp::visitProxies(const GrVisitProxyFunc& func) const {
179 if (fPipelineForFills) {
180 fPipelineForFills->visitProxies(func);
181 } else {
182 fProcessors.visitProxies(func);
183 }
184}
185
186GrDrawOp::FixedFunctionFlags PathInnerTriangulateOp::fixedFunctionFlags() const {
188 if (GrAAType::kNone != fAAType) {
190 }
191 return flags;
192}
193
194GrProcessorSet::Analysis PathInnerTriangulateOp::finalize(const GrCaps& caps,
195 const GrAppliedClip* clip,
196 GrClampType clampType) {
197 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
198 clampType, &fColor);
199}
200
201void PathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args,
202 const GrPipeline* pipelineForStencils,
203 const GrUserStencilSettings* stencil) {
204 SkASSERT(pipelineForStencils);
205 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
207 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils,
208 stencil)); }
209
210void PathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args,
211 const GrUserStencilSettings* stencil) {
212 SkASSERT(fPipelineForFills);
213 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
214 fColor);
215 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills,
216 stencil));
217}
218
219void PathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
220 GrAppliedClip&& appliedClip) {
221 SkASSERT(!fFanTriangulator);
222 SkASSERT(!fFanPolys);
223 SkASSERT(!fPipelineForFills);
224 SkASSERT(!fTessellator);
225 SkASSERT(!fStencilCurvesProgram);
226 SkASSERT(fFanPrograms.empty());
227 SkASSERT(!fCoverHullsProgram);
228
229 if (fPath.countVerbs() <= 0) {
230 return;
231 }
232
233 // If using wireframe, we have to fall back on a standard Redbook "stencil then cover" algorithm
234 // instead of bypassing the stencil buffer to fill the fan directly.
235 bool forceRedbookStencilPass =
237 bool doFill = !(fPathFlags & FillPathFlags::kStencilOnly);
238
239 bool isLinear;
240 fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena);
241 fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear);
242
243 // Create a pipeline for stencil passes if needed.
244 const GrPipeline* pipelineForStencils = nullptr;
245 if (forceRedbookStencilPass || !isLinear) { // Curves always get stencilled.
246 auto pipelineFlags = (fPathFlags & FillPathFlags::kWireframe)
250 args, fAAType, appliedClip.hardClip(), pipelineFlags);
251 }
252
253 // Create a pipeline for fill passes if needed.
254 if (doFill) {
255 fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType,
256 std::move(appliedClip),
257 std::move(fProcessors));
258 }
259
260 // Pass 1: Tessellate the outer curves into the stencil buffer.
261 if (!isLinear) {
262 fTessellator = PathCurveTessellator::Make(args.fArena,
263 args.fCaps->shaderCaps()->fInfinitySupport);
264 auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(),
265 args.fArena,
266 fViewMatrix,
268 fTessellator->patchAttribs());
269 const GrUserStencilSettings* stencilPathSettings =
271 fStencilCurvesProgram = GrTessellationShader::MakeProgram(args,
272 tessShader,
273 pipelineForStencils,
274 stencilPathSettings);
275 }
276
277 // Pass 2: Fill the path's inner fan with a stencil test against the curves.
278 if (fFanPolys) {
279 if (forceRedbookStencilPass) {
280 // Use a standard Redbook "stencil then cover" algorithm instead of bypassing the
281 // stencil buffer to fill the fan directly.
282 const GrUserStencilSettings* stencilPathSettings =
284 this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings);
285 if (doFill) {
286 this->pushFanFillProgram(args,
288 }
289 } else if (isLinear) {
290 // There are no outer curves! Ignore stencil and fill the path directly.
291 SkASSERT(!pipelineForStencils);
292 this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
293 } else if (!fPipelineForFills->hasStencilClip()) {
294 // These are a twist on the standard Redbook stencil settings that allow us to fill the
295 // inner polygon directly to the final render target. By the time these programs
296 // execute, the outer curves will already be stencilled in. So if the stencil value is
297 // zero, then it means the sample in question is not affected by any curves and we can
298 // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
299 // continue the standard Redbook counting process.
300 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
302 0x0000, 0x0000,
304 0xffff, 0xffff,
307 0xffff, 0xffff>());
308
309 constexpr static GrUserStencilSettings kFillOrInvertStencil(
311 0x0000,
313 0xffff,
315 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
317 0xffff>());
318
319 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
320 ? &kFillOrIncrDecrStencil
321 : &kFillOrInvertStencil;
322 this->pushFanFillProgram(args, stencil);
323 } else {
324 // This is the same idea as above, but we use two passes instead of one because there is
325 // a stencil clip. The stencil test isn't expressive enough to do the above tests and
326 // also check the clip bit in a single pass.
327 constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
329 0x0000,
331 0xffff,
334 0xffff>());
335
336 constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
338 0x0000, 0x0000,
339 // No need to check the clip because the previous stencil pass will have only
340 // written to samples already inside the clip.
342 0xffff, 0xffff,
345 0xffff, 0xffff>());
346
347 constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
349 0x0000,
350 // No need to check the clip because the previous stencil pass will have only
351 // written to samples already inside the clip.
353 0xffff,
354 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
357 0xffff>());
358
359 // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
360 this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
361
362 // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
363 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
364 ? &kIncrDecrStencilIfNonzero
365 : &kInvertStencilIfNonZero;
366 this->pushFanStencilProgram(args, pipelineForStencils, stencil);
367 }
368 }
369
370 // Pass 3: Draw convex hulls around each curve.
371 if (doFill && !isLinear) {
372 // By the time this program executes, every pixel will be filled in except the ones touched
373 // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
374 // This will fill in any remaining samples and reset the stencil values back to zero.
375 SkASSERT(fTessellator);
376 auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor,
377 *args.fCaps->shaderCaps());
378 fCoverHullsProgram = GrTessellationShader::MakeProgram(
379 args, hullShader, fPipelineForFills,
381 }
382}
383
384void PathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
385 const GrSurfaceProxyView& writeView,
387 const GrDstProxyView& dstProxyView,
388 GrXferBarrierFlags renderPassXferBarriers,
389 GrLoadOp colorLoadOp) {
390 // DMSAA is not supported on DDL.
391 bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
392 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
393 &dstProxyView, renderPassXferBarriers, colorLoadOp,
394 context->priv().caps()},
395 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
396 if (fStencilCurvesProgram) {
397 context->priv().recordProgramInfo(fStencilCurvesProgram);
398 }
399 for (const GrProgramInfo* fanProgram : fFanPrograms) {
400 context->priv().recordProgramInfo(fanProgram);
401 }
402 if (fCoverHullsProgram) {
403 context->priv().recordProgramInfo(fCoverHullsProgram);
404 }
405}
406
408
409void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
410 const GrCaps& caps = flushState->caps();
411
412 if (!fFanTriangulator) {
413 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
414 flushState->usesMSAASurface(), &flushState->dstProxyView(),
415 flushState->renderPassBarriers(), flushState->colorLoadOp(),
416 &caps}, flushState->detachAppliedClip());
417 if (!fFanTriangulator) {
418 return;
419 }
420 }
421
422 if (fFanPolys) {
423 GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
424 fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
425 }
426
427 if (fTessellator) {
428 auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
429 fTessellator->prepareWithTriangles(flushState,
430 tessShader->viewMatrix(),
431 &fFanBreadcrumbs,
433 fPath.countVerbs());
434 }
435
436 if (!caps.shaderCaps()->fVertexIDSupport) {
437 constexpr static float kStripOrderIDs[4] = {0, 1, 3, 2};
438
439 SKGPU_DEFINE_STATIC_UNIQUE_KEY(gHullVertexBufferKey);
440
441 fHullVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
442 GrGpuBufferType::kVertex, sizeof(kStripOrderIDs), kStripOrderIDs,
443 gHullVertexBufferKey);
444 }
445}
446
447void PathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
448 if (fCoverHullsProgram &&
449 fCoverHullsProgram->geomProc().hasVertexAttributes() &&
450 !fHullVertexBufferIfNoIDSupport) {
451 return;
452 }
453
454 if (fStencilCurvesProgram) {
455 SkASSERT(fTessellator);
456 flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
457 fTessellator->draw(flushState);
458 }
459
460 // Allocation of the fan vertex buffer may have failed but we already pushed back fan programs.
461 if (fFanBuffer) {
462 for (const GrProgramInfo* fanProgram : fFanPrograms) {
463 flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
464 flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
465 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
466 flushState->draw(fFanVertexCount, fBaseFanVertex);
467 }
468 }
469
470 if (fCoverHullsProgram) {
471 SkASSERT(fTessellator);
472 flushState->bindPipelineAndScissorClip(*fCoverHullsProgram, this->bounds());
473 flushState->bindTextures(fCoverHullsProgram->geomProc(), nullptr, *fPipelineForFills);
474 fTessellator->drawHullInstances(flushState, fHullVertexBufferIfNoIDSupport);
475 }
476}
477
478} // namespace skgpu::ganesh
479
480#endif // SK_ENABLE_OPTIMIZE_SIZE
SkPath fPath
GrClampType
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrPrimitiveType
Definition: GrTypesPriv.h:43
GrFillRule GrFillRuleForSkPath(const SkPath &path)
Definition: GrTypesPriv.h:195
GrLoadOp
Definition: GrTypesPriv.h:155
@ kFloat_GrVertexAttribType
Definition: GrTypesPriv.h:313
@ 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 SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
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 codeAppend(const char *str)
void codeAppendf(const char format[],...) SK_PRINTF_LIKE(2
void insertFunction(const char *functionDefinition)
bool hasVertexAttributes() const
Poly * pathToPolys(BreadcrumbTriangleList *breadcrumbList, bool *isLinear)
int polysToTriangles(Poly *polys, GrEagerVertexAllocator *vertexAlloc, BreadcrumbTriangleList *breadcrumbList) const
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
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)
bool hasStencilClip() const
Definition: GrPipeline.h:174
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
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)
GrRenderTargetProxy * asRenderTargetProxy() 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 const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkPathFillType getFillType() const
Definition: SkPath.h:230
int countVerbs() const
Definition: SkPath.cpp:556
static PathCurveTessellator * Make(SkArenaAlloc *arena, bool infinitySupport, PatchAttribs attribs=PatchAttribs::kNone)
void drawHullInstances(GrOpFlushState *, sk_sp< const GrGpuBuffer > vertexBufferIfNeeded) const
void prepareWithTriangles(GrMeshDrawTarget *target, const SkMatrix &shaderMatrix, GrInnerFanTriangulator::BreadcrumbTriangleList *extraTriangles, const PathDrawList &pathDrawList, int totalCombinedPathVerbCnt)
void draw(GrOpFlushState *) const final
PatchAttribs patchAttribs() const
bool empty() const
Definition: SkTArray.h:199
DlColor color
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
@ kNone
Definition: layer.h:53
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
tess::PatchAttribs PatchAttribs
SKGPU_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey)
static constexpr float kCubicCurveType
Definition: Tessellation.h:97
static constexpr float kConicCurveType
Definition: Tessellation.h:98
bool fVertexIDSupport
Definition: GrShaderCaps.h:36
static constexpr Init< Ref, Test, TestMask, PassOp, FailOp, WriteMask > StaticInit()
static constexpr InitSeparate< CWRef, CCWRef, CWTest, CCWTest, CWTestMask, CCWTestMask, CWPassOp, CCWPassOp, CWFailOp, CCWFailOp, CWWriteMask, CCWWriteMask > StaticInitSeparate()
static const GrUserStencilSettings & kUnused
bool fInfinitySupport
Definition: SkSLUtil.h:103