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 ¶metricPrecisionName); 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