• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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/tessellate/shaders/GrStrokeTessellationShader.h"
9 
10 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
11 #include "src/gpu/glsl/GrGLSLVarying.h"
12 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
13 #include "src/gpu/tessellate/WangsFormula.h"
14 
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)15 void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
16     const auto& shader = args.fGeomProc.cast<GrStrokeTessellationShader>();
17     auto* uniHandler = args.fUniformHandler;
18     auto* v = args.fVertBuilder;
19 
20     args.fVaryingHandler->emitAttributes(shader);
21 
22     v->defineConstant("float", "PI", "3.141592653589793238");
23 
24     // The vertex shader chops the curve into 3 sections in order to meet our tessellation
25     // requirements. The stroke tessellator does not allow curve sections to inflect or to rotate
26     // more than 180 degrees.
27     //
28     // We start by chopping at inflections (if the curve has any), or else at midtangent. If we
29     // still don't have 3 sections after that then we just subdivide uniformly in parametric space.
30     using TypeModifier = GrShaderVar::TypeModifier;
31     v->defineConstantf("float", "kParametricEpsilon", "1.0 / (%i * 128)",
32                        args.fShaderCaps->maxTessellationSegments());  // 1/128 of a segment.
33 
34     // [numSegmentsInJoin, innerJoinRadiusMultiplier, prevJoinTangent.xy]
35     v->declareGlobal(GrShaderVar("vsJoinArgs0", SkSLType::kFloat4, TypeModifier::Out));
36 
37     // [radsPerJoinSegment, joinOutsetClamp.xy]
38     v->declareGlobal(GrShaderVar("vsJoinArgs1", SkSLType::kFloat3, TypeModifier::Out));
39 
40     // Curve args.
41     v->declareGlobal(GrShaderVar("vsPts01", SkSLType::kFloat4, TypeModifier::Out));
42     v->declareGlobal(GrShaderVar("vsPts23", SkSLType::kFloat4, TypeModifier::Out));
43     v->declareGlobal(GrShaderVar("vsPts45", SkSLType::kFloat4, TypeModifier::Out));
44     v->declareGlobal(GrShaderVar("vsPts67", SkSLType::kFloat4, TypeModifier::Out));
45     v->declareGlobal(GrShaderVar("vsPts89", SkSLType::kFloat4, TypeModifier::Out));
46     v->declareGlobal(GrShaderVar("vsTans01", SkSLType::kFloat4, TypeModifier::Out));
47     v->declareGlobal(GrShaderVar("vsTans23", SkSLType::kFloat4, TypeModifier::Out));
48     if (shader.hasDynamicStroke()) {
49         // [NUM_RADIAL_SEGMENTS_PER_RADIAN, STROKE_RADIUS]
50         v->declareGlobal(GrShaderVar("vsStrokeArgs", SkSLType::kFloat2, TypeModifier::Out));
51     }
52     if (shader.hasDynamicColor()) {
53         v->declareGlobal(GrShaderVar("vsColor", SkSLType::kHalf4, TypeModifier::Out));
54     }
55 
56     v->insertFunction(kCosineBetweenVectorsFn);
57     v->insertFunction(kMiterExtentFn);
58     v->insertFunction(kUncheckedMixFn);
59     if (shader.hasDynamicStroke()) {
60         v->insertFunction(kNumRadialSegmentsPerRadianFn);
61     }
62 
63     if (!shader.hasDynamicStroke()) {
64         // [PARAMETRIC_PRECISION, NUM_RADIAL_SEGMENTS_PER_RADIAN, JOIN_TYPE, STROKE_RADIUS]
65         const char* tessArgsName;
66         fTessControlArgsUniform = uniHandler->addUniform(nullptr,
67                                                          kVertex_GrShaderFlag |
68                                                          kTessControl_GrShaderFlag |
69                                                          kTessEvaluation_GrShaderFlag,
70                                                          SkSLType::kFloat4, "tessArgs",
71                                                          &tessArgsName);
72         v->codeAppendf(R"(
73         float NUM_RADIAL_SEGMENTS_PER_RADIAN = %s.y;
74         float JOIN_TYPE = %s.z;)", tessArgsName, tessArgsName);
75     } else {
76         const char* parametricPrecisionName;
77         fTessControlArgsUniform = uniHandler->addUniform(nullptr,
78                                                          kVertex_GrShaderFlag |
79                                                          kTessControl_GrShaderFlag |
80                                                          kTessEvaluation_GrShaderFlag,
81                                                          SkSLType::kFloat, "parametricPrecision",
82                                                          &parametricPrecisionName);
83         v->codeAppendf(R"(
84         float STROKE_RADIUS = dynamicStrokeAttr.x;
85         float NUM_RADIAL_SEGMENTS_PER_RADIAN = num_radial_segments_per_radian(%s,STROKE_RADIUS);
86         float JOIN_TYPE = dynamicStrokeAttr.y;)", parametricPrecisionName);
87     }
88 
89     fTranslateUniform = uniHandler->addUniform(nullptr, kTessEvaluation_GrShaderFlag,
90                                                SkSLType::kFloat2, "translate", nullptr);
91     // View matrix uniforms.
92     const char* affineMatrixName;
93     // Hairlines apply the affine matrix in their vertex shader, prior to tessellation.
94     // Otherwise the entire view matrix gets applied at the end of the tess eval shader.
95     auto affineMatrixVisibility = kTessEvaluation_GrShaderFlag;
96     if (shader.stroke().isHairlineStyle()) {
97         affineMatrixVisibility |= kVertex_GrShaderFlag;
98     }
99     fAffineMatrixUniform = uniHandler->addUniform(nullptr, affineMatrixVisibility,
100                                                   SkSLType::kFloat4, "affineMatrix",
101                                                   &affineMatrixName);
102     if (affineMatrixVisibility & kVertex_GrShaderFlag) {
103         v->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);\n", affineMatrixName);
104     }
105 
106     v->codeAppend(R"(
107     // Unpack the control points.
108     float2 prevControlPoint = prevCtrlPtAttr;
109     float4x2 P = float4x2(pts01Attr.xy, pts01Attr.zw, pts23Attr.xy, pts23Attr.zw);)");
110 
111     if (shader.stroke().isHairlineStyle()) {
112         // Hairline case. Transform the points before tessellation. We can still hold off on the
113         // translate until the end; we just need to perform the scale and skew right now.
114         v->codeAppend(R"(
115         P = AFFINE_MATRIX * P;
116         if (isinf(pts23Attr.w)) {
117             // If y3 is infinity then x3 is a conic weight. Don't transform.
118             P[3] = pts23Attr.zw;
119         }
120         prevControlPoint = AFFINE_MATRIX * prevControlPoint;)");
121     }
122 
123     v->codeAppend(R"(
124     // Find the tangents. It's imperative that we compute these tangents from the original
125     // (pre-chopping) input points or else the seams might crack.
126     float2 prevJoinTangent = P[0] - prevControlPoint;
127     float2 tan0 = ((P[1] == P[0]) ? P[2] : P[1]) - P[0];
128     float2 tan1 = (P[3] == P[2] || isinf(P[3].y)) ? P[2] - P[1] : P[3] - P[2];
129 
130     if (tan0 == float2(0)) {
131         // [p0, p0, p0, p3] is a reserved pattern that means this patch is a "bowtie".
132         P[3] = P[0];  // Colocate all the points on the center of the bowtie.
133         // Use the final curve section to draw the bowtie. Since the points are colocated, this
134         // curve will register as a line, which overrides innerTangents as [tan0, tan0]. That
135         // disables the first two sections of the curve because their tangents and points are all
136         // equal.
137         tan0 = prevJoinTangent;
138         prevJoinTangent = float2(0);  // Disable the join section.
139     }
140 
141     if (tan1 == float2(0)) {
142         // [p0, p3, p3, p3] is a reserved pattern that means this patch is a join only. Colocate all
143         // the curve's points to ensure it gets disabled by the tessellation stages.
144         P[1] = P[2] = P[3] = P[0];
145         // Since the points are colocated, this curve will register as a line, which overrides
146         // innerTangents as [tan0, tan0]. Setting tan1=tan0 as well results in all tangents and all
147         // points being equal, which disables every section of the curve.
148         tan1 = tan0;
149     }
150 
151     // Calculate the number of segments to chop the join into.
152     float cosTheta = cosine_between_vectors(prevJoinTangent, tan0);
153     float joinRotation = (cosTheta == 1) ? 0 : acos(cosTheta);
154     if (cross_length_2d(prevJoinTangent, tan0) < 0) {
155         joinRotation = -joinRotation;
156     }
157     float joinRadialSegments = abs(joinRotation) * NUM_RADIAL_SEGMENTS_PER_RADIAN;
158     float numSegmentsInJoin = (joinRadialSegments != 0 /*Is the join non-empty?*/ &&
159                                JOIN_TYPE >= 0 /*Is the join not a round type?*/)
160             ? sign(JOIN_TYPE) + 1  // Non-empty bevel joins have 1 segment and miters have 2.
161             : ceil(joinRadialSegments);  // Otherwise round up the number of radial segments.
162 
163     // Extends the middle join edge to the miter point.
164     float innerJoinRadiusMultiplier = 1;
165     if (JOIN_TYPE > 0 /*Is the join a miter type?*/) {
166         innerJoinRadiusMultiplier = miter_extent(cosTheta, JOIN_TYPE/*miterLimit*/);
167     }
168 
169     // Clamps join geometry to the exterior side of the junction.
170     float2 joinOutsetClamp = float2(-1, 1);
171     if (joinRadialSegments > .1 /*Does the join rotate more than 1/10 of a segment?*/) {
172         // Only clamp if the join angle is large enough to guarantee there won't be cracks on
173         // the interior side of the junction.
174         joinOutsetClamp = (joinRotation < 0) ? float2(-1, 0) : float2(0, 1);
175     }
176 
177     // Pack join args for the tessellation control stage.
178     vsJoinArgs0 = float4(numSegmentsInJoin, innerJoinRadiusMultiplier, prevJoinTangent);
179     vsJoinArgs1 = float3(joinRotation / numSegmentsInJoin, joinOutsetClamp);
180 
181     // Now find where to chop the curve so the resulting sub-curves are convex and do not rotate
182     // more than 180 degrees. We don't need to worry about cusps because the caller chops those out
183     // on the CPU. Start by finding the cubic's power basis coefficients. These define the bezier
184     // curve as:
185     //
186     //                                    |T^3|
187     //     Cubic(T) = x,y = |A  3B  3C| * |T^2| + P0
188     //                      |.   .   .|   |T  |
189     //
190     // And the tangent direction (scaled by a uniform 1/3) will be:
191     //
192     //                                                 |T^2|
193     //     Tangent_Direction(T) = dx,dy = |A  2B  C| * |T  |
194     //                                    |.   .  .|   |1  |
195     //
196     float2 C = P[1] - P[0];
197     float2 D = P[2] - P[1];
198     float2 E = P[3] - P[0];
199     float2 B = D - C;
200     float2 A = fma(float2(-3), D, E);
201 
202     // Now find the cubic's inflection function. There are inflections where F' x F'' == 0.
203     // We formulate this as a quadratic equation:  F' x F'' == aT^2 + bT + c == 0.
204     // See: https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
205     // NOTE: We only need the roots, so a uniform scale factor does not affect the solution.
206     float a = cross_length_2d(A, B);
207     float b = cross_length_2d(A, C);
208     float c = cross_length_2d(B, C);
209     float b_over_2 = b*.5;
210     float discr_over_4 = b_over_2*b_over_2 - a*c;
211 
212     float2x2 innerTangents = float2x2(0);
213     if (discr_over_4 <= 0) {
214         // The curve does not inflect. This means it might rotate more than 180 degrees instead.
215         // Craft a quadratic whose roots are the points were rotation == 180 deg and 0. (These are
216         // the points where the tangent is parallel to tan0.)
217         //
218         //      Tangent_Direction(T) x tan0 == 0
219         //      (AT^2 x tan0) + (2BT x tan0) + (C x tan0) == 0
220         //      (A x C)T^2 + (2B x C)T + (C x C) == 0  [[because tan0 == P1 - P0 == C]]
221         //      bT^2 + 2cT + 0 == 0  [[because A x C == b, B x C == c]]
222         //
223         // NOTE: When P0 == P1 then C != tan0, C == 0 and these roots will be undefined. But that's
224         // ok because when P0 == P1 the curve cannot rotate more than 180 degrees anyway.
225         a = b;
226         b_over_2 = c;
227         c = 0;
228         discr_over_4 = b_over_2*b_over_2;
229         innerTangents[0] = -C;
230     }
231 
232     // Solve our quadratic equation for the chop points. This is inspired by the quadratic formula
233     // from Numerical Recipes in C.
234     float q = sqrt(discr_over_4);
235     if (b_over_2 > 0) {
236         q = -q;
237     }
238     q -= b_over_2;
239     float2 chopT = float2((a != 0) ? q/a : 0,
240                           (q != 0) ? c/q : 0);
241 
242     // Reposition any chop points that fall outside ~0..1 and clear their innerTangent.
243     int numOutside = 0;
244     if (chopT[0] <= kParametricEpsilon || chopT[0] >= 1 - kParametricEpsilon) {
245         innerTangents[0] = float2(0);
246         ++numOutside;
247     }
248     if (chopT[1] <= kParametricEpsilon || chopT[1] >= 1 - kParametricEpsilon) {
249         // Swap places with chopT[0]. This ensures chopT[0] is outside when numOutside > 0.
250         chopT = chopT.ts;
251         innerTangents = float2x2(0,0, innerTangents[0]);
252         ++numOutside;
253     }
254     if (numOutside == 2) {
255         chopT[1] = 2/3.0;
256     }
257     if (numOutside >= 1) {
258         chopT[0] = (chopT[1] <= .5) ? chopT[1] * .5 : fma(chopT[1], .5, .5);
259     }
260 
261     // Sort the chop points.
262     if (chopT[0] > chopT[1]) {
263         chopT = chopT.ts;
264         innerTangents = float2x2(innerTangents[1], innerTangents[0]);
265     }
266 
267     // If the curve is a straight line, point, or conic, don't chop it into sections after all.
268     if ((P[0] == P[1] && P[2] == P[3]) || isinf(P[3].y)) {
269         chopT = float2(0);
270         innerTangents = float2x2(tan0, tan0);
271     }
272 
273     // Chop the curve at chopT[0] and chopT[1].
274     float4 ab = unchecked_mix(P[0].xyxy, P[1].xyxy, chopT.sstt);
275     float4 bc = unchecked_mix(P[1].xyxy, P[2].xyxy, chopT.sstt);
276     float4 cd = isinf(P[3].y) ? P[2].xyxy : unchecked_mix(P[2].xyxy, P[3].xyxy, chopT.sstt);
277     float4 abc = unchecked_mix(ab, bc, chopT.sstt);
278     float4 bcd = unchecked_mix(bc, cd, chopT.sstt);
279     float4 abcd = unchecked_mix(abc, bcd, chopT.sstt);
280     float4 middle = unchecked_mix(abc, bcd, chopT.ttss);
281 
282     // Find tangents at the chop points if an inner tangent wasn't specified.
283     if (innerTangents[0] == float2(0)) {
284         innerTangents[0] = bcd.xy - abc.xy;
285     }
286     if (innerTangents[1] == float2(0)) {
287         innerTangents[1] = bcd.zw - abc.zw;
288     }
289 
290     // Pack curve args for the tessellation control stage.
291     vsPts01 = float4(P[0], ab.xy);
292     vsPts23 = float4(abc.xy, abcd.xy);
293     vsPts45 = middle;
294     vsPts67 = float4(abcd.zw, bcd.zw);
295     vsPts89 = float4(cd.zw, P[3]);
296     vsTans01 = float4(tan0, innerTangents[0]);
297     vsTans23 = float4(innerTangents[1], tan1);)");
298     if (shader.hasDynamicStroke()) {
299         v->codeAppend(R"(
300         vsStrokeArgs = float2(NUM_RADIAL_SEGMENTS_PER_RADIAN, STROKE_RADIUS);)");
301     }
302     if (shader.hasDynamicColor()) {
303         v->codeAppend(R"(
304         vsColor = dynamicColorAttr;)");
305     }
306 
307     if (shader.hasDynamicColor()) {
308         // Color gets passed in from the tess evaluation shader.
309         fDynamicColorName = "dynamicColor";
310         SkString flatness(args.fShaderCaps->preferFlatInterpolation() ? "flat" : "");
311         args.fFragBuilder->declareGlobal(GrShaderVar(fDynamicColorName, SkSLType::kHalf4,
312                                                      TypeModifier::In, 0, SkString(), flatness));
313     }
314     this->emitFragmentCode(shader, args);
315 }
316 
getTessControlShaderGLSL(const GrGeometryProcessor & geomProc,const char * versionAndExtensionDecls,const GrGLSLUniformHandler & uniformHandler,const GrShaderCaps & shaderCaps) const317 SkString GrStrokeTessellationShader::HardwareImpl::getTessControlShaderGLSL(
318         const GrGeometryProcessor& geomProc,
319         const char* versionAndExtensionDecls,
320         const GrGLSLUniformHandler& uniformHandler,
321         const GrShaderCaps& shaderCaps) const {
322     const auto& shader = geomProc.cast<GrStrokeTessellationShader>();
323     SkASSERT(shader.mode() == GrStrokeTessellationShader::Mode::kHardwareTessellation);
324 
325     SkString code(versionAndExtensionDecls);
326     // Run 3 invocations: 1 for each section that the vertex shader chopped the curve into.
327     code.append("layout(vertices = 3) out;\n");
328     code.appendf("precision highp float;\n");
329 
330     code.appendf("#define float2 vec2\n");
331     code.appendf("#define float3 vec3\n");
332     code.appendf("#define float4 vec4\n");
333     code.appendf("#define float2x2 mat2\n");
334     code.appendf("#define float3x2 mat3x2\n");
335     code.appendf("#define float4x2 mat4x2\n");
336     code.appendf("#define PI 3.141592653589793238\n");
337     code.appendf("#define MAX_TESSELLATION_SEGMENTS %i.0\n", shaderCaps.maxTessellationSegments());
338     code.appendf("#define cross cross2d\n");  // GLSL already has a function named "cross".
339 
340     const char* tessArgsName = uniformHandler.getUniformCStr(fTessControlArgsUniform);
341     if (!shader.hasDynamicStroke()) {
342         code.appendf("uniform vec4 %s;\n", tessArgsName);
343         code.appendf("#define PARAMETRIC_PRECISION %s.x\n", tessArgsName);
344         code.appendf("#define NUM_RADIAL_SEGMENTS_PER_RADIAN %s.y\n", tessArgsName);
345     } else {
346         code.appendf("uniform float %s;\n", tessArgsName);
347         code.appendf("#define PARAMETRIC_PRECISION %s\n", tessArgsName);
348         code.appendf("#define NUM_RADIAL_SEGMENTS_PER_RADIAN vsStrokeArgs[0].x\n");
349     }
350 
351     code.append(skgpu::wangs_formula::as_sksl());
352     code.append(kCosineBetweenVectorsFn);
353     code.append(kMiterExtentFn);
354     code.append(R"(
355     float cross2d(vec2 a, vec2 b) {
356         return determinant(mat2(a,b));
357     })");
358 
359     code.append(R"(
360     in vec4 vsJoinArgs0[];
361     in vec3 vsJoinArgs1[];
362     in vec4 vsPts01[];
363     in vec4 vsPts23[];
364     in vec4 vsPts45[];
365     in vec4 vsPts67[];
366     in vec4 vsPts89[];
367     in vec4 vsTans01[];
368     in vec4 vsTans23[];)");
369     if (shader.hasDynamicStroke()) {
370         code.append(R"(
371         in vec2 vsStrokeArgs[];)");
372     }
373     if (shader.hasDynamicColor()) {
374         code.append(R"(
375         in mediump vec4 vsColor[];)");
376     }
377 
378     code.append(R"(
379     out vec4 tcsPts01[];
380     out vec4 tcsPt2Tan0[];
381     out vec3 tcsTessArgs[];  // [numCombinedSegments, numParametricSegments, radsPerSegment]
382     patch out vec4 tcsJoinArgs0; // [numSegmentsInJoin, innerJoinRadiusMultiplier,
383                                  //  prevJoinTangent.xy]
384     patch out vec3 tcsJoinArgs1;  // [radsPerJoinSegment, joinOutsetClamp.xy]
385     patch out vec4 tcsEndPtEndTan;)");
386     if (shader.hasDynamicStroke()) {
387         code.append(R"(
388         patch out float tcsStrokeRadius;)");
389     }
390     if (shader.hasDynamicColor()) {
391         code.append(R"(
392         patch out mediump vec4 tcsColor;)");
393     }
394 
395     code.append(R"(
396     void main() {
397         // Forward join args to the evaluation stage.
398         tcsJoinArgs0 = vsJoinArgs0[0];
399         tcsJoinArgs1 = vsJoinArgs1[0];)");
400     if (shader.hasDynamicStroke()) {
401         code.append(R"(
402         tcsStrokeRadius = vsStrokeArgs[0].y;)");
403     }
404     if (shader.hasDynamicColor()) {
405         code.append(R"(
406         tcsColor = vsColor[0];)");
407     }
408 
409     code.append(R"(
410         // Unpack the curve args from the vertex shader.
411         mat4x2 P;
412         mat2 tangents;
413         if (gl_InvocationID == 0) {
414             // This is the first section of the curve.
415             P = mat4x2(vsPts01[0], vsPts23[0]);
416             tangents = mat2(vsTans01[0]);
417         } else if (gl_InvocationID == 1) {
418             // This is the middle section of the curve.
419             P = mat4x2(vsPts23[0].zw, vsPts45[0], vsPts67[0].xy);
420             tangents = mat2(vsTans01[0].zw, vsTans23[0].xy);
421         } else {
422             // This is the final section of the curve.
423             P = mat4x2(vsPts67[0], vsPts89[0]);
424             tangents = mat2(vsTans23[0]);
425         }
426 
427         // Calculate the number of parametric segments. The final tessellated strip will be a
428         // composition of these parametric segments as well as radial segments.
429         float w = isinf(P[3].y) ? P[3].x : -1.0; // w<0 means the curve is an integral cubic.
430         float numParametricSegments;
431         if (w < 0.0) {
432             numParametricSegments = wangs_formula_cubic(PARAMETRIC_PRECISION, P[0], P[1], P[2],
433                                                         P[3], mat2(1));
434         } else {
435             numParametricSegments = wangs_formula_conic(PARAMETRIC_PRECISION, P[0], P[1], P[2], w);
436         }
437         if (P[0] == P[1] && P[2] == P[3]) {
438             // This is how the patch builder articulates lineTos but Wang's formula returns
439             // >>1 segment in this scenario. Assign 1 parametric segment.
440             numParametricSegments = 1.0;
441         }
442 
443         // Determine the curve's total rotation. The vertex shader ensures our curve does not rotate
444         // more than 180 degrees or inflect, so the inverse cosine has enough range.
445         float cosTheta = cosine_between_vectors(tangents[0], tangents[1]);
446         float rotation = acos(cosTheta);
447 
448         // Adjust sign of rotation to match the direction the curve turns.
449         // NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5).
450         // NOTE: F'(.5) x F''(.5) has the same sign as (P2 - P0) x (P3 - P1)
451         float turn = isinf(P[3].y) ? cross2d(P[1] - P[0], P[2] - P[1])
452                                    : cross2d(P[2] - P[0], P[3] - P[1]);
453         if (turn == 0.0) {  // This is the case for joins and cusps where points are co-located.
454             turn = determinant(tangents);
455         }
456         if (turn < 0.0) {
457             rotation = -rotation;
458         }
459 
460         // Calculate the number of evenly spaced radial segments to chop this section of the curve
461         // into. Radial segments divide the curve's rotation into even steps. The final tessellated
462         // strip will be a composition of both parametric and radial segments.
463         float numRadialSegments = abs(rotation) * NUM_RADIAL_SEGMENTS_PER_RADIAN;
464         numRadialSegments = max(ceil(numRadialSegments), 1.0);
465 
466         // The first and last edges are shared by both the parametric and radial sets of edges, so
467         // the total number of edges is:
468         //
469         //   numCombinedEdges = numParametricEdges + numRadialEdges - 2
470         //
471         // It's also important to differentiate between the number of edges and segments in a strip:
472         //
473         //   numCombinedSegments = numCombinedEdges - 1
474         //
475         // So the total number of segments in the combined strip is:
476         //
477         //   numCombinedSegments = numParametricEdges + numRadialEdges - 2 - 1
478         //                       = numParametricSegments + 1 + numRadialSegments + 1 - 2 - 1
479         //                       = numParametricSegments + numRadialSegments - 1
480         //
481         float numCombinedSegments = numParametricSegments + numRadialSegments - 1.0;
482 
483         if (P[0] == P[3] && tangents[0] == tangents[1]) {
484             // The vertex shader intentionally disabled our section. Set numCombinedSegments to 0.
485             numCombinedSegments = 0.0;
486         }
487 
488         // Pack the args for the evaluation stage.
489         tcsPts01[gl_InvocationID] = vec4(P[0], P[1]);
490         tcsPt2Tan0[gl_InvocationID] = vec4(P[2], tangents[0]);
491         tcsTessArgs[gl_InvocationID] = vec3(numCombinedSegments, numParametricSegments,
492                                             rotation / numRadialSegments);
493         if (gl_InvocationID == 2) {
494             tcsEndPtEndTan = vec4(P[3], tangents[1]);
495         }
496 
497         barrier();
498 
499         // Tessellate a quad strip with enough segments for the join plus all 3 curve sections
500         // combined.
501         float numTotalCombinedSegments = tcsJoinArgs0.x + tcsTessArgs[0].x + tcsTessArgs[1].x +
502                                          tcsTessArgs[2].x;
503 
504         if (tcsJoinArgs0.x != 0.0 && tcsJoinArgs0.x != numTotalCombinedSegments) {
505             // We are tessellating a quad strip with both a single-sided join and a double-sided
506             // stroke. Add one more edge to the join. This new edge will fall parallel with the
507             // first edge of the stroke, eliminating artifacts on the transition from single
508             // sided to double.
509             ++tcsJoinArgs0.x;
510             ++numTotalCombinedSegments;
511         }
512 
513         numTotalCombinedSegments = min(numTotalCombinedSegments, MAX_TESSELLATION_SEGMENTS);
514         gl_TessLevelInner[0] = numTotalCombinedSegments;
515         gl_TessLevelInner[1] = 2.0;
516         gl_TessLevelOuter[0] = 2.0;
517         gl_TessLevelOuter[1] = numTotalCombinedSegments;
518         gl_TessLevelOuter[2] = 2.0;
519         gl_TessLevelOuter[3] = numTotalCombinedSegments;
520     })");
521 
522     return code;
523 }
524 
getTessEvaluationShaderGLSL(const GrGeometryProcessor & geomProc,const char * versionAndExtensionDecls,const GrGLSLUniformHandler & uniformHandler,const GrShaderCaps & shaderCaps) const525 SkString GrStrokeTessellationShader::HardwareImpl::getTessEvaluationShaderGLSL(
526         const GrGeometryProcessor& geomProc,
527         const char* versionAndExtensionDecls,
528         const GrGLSLUniformHandler& uniformHandler,
529         const GrShaderCaps& shaderCaps) const {
530     const auto& shader = geomProc.cast<GrStrokeTessellationShader>();
531     SkASSERT(shader.mode() == GrStrokeTessellationShader::Mode::kHardwareTessellation);
532 
533     SkString code(versionAndExtensionDecls);
534     code.append("layout(quads, equal_spacing, ccw) in;\n");
535     code.appendf("precision highp float;\n");
536 
537     code.appendf("#define float2 vec2\n");
538     code.appendf("#define float3 vec3\n");
539     code.appendf("#define float4 vec4\n");
540     code.appendf("#define float2x2 mat2\n");
541     code.appendf("#define float3x2 mat3x2\n");
542     code.appendf("#define float4x2 mat4x2\n");
543     code.appendf("#define PI 3.141592653589793238\n");
544 
545     if (!shader.hasDynamicStroke()) {
546         const char* tessArgsName = uniformHandler.getUniformCStr(fTessControlArgsUniform);
547         code.appendf("uniform vec4 %s;\n", tessArgsName);
548         code.appendf("#define STROKE_RADIUS %s.w\n", tessArgsName);
549     } else {
550         code.appendf("#define STROKE_RADIUS tcsStrokeRadius\n");
551     }
552 
553     const char* translateName = uniformHandler.getUniformCStr(fTranslateUniform);
554     code.appendf("uniform vec2 %s;\n", translateName);
555     code.appendf("#define TRANSLATE %s\n", translateName);
556     const char* affineMatrixName = uniformHandler.getUniformCStr(fAffineMatrixUniform);
557     code.appendf("uniform vec4 %s;\n", affineMatrixName);
558     code.appendf("#define AFFINE_MATRIX mat2(%s)\n", affineMatrixName);
559 
560     code.append(R"(
561     in vec4 tcsPts01[];
562     in vec4 tcsPt2Tan0[];
563     in vec3 tcsTessArgs[];  // [numCombinedSegments, numParametricSegments, radsPerSegment]
564     patch in vec4 tcsJoinArgs0;  // [numSegmentsInJoin, innerJoinRadiusMultiplier,
565                                  //  prevJoinTangent.xy]
566     patch in vec3 tcsJoinArgs1;  // [radsPerJoinSegment, joinOutsetClamp.xy]
567     patch in vec4 tcsEndPtEndTan;)");
568     if (shader.hasDynamicStroke()) {
569         code.append(R"(
570         patch in float tcsStrokeRadius;)");
571     }
572     if (shader.hasDynamicColor()) {
573         code.appendf(R"(
574         patch in mediump vec4 tcsColor;
575         %s out mediump vec4 %s;)",
576         shaderCaps.preferFlatInterpolation() ? "flat" : "", fDynamicColorName.c_str());
577     }
578 
579     code.append(R"(
580     uniform vec4 sk_RTAdjust;)");
581 
582     code.append(kUncheckedMixFn);
583 
584     code.append(R"(
585     void main() {
586         // Our patch is composed of exactly "numTotalCombinedSegments + 1" stroke-width edges that
587         // run orthogonal to the curve and make a strip of "numTotalCombinedSegments" quads.
588         // Determine which discrete edge belongs to this invocation. An edge can either come from a
589         // parametric segment or a radial one.
590         float numSegmentsInJoin = tcsJoinArgs0.x;
591         float numTotalCombinedSegments = numSegmentsInJoin + tcsTessArgs[0].x + tcsTessArgs[1].x +
592                                          tcsTessArgs[2].x;
593         float combinedEdgeID = round(gl_TessCoord.x * numTotalCombinedSegments);
594         float strokeOutset = gl_TessCoord.y * 2.0 - 1.0;
595 
596         // Furthermore, the vertex shader may have chopped the curve into 3 different sections.
597         // Determine which section we belong to, and where we fall relative to its first edge.
598         float2 p0, p1, p2, p3;
599         vec2 tan0;
600         float numParametricSegments, radsPerSegment;
601         if (combinedEdgeID < numSegmentsInJoin || numSegmentsInJoin == numTotalCombinedSegments) {
602             // Our edge belongs to the join preceding the curve.
603             p3 = p2 = p1 = p0 = tcsPts01[0].xy;
604             tan0 = tcsJoinArgs0.zw;
605             numParametricSegments = 1;
606             radsPerSegment = tcsJoinArgs1.x;
607             strokeOutset = clamp(strokeOutset, tcsJoinArgs1.y, tcsJoinArgs1.z);
608             strokeOutset *= (combinedEdgeID == 1.0) ? tcsJoinArgs0.y : 1.0;
609         } else if ((combinedEdgeID -= numSegmentsInJoin) < tcsTessArgs[0].x) {
610             // Our edge belongs to the first curve section.
611             p0=tcsPts01[0].xy, p1=tcsPts01[0].zw, p2=tcsPt2Tan0[0].xy, p3=tcsPts01[1].xy;
612             tan0 = tcsPt2Tan0[0].zw;
613             numParametricSegments = tcsTessArgs[0].y;
614             radsPerSegment = tcsTessArgs[0].z;
615         } else if ((combinedEdgeID -= tcsTessArgs[0].x) < tcsTessArgs[1].x) {
616             // Our edge belongs to the second curve section.
617             p0=tcsPts01[1].xy, p1=tcsPts01[1].zw, p2=tcsPt2Tan0[1].xy, p3=tcsPts01[2].xy;
618             tan0 = tcsPt2Tan0[1].zw;
619             numParametricSegments = tcsTessArgs[1].y;
620             radsPerSegment = tcsTessArgs[1].z;
621         } else {
622             // Our edge belongs to the third curve section.
623             combinedEdgeID -= tcsTessArgs[1].x;
624             p0=tcsPts01[2].xy, p1=tcsPts01[2].zw, p2=tcsPt2Tan0[2].xy, p3=tcsEndPtEndTan.xy;
625             tan0 = tcsPt2Tan0[2].zw;
626             numParametricSegments = tcsTessArgs[2].y;
627             radsPerSegment = tcsTessArgs[2].z;
628         }
629         float2 tan1 = tcsEndPtEndTan.zw;
630         bool isFinalEdge = (gl_TessCoord.x == 1);
631         float w = -1.0;  // w<0 means the curve is an integral cubic.
632         if (isinf(p3.y)) {
633             w = p3.x;  // The curve is actually a conic.
634             p3 = p2;  // Setting p3 equal to p2 works for the remaining rotational logic.
635         })");
636 
637     GrGPArgs gpArgs;
638     this->emitTessellationCode(shader, &code, &gpArgs, shaderCaps);
639 
640     // Manually map the position to OpenGL clip space, since we are generating raw GLSL.
641     code.appendf(R"(
642         gl_Position = vec4(%s * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);)",
643         gpArgs.fPositionVar.c_str());
644 
645     if (shader.hasDynamicColor()) {
646         // Pass color on to the fragment shader.
647         code.appendf(R"(
648         %s = tcsColor;)", fDynamicColorName.c_str());
649     }
650 
651     code.append(R"(
652     })");
653 
654     return code;
655 }
656