1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/gpu/tessellate/shaders/GrStrokeTessellationShader.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" 11cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" 12cb93a386Sopenharmony_ci#include "src/gpu/tessellate/StrokeFixedCountTessellator.h" 13cb93a386Sopenharmony_ci#include "src/gpu/tessellate/WangsFormula.h" 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ciusing skgpu::VertexWriter; 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_civoid GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { 18cb93a386Sopenharmony_ci const auto& shader = args.fGeomProc.cast<GrStrokeTessellationShader>(); 19cb93a386Sopenharmony_ci SkPaint::Join joinType = shader.stroke().getJoin(); 20cb93a386Sopenharmony_ci args.fVaryingHandler->emitAttributes(shader); 21cb93a386Sopenharmony_ci 22cb93a386Sopenharmony_ci args.fVertBuilder->defineConstant("float", "PI", "3.141592653589793238"); 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci // Helper functions. 25cb93a386Sopenharmony_ci if (shader.hasDynamicStroke()) { 26cb93a386Sopenharmony_ci args.fVertBuilder->insertFunction(kNumRadialSegmentsPerRadianFn); 27cb93a386Sopenharmony_ci } 28cb93a386Sopenharmony_ci args.fVertBuilder->insertFunction(kCosineBetweenVectorsFn); 29cb93a386Sopenharmony_ci args.fVertBuilder->insertFunction(kMiterExtentFn); 30cb93a386Sopenharmony_ci args.fVertBuilder->insertFunction(kUncheckedMixFn); 31cb93a386Sopenharmony_ci args.fVertBuilder->insertFunction(skgpu::wangs_formula::as_sksl().c_str()); 32cb93a386Sopenharmony_ci 33cb93a386Sopenharmony_ci // Tessellation control uniforms and/or dynamic attributes. 34cb93a386Sopenharmony_ci if (!shader.hasDynamicStroke()) { 35cb93a386Sopenharmony_ci // [PARAMETRIC_PRECISION, NUM_RADIAL_SEGMENTS_PER_RADIAN, JOIN_TYPE, STROKE_RADIUS] 36cb93a386Sopenharmony_ci const char* tessArgsName; 37cb93a386Sopenharmony_ci fTessControlArgsUniform = args.fUniformHandler->addUniform( 38cb93a386Sopenharmony_ci nullptr, kVertex_GrShaderFlag, kFloat4_GrSLType, "tessControlArgs", 39cb93a386Sopenharmony_ci &tessArgsName); 40cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf(R"( 41cb93a386Sopenharmony_ci float PARAMETRIC_PRECISION = %s.x; 42cb93a386Sopenharmony_ci float NUM_RADIAL_SEGMENTS_PER_RADIAN = %s.y; 43cb93a386Sopenharmony_ci float JOIN_TYPE = %s.z; 44cb93a386Sopenharmony_ci float STROKE_RADIUS = %s.w;)", tessArgsName, tessArgsName, tessArgsName, tessArgsName); 45cb93a386Sopenharmony_ci } else { 46cb93a386Sopenharmony_ci const char* parametricPrecisionName; 47cb93a386Sopenharmony_ci fTessControlArgsUniform = args.fUniformHandler->addUniform( 48cb93a386Sopenharmony_ci nullptr, kVertex_GrShaderFlag, kFloat_GrSLType, "parametricPrecision", 49cb93a386Sopenharmony_ci ¶metricPrecisionName); 50cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf(R"( 51cb93a386Sopenharmony_ci float PARAMETRIC_PRECISION = %s; 52cb93a386Sopenharmony_ci float STROKE_RADIUS = dynamicStrokeAttr.x; 53cb93a386Sopenharmony_ci float NUM_RADIAL_SEGMENTS_PER_RADIAN = num_radial_segments_per_radian( 54cb93a386Sopenharmony_ci PARAMETRIC_PRECISION, STROKE_RADIUS); 55cb93a386Sopenharmony_ci float JOIN_TYPE = dynamicStrokeAttr.y;)", parametricPrecisionName); 56cb93a386Sopenharmony_ci } 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci if (shader.hasDynamicColor()) { 59cb93a386Sopenharmony_ci // Create a varying for color to get passed in through. 60cb93a386Sopenharmony_ci GrGLSLVarying dynamicColor{kHalf4_GrSLType}; 61cb93a386Sopenharmony_ci args.fVaryingHandler->addVarying("dynamicColor", &dynamicColor); 62cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf("%s = dynamicColorAttr;", dynamicColor.vsOut()); 63cb93a386Sopenharmony_ci fDynamicColorName = dynamicColor.fsIn(); 64cb93a386Sopenharmony_ci } 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci if (shader.mode() == GrStrokeTessellationShader::Mode::kLog2Indirect) { 67cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 68cb93a386Sopenharmony_ci float NUM_TOTAL_EDGES = abs(argsAttr.z);)"); 69cb93a386Sopenharmony_ci } else { 70cb93a386Sopenharmony_ci SkASSERT(shader.mode() == GrStrokeTessellationShader::Mode::kFixedCount); 71cb93a386Sopenharmony_ci const char* edgeCountName; 72cb93a386Sopenharmony_ci fEdgeCountUniform = args.fUniformHandler->addUniform( 73cb93a386Sopenharmony_ci nullptr, kVertex_GrShaderFlag, kFloat_GrSLType, "edgeCount", &edgeCountName); 74cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf(R"( 75cb93a386Sopenharmony_ci float NUM_TOTAL_EDGES = %s;)", edgeCountName); 76cb93a386Sopenharmony_ci } 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_ci // View matrix uniforms. 79cb93a386Sopenharmony_ci const char* translateName, *affineMatrixName; 80cb93a386Sopenharmony_ci fAffineMatrixUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag, 81cb93a386Sopenharmony_ci kFloat4_GrSLType, "affineMatrix", 82cb93a386Sopenharmony_ci &affineMatrixName); 83cb93a386Sopenharmony_ci fTranslateUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag, 84cb93a386Sopenharmony_ci kFloat2_GrSLType, "translate", 85cb93a386Sopenharmony_ci &translateName); 86cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);\n", affineMatrixName); 87cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;\n", translateName); 88cb93a386Sopenharmony_ci 89cb93a386Sopenharmony_ci if (shader.hasExplicitCurveType()) { 90cb93a386Sopenharmony_ci args.fVertBuilder->insertFunction(SkStringPrintf(R"( 91cb93a386Sopenharmony_ci bool is_conic_curve() { return curveTypeAttr != %g; })", kCubicCurveType).c_str()); 92cb93a386Sopenharmony_ci } else { 93cb93a386Sopenharmony_ci args.fVertBuilder->insertFunction(R"( 94cb93a386Sopenharmony_ci bool is_conic_curve() { return isinf(pts23Attr.w); })"); 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci 97cb93a386Sopenharmony_ci // Tessellation code. 98cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 99cb93a386Sopenharmony_ci float2 p0=pts01Attr.xy, p1=pts01Attr.zw, p2=pts23Attr.xy, p3=pts23Attr.zw; 100cb93a386Sopenharmony_ci float2 lastControlPoint = argsAttr.xy; 101cb93a386Sopenharmony_ci float w = -1; // w<0 means the curve is an integral cubic. 102cb93a386Sopenharmony_ci if (is_conic_curve()) { 103cb93a386Sopenharmony_ci // Conics are 3 points, with the weight in p3. 104cb93a386Sopenharmony_ci w = p3.x; 105cb93a386Sopenharmony_ci p3 = p2; // Setting p3 equal to p2 works for the remaining rotational logic. 106cb93a386Sopenharmony_ci })"); 107cb93a386Sopenharmony_ci if (shader.stroke().isHairlineStyle()) { 108cb93a386Sopenharmony_ci // Hairline case. Transform the points before tessellation. We can still hold off on the 109cb93a386Sopenharmony_ci // translate until the end; we just need to perform the scale and skew right now. 110cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 111cb93a386Sopenharmony_ci p0 = AFFINE_MATRIX * p0; 112cb93a386Sopenharmony_ci p1 = AFFINE_MATRIX * p1; 113cb93a386Sopenharmony_ci p2 = AFFINE_MATRIX * p2; 114cb93a386Sopenharmony_ci p3 = AFFINE_MATRIX * p3; 115cb93a386Sopenharmony_ci lastControlPoint = AFFINE_MATRIX * lastControlPoint;)"); 116cb93a386Sopenharmony_ci } 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 119cb93a386Sopenharmony_ci // Find how many parametric segments this stroke requires. 120cb93a386Sopenharmony_ci float numParametricSegments; 121cb93a386Sopenharmony_ci if (w < 0) { 122cb93a386Sopenharmony_ci numParametricSegments = wangs_formula_cubic(PARAMETRIC_PRECISION, p0, p1, p2, p3, 123cb93a386Sopenharmony_ci float2x2(1)); 124cb93a386Sopenharmony_ci } else { 125cb93a386Sopenharmony_ci numParametricSegments = wangs_formula_conic(PARAMETRIC_PRECISION, p0, p1, p2, w); 126cb93a386Sopenharmony_ci } 127cb93a386Sopenharmony_ci if (p0 == p1 && p2 == p3) { 128cb93a386Sopenharmony_ci // This is how we describe lines, but Wang's formula does not return 1 in this case. 129cb93a386Sopenharmony_ci numParametricSegments = 1; 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci // Find the starting and ending tangents. 133cb93a386Sopenharmony_ci float2 tan0 = ((p0 == p1) ? (p1 == p2) ? p3 : p2 : p1) - p0; 134cb93a386Sopenharmony_ci float2 tan1 = p3 - ((p3 == p2) ? (p2 == p1) ? p0 : p1 : p2); 135cb93a386Sopenharmony_ci if (tan0 == float2(0)) { 136cb93a386Sopenharmony_ci // The stroke is a point. This special case tells us to draw a stroke-width circle as a 137cb93a386Sopenharmony_ci // 180 degree point stroke instead. 138cb93a386Sopenharmony_ci tan0 = float2(1,0); 139cb93a386Sopenharmony_ci tan1 = float2(-1,0); 140cb93a386Sopenharmony_ci })"); 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci if (args.fShaderCaps->vertexIDSupport()) { 143cb93a386Sopenharmony_ci // If we don't have sk_VertexID support then "edgeID" already came in as a vertex attrib. 144cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 145cb93a386Sopenharmony_ci float edgeID = float(sk_VertexID >> 1); 146cb93a386Sopenharmony_ci if ((sk_VertexID & 1) != 0) { 147cb93a386Sopenharmony_ci edgeID = -edgeID; 148cb93a386Sopenharmony_ci })"); 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci // Potential optimization: (shader.hasDynamicStroke() && shader.hasRoundJoins())? 152cb93a386Sopenharmony_ci if (shader.stroke().getJoin() == SkPaint::kRound_Join || shader.hasDynamicStroke()) { 153cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 154cb93a386Sopenharmony_ci // Determine how many edges to give to the round join. We emit the first and final edges 155cb93a386Sopenharmony_ci // of the join twice: once full width and once restricted to half width. This guarantees 156cb93a386Sopenharmony_ci // perfect seaming by matching the vertices from the join as well as from the strokes on 157cb93a386Sopenharmony_ci // either side. 158cb93a386Sopenharmony_ci float joinRads = acos(cosine_between_vectors(p0 - lastControlPoint, tan0)); 159cb93a386Sopenharmony_ci float numRadialSegmentsInJoin = max(ceil(joinRads * NUM_RADIAL_SEGMENTS_PER_RADIAN), 1); 160cb93a386Sopenharmony_ci // +2 because we emit the beginning and ending edges twice (see above comment). 161cb93a386Sopenharmony_ci float numEdgesInJoin = numRadialSegmentsInJoin + 2; 162cb93a386Sopenharmony_ci // The stroke section needs at least two edges. Don't assign more to the join than 163cb93a386Sopenharmony_ci // "NUM_TOTAL_EDGES - 2". 164cb93a386Sopenharmony_ci numEdgesInJoin = min(numEdgesInJoin, NUM_TOTAL_EDGES - 2);)"); 165cb93a386Sopenharmony_ci if (shader.mode() == GrStrokeTessellationShader::Mode::kLog2Indirect) { 166cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 167cb93a386Sopenharmony_ci // Negative argsAttr.z means the join is an internal chop or circle, and both of 168cb93a386Sopenharmony_ci // those have empty joins. All we need is a bevel join. 169cb93a386Sopenharmony_ci if (argsAttr.z < 0) { 170cb93a386Sopenharmony_ci // +2 because we emit the beginning and ending edges twice (see above comment). 171cb93a386Sopenharmony_ci numEdgesInJoin = 1 + 2; 172cb93a386Sopenharmony_ci })"); 173cb93a386Sopenharmony_ci } 174cb93a386Sopenharmony_ci if (shader.hasDynamicStroke()) { 175cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 176cb93a386Sopenharmony_ci if (JOIN_TYPE >= 0 /*Is the join not a round type?*/) { 177cb93a386Sopenharmony_ci // Bevel and miter joins get 1 and 2 segments respectively. 178cb93a386Sopenharmony_ci // +2 because we emit the beginning and ending edges twice (see above comments). 179cb93a386Sopenharmony_ci numEdgesInJoin = sign(JOIN_TYPE) + 1 + 2; 180cb93a386Sopenharmony_ci })"); 181cb93a386Sopenharmony_ci } 182cb93a386Sopenharmony_ci } else { 183cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf(R"( 184cb93a386Sopenharmony_ci float numEdgesInJoin = %i;)", 185cb93a386Sopenharmony_ci skgpu::StrokeFixedCountTessellator::NumFixedEdgesInJoin(joinType)); 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci 188cb93a386Sopenharmony_ci args.fVertBuilder->codeAppend(R"( 189cb93a386Sopenharmony_ci // Find which direction the curve turns. 190cb93a386Sopenharmony_ci // NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5). 191cb93a386Sopenharmony_ci // NOTE: F'(.5) x F''(.5) has the same sign as (P2 - P0) x (P3 - P1) 192cb93a386Sopenharmony_ci float turn = cross(p2 - p0, p3 - p1); 193cb93a386Sopenharmony_ci float combinedEdgeID = abs(edgeID) - numEdgesInJoin; 194cb93a386Sopenharmony_ci if (combinedEdgeID < 0) { 195cb93a386Sopenharmony_ci tan1 = tan0; 196cb93a386Sopenharmony_ci // Don't let tan0 become zero. The code as-is isn't built to handle that case. tan0=0 197cb93a386Sopenharmony_ci // means the join is disabled, and to disable it with the existing code we can leave 198cb93a386Sopenharmony_ci // tan0 equal to tan1. 199cb93a386Sopenharmony_ci if (lastControlPoint != p0) { 200cb93a386Sopenharmony_ci tan0 = p0 - lastControlPoint; 201cb93a386Sopenharmony_ci } 202cb93a386Sopenharmony_ci turn = cross(tan0, tan1); 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_ci // Calculate the curve's starting angle and rotation. 206cb93a386Sopenharmony_ci float cosTheta = cosine_between_vectors(tan0, tan1); 207cb93a386Sopenharmony_ci float rotation = acos(cosTheta); 208cb93a386Sopenharmony_ci if (turn < 0) { 209cb93a386Sopenharmony_ci // Adjust sign of rotation to match the direction the curve turns. 210cb93a386Sopenharmony_ci rotation = -rotation; 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ci float numRadialSegments; 214cb93a386Sopenharmony_ci float strokeOutset = sign(edgeID); 215cb93a386Sopenharmony_ci if (combinedEdgeID < 0) { 216cb93a386Sopenharmony_ci // We belong to the preceding join. The first and final edges get duplicated, so we only 217cb93a386Sopenharmony_ci // have "numEdgesInJoin - 2" segments. 218cb93a386Sopenharmony_ci numRadialSegments = numEdgesInJoin - 2; 219cb93a386Sopenharmony_ci numParametricSegments = 1; // Joins don't have parametric segments. 220cb93a386Sopenharmony_ci p3 = p2 = p1 = p0; // Colocate all points on the junction point. 221cb93a386Sopenharmony_ci // Shift combinedEdgeID to the range [-1, numRadialSegments]. This duplicates the first 222cb93a386Sopenharmony_ci // edge and lands one edge at the very end of the join. (The duplicated final edge will 223cb93a386Sopenharmony_ci // actually come from the section of our strip that belongs to the stroke.) 224cb93a386Sopenharmony_ci combinedEdgeID += numRadialSegments + 1; 225cb93a386Sopenharmony_ci // We normally restrict the join on one side of the junction, but if the tangents are 226cb93a386Sopenharmony_ci // nearly equivalent this could theoretically result in bad seaming and/or cracks on the 227cb93a386Sopenharmony_ci // side we don't put it on. If the tangents are nearly equivalent then we leave the join 228cb93a386Sopenharmony_ci // double-sided. 229cb93a386Sopenharmony_ci float sinEpsilon = 1e-2; // ~= sin(180deg / 3000) 230cb93a386Sopenharmony_ci bool tangentsNearlyParallel = 231cb93a386Sopenharmony_ci (abs(turn) * inversesqrt(dot(tan0, tan0) * dot(tan1, tan1))) < sinEpsilon; 232cb93a386Sopenharmony_ci if (!tangentsNearlyParallel || dot(tan0, tan1) < 0) { 233cb93a386Sopenharmony_ci // There are two edges colocated at the beginning. Leave the first one double sided 234cb93a386Sopenharmony_ci // for seaming with the previous stroke. (The double sided edge at the end will 235cb93a386Sopenharmony_ci // actually come from the section of our strip that belongs to the stroke.) 236cb93a386Sopenharmony_ci if (combinedEdgeID >= 0) { 237cb93a386Sopenharmony_ci strokeOutset = (turn < 0) ? min(strokeOutset, 0) : max(strokeOutset, 0); 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci combinedEdgeID = max(combinedEdgeID, 0); 241cb93a386Sopenharmony_ci } else { 242cb93a386Sopenharmony_ci // We belong to the stroke. 243cb93a386Sopenharmony_ci float maxCombinedSegments = NUM_TOTAL_EDGES - numEdgesInJoin - 1; 244cb93a386Sopenharmony_ci numRadialSegments = max(ceil(abs(rotation) * NUM_RADIAL_SEGMENTS_PER_RADIAN), 1); 245cb93a386Sopenharmony_ci numRadialSegments = min(numRadialSegments, maxCombinedSegments); 246cb93a386Sopenharmony_ci numParametricSegments = min(numParametricSegments, 247cb93a386Sopenharmony_ci maxCombinedSegments - numRadialSegments + 1); 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci 250cb93a386Sopenharmony_ci // Additional parameters for emitTessellationCode(). 251cb93a386Sopenharmony_ci float radsPerSegment = rotation / numRadialSegments; 252cb93a386Sopenharmony_ci float numCombinedSegments = numParametricSegments + numRadialSegments - 1; 253cb93a386Sopenharmony_ci bool isFinalEdge = (combinedEdgeID >= numCombinedSegments); 254cb93a386Sopenharmony_ci if (combinedEdgeID > numCombinedSegments) { 255cb93a386Sopenharmony_ci strokeOutset = 0; // The strip has more edges than we need. Drop this one. 256cb93a386Sopenharmony_ci })"); 257cb93a386Sopenharmony_ci 258cb93a386Sopenharmony_ci if (joinType == SkPaint::kMiter_Join || shader.hasDynamicStroke()) { 259cb93a386Sopenharmony_ci args.fVertBuilder->codeAppendf(R"( 260cb93a386Sopenharmony_ci // Edge #2 extends to the miter point. 261cb93a386Sopenharmony_ci if (abs(edgeID) == 2 && %s) { 262cb93a386Sopenharmony_ci strokeOutset *= miter_extent(cosTheta, JOIN_TYPE/*miterLimit*/); 263cb93a386Sopenharmony_ci })", shader.hasDynamicStroke() ? "JOIN_TYPE > 0/*Is the join a miter type?*/" : "true"); 264cb93a386Sopenharmony_ci } 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_ci this->emitTessellationCode(shader, &args.fVertBuilder->code(), gpArgs, *args.fShaderCaps); 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ci this->emitFragmentCode(shader, args); 269cb93a386Sopenharmony_ci} 270