Skip to content

Commit 418eda2

Browse files
csmartdalton86Skia Commit-Bot
authored andcommitted
Slightly improve cubic chop precision in stroke tessellation shaders
Also adds GrGLSLVertexGeoBuilder::appendRawFunctionDefinition(). This allows us to define functions in vertex and geometry shaders whose names don't get mangled. Bug: skia:10419 Change-Id: I90319b54bcbbb7000c7809cb6ce8d1969a3bd8c5 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/327456 Reviewed-by: Brian Salomon <[email protected]> Commit-Queue: Chris Dalton <[email protected]>
1 parent fd0b158 commit 418eda2

File tree

2 files changed

+37
-13
lines changed

2 files changed

+37
-13
lines changed

src/gpu/glsl/GrGLSLVertexGeoBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
* geometry for the rasterizer.
1616
*/
1717
class GrGLSLVertexGeoBuilder : public GrGLSLShaderBuilder {
18+
public:
19+
// Copies the given text verbatim to the function definitions section. Does not mangle the name.
20+
// 'functionDefinition' should be a fully valid SkSL function, complete with return type, name,
21+
// arguments, braces, and a body.
22+
void insertFunction(const char* functionDefinition) {
23+
this->functions().append(functionDefinition);
24+
}
25+
1826
protected:
1927
GrGLSLVertexGeoBuilder(GrGLSLProgramBuilder* program) : INHERITED(program) {}
2028

src/gpu/tessellate/GrStrokeTessellateShader.cpp

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)