@@ -76,6 +76,15 @@ class GrStrokeTessellateShader::Impl : public GrGLSLGeometryProcessor {
7676 v->declareGlobal (GrShaderVar (" vsTans01" , kFloat4_GrSLType , TypeModifier::Out));
7777 v->declareGlobal (GrShaderVar (" vsTans23" , kFloat4_GrSLType , TypeModifier::Out));
7878 v->declareGlobal (GrShaderVar (" vsPrevJoinTangent" , kFloat2_GrSLType , TypeModifier::Out));
79+
80+ // Unlike mix(), this does not return b when t==1. But it otherwise seems to get better
81+ // precision than "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points.
82+ // The responsibility falls on the caller to ensure t != 1 before calling.
83+ v->insertFunction (R"(
84+ float4 unchecked_mix(float4 a, float4 b, float4 t) {
85+ return fma(b - a, t, a);
86+ })" );
87+
7988 v->codeAppendf (R"(
8089 // Unpack the control points.
8190 float4x2 P = float4x2(inputPts01, inputPts23);
@@ -206,13 +215,13 @@ class GrStrokeTessellateShader::Impl : public GrGLSLGeometryProcessor {
206215 }
207216
208217 // Chop the curve at chopT[0] and chopT[1].
209- float4 ab = mix (P[0].xyxy, P[1].xyxy, chopT.sstt);
210- float4 bc = mix (P[1].xyxy, P[2].xyxy, chopT.sstt);
211- float4 cd = mix (P[2].xyxy, P[3].xyxy, chopT.sstt);
212- float4 abc = mix (ab, bc, chopT.sstt);
213- float4 bcd = mix (bc, cd, chopT.sstt);
214- float4 abcd = mix (abc, bcd, chopT.sstt);
215- float4 middle = mix (abc, bcd, chopT.ttss);
218+ float4 ab = unchecked_mix (P[0].xyxy, P[1].xyxy, chopT.sstt);
219+ float4 bc = unchecked_mix (P[1].xyxy, P[2].xyxy, chopT.sstt);
220+ float4 cd = unchecked_mix (P[2].xyxy, P[3].xyxy, chopT.sstt);
221+ float4 abc = unchecked_mix (ab, bc, chopT.sstt);
222+ float4 bcd = unchecked_mix (bc, cd, chopT.sstt);
223+ float4 abcd = unchecked_mix (abc, bcd, chopT.sstt);
224+ float4 middle = unchecked_mix (abc, bcd, chopT.ttss);
216225
217226 // Find tangents at the chop points if an inner tangent wasn't specified.
218227 if (innerTangents[0] == float2(0)) {
@@ -522,6 +531,13 @@ SkString GrStrokeTessellateShader::getTessEvaluationShaderGLSL(
522531
523532 uniform vec4 sk_RTAdjust;
524533
534+ // Unlike mix(), this does not return b when t==1. But it otherwise seems to get better
535+ // precision than "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points.
536+ // We override this result anyway when t==1 so it shouldn't be a problem.
537+ vec2 unchecked_mix(vec2 a, vec2 b, float t) {
538+ return fma(b - a, vec2(t), a);
539+ }
540+
525541 void main() {
526542 // Our patch is composed of exactly "numTotalCombinedSegments + 1" stroke-width edges that
527543 // run orthogonal to the curve and make a strip of "numTotalCombinedSegments" quads.
@@ -666,12 +682,12 @@ SkString GrStrokeTessellateShader::getTessEvaluationShaderGLSL(
666682 float T = max(parametricT, radialT);
667683
668684 // Evaluate the cubic at T. Use De Casteljau's for its accuracy and stability.
669- vec2 ab = mix (P[0], P[1], T);
670- vec2 bc = mix (P[1], P[2], T);
671- vec2 cd = mix (P[2], P[3], T);
672- vec2 abc = mix (ab, bc, T);
673- vec2 bcd = mix (bc, cd, T);
674- vec2 position = mix (abc, bcd, T);
685+ vec2 ab = unchecked_mix (P[0], P[1], T);
686+ vec2 bc = unchecked_mix (P[1], P[2], T);
687+ vec2 cd = unchecked_mix (P[2], P[3], T);
688+ vec2 abc = unchecked_mix (ab, bc, T);
689+ vec2 bcd = unchecked_mix (bc, cd, T);
690+ vec2 position = unchecked_mix (abc, bcd, T);
675691
676692 // If we went with T=parametricT, then update the tangent. Otherwise leave it at the radial
677693 // tangent found previously. (In the event that parametricT == radialT, we keep the radial
0 commit comments