Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Private Member Functions | List of all members
GrStrokeTessellationShader::Impl Class Reference
Inheritance diagram for GrStrokeTessellationShader::Impl:
GrFragmentProcessor::ProgramImpl

Private Member Functions

void onEmitCode (EmitArgs &, GrGPArgs *) override
 
void setData (const GrGLSLProgramDataManager &pdman, const GrShaderCaps &, const GrGeometryProcessor &) final
 

Additional Inherited Members

- Public Types inherited from GrFragmentProcessor::ProgramImpl
using UniformHandle = GrGLSLUniformHandler::UniformHandle
 
using SamplerHandle = GrGLSLUniformHandler::SamplerHandle
 
- Public Member Functions inherited from GrFragmentProcessor::ProgramImpl
 ProgramImpl ()=default
 
virtual ~ProgramImpl ()=default
 
virtual void emitCode (EmitArgs &)=0
 
void setData (const GrGLSLProgramDataManager &pdman, const GrFragmentProcessor &processor)
 
int numChildProcessors () const
 
ProgramImplchildProcessor (int index) const
 
void setFunctionName (SkString name)
 
const char * functionName () const
 
SkString invokeChild (int childIndex, EmitArgs &parentArgs, std::string_view skslCoords={})
 
SkString invokeChildWithMatrix (int childIndex, EmitArgs &parentArgs)
 
SkString invokeChild (int childIndex, const char *inputColor, EmitArgs &parentArgs, std::string_view skslCoords={})
 
SkString invokeChildWithMatrix (int childIndex, const char *inputColor, EmitArgs &parentArgs)
 
SkString invokeChild (int childIndex, const char *inputColor, const char *destColor, EmitArgs &parentArgs, std::string_view skslCoords={})
 
SkString invokeChildWithMatrix (int childIndex, const char *inputColor, const char *destColor, EmitArgs &parentArgs)
 

Detailed Description

Definition at line 142 of file GrStrokeTessellationShader.cpp.

Member Function Documentation

◆ onEmitCode()

void GrStrokeTessellationShader::Impl::onEmitCode ( EmitArgs args,
GrGPArgs *  gpArgs 
)
overrideprivate

Definition at line 185 of file GrStrokeTessellationShader.cpp.

