/src/skia/src/gpu/ganesh/ops/PathInnerTriangulateOp.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
8 | | #include "src/gpu/ganesh/ops/PathInnerTriangulateOp.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/GrGLSLVertexGeoBuilder.h" |
16 | | #include "src/gpu/ganesh/tessellate/GrPathTessellationShader.h" |
17 | | #include "src/gpu/ganesh/tessellate/PathTessellator.h" |
18 | | |
19 | | using namespace skia_private; |
20 | | |
21 | | #if !defined(SK_ENABLE_OPTIMIZE_SIZE) |
22 | | |
23 | | namespace skgpu::ganesh { |
24 | | |
25 | | namespace { |
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. |
29 | | class HullShader : public GrPathTessellationShader { |
30 | | public: |
31 | | HullShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps) |
32 | | : GrPathTessellationShader(kTessellate_HullShader_ClassID, |
33 | | GrPrimitiveType::kTriangleStrip, |
34 | | viewMatrix, |
35 | | color, |
36 | 0 | PatchAttribs::kNone) { |
37 | 0 | fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, SkSLType::kFloat4); |
38 | 0 | fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, SkSLType::kFloat4); |
39 | 0 | 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 | 0 | fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, SkSLType::kFloat); |
44 | 0 | } |
45 | 0 | this->setInstanceAttributesWithImplicitOffsets(fInstanceAttribs.data(), |
46 | 0 | fInstanceAttribs.size()); |
47 | 0 | SkASSERT(fInstanceAttribs.size() <= kMaxInstanceAttribCount); |
48 | |
|
49 | 0 | if (!shaderCaps.fVertexIDSupport) { |
50 | 0 | constexpr static Attribute kVertexIdxAttrib("vertexidx", kFloat_GrVertexAttribType, |
51 | 0 | SkSLType::kFloat); |
52 | 0 | this->setVertexAttributesWithImplicitOffsets(&kVertexIdxAttrib, 1); |
53 | 0 | } |
54 | 0 | } Unexecuted instantiation: PathInnerTriangulateOp.cpp:skgpu::ganesh::(anonymous namespace)::HullShader::HullShader(SkMatrix const&, SkRGBA4f<(SkAlphaType)2>, GrShaderCaps const&) Unexecuted instantiation: PathInnerTriangulateOp.cpp:skgpu::ganesh::(anonymous namespace)::HullShader::HullShader(SkMatrix const&, SkRGBA4f<(SkAlphaType)2>, GrShaderCaps const&) |
55 | | |
56 | | private: |
57 | 0 | const char* name() const final { return "tessellate_HullShader"; } |
58 | 0 | void addToKey(const GrShaderCaps&, KeyBuilder*) const final {} |
59 | | std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final; |
60 | | |
61 | | constexpr static int kMaxInstanceAttribCount = 3; |
62 | | STArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs; |
63 | | }; |
64 | | |
65 | | std::unique_ptr<GrGeometryProcessor::ProgramImpl> HullShader::makeProgramImpl( |
66 | 0 | const GrShaderCaps&) const { |
67 | 0 | class Impl : public GrPathTessellationShader::Impl { |
68 | 0 | void emitVertexCode(const GrShaderCaps& shaderCaps, |
69 | 0 | const GrPathTessellationShader&, |
70 | 0 | GrGLSLVertexBuilder* v, |
71 | 0 | GrGLSLVaryingHandler*, |
72 | 0 | GrGPArgs* gpArgs) override { |
73 | 0 | if (shaderCaps.fInfinitySupport) { |
74 | 0 | v->insertFunction( |
75 | 0 | "bool is_conic_curve() { return isinf(p23.w); }" |
76 | 0 | "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 | 0 | "return isinf(p23.z) == false;" |
81 | 0 | "}" |
82 | 0 | ); |
83 | 0 | } else { |
84 | 0 | v->insertFunction(SkStringPrintf( |
85 | 0 | "bool is_conic_curve() { return curveType != %g; }", |
86 | 0 | tess::kCubicCurveType).c_str()); |
87 | 0 | v->insertFunction(SkStringPrintf( |
88 | 0 | "bool is_non_triangular_conic_curve() {" |
89 | 0 | "return curveType == %g;" |
90 | 0 | "}", tess::kConicCurveType).c_str()); |
91 | 0 | } |
92 | 0 | v->codeAppend( |
93 | 0 | "float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;" |
94 | 0 | "if (is_conic_curve()) {" |
95 | | // Conics are 3 points, with the weight in p3. |
96 | 0 | "float w = p3.x;" |
97 | 0 | "p3 = p2;" // Duplicate the endpoint for shared code that also runs on cubics. |
98 | 0 | "if (is_non_triangular_conic_curve()) {" |
99 | | // Convert the points to a trapeziodal hull that circumcscribes the conic. |
100 | 0 | "float2 p1w = p1 * w;" |
101 | 0 | "float T = .51;" // Bias outward a bit to ensure we cover the outermost samples. |
102 | 0 | "float2 c1 = mix(p0, p1w, T);" |
103 | 0 | "float2 c2 = mix(p2, p1w, T);" |
104 | 0 | "float iw = 1 / mix(1, w, T);" |
105 | 0 | "p2 = c2 * iw;" |
106 | 0 | "p1 = c1 * iw;" |
107 | 0 | "}" |
108 | 0 | "}" |
109 | | |
110 | | // Translate the points to v0..3 where v0=0. |
111 | 0 | "float2 v1 = p1 - p0;" |
112 | 0 | "float2 v2 = p2 - p0;" |
113 | 0 | "float2 v3 = p3 - p0;" |
114 | | |
115 | | // Reorder the points so v2 bisects v1 and v3. |
116 | 0 | "if (sign(cross_length_2d(v2, v1)) == sign(cross_length_2d(v2, v3))) {" |
117 | 0 | "float2 tmp = p2;" |
118 | 0 | "if (sign(cross_length_2d(v1, v2)) != sign(cross_length_2d(v1, v3))) {" |
119 | 0 | "p2 = p1;" // swap(p2, p1) |
120 | 0 | "p1 = tmp;" |
121 | 0 | "} else {" |
122 | 0 | "p2 = p3;" // swap(p2, p3) |
123 | 0 | "p3 = tmp;" |
124 | 0 | "}" |
125 | 0 | "}" |
126 | 0 | ); |
127 | |
|
128 | 0 | if (shaderCaps.fVertexIDSupport) { |
129 | | // If we don't have sk_VertexID support then "vertexidx" already came in as a |
130 | | // vertex attrib. |
131 | 0 | v->codeAppend( |
132 | | // sk_VertexID comes in fan order. Convert to strip order. |
133 | 0 | "int vertexidx = sk_VertexID;" |
134 | 0 | "vertexidx ^= vertexidx >> 1;"); |
135 | 0 | } |
136 | |
|
137 | 0 | v->codeAppend( |
138 | | // Find the "turn direction" of each corner and net turn direction. |
139 | 0 | "float vertexdir = 0;" |
140 | 0 | "float netdir = 0;" |
141 | 0 | "float2 prev, next;" |
142 | 0 | "float dir;" |
143 | 0 | "float2 localcoord;" |
144 | 0 | "float2 nextcoord;" |
145 | 0 | ); |
146 | |
|
147 | 0 | for (int i = 0; i < 4; ++i) { |
148 | 0 | v->codeAppendf( |
149 | 0 | "prev = p%i - p%i;", i, (i + 3) % 4); |
150 | 0 | v->codeAppendf( |
151 | 0 | "next = p%i - p%i;", (i + 1) % 4, i); |
152 | 0 | v->codeAppendf( |
153 | 0 | "dir = sign(cross_length_2d(prev, next));" |
154 | 0 | "if (vertexidx == %i) {" |
155 | 0 | "vertexdir = dir;" |
156 | 0 | "localcoord = p%i;" |
157 | 0 | "nextcoord = p%i;" |
158 | 0 | "}" |
159 | 0 | "netdir += dir;", i, i, (i + 1) % 4); |
160 | 0 | } |
161 | |
|
162 | 0 | v->codeAppend( |
163 | | // Remove the non-convex vertex, if any. |
164 | 0 | "if (vertexdir != sign(netdir)) {" |
165 | 0 | "localcoord = nextcoord;" |
166 | 0 | "}" |
167 | |
|
168 | 0 | "float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;"); |
169 | 0 | gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localcoord"); |
170 | 0 | gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertexpos"); |
171 | 0 | } |
172 | 0 | }; |
173 | 0 | return std::make_unique<Impl>(); |
174 | 0 | } |
175 | | |
176 | | } // anonymous namespace |
177 | | |
178 | 0 | void PathInnerTriangulateOp::visitProxies(const GrVisitProxyFunc& func) const { |
179 | 0 | if (fPipelineForFills) { |
180 | 0 | fPipelineForFills->visitProxies(func); |
181 | 0 | } else { |
182 | 0 | fProcessors.visitProxies(func); |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | 0 | GrDrawOp::FixedFunctionFlags PathInnerTriangulateOp::fixedFunctionFlags() const { |
187 | 0 | auto flags = FixedFunctionFlags::kUsesStencil; |
188 | 0 | if (GrAAType::kNone != fAAType) { |
189 | 0 | flags |= FixedFunctionFlags::kUsesHWAA; |
190 | 0 | } |
191 | 0 | return flags; |
192 | 0 | } |
193 | | |
194 | | GrProcessorSet::Analysis PathInnerTriangulateOp::finalize(const GrCaps& caps, |
195 | | const GrAppliedClip* clip, |
196 | 0 | GrClampType clampType) { |
197 | 0 | return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps, |
198 | 0 | clampType, &fColor); |
199 | 0 | } |
200 | | |
201 | | void PathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args, |
202 | | const GrPipeline* pipelineForStencils, |
203 | 0 | const GrUserStencilSettings* stencil) { |
204 | 0 | SkASSERT(pipelineForStencils); |
205 | 0 | auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix, |
206 | 0 | SK_PMColor4fTRANSPARENT); |
207 | 0 | fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils, |
208 | 0 | stencil)); } Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanStencilProgram(GrTessellationShader::ProgramArgs const&, GrPipeline const*, GrUserStencilSettings const*) Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanStencilProgram(GrTessellationShader::ProgramArgs const&, GrPipeline const*, GrUserStencilSettings const*) |
209 | | |
210 | | void PathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args, |
211 | 0 | const GrUserStencilSettings* stencil) { |
212 | 0 | SkASSERT(fPipelineForFills); |
213 | 0 | auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix, |
214 | 0 | fColor); |
215 | 0 | fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills, |
216 | 0 | stencil)); |
217 | 0 | } Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanFillProgram(GrTessellationShader::ProgramArgs const&, GrUserStencilSettings const*) Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::pushFanFillProgram(GrTessellationShader::ProgramArgs const&, GrUserStencilSettings const*) |
218 | | |
219 | | void PathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args, |
220 | 0 | GrAppliedClip&& appliedClip) { |
221 | 0 | SkASSERT(!fFanTriangulator); |
222 | 0 | SkASSERT(!fFanPolys); |
223 | 0 | SkASSERT(!fPipelineForFills); |
224 | 0 | SkASSERT(!fTessellator); |
225 | 0 | SkASSERT(!fStencilCurvesProgram); |
226 | 0 | SkASSERT(fFanPrograms.empty()); |
227 | 0 | SkASSERT(!fCoverHullsProgram); |
228 | |
|
229 | 0 | if (fPath.countVerbs() <= 0) { |
230 | 0 | return; |
231 | 0 | } |
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 | 0 | bool forceRedbookStencilPass = |
236 | 0 | (fPathFlags & (FillPathFlags::kStencilOnly | FillPathFlags::kWireframe)); |
237 | 0 | bool doFill = !(fPathFlags & FillPathFlags::kStencilOnly); |
238 | |
|
239 | 0 | bool isLinear; |
240 | 0 | fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena); |
241 | 0 | fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear); |
242 | | |
243 | | // Create a pipeline for stencil passes if needed. |
244 | 0 | const GrPipeline* pipelineForStencils = nullptr; |
245 | 0 | if (forceRedbookStencilPass || !isLinear) { // Curves always get stencilled. |
246 | 0 | auto pipelineFlags = (fPathFlags & FillPathFlags::kWireframe) |
247 | 0 | ? GrPipeline::InputFlags::kWireframe |
248 | 0 | : GrPipeline::InputFlags::kNone; |
249 | 0 | pipelineForStencils = GrPathTessellationShader::MakeStencilOnlyPipeline( |
250 | 0 | args, fAAType, appliedClip.hardClip(), pipelineFlags); |
251 | 0 | } |
252 | | |
253 | | // Create a pipeline for fill passes if needed. |
254 | 0 | if (doFill) { |
255 | 0 | fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType, |
256 | 0 | std::move(appliedClip), |
257 | 0 | std::move(fProcessors)); |
258 | 0 | } |
259 | | |
260 | | // Pass 1: Tessellate the outer curves into the stencil buffer. |
261 | 0 | if (!isLinear) { |
262 | 0 | fTessellator = PathCurveTessellator::Make(args.fArena, |
263 | 0 | args.fCaps->shaderCaps()->fInfinitySupport); |
264 | 0 | auto* tessShader = GrPathTessellationShader::Make(*args.fCaps->shaderCaps(), |
265 | 0 | args.fArena, |
266 | 0 | fViewMatrix, |
267 | 0 | SK_PMColor4fTRANSPARENT, |
268 | 0 | fTessellator->patchAttribs()); |
269 | 0 | const GrUserStencilSettings* stencilPathSettings = |
270 | 0 | GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath)); |
271 | 0 | fStencilCurvesProgram = GrTessellationShader::MakeProgram(args, |
272 | 0 | tessShader, |
273 | 0 | pipelineForStencils, |
274 | 0 | stencilPathSettings); |
275 | 0 | } |
276 | | |
277 | | // Pass 2: Fill the path's inner fan with a stencil test against the curves. |
278 | 0 | if (fFanPolys) { |
279 | 0 | if (forceRedbookStencilPass) { |
280 | | // Use a standard Redbook "stencil then cover" algorithm instead of bypassing the |
281 | | // stencil buffer to fill the fan directly. |
282 | 0 | const GrUserStencilSettings* stencilPathSettings = |
283 | 0 | GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath)); |
284 | 0 | this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings); |
285 | 0 | if (doFill) { |
286 | 0 | this->pushFanFillProgram(args, |
287 | 0 | GrPathTessellationShader::TestAndResetStencilSettings()); |
288 | 0 | } |
289 | 0 | } else if (isLinear) { |
290 | | // There are no outer curves! Ignore stencil and fill the path directly. |
291 | 0 | SkASSERT(!pipelineForStencils); |
292 | 0 | this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused); |
293 | 0 | } 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 | 0 | constexpr static GrUserStencilSettings kFillOrIncrDecrStencil( |
301 | 0 | GrUserStencilSettings::StaticInitSeparate< |
302 | 0 | 0x0000, 0x0000, |
303 | 0 | GrUserStencilTest::kEqual, GrUserStencilTest::kEqual, |
304 | 0 | 0xffff, 0xffff, |
305 | 0 | GrUserStencilOp::kKeep, GrUserStencilOp::kKeep, |
306 | 0 | GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap, |
307 | 0 | 0xffff, 0xffff>()); |
308 | |
|
309 | 0 | constexpr static GrUserStencilSettings kFillOrInvertStencil( |
310 | 0 | GrUserStencilSettings::StaticInit< |
311 | 0 | 0x0000, |
312 | 0 | GrUserStencilTest::kEqual, |
313 | 0 | 0xffff, |
314 | 0 | GrUserStencilOp::kKeep, |
315 | | // "Zero" instead of "Invert" because the fan only touches any given pixel once. |
316 | 0 | GrUserStencilOp::kZero, |
317 | 0 | 0xffff>()); |
318 | |
|
319 | 0 | auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding) |
320 | 0 | ? &kFillOrIncrDecrStencil |
321 | 0 | : &kFillOrInvertStencil; |
322 | 0 | this->pushFanFillProgram(args, stencil); |
323 | 0 | } 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 | 0 | constexpr static GrUserStencilSettings kFillIfZeroAndInClip( |
328 | 0 | GrUserStencilSettings::StaticInit< |
329 | 0 | 0x0000, |
330 | 0 | GrUserStencilTest::kEqualIfInClip, |
331 | 0 | 0xffff, |
332 | 0 | GrUserStencilOp::kKeep, |
333 | 0 | GrUserStencilOp::kKeep, |
334 | 0 | 0xffff>()); |
335 | |
|
336 | 0 | constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero( |
337 | 0 | GrUserStencilSettings::StaticInitSeparate< |
338 | 0 | 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. |
341 | 0 | GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual, |
342 | 0 | 0xffff, 0xffff, |
343 | 0 | GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap, |
344 | 0 | GrUserStencilOp::kKeep, GrUserStencilOp::kKeep, |
345 | 0 | 0xffff, 0xffff>()); |
346 | |
|
347 | 0 | constexpr static GrUserStencilSettings kInvertStencilIfNonZero( |
348 | 0 | GrUserStencilSettings::StaticInit< |
349 | 0 | 0x0000, |
350 | | // No need to check the clip because the previous stencil pass will have only |
351 | | // written to samples already inside the clip. |
352 | 0 | GrUserStencilTest::kNotEqual, |
353 | 0 | 0xffff, |
354 | | // "Zero" instead of "Invert" because the fan only touches any given pixel once. |
355 | 0 | GrUserStencilOp::kZero, |
356 | 0 | GrUserStencilOp::kKeep, |
357 | 0 | 0xffff>()); |
358 | | |
359 | | // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero. |
360 | 0 | this->pushFanFillProgram(args, &kFillIfZeroAndInClip); |
361 | | |
362 | | // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0. |
363 | 0 | auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding) |
364 | 0 | ? &kIncrDecrStencilIfNonzero |
365 | 0 | : &kInvertStencilIfNonZero; |
366 | 0 | this->pushFanStencilProgram(args, pipelineForStencils, stencil); |
367 | 0 | } |
368 | 0 | } |
369 | | |
370 | | // Pass 3: Draw convex hulls around each curve. |
371 | 0 | 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 | 0 | SkASSERT(fTessellator); |
376 | 0 | auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor, |
377 | 0 | *args.fCaps->shaderCaps()); |
378 | 0 | fCoverHullsProgram = GrTessellationShader::MakeProgram( |
379 | 0 | args, hullShader, fPipelineForFills, |
380 | 0 | GrPathTessellationShader::TestAndResetStencilSettings()); |
381 | 0 | } |
382 | 0 | } Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::prePreparePrograms(GrTessellationShader::ProgramArgs const&, GrAppliedClip&&) Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::prePreparePrograms(GrTessellationShader::ProgramArgs const&, GrAppliedClip&&) |
383 | | |
384 | | void PathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context, |
385 | | const GrSurfaceProxyView& writeView, |
386 | | GrAppliedClip* clip, |
387 | | const GrDstProxyView& dstProxyView, |
388 | | GrXferBarrierFlags renderPassXferBarriers, |
389 | 0 | GrLoadOp colorLoadOp) { |
390 | | // DMSAA is not supported on DDL. |
391 | 0 | bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1; |
392 | 0 | this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface, |
393 | 0 | &dstProxyView, renderPassXferBarriers, colorLoadOp, |
394 | 0 | context->priv().caps()}, |
395 | 0 | (clip) ? std::move(*clip) : GrAppliedClip::Disabled()); |
396 | 0 | if (fStencilCurvesProgram) { |
397 | 0 | context->priv().recordProgramInfo(fStencilCurvesProgram); |
398 | 0 | } |
399 | 0 | for (const GrProgramInfo* fanProgram : fFanPrograms) { |
400 | 0 | context->priv().recordProgramInfo(fanProgram); |
401 | 0 | } |
402 | 0 | if (fCoverHullsProgram) { |
403 | 0 | context->priv().recordProgramInfo(fCoverHullsProgram); |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | SKGPU_DECLARE_STATIC_UNIQUE_KEY(gHullVertexBufferKey); |
408 | | |
409 | 0 | void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) { |
410 | 0 | const GrCaps& caps = flushState->caps(); |
411 | |
|
412 | 0 | if (!fFanTriangulator) { |
413 | 0 | this->prePreparePrograms({flushState->allocator(), flushState->writeView(), |
414 | 0 | flushState->usesMSAASurface(), &flushState->dstProxyView(), |
415 | 0 | flushState->renderPassBarriers(), flushState->colorLoadOp(), |
416 | 0 | &caps}, flushState->detachAppliedClip()); |
417 | 0 | if (!fFanTriangulator) { |
418 | 0 | return; |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | 0 | if (fFanPolys) { |
423 | 0 | GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex); |
424 | 0 | fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs); |
425 | 0 | } |
426 | |
|
427 | 0 | if (fTessellator) { |
428 | 0 | auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>(); |
429 | 0 | fTessellator->prepareWithTriangles(flushState, |
430 | 0 | tessShader->viewMatrix(), |
431 | 0 | &fFanBreadcrumbs, |
432 | 0 | {SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT}, |
433 | 0 | fPath.countVerbs()); |
434 | 0 | } |
435 | |
|
436 | 0 | if (!caps.shaderCaps()->fVertexIDSupport) { |
437 | 0 | constexpr static float kStripOrderIDs[4] = {0, 1, 3, 2}; |
438 | |
|
439 | 0 | SKGPU_DEFINE_STATIC_UNIQUE_KEY(gHullVertexBufferKey); |
440 | |
|
441 | 0 | fHullVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer( |
442 | 0 | GrGpuBufferType::kVertex, sizeof(kStripOrderIDs), kStripOrderIDs, |
443 | 0 | gHullVertexBufferKey); |
444 | 0 | } |
445 | 0 | } |
446 | | |
447 | 0 | void PathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { |
448 | 0 | if (fCoverHullsProgram && |
449 | 0 | fCoverHullsProgram->geomProc().hasVertexAttributes() && |
450 | 0 | !fHullVertexBufferIfNoIDSupport) { |
451 | 0 | return; |
452 | 0 | } |
453 | | |
454 | 0 | if (fStencilCurvesProgram) { |
455 | 0 | SkASSERT(fTessellator); |
456 | 0 | flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds()); |
457 | 0 | fTessellator->draw(flushState); |
458 | 0 | } |
459 | | |
460 | | // Allocation of the fan vertex buffer may have failed but we already pushed back fan programs. |
461 | 0 | if (fFanBuffer) { |
462 | 0 | for (const GrProgramInfo* fanProgram : fFanPrograms) { |
463 | 0 | flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds()); |
464 | 0 | flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline()); |
465 | 0 | flushState->bindBuffers(nullptr, nullptr, fFanBuffer); |
466 | 0 | flushState->draw(fFanVertexCount, fBaseFanVertex); |
467 | 0 | } |
468 | 0 | } |
469 | |
|
470 | 0 | if (fCoverHullsProgram) { |
471 | 0 | SkASSERT(fTessellator); |
472 | 0 | flushState->bindPipelineAndScissorClip(*fCoverHullsProgram, this->bounds()); |
473 | 0 | flushState->bindTextures(fCoverHullsProgram->geomProc(), nullptr, *fPipelineForFills); |
474 | 0 | fTessellator->drawHullInstances(flushState, fHullVertexBufferIfNoIDSupport); |
475 | 0 | } |
476 | 0 | } Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::onExecute(GrOpFlushState*, SkRect const&) Unexecuted instantiation: skgpu::ganesh::PathInnerTriangulateOp::onExecute(GrOpFlushState*, SkRect const&) |
477 | | |
478 | | } // namespace skgpu::ganesh |
479 | | |
480 | | #endif // SK_ENABLE_OPTIMIZE_SIZE |