23static const char* kRobustNormalizeDiffFn =
24"float2 robust_normalize_diff(float2 a, float2 b) {"
25 "float2 diff = a - b;"
26 "if (diff == float2(0.0)) {"
29 "float invMag = 1.0 / max(abs(diff.x), abs(diff.y));"
30 "return normalize(invMag * diff);"
38static const char* kCosineBetweenUnitVectorsFn =
39"float cosine_between_unit_vectors(float2 a, float2 b) {"
42 "return clamp(dot(a, b), -1.0, 1.0);"
51static const char* kMiterExtentFn =
52"float miter_extent(float cosTheta, float miterLimit) {"
53 "float x = fma(cosTheta, .5, .5);"
54 "return (x * miterLimit * miterLimit >= 1.0) ? inversesqrt(x) : sqrt(x);"
62static const char* kNumRadialSegmentsPerRadianFn =
63"float num_radial_segments_per_radian(float approxDevStrokeRadius) {"
64 "return .5 / acos(max(1.0 - (1.0 / PRECISION) / approxDevStrokeRadius, -1.0));"
72static const char* kUncheckedMixFn =
73"float unchecked_mix(float a, float b, float T) {"
74 "return fma(b - a, T, a);"
76"float2 unchecked_mix(float2 a, float2 b, float T) {"
77 "return fma(b - a, float2(T), a);"
79"float4 unchecked_mix(float4 a, float4 b, float4 T) {"
80 "return fma(b - a, T, a);"
95 , fPatchAttribs(attribs |
PatchAttribs::kJoinControlPoint)
111 if (fPatchAttribs & PatchAttribs::kStrokeParams) {
116 fAttribs.emplace_back(
"dynamicColorAttr",
117 (fPatchAttribs & PatchAttribs::kWideColorIfEnabled)
122 if (fPatchAttribs & PatchAttribs::kExplicitCurveType) {
136 SkASSERT(fAttribs.size() <= kMaxAttribCount);
143 void onEmitCode(
EmitArgs&, GrGPArgs*)
override;
169 GrGPArgs* gpArgs,
const GrShaderCaps& shaderCaps)
const;
185void GrStrokeTessellationShader::Impl::onEmitCode(EmitArgs&
args, GrGPArgs* gpArgs) {
188 args.fVaryingHandler->emitAttributes(shader);
190 args.fVertBuilder->defineConstant(
"float",
"PI",
"3.141592653589793238");
196 float maxEdges =
args.fShaderCaps->fVertexIDSupport ? FixedCountStrokes::kMaxEdges
197 : FixedCountStrokes::kMaxEdgesNoVertexIDs;
198 args.fVertBuilder->defineConstant(
"NUM_TOTAL_EDGES", maxEdges);
201 if (shader.hasDynamicStroke()) {
202 args.fVertBuilder->insertFunction(kNumRadialSegmentsPerRadianFn);
204 args.fVertBuilder->insertFunction(kRobustNormalizeDiffFn);
205 args.fVertBuilder->insertFunction(kCosineBetweenUnitVectorsFn);
206 args.fVertBuilder->insertFunction(kMiterExtentFn);
207 args.fVertBuilder->insertFunction(kUncheckedMixFn);
211 if (!shader.hasDynamicStroke()) {
213 const char* tessArgsName;
214 fTessControlArgsUniform =
args.fUniformHandler->addUniform(
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);
225 SkASSERT(!shader.stroke().isHairlineStyle());
226 const char* maxScaleName;
227 fTessControlArgsUniform =
args.fUniformHandler->addUniform(
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);
238 if (shader.hasDynamicColor()) {
241 args.fVaryingHandler->addVarying(
"dynamicColor", &dynamicColor);
242 args.fVertBuilder->codeAppendf(
"%s = dynamicColorAttr;", dynamicColor.vsOut());
243 fDynamicColorName = dynamicColor.fsIn();
247 const char* translateName, *affineMatrixName;
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);
258 if (shader.hasExplicitCurveType()) {
260 "bool is_conic_curve() { return curveTypeAttr != %g; }",
263 args.fVertBuilder->insertFunction(
264 "bool is_conic_curve() { return isinf(pts23Attr.w); }");
268 args.fVertBuilder->codeAppend(
269 "float2 p0=pts01Attr.xy, p1=pts01Attr.zw, p2=pts23Attr.xy, p3=pts23Attr.zw;"
270 "float2 lastControlPoint = argsAttr.xy;"
272 "if (is_conic_curve()) {"
282 args.fVertBuilder->codeAppend(
284 "float numParametricSegments;"
286 "if (p0 == p1 && p2 == p3) {"
287 "numParametricSegments = 1;"
289 "numParametricSegments = wangs_formula_cubic(PRECISION, p0, p1, p2, p3, AFFINE_MATRIX);"
292 "numParametricSegments = wangs_formula_conic(PRECISION,"
293 "AFFINE_MATRIX * p0,"
294 "AFFINE_MATRIX * p1,"
295 "AFFINE_MATRIX * p2, w);"
299 if (shader.stroke().isHairlineStyle()) {
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;"
311 args.fVertBuilder->codeAppend(
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)) {"
318 "tan0 = float2(1,0);"
319 "tan1 = float2(-1,0);"
323 if (
args.fShaderCaps->fVertexIDSupport) {
325 args.fVertBuilder->codeAppend(
326 "float edgeID = float(sk_VertexID >> 1);"
327 "if ((sk_VertexID & 1) != 0) {"
335 args.fVertBuilder->codeAppend(
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);"
344 "float numEdgesInJoin = numRadialSegmentsInJoin + 2;"
348 "numEdgesInJoin = min(numEdgesInJoin, NUM_TOTAL_EDGES - 2);");
349 if (shader.hasDynamicStroke()) {
350 args.fVertBuilder->codeAppend(
351 "if (JOIN_TYPE >= 0) {"
354 "numEdgesInJoin = sign(JOIN_TYPE) + 1 + 2;"
358 args.fVertBuilder->codeAppendf(
"float numEdgesInJoin = %i;",
362 args.fVertBuilder->codeAppend(
366 "float turn = cross_length_2d(p2 - p0, p3 - p1);"
367 "float combinedEdgeID = abs(edgeID) - numEdgesInJoin;"
368 "if (combinedEdgeID < 0) {"
373 "if (lastControlPoint != p0) {"
374 "tan0 = robust_normalize_diff(p0, lastControlPoint);"
376 "turn = cross_length_2d(tan0, tan1);"
380 "float cosTheta = cosine_between_unit_vectors(tan0, tan1);"
381 "float rotation = acos(cosTheta);"
384 "rotation = -rotation;"
387 "float numRadialSegments;"
388 "float strokeOutset = sign(edgeID);"
389 "if (combinedEdgeID < 0) {"
392 "numRadialSegments = numEdgesInJoin - 2;"
393 "numParametricSegments = 1;"
398 "combinedEdgeID += numRadialSegments + 1;"
403 " float sinEpsilon = 1e-2;"
404 "bool tangentsNearlyParallel ="
405 "(abs(turn) * inversesqrt(dot(tan0, tan0) * dot(tan1, tan1))) < sinEpsilon;"
406 "if (!tangentsNearlyParallel || dot(tan0, tan1) < 0) {"
410 "if (combinedEdgeID >= 0) {"
411 "strokeOutset = (turn < 0) ? min(strokeOutset, 0) : max(strokeOutset, 0);"
414 "combinedEdgeID = max(combinedEdgeID, 0);"
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);"
427 "float radsPerSegment = rotation / numRadialSegments;"
428 "float numCombinedSegments = numParametricSegments + numRadialSegments - 1;"
429 "bool isFinalEdge = (combinedEdgeID >= numCombinedSegments);"
430 "if (combinedEdgeID > numCombinedSegments) {"
435 args.fVertBuilder->codeAppendf(
437 "if (abs(edgeID) == 2 && %s) {"
438 "strokeOutset *= miter_extent(cosTheta, JOIN_TYPE);"
439 "}", shader.hasDynamicStroke() ?
"JOIN_TYPE > 0" :
"true");
442 this->emitTessellationCode(shader, &
args.fVertBuilder->code(), gpArgs, *
args.fShaderCaps);
444 this->emitFragmentCode(shader,
args);
447void GrStrokeTessellationShader::Impl::emitTessellationCode(
473 "float2 tangent, strokeCoord;"
474 "if (combinedEdgeID != 0 && !isFinalEdge) {"
482 "float2 A, B, C = p1 - p0;"
483 "float2 D = p3 - p0;"
496 "float2 E = p2 - p1;"
498 "A = fma(float2(-3), E, D);"
509 "float2 B_ = B * (numParametricSegments * 2.0);"
510 "float2 C_ = C * (numParametricSegments * numParametricSegments);"
518 "float lastParametricEdgeID = 0.0;"
519 "float maxParametricEdgeID = min(numParametricSegments - 1.0, combinedEdgeID);"
520 "float negAbsRadsPerSegment = -abs(radsPerSegment);"
521 "float maxRotation0 = (1.0 + combinedEdgeID) * abs(radsPerSegment);"
522 "for (int exp = %i - 1; exp >= 0; --exp) {"
524 "float testParametricID = lastParametricEdgeID + exp2(float(exp));"
525 "if (testParametricID <= maxParametricEdgeID) {"
526 "float2 testTan = fma(float2(testParametricID), A, B_);"
527 "testTan = fma(float2(testParametricID), testTan, C_);"
528 "float cosRotation = dot(normalize(testTan), tan0);"
529 "float maxRotation = fma(testParametricID, negAbsRadsPerSegment, maxRotation0);"
530 "maxRotation = min(maxRotation, PI);"
533 "if (cosRotation >= cos(maxRotation)) {"
535 "lastParametricEdgeID = testParametricID;"
541 "float parametricT = lastParametricEdgeID / numParametricSegments;"
545 "float lastRadialEdgeID = combinedEdgeID - lastParametricEdgeID;"
548 "float angle0 = acos(clamp(tan0.x, -1.0, 1.0));"
549 "angle0 = tan0.y >= 0.0 ? angle0 : -angle0;"
553 "float radialAngle = fma(lastRadialEdgeID, radsPerSegment, angle0);"
554 "tangent = float2(cos(radialAngle), sin(radialAngle));"
555 "float2 norm = float2(-tangent.y, tangent.x);"
565 "float a=dot(norm,A), b_over_2=dot(norm,B), c=dot(norm,C);"
566 "float discr_over_4 = max(b_over_2*b_over_2 - a*c, 0.0);"
567 "float q = sqrt(discr_over_4);"
568 "if (b_over_2 > 0.0) {"
576 "float _5qa = -.5*q*a;"
577 "float2 root = (abs(fma(q,q,_5qa)) < abs(fma(a,c,_5qa))) ? float2(q,a) : float2(c,q);"
578 "float radialT = (root.t != 0.0) ? root.s / root.t : 0.0;"
579 "radialT = clamp(radialT, 0.0, 1.0);"
581 "if (lastRadialEdgeID == 0.0) {"
589 "float T = max(parametricT, radialT);"
592 "float2 ab = unchecked_mix(p0, p1, T);"
593 "float2 bc = unchecked_mix(p1, p2, T);"
594 "float2 cd = unchecked_mix(p2, p3, T);"
595 "float2 abc = unchecked_mix(ab, bc, T);"
596 "float2 bcd = unchecked_mix(bc, cd, T);"
597 "float2 abcd = unchecked_mix(abc, bcd, T);"
600 "float u = unchecked_mix(1.0, w, T);"
601 "float v = w + 1 - u;"
602 "float uv = unchecked_mix(u, v, T);"
607 "if (T != radialT) {"
609 "tangent = w >= 0.0 ? robust_normalize_diff(bc*u, ab*v)"
610 ": robust_normalize_diff(bcd, abc);"
613 "strokeCoord = (w >= 0.0) ? abc/uv : abcd;"
617 "tangent = (combinedEdgeID == 0) ? tan0 : tan1;"
618 "strokeCoord = (combinedEdgeID == 0) ? p0 : p3;"
623 "float2 ortho = float2(tangent.y, -tangent.x);"
624 "strokeCoord += ortho * (STROKE_RADIUS * strokeOutset);");
628 code->append(
"float2 devCoord = AFFINE_MATRIX * strokeCoord + TRANSLATE;");
634 "float2 devCoord = strokeCoord + TRANSLATE;"
635 "float2 localCoord = inverse(AFFINE_MATRIX) * strokeCoord;");
642 const EmitArgs&
args) {
645 const char* colorUniformName;
649 args.fFragBuilder->codeAppendf(
"half4 %s = %s;",
args.fOutputColor, colorUniformName);
651 args.fFragBuilder->codeAppendf(
"half4 %s = %s;",
args.fOutputColor,
652 fDynamicColorName.c_str());
654 args.fFragBuilder->codeAppendf(
"const half4 %s = half4(1);",
args.fOutputCoverage);
673 pdman.
set3f(fTessControlArgsUniform,
674 numRadialSegmentsPerRadian,
679 pdman.
set1f(fTessControlArgsUniform, maxScale);
684 pdman.
set2f(fTranslateUniform,
m.getTranslateX(),
m.getTranslateY());
685 pdman.
set4f(fAffineMatrixUniform,
m.getScaleX(),
m.getSkewY(),
m.getSkewX(),
694 bool keyNeedsJoin = !(fPatchAttribs & PatchAttribs::kStrokeParams);
704std::unique_ptr<GrGeometryProcessor::ProgramImpl> GrStrokeTessellationShader::makeProgramImpl(
706 return std::make_unique<Impl>();
@ kFloat2_GrVertexAttribType
@ kUByte4_norm_GrVertexAttribType
@ kFloat_GrVertexAttribType
@ kFloat4_GrVertexAttribType
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
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
void setInstanceAttributesWithImplicitOffsets(const Attribute *attrs, int attrCount)
size_t instanceStride() const
void setVertexAttributesWithImplicitOffsets(const Attribute *attrs, int attrCount)
bool hasDynamicColor() const
GrStrokeTessellationShader(const GrShaderCaps &, PatchAttribs, const SkMatrix &viewMatrix, const SkStrokeRec &, SkPMColor4f)
bool hasDynamicStroke() const
PatchAttribs attribs() const
const SkStrokeRec & stroke() const
static const char * WangsFormulaSkSL()
const SkPMColor4f & color() const
const SkMatrix & viewMatrix() const
SkScalar getMaxScale() const
@ kMiter_Join
extends to miter limit
bool isHairlineStyle() const
SkScalar getWidth() const
SkPaint::Join getJoin() const
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
float GetJoinType(const SkStrokeRec &stroke)
float CalcNumRadialSegmentsPerRadian(float approxDevStrokeRadius)
static constexpr int kMaxResolveLevel
constexpr size_t PatchAttribsStride(PatchAttribs attribs)
static constexpr float kPrecision
static constexpr float kCubicCurveType
constexpr int NumFixedEdgesInJoin(SkPaint::Join joinType)
SIN Vec< N, float > abs(const Vec< N, float > &x)
const float * vec() const