185 {
186 const auto& shader = args.fGeomProc.cast<GrStrokeTessellationShader>();
187 SkPaint::Join joinType = shader.stroke().getJoin();
188 args.fVaryingHandler->emitAttributes(shader);
189
190 args.fVertBuilder->defineConstant("float", "PI", "3.141592653589793238");
191 args.fVertBuilder->defineConstant("PRECISION", skgpu::tess::kPrecision);
192 // There is an artificial maximum number of edges (compared to the max limit calculated based on
193 // the number of radial segments per radian, Wang's formula, and join type). When there is
194 // vertex ID support, the limit is what can be represented in a uint16; otherwise the limit is
195 // the size of the fallback vertex buffer.
196 float maxEdges = args.fShaderCaps->fVertexIDSupport ? FixedCountStrokes::kMaxEdges
197 : FixedCountStrokes::kMaxEdgesNoVertexIDs;
198 args.fVertBuilder->defineConstant("NUM_TOTAL_EDGES", maxEdges);
199
200 // Helper functions.
201 if (shader.hasDynamicStroke()) {
202 args.fVertBuilder->insertFunction(kNumRadialSegmentsPerRadianFn);
203 }
204 args.fVertBuilder->insertFunction(kRobustNormalizeDiffFn);
205 args.fVertBuilder->insertFunction(kCosineBetweenUnitVectorsFn);
206 args.fVertBuilder->insertFunction(kMiterExtentFn);
207 args.fVertBuilder->insertFunction(kUncheckedMixFn);
208 args.fVertBuilder->insertFunction(GrTessellationShader::WangsFormulaSkSL());
209
210 // Tessellation control uniforms and/or dynamic attributes.
211 if (!shader.hasDynamicStroke()) {
212 // [NUM_RADIAL_SEGMENTS_PER_RADIAN, JOIN_TYPE, STROKE_RADIUS]
213 const char* tessArgsName;
214 fTessControlArgsUniform = args.fUniformHandler->addUniform(
215 nullptr, kVertex_GrShaderFlag, SkSLType::kFloat3, "tessControlArgs",
216 &tessArgsName);
217 args.fVertBuilder->codeAppendf(
218 "float NUM_RADIAL_SEGMENTS_PER_RADIAN = %s.x;"
219 "float JOIN_TYPE = %s.y;"
220 "float STROKE_RADIUS = %s.z;", tessArgsName, tessArgsName, tessArgsName);
221 } else {
222 // The shader does not currently support dynamic hairlines, so this case only needs to
223 // configure NUM_RADIAL_SEGMENTS_PER_RADIAN based on the fixed maxScale and per-instance
224 // stroke radius attribute that's defined in local space.
225 SkASSERT(!shader.stroke().isHairlineStyle());
226 const char* maxScaleName;
227 fTessControlArgsUniform = args.fUniformHandler->addUniform(
228 nullptr, kVertex_GrShaderFlag, SkSLType::kFloat, "maxScale",
229 &maxScaleName);
230 args.fVertBuilder->codeAppendf(
231 "float STROKE_RADIUS = dynamicStrokeAttr.x;"
232 "float JOIN_TYPE = dynamicStrokeAttr.y;"
233 "float NUM_RADIAL_SEGMENTS_PER_RADIAN = num_radial_segments_per_radian("
234 "%s * STROKE_RADIUS);", maxScaleName);
235
236 }
237
238 if (shader.hasDynamicColor()) {
239 // Create a varying for color to get passed in through.
240 GrGLSLVarying dynamicColor{SkSLType::kHalf4};
241 args.fVaryingHandler->addVarying("dynamicColor", &dynamicColor);
242 args.fVertBuilder->codeAppendf("%s = dynamicColorAttr;", dynamicColor.vsOut());
243 fDynamicColorName = dynamicColor.fsIn();
244 }
245
246 // View matrix uniforms.
247 const char* translateName, *affineMatrixName;
248 fAffineMatrixUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
249 SkSLType::kFloat4, "affineMatrix",
250 &affineMatrixName);
251 fTranslateUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
252 SkSLType::kFloat2, "translate",
253 &translateName);
254 args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s.xy, %s.zw);\n",
255 affineMatrixName, affineMatrixName);
256 args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;\n", translateName);
257
258 if (shader.hasExplicitCurveType()) {
259 args.fVertBuilder->insertFunction(SkStringPrintf(
260 "bool is_conic_curve() { return curveTypeAttr != %g; }",
262 } else {
263 args.fVertBuilder->insertFunction(
264 "bool is_conic_curve() { return isinf(pts23Attr.w); }");
265 }
266
267 // Tessellation code.
268 args.fVertBuilder->codeAppend(
269 "float2 p0=pts01Attr.xy, p1=pts01Attr.zw, p2=pts23Attr.xy, p3=pts23Attr.zw;"
270 "float2 lastControlPoint = argsAttr.xy;"
271 "float w = -1;" // w<0 means the curve is an integral cubic.
272 "if (is_conic_curve()) {"
273 // Conics are 3 points, with the weight in p3.
274 "w = p3.x;"
275 "p3 = p2;" // Setting p3 equal to p2 works for the remaining rotational logic.
276 "}"
277 );
278
279 // Emit code to call Wang's formula to determine parametric segments. We do this before
280 // transform points for hairlines so that it is consistent with how the CPU tested the control
281 // points for chopping.
282 args.fVertBuilder->codeAppend(
283 // Find how many parametric segments this stroke requires.
284 "float numParametricSegments;"
285 "if (w < 0) {"
286 "if (p0 == p1 && p2 == p3) {"
287 "numParametricSegments = 1;" // a line
288 "} else {"
289 "numParametricSegments = wangs_formula_cubic(PRECISION, p0, p1, p2, p3, AFFINE_MATRIX);"
290 "}"
291 "} else {"
292 "numParametricSegments = wangs_formula_conic(PRECISION,"
293 "AFFINE_MATRIX * p0,"
294 "AFFINE_MATRIX * p1,"
295 "AFFINE_MATRIX * p2, w);"
296 "}"
297 );
298
299 if (shader.stroke().isHairlineStyle()) {
300 // Hairline case. Transform the points before tessellation. We can still hold off on the
301 // translate until the end; we just need to perform the scale and skew right now.
302 args.fVertBuilder->codeAppend(
303 "p0 = AFFINE_MATRIX * p0;"
304 "p1 = AFFINE_MATRIX * p1;"
305 "p2 = AFFINE_MATRIX * p2;"
306 "p3 = AFFINE_MATRIX * p3;"
307 "lastControlPoint = AFFINE_MATRIX * lastControlPoint;"
308 );
309 }
310
311 args.fVertBuilder->codeAppend(
312 // Find the starting and ending tangents.
313 "float2 tan0 = robust_normalize_diff((p0 == p1) ? ((p1 == p2) ? p3 : p2) : p1, p0);"
314 "float2 tan1 = robust_normalize_diff(p3, (p3 == p2) ? ((p2 == p1) ? p0 : p1) : p2);"
315 "if (tan0 == float2(0)) {"
316 // The stroke is a point. This special case tells us to draw a stroke-width circle as a
317 // 180 degree point stroke instead.
318 "tan0 = float2(1,0);"
319 "tan1 = float2(-1,0);"
320 "}"
321 );
322
323 if (args.fShaderCaps->fVertexIDSupport) {
324 // If we don't have sk_VertexID support then "edgeID" already came in as a vertex attrib.
325 args.fVertBuilder->codeAppend(
326 "float edgeID = float(sk_VertexID >> 1);"
327 "if ((sk_VertexID & 1) != 0) {"
328 "edgeID = -edgeID;"
329 "}"
330 );
331 }
332
333 // Potential optimization: (shader.hasDynamicStroke() && shader.hasRoundJoins())?
334 if (shader.stroke().getJoin() == SkPaint::kRound_Join || shader.hasDynamicStroke()) {
335 args.fVertBuilder->codeAppend(
336 // Determine how many edges to give to the round join. We emit the first and final edges
337 // of the join twice: once full width and once restricted to half width. This guarantees
338 // perfect seaming by matching the vertices from the join as well as from the strokes on
339 // either side.
340 "float2 prevTan = robust_normalize_diff(p0, lastControlPoint);"
341 "float joinRads = acos(cosine_between_unit_vectors(prevTan, tan0));"
342 "float numRadialSegmentsInJoin = max(ceil(joinRads * NUM_RADIAL_SEGMENTS_PER_RADIAN), 1);"
343 // +2 because we emit the beginning and ending edges twice (see above comment).
344 "float numEdgesInJoin = numRadialSegmentsInJoin + 2;"
345 // The stroke section needs at least two edges. Don't assign more to the join than
346 // "NUM_TOTAL_EDGES - 2". (This is only relevant when the ideal max edge count calculated
347 // on the CPU had to be limited to NUM_TOTAL_EDGES in the draw call).
348 "numEdgesInJoin = min(numEdgesInJoin, NUM_TOTAL_EDGES - 2);");
349 if (shader.hasDynamicStroke()) {
350 args.fVertBuilder->codeAppend(
351 "if (JOIN_TYPE >= 0) {" // Is the join not a round type?
352 // Bevel and miter joins get 1 and 2 segments respectively.
353 // +2 because we emit the beginning and ending edges twice (see above comments).
354 "numEdgesInJoin = sign(JOIN_TYPE) + 1 + 2;"
355 "}");
356 }
357 } else {
358 args.fVertBuilder->codeAppendf("float numEdgesInJoin = %i;",
360 }
361
362 args.fVertBuilder->codeAppend(
363 // Find which direction the curve turns.
364 // NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5).
365 // NOTE: F'(.5) x F''(.5) has the same sign as (P2 - P0) x (P3 - P1)
366 "float turn = cross_length_2d(p2 - p0, p3 - p1);"
367 "float combinedEdgeID = abs(edgeID) - numEdgesInJoin;"
368 "if (combinedEdgeID < 0) {"
369 "tan1 = tan0;"
370 // Don't let tan0 become zero. The code as-is isn't built to handle that case. tan0=0
371 // means the join is disabled, and to disable it with the existing code we can leave
372 // tan0 equal to tan1.
373 "if (lastControlPoint != p0) {"
374 "tan0 = robust_normalize_diff(p0, lastControlPoint);"
375 "}"
376 "turn = cross_length_2d(tan0, tan1);"
377 "}"
378
379 // Calculate the curve's starting angle and rotation.
380 "float cosTheta = cosine_between_unit_vectors(tan0, tan1);"
381 "float rotation = acos(cosTheta);"
382 "if (turn < 0) {"
383 // Adjust sign of rotation to match the direction the curve turns.
384 "rotation = -rotation;"
385 "}"
386
387 "float numRadialSegments;"
388 "float strokeOutset = sign(edgeID);"
389 "if (combinedEdgeID < 0) {"
390 // We belong to the preceding join. The first and final edges get duplicated, so we only
391 // have "numEdgesInJoin - 2" segments.
392 "numRadialSegments = numEdgesInJoin - 2;"
393 "numParametricSegments = 1;" // Joins don't have parametric segments.
394 "p3 = p2 = p1 = p0;" // Colocate all points on the junction point.
395 // Shift combinedEdgeID to the range [-1, numRadialSegments]. This duplicates the first
396 // edge and lands one edge at the very end of the join. (The duplicated final edge will
397 // actually come from the section of our strip that belongs to the stroke.)
398 "combinedEdgeID += numRadialSegments + 1;"
399 // We normally restrict the join on one side of the junction, but if the tangents are
400 // nearly equivalent this could theoretically result in bad seaming and/or cracks on the
401 // side we don't put it on. If the tangents are nearly equivalent then we leave the join
402 // double-sided.
403 " float sinEpsilon = 1e-2;" // ~= sin(180deg / 3000)
404 "bool tangentsNearlyParallel ="
405 "(abs(turn) * inversesqrt(dot(tan0, tan0) * dot(tan1, tan1))) < sinEpsilon;"
406 "if (!tangentsNearlyParallel || dot(tan0, tan1) < 0) {"
407 // There are two edges colocated at the beginning. Leave the first one double sided
408 // for seaming with the previous stroke. (The double sided edge at the end will
409 // actually come from the section of our strip that belongs to the stroke.)
410 "if (combinedEdgeID >= 0) {"
411 "strokeOutset = (turn < 0) ? min(strokeOutset, 0) : max(strokeOutset, 0);"
412 "}"
413 "}"
414 "combinedEdgeID = max(combinedEdgeID, 0);"
415 "} else {"
416 // We belong to the stroke. Unless NUM_RADIAL_SEGMENTS_PER_RADIAN is incredibly high,
417 // clamping to maxCombinedSegments will be a no-op because the draw call was invoked with
418 // sufficient vertices to cover the worst case scenario of 180 degree rotation.
419 "float maxCombinedSegments = NUM_TOTAL_EDGES - numEdgesInJoin - 1;"
420 "numRadialSegments = max(ceil(abs(rotation) * NUM_RADIAL_SEGMENTS_PER_RADIAN), 1);"
421 "numRadialSegments = min(numRadialSegments, maxCombinedSegments);"
422 "numParametricSegments = min(numParametricSegments,"
423 "maxCombinedSegments - numRadialSegments + 1);"
424 "}"
425
426 // Additional parameters for emitTessellationCode().
427 "float radsPerSegment = rotation / numRadialSegments;"
428 "float numCombinedSegments = numParametricSegments + numRadialSegments - 1;"
429 "bool isFinalEdge = (combinedEdgeID >= numCombinedSegments);"
430 "if (combinedEdgeID > numCombinedSegments) {"
431 "strokeOutset = 0;" // The strip has more edges than we need. Drop this one.
432 "}");
433
434 if (joinType == SkPaint::kMiter_Join || shader.hasDynamicStroke()) {
435 args.fVertBuilder->codeAppendf(
436 // Edge #2 extends to the miter point.
437 "if (abs(edgeID) == 2 && %s) {"
438 "strokeOutset *= miter_extent(cosTheta, JOIN_TYPE);" // miterLimit
439 "}", shader.hasDynamicStroke() ? "JOIN_TYPE > 0" /*Is the join a miter type?*/ : "true");
440 }
441
442 this->emitTessellationCode(shader, &args.fVertBuilder->code(), gpArgs, *args.fShaderCaps);
443
444 this->emitFragmentCode(shader, args);
445}
@ kVertex_GrShaderFlag
#define SkASSERT(cond)
Definition SkAssert.h:116
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
const SkStrokeRec & stroke() const
static const char * WangsFormulaSkSL()
@ kRound_Join
adds circle
Definition SkPaint.h:360
@ kMiter_Join
extends to miter limit
Definition SkPaint.h:359
SkPaint::Join getJoin() const
Definition SkStrokeRec.h:45
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static constexpr float kPrecision
static constexpr float kCubicCurveType
constexpr int NumFixedEdgesInJoin(SkPaint::Join joinType)

◆ setData()

void GrStrokeTessellationShader::Impl::setData ( const GrGLSLProgramDataManager pdman,
const GrShaderCaps ,
const GrGeometryProcessor geomProc 
)
finalprivate

Definition at line 657 of file GrStrokeTessellationShader.cpp.

659 {
660 const auto& shader = geomProc.cast<GrStrokeTessellationShader>();
661 const auto& stroke = shader.stroke();
662
663 // getMaxScale() returns -1 if it can't compute a scale factor (e.g. perspective), taking the
664 // absolute value automatically converts that to an identity scale factor for our purposes.
665 const float maxScale = std::abs(shader.viewMatrix().getMaxScale());
666 if (!shader.hasDynamicStroke()) {
667 // Set up the tessellation control uniforms. In the hairline case we transform prior to
668 // tessellation, so it will be defined in device space units instead of local units.
669 const float strokeRadius = 0.5f * (stroke.isHairlineStyle() ? 1.f : stroke.getWidth());
670 float numRadialSegmentsPerRadian = skgpu::tess::CalcNumRadialSegmentsPerRadian(
671 (stroke.isHairlineStyle() ? 1.f : maxScale) * strokeRadius);
672
673 pdman.set3f(fTessControlArgsUniform,
674 numRadialSegmentsPerRadian, // NUM_RADIAL_SEGMENTS_PER_RADIAN
675 skgpu::tess::GetJoinType(stroke), // JOIN_TYPE
676 strokeRadius); // STROKE_RADIUS
677 } else {
679 pdman.set1f(fTessControlArgsUniform, maxScale);
680 }
681
682 // Set up the view matrix, if any.
683 const SkMatrix& m = shader.viewMatrix();
684 pdman.set2f(fTranslateUniform, m.getTranslateX(), m.getTranslateY());
685 pdman.set4f(fAffineMatrixUniform, m.getScaleX(), m.getSkewY(), m.getSkewX(),
686 m.getScaleY());
687
688 if (!shader.hasDynamicColor()) {
689 pdman.set4fv(fColorUniform, 1, shader.color().vec());
690 }
691}
virtual void set4fv(UniformHandle, int arrayCount, const float v[]) const =0
virtual void set2f(UniformHandle, float, float) const =0
virtual void set4f(UniformHandle, float, float, float, float) const =0
virtual void set3f(UniformHandle, float, float, float) const =0
virtual void set1f(UniformHandle, float v0) const =0
const T & cast() const
bool isHairlineStyle() const
Definition SkStrokeRec.h:47
SkScalar getWidth() const
Definition SkStrokeRec.h:42
float GetJoinType(const SkStrokeRec &stroke)
float CalcNumRadialSegmentsPerRadian(float approxDevStrokeRadius)

The documentation for this class was generated from the following file: