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