1*c8dee2aaSAndroid Build Coastguard Worker// Graphite-specific vertex shader code 2*c8dee2aaSAndroid Build Coastguard Worker 3*c8dee2aaSAndroid Build Coastguard Workerconst float $PI = 3.141592653589793238; 4*c8dee2aaSAndroid Build Coastguard Worker 5*c8dee2aaSAndroid Build Coastguard Worker/////////////////////////////////////////////////////////////////////////////////////////////////// 6*c8dee2aaSAndroid Build Coastguard Worker// Support functions for tessellating path renderers 7*c8dee2aaSAndroid Build Coastguard Worker 8*c8dee2aaSAndroid Build Coastguard Workerconst float $kCubicCurveType = 0; // skgpu::tess::kCubicCurveType 9*c8dee2aaSAndroid Build Coastguard Workerconst float $kConicCurveType = 1; // skgpu::tess::kConicCurveType 10*c8dee2aaSAndroid Build Coastguard Workerconst float $kTriangularConicCurveType = 2; // skgpu::tess::kTriangularConicCurveType 11*c8dee2aaSAndroid Build Coastguard Worker 12*c8dee2aaSAndroid Build Coastguard Worker// This function can be used on GPUs with infinity support to infer the curve type from the specific 13*c8dee2aaSAndroid Build Coastguard Worker// path control-point encoding used by tessellating path renderers. Calling this function on a 14*c8dee2aaSAndroid Build Coastguard Worker// platform that lacks infinity support may result in a shader compilation error. 15*c8dee2aaSAndroid Build Coastguard Worker$pure float curve_type_using_inf_support(float4 p23) { 16*c8dee2aaSAndroid Build Coastguard Worker return isinf(p23.z) ? $kTriangularConicCurveType : 17*c8dee2aaSAndroid Build Coastguard Worker isinf(p23.w) ? $kConicCurveType : 18*c8dee2aaSAndroid Build Coastguard Worker $kCubicCurveType; 19*c8dee2aaSAndroid Build Coastguard Worker} 20*c8dee2aaSAndroid Build Coastguard Worker 21*c8dee2aaSAndroid Build Coastguard Worker$pure bool $is_conic_curve(float curveType) { 22*c8dee2aaSAndroid Build Coastguard Worker return curveType != $kCubicCurveType; 23*c8dee2aaSAndroid Build Coastguard Worker} 24*c8dee2aaSAndroid Build Coastguard Worker 25*c8dee2aaSAndroid Build Coastguard Worker$pure bool $is_triangular_conic_curve(float curveType) { 26*c8dee2aaSAndroid Build Coastguard Worker return curveType == $kTriangularConicCurveType; 27*c8dee2aaSAndroid Build Coastguard Worker} 28*c8dee2aaSAndroid Build Coastguard Worker 29*c8dee2aaSAndroid Build Coastguard Worker// Wang's formula gives the minimum number of evenly spaced (in the parametric sense) line segments 30*c8dee2aaSAndroid Build Coastguard Worker// that a bezier curve must be chopped into in order to guarantee all lines stay within a distance 31*c8dee2aaSAndroid Build Coastguard Worker// of "1/precision" pixels from the true curve. Its definition for a bezier curve of degree "n" is 32*c8dee2aaSAndroid Build Coastguard Worker// as follows: 33*c8dee2aaSAndroid Build Coastguard Worker// 34*c8dee2aaSAndroid Build Coastguard Worker// maxLength = max([length(p[i+2] - 2p[i+1] + p[i]) for (0 <= i <= n-2)]) 35*c8dee2aaSAndroid Build Coastguard Worker// numParametricSegments = sqrt(maxLength * precision * n*(n - 1)/8) 36*c8dee2aaSAndroid Build Coastguard Worker// 37*c8dee2aaSAndroid Build Coastguard Worker// (Goldman, Ron. (2003). 5.6.3 Wang's Formula. "Pyramid Algorithms: A Dynamic Programming Approach 38*c8dee2aaSAndroid Build Coastguard Worker// to Curves and Surfaces for Geometric Modeling". Morgan Kaufmann Publishers.) 39*c8dee2aaSAndroid Build Coastguard Worker 40*c8dee2aaSAndroid Build Coastguard Workerconst float $kDegree = 3; 41*c8dee2aaSAndroid Build Coastguard Workerconst float $kPrecision = 4; // Must match skgpu::tess::kPrecision 42*c8dee2aaSAndroid Build Coastguard Workerconst float $kLengthTerm = ($kDegree * ($kDegree - 1) / 8.0) * $kPrecision; 43*c8dee2aaSAndroid Build Coastguard Workerconst float $kLengthTermPow2 = (($kDegree * $kDegree) * (($kDegree - 1) * ($kDegree - 1)) / 64.0) * 44*c8dee2aaSAndroid Build Coastguard Worker ($kPrecision * $kPrecision); 45*c8dee2aaSAndroid Build Coastguard Worker 46*c8dee2aaSAndroid Build Coastguard Worker// Returns the length squared of the largest forward difference from Wang's cubic formula. 47*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_max_fdiff_p2(float2 p0, float2 p1, float2 p2, float2 p3, 48*c8dee2aaSAndroid Build Coastguard Worker float2x2 matrix) { 49*c8dee2aaSAndroid Build Coastguard Worker float2 d0 = matrix * (fma(float2(-2), p1, p2) + p0); 50*c8dee2aaSAndroid Build Coastguard Worker float2 d1 = matrix * (fma(float2(-2), p2, p3) + p1); 51*c8dee2aaSAndroid Build Coastguard Worker return max(dot(d0,d0), dot(d1,d1)); 52*c8dee2aaSAndroid Build Coastguard Worker} 53*c8dee2aaSAndroid Build Coastguard Worker 54*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_cubic(float2 p0, float2 p1, float2 p2, float2 p3, 55*c8dee2aaSAndroid Build Coastguard Worker float2x2 matrix) { 56*c8dee2aaSAndroid Build Coastguard Worker float m = $wangs_formula_max_fdiff_p2(p0, p1, p2, p3, matrix); 57*c8dee2aaSAndroid Build Coastguard Worker return max(ceil(sqrt($kLengthTerm * sqrt(m))), 1.0); 58*c8dee2aaSAndroid Build Coastguard Worker} 59*c8dee2aaSAndroid Build Coastguard Worker 60*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_cubic_log2(float2 p0, float2 p1, float2 p2, float2 p3, 61*c8dee2aaSAndroid Build Coastguard Worker float2x2 matrix) { 62*c8dee2aaSAndroid Build Coastguard Worker float m = $wangs_formula_max_fdiff_p2(p0, p1, p2, p3, matrix); 63*c8dee2aaSAndroid Build Coastguard Worker return ceil(log2(max($kLengthTermPow2 * m, 1.0)) * .25); 64*c8dee2aaSAndroid Build Coastguard Worker} 65*c8dee2aaSAndroid Build Coastguard Worker 66*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_conic_p2(float2 p0, float2 p1, float2 p2, float w) { 67*c8dee2aaSAndroid Build Coastguard Worker // Translate the bounding box center to the origin. 68*c8dee2aaSAndroid Build Coastguard Worker float2 C = (min(min(p0, p1), p2) + max(max(p0, p1), p2)) * 0.5; 69*c8dee2aaSAndroid Build Coastguard Worker p0 -= C; 70*c8dee2aaSAndroid Build Coastguard Worker p1 -= C; 71*c8dee2aaSAndroid Build Coastguard Worker p2 -= C; 72*c8dee2aaSAndroid Build Coastguard Worker 73*c8dee2aaSAndroid Build Coastguard Worker // Compute max length. 74*c8dee2aaSAndroid Build Coastguard Worker float m = sqrt(max(max(dot(p0,p0), dot(p1,p1)), dot(p2,p2))); 75*c8dee2aaSAndroid Build Coastguard Worker 76*c8dee2aaSAndroid Build Coastguard Worker // Compute forward differences. 77*c8dee2aaSAndroid Build Coastguard Worker float2 dp = fma(float2(-2.0 * w), p1, p0) + p2; 78*c8dee2aaSAndroid Build Coastguard Worker float dw = abs(fma(-2.0, w, 2.0)); 79*c8dee2aaSAndroid Build Coastguard Worker 80*c8dee2aaSAndroid Build Coastguard Worker // Compute numerator and denominator for parametric step size of linearization. Here, the 81*c8dee2aaSAndroid Build Coastguard Worker // epsilon referenced from the cited paper is 1/precision. 82*c8dee2aaSAndroid Build Coastguard Worker float rp_minus_1 = max(0.0, fma(m, $kPrecision, -1.0)); 83*c8dee2aaSAndroid Build Coastguard Worker float numer = length(dp) * $kPrecision + rp_minus_1 * dw; 84*c8dee2aaSAndroid Build Coastguard Worker float denom = 4 * min(w, 1.0); 85*c8dee2aaSAndroid Build Coastguard Worker 86*c8dee2aaSAndroid Build Coastguard Worker return numer/denom; 87*c8dee2aaSAndroid Build Coastguard Worker} 88*c8dee2aaSAndroid Build Coastguard Worker 89*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_conic(float2 p0, float2 p1, float2 p2, float w) { 90*c8dee2aaSAndroid Build Coastguard Worker float n2 = $wangs_formula_conic_p2(p0, p1, p2, w); 91*c8dee2aaSAndroid Build Coastguard Worker return max(ceil(sqrt(n2)), 1.0); 92*c8dee2aaSAndroid Build Coastguard Worker} 93*c8dee2aaSAndroid Build Coastguard Worker 94*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_conic_log2(float2 p0, float2 p1, float2 p2, float w) { 95*c8dee2aaSAndroid Build Coastguard Worker float n2 = $wangs_formula_conic_p2(p0, p1, p2, w); 96*c8dee2aaSAndroid Build Coastguard Worker return ceil(log2(max(n2, 1.0)) * .5); 97*c8dee2aaSAndroid Build Coastguard Worker} 98*c8dee2aaSAndroid Build Coastguard Worker 99*c8dee2aaSAndroid Build Coastguard Worker// Returns the normalized difference between a and b, i.e. normalize(a - b), with care taken for 100*c8dee2aaSAndroid Build Coastguard Worker// if 'a' and/or 'b' have large coordinates. 101*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $robust_normalize_diff(float2 a, float2 b) { 102*c8dee2aaSAndroid Build Coastguard Worker float2 diff = a - b; 103*c8dee2aaSAndroid Build Coastguard Worker if (diff == float2(0.0)) { 104*c8dee2aaSAndroid Build Coastguard Worker return float2(0.0); 105*c8dee2aaSAndroid Build Coastguard Worker } else { 106*c8dee2aaSAndroid Build Coastguard Worker float invMag = 1.0 / max(abs(diff.x), abs(diff.y)); 107*c8dee2aaSAndroid Build Coastguard Worker return normalize(invMag * diff); 108*c8dee2aaSAndroid Build Coastguard Worker } 109*c8dee2aaSAndroid Build Coastguard Worker} 110*c8dee2aaSAndroid Build Coastguard Worker 111*c8dee2aaSAndroid Build Coastguard Worker// Returns the cosine of the angle between a and b, assuming a and b are unit vectors already. 112*c8dee2aaSAndroid Build Coastguard Worker// Guaranteed to be between [-1, 1]. 113*c8dee2aaSAndroid Build Coastguard Worker$pure float $cosine_between_unit_vectors(float2 a, float2 b) { 114*c8dee2aaSAndroid Build Coastguard Worker // Since a and b are assumed to be normalized, the cosine is equal to the dot product, although 115*c8dee2aaSAndroid Build Coastguard Worker // we clamp that to ensure it falls within the expected range of [-1, 1]. 116*c8dee2aaSAndroid Build Coastguard Worker return clamp(dot(a, b), -1.0, 1.0); 117*c8dee2aaSAndroid Build Coastguard Worker} 118*c8dee2aaSAndroid Build Coastguard Worker 119*c8dee2aaSAndroid Build Coastguard Worker// Extends the middle radius to either the miter point, or the bevel edge if we surpassed the 120*c8dee2aaSAndroid Build Coastguard Worker// miter limit and need to revert to a bevel join. 121*c8dee2aaSAndroid Build Coastguard Worker$pure float $miter_extent(float cosTheta, float miterLimit) { 122*c8dee2aaSAndroid Build Coastguard Worker float x = fma(cosTheta, .5, .5); 123*c8dee2aaSAndroid Build Coastguard Worker return (x * miterLimit * miterLimit >= 1.0) ? inversesqrt(x) : sqrt(x); 124*c8dee2aaSAndroid Build Coastguard Worker} 125*c8dee2aaSAndroid Build Coastguard Worker 126*c8dee2aaSAndroid Build Coastguard Worker// Returns the number of radial segments required for each radian of rotation, in order for the 127*c8dee2aaSAndroid Build Coastguard Worker// curve to appear "smooth" as defined by the approximate device-space stroke radius. 128*c8dee2aaSAndroid Build Coastguard Worker$pure float $num_radial_segments_per_radian(float approxDevStrokeRadius) { 129*c8dee2aaSAndroid Build Coastguard Worker return .5 / acos(max(1.0 - (1.0 / $kPrecision) / approxDevStrokeRadius, -1.0)); 130*c8dee2aaSAndroid Build Coastguard Worker} 131*c8dee2aaSAndroid Build Coastguard Worker 132*c8dee2aaSAndroid Build Coastguard Worker// Unlike mix(), this does not return b when t==1. But it otherwise seems to get better 133*c8dee2aaSAndroid Build Coastguard Worker// precision than "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points. 134*c8dee2aaSAndroid Build Coastguard Worker// We override this result anyway when t==1 so it shouldn't be a problem. 135*c8dee2aaSAndroid Build Coastguard Worker$pure float $unchecked_mix(float a, float b, float T) { 136*c8dee2aaSAndroid Build Coastguard Worker return fma(b - a, T, a); 137*c8dee2aaSAndroid Build Coastguard Worker} 138*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $unchecked_mix(float2 a, float2 b, float T) { 139*c8dee2aaSAndroid Build Coastguard Worker return fma(b - a, float2(T), a); 140*c8dee2aaSAndroid Build Coastguard Worker} 141*c8dee2aaSAndroid Build Coastguard Worker$pure float4 $unchecked_mix(float4 a, float4 b, float4 T) { 142*c8dee2aaSAndroid Build Coastguard Worker return fma(b - a, T, a); 143*c8dee2aaSAndroid Build Coastguard Worker} 144*c8dee2aaSAndroid Build Coastguard Worker 145*c8dee2aaSAndroid Build Coastguard Worker// Compute a vertex position for the curve described by p01 and p23 packed control points, 146*c8dee2aaSAndroid Build Coastguard Worker// tessellated to the given resolve level, and assuming it will be drawn as a filled curve. 147*c8dee2aaSAndroid Build Coastguard Worker$pure float2 tessellate_filled_curve(float2x2 vectorXform, 148*c8dee2aaSAndroid Build Coastguard Worker float resolveLevel, float idxInResolveLevel, 149*c8dee2aaSAndroid Build Coastguard Worker float4 p01, float4 p23, 150*c8dee2aaSAndroid Build Coastguard Worker float curveType) { 151*c8dee2aaSAndroid Build Coastguard Worker float2 localcoord; 152*c8dee2aaSAndroid Build Coastguard Worker if ($is_triangular_conic_curve(curveType)) { 153*c8dee2aaSAndroid Build Coastguard Worker // This patch is an exact triangle. 154*c8dee2aaSAndroid Build Coastguard Worker localcoord = (resolveLevel != 0) ? p01.zw 155*c8dee2aaSAndroid Build Coastguard Worker : (idxInResolveLevel != 0) ? p23.xy 156*c8dee2aaSAndroid Build Coastguard Worker : p01.xy; 157*c8dee2aaSAndroid Build Coastguard Worker } else { 158*c8dee2aaSAndroid Build Coastguard Worker float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw; 159*c8dee2aaSAndroid Build Coastguard Worker float w = -1; // w < 0 tells us to treat the instance as an integral cubic. 160*c8dee2aaSAndroid Build Coastguard Worker float maxResolveLevel; 161*c8dee2aaSAndroid Build Coastguard Worker if ($is_conic_curve(curveType)) { 162*c8dee2aaSAndroid Build Coastguard Worker // Conics are 3 points, with the weight in p3. 163*c8dee2aaSAndroid Build Coastguard Worker w = p3.x; 164*c8dee2aaSAndroid Build Coastguard Worker maxResolveLevel = $wangs_formula_conic_log2(vectorXform*p0, 165*c8dee2aaSAndroid Build Coastguard Worker vectorXform*p1, 166*c8dee2aaSAndroid Build Coastguard Worker vectorXform*p2, w); 167*c8dee2aaSAndroid Build Coastguard Worker p1 *= w; // Unproject p1. 168*c8dee2aaSAndroid Build Coastguard Worker p3 = p2; // Duplicate the endpoint for shared code that also runs on cubics. 169*c8dee2aaSAndroid Build Coastguard Worker } else { 170*c8dee2aaSAndroid Build Coastguard Worker // The patch is an integral cubic. 171*c8dee2aaSAndroid Build Coastguard Worker maxResolveLevel = $wangs_formula_cubic_log2(p0, p1, p2, p3, vectorXform); 172*c8dee2aaSAndroid Build Coastguard Worker } 173*c8dee2aaSAndroid Build Coastguard Worker if (resolveLevel > maxResolveLevel) { 174*c8dee2aaSAndroid Build Coastguard Worker // This vertex is at a higher resolve level than we need. Demote to a lower 175*c8dee2aaSAndroid Build Coastguard Worker // resolveLevel, which will produce a degenerate triangle. 176*c8dee2aaSAndroid Build Coastguard Worker idxInResolveLevel = floor(ldexp(idxInResolveLevel, 177*c8dee2aaSAndroid Build Coastguard Worker int(maxResolveLevel - resolveLevel))); 178*c8dee2aaSAndroid Build Coastguard Worker resolveLevel = maxResolveLevel; 179*c8dee2aaSAndroid Build Coastguard Worker } 180*c8dee2aaSAndroid Build Coastguard Worker // Promote our location to a discrete position in the maximum fixed resolve level. 181*c8dee2aaSAndroid Build Coastguard Worker // This is extra paranoia to ensure we get the exact same fp32 coordinates for 182*c8dee2aaSAndroid Build Coastguard Worker // colocated points from different resolve levels (e.g., the vertices T=3/4 and 183*c8dee2aaSAndroid Build Coastguard Worker // T=6/8 should be exactly colocated). 184*c8dee2aaSAndroid Build Coastguard Worker float fixedVertexID = floor(.5 + ldexp(idxInResolveLevel, int(5 - resolveLevel))); 185*c8dee2aaSAndroid Build Coastguard Worker if (0 < fixedVertexID && fixedVertexID < 32) { 186*c8dee2aaSAndroid Build Coastguard Worker float T = fixedVertexID * (1 / 32.0); 187*c8dee2aaSAndroid Build Coastguard Worker 188*c8dee2aaSAndroid Build Coastguard Worker // Evaluate at T. Use De Casteljau's for its accuracy and stability. 189*c8dee2aaSAndroid Build Coastguard Worker float2 ab = mix(p0, p1, T); 190*c8dee2aaSAndroid Build Coastguard Worker float2 bc = mix(p1, p2, T); 191*c8dee2aaSAndroid Build Coastguard Worker float2 cd = mix(p2, p3, T); 192*c8dee2aaSAndroid Build Coastguard Worker float2 abc = mix(ab, bc, T); 193*c8dee2aaSAndroid Build Coastguard Worker float2 bcd = mix(bc, cd, T); 194*c8dee2aaSAndroid Build Coastguard Worker float2 abcd = mix(abc, bcd, T); 195*c8dee2aaSAndroid Build Coastguard Worker 196*c8dee2aaSAndroid Build Coastguard Worker // Evaluate the conic weight at T. 197*c8dee2aaSAndroid Build Coastguard Worker float u = mix(1.0, w, T); 198*c8dee2aaSAndroid Build Coastguard Worker float v = w + 1 - u; // == mix(w, 1, T) 199*c8dee2aaSAndroid Build Coastguard Worker float uv = mix(u, v, T); 200*c8dee2aaSAndroid Build Coastguard Worker 201*c8dee2aaSAndroid Build Coastguard Worker localcoord = (w < 0) ? /*cubic*/ abcd : /*conic*/ abc/uv; 202*c8dee2aaSAndroid Build Coastguard Worker } else { 203*c8dee2aaSAndroid Build Coastguard Worker localcoord = (fixedVertexID == 0) ? p0.xy : p3.xy; 204*c8dee2aaSAndroid Build Coastguard Worker } 205*c8dee2aaSAndroid Build Coastguard Worker } 206*c8dee2aaSAndroid Build Coastguard Worker return localcoord; 207*c8dee2aaSAndroid Build Coastguard Worker} 208*c8dee2aaSAndroid Build Coastguard Worker 209*c8dee2aaSAndroid Build Coastguard Worker// Device coords are in xy, local coords are in zw, since for now perspective isn't supported. 210*c8dee2aaSAndroid Build Coastguard Worker$pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, 211*c8dee2aaSAndroid Build Coastguard Worker float2x2 affineMatrix, 212*c8dee2aaSAndroid Build Coastguard Worker float2 translate, 213*c8dee2aaSAndroid Build Coastguard Worker float maxScale /* derived from affineMatrix */, 214*c8dee2aaSAndroid Build Coastguard Worker float4 p01, float4 p23, 215*c8dee2aaSAndroid Build Coastguard Worker float2 lastControlPoint, 216*c8dee2aaSAndroid Build Coastguard Worker float2 strokeParams, 217*c8dee2aaSAndroid Build Coastguard Worker float curveType) { 218*c8dee2aaSAndroid Build Coastguard Worker float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw; 219*c8dee2aaSAndroid Build Coastguard Worker float w = -1; // w<0 means the curve is an integral cubic. 220*c8dee2aaSAndroid Build Coastguard Worker if ($is_conic_curve(curveType)) { 221*c8dee2aaSAndroid Build Coastguard Worker // Conics are 3 points, with the weight in p3. 222*c8dee2aaSAndroid Build Coastguard Worker w = p3.x; 223*c8dee2aaSAndroid Build Coastguard Worker p3 = p2; // Setting p3 equal to p2 works for the remaining rotational logic. 224*c8dee2aaSAndroid Build Coastguard Worker } 225*c8dee2aaSAndroid Build Coastguard Worker 226*c8dee2aaSAndroid Build Coastguard Worker // Call Wang's formula to determine parametric segments before transform points for hairlines 227*c8dee2aaSAndroid Build Coastguard Worker // so that it is consistent with how the CPU tested the control points for chopping. 228*c8dee2aaSAndroid Build Coastguard Worker float numParametricSegments; 229*c8dee2aaSAndroid Build Coastguard Worker if (w < 0) { 230*c8dee2aaSAndroid Build Coastguard Worker if (p0 == p1 && p2 == p3) { 231*c8dee2aaSAndroid Build Coastguard Worker numParametricSegments = 1; // a line 232*c8dee2aaSAndroid Build Coastguard Worker } else { 233*c8dee2aaSAndroid Build Coastguard Worker numParametricSegments = $wangs_formula_cubic(p0, p1, p2, p3, affineMatrix); 234*c8dee2aaSAndroid Build Coastguard Worker } 235*c8dee2aaSAndroid Build Coastguard Worker } else { 236*c8dee2aaSAndroid Build Coastguard Worker numParametricSegments = $wangs_formula_conic(affineMatrix * p0, 237*c8dee2aaSAndroid Build Coastguard Worker affineMatrix * p1, 238*c8dee2aaSAndroid Build Coastguard Worker affineMatrix * p2, w); 239*c8dee2aaSAndroid Build Coastguard Worker } 240*c8dee2aaSAndroid Build Coastguard Worker 241*c8dee2aaSAndroid Build Coastguard Worker // Matches skgpu::tess::StrokeParams 242*c8dee2aaSAndroid Build Coastguard Worker float strokeRadius = strokeParams.x; 243*c8dee2aaSAndroid Build Coastguard Worker float joinType = strokeParams.y; // <0 = round join, ==0 = bevel join, >0 encodes miter limit 244*c8dee2aaSAndroid Build Coastguard Worker bool isHairline = strokeParams.x == 0.0; 245*c8dee2aaSAndroid Build Coastguard Worker float numRadialSegmentsPerRadian; 246*c8dee2aaSAndroid Build Coastguard Worker if (isHairline) { 247*c8dee2aaSAndroid Build Coastguard Worker numRadialSegmentsPerRadian = $num_radial_segments_per_radian(1.0); 248*c8dee2aaSAndroid Build Coastguard Worker strokeRadius = 0.5; 249*c8dee2aaSAndroid Build Coastguard Worker } else { 250*c8dee2aaSAndroid Build Coastguard Worker numRadialSegmentsPerRadian = $num_radial_segments_per_radian(maxScale * strokeParams.x); 251*c8dee2aaSAndroid Build Coastguard Worker } 252*c8dee2aaSAndroid Build Coastguard Worker 253*c8dee2aaSAndroid Build Coastguard Worker if (isHairline) { 254*c8dee2aaSAndroid Build Coastguard Worker // Hairline case. Transform the points before tessellation. We can still hold off on the 255*c8dee2aaSAndroid Build Coastguard Worker // translate until the end; we just need to perform the scale and skew right now. 256*c8dee2aaSAndroid Build Coastguard Worker p0 = affineMatrix * p0; 257*c8dee2aaSAndroid Build Coastguard Worker p1 = affineMatrix * p1; 258*c8dee2aaSAndroid Build Coastguard Worker p2 = affineMatrix * p2; 259*c8dee2aaSAndroid Build Coastguard Worker p3 = affineMatrix * p3; 260*c8dee2aaSAndroid Build Coastguard Worker lastControlPoint = affineMatrix * lastControlPoint; 261*c8dee2aaSAndroid Build Coastguard Worker } 262*c8dee2aaSAndroid Build Coastguard Worker 263*c8dee2aaSAndroid Build Coastguard Worker // Find the starting and ending tangents. 264*c8dee2aaSAndroid Build Coastguard Worker float2 tan0 = $robust_normalize_diff((p0 == p1) ? ((p1 == p2) ? p3 : p2) : p1, p0); 265*c8dee2aaSAndroid Build Coastguard Worker float2 tan1 = $robust_normalize_diff(p3, (p3 == p2) ? ((p2 == p1) ? p0 : p1) : p2); 266*c8dee2aaSAndroid Build Coastguard Worker if (tan0 == float2(0)) { 267*c8dee2aaSAndroid Build Coastguard Worker // The stroke is a point. This special case tells us to draw a stroke-width circle as a 268*c8dee2aaSAndroid Build Coastguard Worker // 180 degree point stroke instead. 269*c8dee2aaSAndroid Build Coastguard Worker tan0 = float2(1,0); 270*c8dee2aaSAndroid Build Coastguard Worker tan1 = float2(-1,0); 271*c8dee2aaSAndroid Build Coastguard Worker } 272*c8dee2aaSAndroid Build Coastguard Worker 273*c8dee2aaSAndroid Build Coastguard Worker // Determine how many edges to give to the join. We emit the first and final edges 274*c8dee2aaSAndroid Build Coastguard Worker // of the join twice: once full width and once restricted to half width. This guarantees 275*c8dee2aaSAndroid Build Coastguard Worker // perfect seaming by matching the vertices from the join as well as from the strokes on 276*c8dee2aaSAndroid Build Coastguard Worker // either side. 277*c8dee2aaSAndroid Build Coastguard Worker float numEdgesInJoin; 278*c8dee2aaSAndroid Build Coastguard Worker if (joinType >= 0 /*Is the join not a round type?*/) { 279*c8dee2aaSAndroid Build Coastguard Worker // Bevel(0) and miter(+) joins get 1 and 2 segments respectively. 280*c8dee2aaSAndroid Build Coastguard Worker // +2 because we emit the beginning and ending edges twice (see above comments). 281*c8dee2aaSAndroid Build Coastguard Worker numEdgesInJoin = sign(joinType) + (1 + 2); 282*c8dee2aaSAndroid Build Coastguard Worker } else { 283*c8dee2aaSAndroid Build Coastguard Worker float2 prevTan = $robust_normalize_diff(p0, lastControlPoint); 284*c8dee2aaSAndroid Build Coastguard Worker float joinRads = acos($cosine_between_unit_vectors(prevTan, tan0)); 285*c8dee2aaSAndroid Build Coastguard Worker float numRadialSegmentsInJoin = max(ceil(joinRads * numRadialSegmentsPerRadian), 1); 286*c8dee2aaSAndroid Build Coastguard Worker // +2 because we emit the beginning and ending edges twice (see above comment). 287*c8dee2aaSAndroid Build Coastguard Worker numEdgesInJoin = numRadialSegmentsInJoin + 2; 288*c8dee2aaSAndroid Build Coastguard Worker // The stroke section needs at least two edges. Don't assign more to the join than 289*c8dee2aaSAndroid Build Coastguard Worker // "maxEdges - 2". (This is only relevant when the ideal max edge count calculated 290*c8dee2aaSAndroid Build Coastguard Worker // on the CPU had to be limited to maxEdges in the draw call). 291*c8dee2aaSAndroid Build Coastguard Worker numEdgesInJoin = min(numEdgesInJoin, maxEdges - 2); 292*c8dee2aaSAndroid Build Coastguard Worker } 293*c8dee2aaSAndroid Build Coastguard Worker 294*c8dee2aaSAndroid Build Coastguard Worker // Find which direction the curve turns. 295*c8dee2aaSAndroid Build Coastguard Worker // NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5). 296*c8dee2aaSAndroid Build Coastguard Worker // NOTE: F'(.5) x F''(.5) has the same sign as (P2 - P0) x (P3 - P1) 297*c8dee2aaSAndroid Build Coastguard Worker float turn = cross_length_2d(p2 - p0, p3 - p1); 298*c8dee2aaSAndroid Build Coastguard Worker float combinedEdgeID = abs(edgeID) - numEdgesInJoin; 299*c8dee2aaSAndroid Build Coastguard Worker if (combinedEdgeID < 0) { 300*c8dee2aaSAndroid Build Coastguard Worker tan1 = tan0; 301*c8dee2aaSAndroid Build Coastguard Worker // Don't let tan0 become zero. The code as-is isn't built to handle that case. tan0=0 302*c8dee2aaSAndroid Build Coastguard Worker // means the join is disabled, and to disable it with the existing code we can leave 303*c8dee2aaSAndroid Build Coastguard Worker // tan0 equal to tan1. 304*c8dee2aaSAndroid Build Coastguard Worker if (lastControlPoint != p0) { 305*c8dee2aaSAndroid Build Coastguard Worker tan0 = $robust_normalize_diff(p0, lastControlPoint); 306*c8dee2aaSAndroid Build Coastguard Worker } 307*c8dee2aaSAndroid Build Coastguard Worker turn = cross_length_2d(tan0, tan1); 308*c8dee2aaSAndroid Build Coastguard Worker } 309*c8dee2aaSAndroid Build Coastguard Worker 310*c8dee2aaSAndroid Build Coastguard Worker // Calculate the curve's starting angle and rotation. 311*c8dee2aaSAndroid Build Coastguard Worker float cosTheta = $cosine_between_unit_vectors(tan0, tan1); 312*c8dee2aaSAndroid Build Coastguard Worker float rotation = acos(cosTheta); 313*c8dee2aaSAndroid Build Coastguard Worker if (turn < 0) { 314*c8dee2aaSAndroid Build Coastguard Worker // Adjust sign of rotation to match the direction the curve turns. 315*c8dee2aaSAndroid Build Coastguard Worker rotation = -rotation; 316*c8dee2aaSAndroid Build Coastguard Worker } 317*c8dee2aaSAndroid Build Coastguard Worker 318*c8dee2aaSAndroid Build Coastguard Worker float numRadialSegments; 319*c8dee2aaSAndroid Build Coastguard Worker float strokeOutset = sign(edgeID); 320*c8dee2aaSAndroid Build Coastguard Worker if (combinedEdgeID < 0) { 321*c8dee2aaSAndroid Build Coastguard Worker // We belong to the preceding join. The first and final edges get duplicated, so we only 322*c8dee2aaSAndroid Build Coastguard Worker // have "numEdgesInJoin - 2" segments. 323*c8dee2aaSAndroid Build Coastguard Worker numRadialSegments = numEdgesInJoin - 2; 324*c8dee2aaSAndroid Build Coastguard Worker numParametricSegments = 1; // Joins don't have parametric segments. 325*c8dee2aaSAndroid Build Coastguard Worker p3 = p2 = p1 = p0; // Colocate all points on the junction point. 326*c8dee2aaSAndroid Build Coastguard Worker // Shift combinedEdgeID to the range [-1, numRadialSegments]. This duplicates the first 327*c8dee2aaSAndroid Build Coastguard Worker // edge and lands one edge at the very end of the join. (The duplicated final edge will 328*c8dee2aaSAndroid Build Coastguard Worker // actually come from the section of our strip that belongs to the stroke.) 329*c8dee2aaSAndroid Build Coastguard Worker combinedEdgeID += numRadialSegments + 1; 330*c8dee2aaSAndroid Build Coastguard Worker if (combinedEdgeID < 0) { 331*c8dee2aaSAndroid Build Coastguard Worker combinedEdgeID = 0; 332*c8dee2aaSAndroid Build Coastguard Worker } else { 333*c8dee2aaSAndroid Build Coastguard Worker // We normally restrict the join on one side of the junction, but if the tangents are 334*c8dee2aaSAndroid Build Coastguard Worker // nearly equivalent this could theoretically result in bad seaming and/or cracks on the 335*c8dee2aaSAndroid Build Coastguard Worker // side we don't put it on. If the tangents are nearly equivalent then we leave the join 336*c8dee2aaSAndroid Build Coastguard Worker // double-sided. 337*c8dee2aaSAndroid Build Coastguard Worker const float sinEpsilon = 1e-2; // ~= sin(180deg / 3000) 338*c8dee2aaSAndroid Build Coastguard Worker bool tangentsNearlyParallel = 339*c8dee2aaSAndroid Build Coastguard Worker (abs(turn) * inversesqrt(dot(tan0, tan0) * dot(tan1, tan1))) < sinEpsilon; 340*c8dee2aaSAndroid Build Coastguard Worker if (!tangentsNearlyParallel || dot(tan0, tan1) < 0) { 341*c8dee2aaSAndroid Build Coastguard Worker // There are two edges colocated at the beginning. Leave the first one double sided 342*c8dee2aaSAndroid Build Coastguard Worker // for seaming with the previous stroke. (The double sided edge at the end will 343*c8dee2aaSAndroid Build Coastguard Worker // actually come from the section of our strip that belongs to the stroke.) 344*c8dee2aaSAndroid Build Coastguard Worker strokeOutset = (turn < 0) ? min(strokeOutset, 0) : max(strokeOutset, 0); 345*c8dee2aaSAndroid Build Coastguard Worker } 346*c8dee2aaSAndroid Build Coastguard Worker } 347*c8dee2aaSAndroid Build Coastguard Worker } else { 348*c8dee2aaSAndroid Build Coastguard Worker // We belong to the stroke. Unless numRadialSegmentsPerRadian is incredibly high, 349*c8dee2aaSAndroid Build Coastguard Worker // clamping to maxCombinedSegments will be a no-op because the draw call was invoked with 350*c8dee2aaSAndroid Build Coastguard Worker // sufficient vertices to cover the worst case scenario of 180 degree rotation. 351*c8dee2aaSAndroid Build Coastguard Worker float maxCombinedSegments = maxEdges - numEdgesInJoin - 1; 352*c8dee2aaSAndroid Build Coastguard Worker numRadialSegments = max(ceil(abs(rotation) * numRadialSegmentsPerRadian), 1); 353*c8dee2aaSAndroid Build Coastguard Worker numRadialSegments = min(numRadialSegments, maxCombinedSegments); 354*c8dee2aaSAndroid Build Coastguard Worker numParametricSegments = min(numParametricSegments, 355*c8dee2aaSAndroid Build Coastguard Worker maxCombinedSegments - numRadialSegments + 1); 356*c8dee2aaSAndroid Build Coastguard Worker } 357*c8dee2aaSAndroid Build Coastguard Worker 358*c8dee2aaSAndroid Build Coastguard Worker // Additional parameters for final tessellation evaluation. 359*c8dee2aaSAndroid Build Coastguard Worker float radsPerSegment = rotation / numRadialSegments; 360*c8dee2aaSAndroid Build Coastguard Worker float numCombinedSegments = numParametricSegments + numRadialSegments - 1; 361*c8dee2aaSAndroid Build Coastguard Worker bool isFinalEdge = (combinedEdgeID >= numCombinedSegments); 362*c8dee2aaSAndroid Build Coastguard Worker if (combinedEdgeID > numCombinedSegments) { 363*c8dee2aaSAndroid Build Coastguard Worker strokeOutset = 0; // The strip has more edges than we need. Drop this one. 364*c8dee2aaSAndroid Build Coastguard Worker } 365*c8dee2aaSAndroid Build Coastguard Worker // Edge #2 extends to the miter point. 366*c8dee2aaSAndroid Build Coastguard Worker if (abs(edgeID) == 2 && joinType > 0/*Is the join a miter type?*/) { 367*c8dee2aaSAndroid Build Coastguard Worker strokeOutset *= $miter_extent(cosTheta, joinType/*miterLimit*/); 368*c8dee2aaSAndroid Build Coastguard Worker } 369*c8dee2aaSAndroid Build Coastguard Worker 370*c8dee2aaSAndroid Build Coastguard Worker float2 tangent, strokeCoord; 371*c8dee2aaSAndroid Build Coastguard Worker if (combinedEdgeID != 0 && !isFinalEdge) { 372*c8dee2aaSAndroid Build Coastguard Worker // Compute the location and tangent direction of the stroke edge with the integral id 373*c8dee2aaSAndroid Build Coastguard Worker // "combinedEdgeID", where combinedEdgeID is the sorted-order index of parametric and radial 374*c8dee2aaSAndroid Build Coastguard Worker // edges. Start by finding the tangent function's power basis coefficients. These define a 375*c8dee2aaSAndroid Build Coastguard Worker // tangent direction (scaled by some uniform value) as: 376*c8dee2aaSAndroid Build Coastguard Worker // |T^2| 377*c8dee2aaSAndroid Build Coastguard Worker // Tangent_Direction(T) = dx,dy = |A 2B C| * |T | 378*c8dee2aaSAndroid Build Coastguard Worker // |. . .| |1 | 379*c8dee2aaSAndroid Build Coastguard Worker float2 A, B, C = p1 - p0; 380*c8dee2aaSAndroid Build Coastguard Worker float2 D = p3 - p0; 381*c8dee2aaSAndroid Build Coastguard Worker if (w >= 0.0) { 382*c8dee2aaSAndroid Build Coastguard Worker // P0..P2 represent a conic and P3==P2. The derivative of a conic has a cumbersome 383*c8dee2aaSAndroid Build Coastguard Worker // order-4 denominator. However, this isn't necessary if we are only interested in a 384*c8dee2aaSAndroid Build Coastguard Worker // vector in the same *direction* as a given tangent line. Since the denominator scales 385*c8dee2aaSAndroid Build Coastguard Worker // dx and dy uniformly, we can throw it out completely after evaluating the derivative 386*c8dee2aaSAndroid Build Coastguard Worker // with the standard quotient rule. This leaves us with a simpler quadratic function 387*c8dee2aaSAndroid Build Coastguard Worker // that we use to find a tangent. 388*c8dee2aaSAndroid Build Coastguard Worker C *= w; 389*c8dee2aaSAndroid Build Coastguard Worker B = .5*D - C; 390*c8dee2aaSAndroid Build Coastguard Worker A = (w - 1.0) * D; 391*c8dee2aaSAndroid Build Coastguard Worker p1 *= w; 392*c8dee2aaSAndroid Build Coastguard Worker } else { 393*c8dee2aaSAndroid Build Coastguard Worker float2 E = p2 - p1; 394*c8dee2aaSAndroid Build Coastguard Worker B = E - C; 395*c8dee2aaSAndroid Build Coastguard Worker A = fma(float2(-3), E, D); 396*c8dee2aaSAndroid Build Coastguard Worker } 397*c8dee2aaSAndroid Build Coastguard Worker // FIXME(crbug.com/800804,skbug.com/11268): Consider normalizing the exponents in A,B,C at 398*c8dee2aaSAndroid Build Coastguard Worker // this point in order to prevent fp32 overflow. 399*c8dee2aaSAndroid Build Coastguard Worker 400*c8dee2aaSAndroid Build Coastguard Worker // Now find the coefficients that give a tangent direction from a parametric edge ID: 401*c8dee2aaSAndroid Build Coastguard Worker // 402*c8dee2aaSAndroid Build Coastguard Worker // |parametricEdgeID^2| 403*c8dee2aaSAndroid Build Coastguard Worker // Tangent_Direction(parametricEdgeID) = dx,dy = |A B_ C_| * |parametricEdgeID | 404*c8dee2aaSAndroid Build Coastguard Worker // |. . .| |1 | 405*c8dee2aaSAndroid Build Coastguard Worker // 406*c8dee2aaSAndroid Build Coastguard Worker float2 B_ = B * (numParametricSegments * 2.0); 407*c8dee2aaSAndroid Build Coastguard Worker float2 C_ = C * (numParametricSegments * numParametricSegments); 408*c8dee2aaSAndroid Build Coastguard Worker 409*c8dee2aaSAndroid Build Coastguard Worker // Run a binary search to determine the highest parametric edge that is located on or before 410*c8dee2aaSAndroid Build Coastguard Worker // the combinedEdgeID. A combined ID is determined by the sum of complete parametric and 411*c8dee2aaSAndroid Build Coastguard Worker // radial segments behind it. i.e., find the highest parametric edge where: 412*c8dee2aaSAndroid Build Coastguard Worker // 413*c8dee2aaSAndroid Build Coastguard Worker // parametricEdgeID + floor(numRadialSegmentsAtParametricT) <= combinedEdgeID 414*c8dee2aaSAndroid Build Coastguard Worker // 415*c8dee2aaSAndroid Build Coastguard Worker float lastParametricEdgeID = 0.0; 416*c8dee2aaSAndroid Build Coastguard Worker float maxParametricEdgeID = min(numParametricSegments - 1.0, combinedEdgeID); 417*c8dee2aaSAndroid Build Coastguard Worker float negAbsRadsPerSegment = -abs(radsPerSegment); 418*c8dee2aaSAndroid Build Coastguard Worker float maxRotation0 = (1.0 + combinedEdgeID) * abs(radsPerSegment); 419*c8dee2aaSAndroid Build Coastguard Worker for (float exp = 32.0; exp >= 1.0; exp *= 0.5) { 420*c8dee2aaSAndroid Build Coastguard Worker // Test the parametric edge at lastParametricEdgeID + (32, 16, 8, 4, 2, 1). 421*c8dee2aaSAndroid Build Coastguard Worker float testParametricID = lastParametricEdgeID + exp; 422*c8dee2aaSAndroid Build Coastguard Worker if (testParametricID <= maxParametricEdgeID) { 423*c8dee2aaSAndroid Build Coastguard Worker float2 testTan = fma(float2(testParametricID), A, B_); 424*c8dee2aaSAndroid Build Coastguard Worker testTan = fma(float2(testParametricID), testTan, C_); 425*c8dee2aaSAndroid Build Coastguard Worker float cosRotation = dot(normalize(testTan), tan0); 426*c8dee2aaSAndroid Build Coastguard Worker float maxRotation = fma(testParametricID, negAbsRadsPerSegment, maxRotation0); 427*c8dee2aaSAndroid Build Coastguard Worker maxRotation = min(maxRotation, $PI); 428*c8dee2aaSAndroid Build Coastguard Worker // Is rotation <= maxRotation? (i.e., is the number of complete radial segments 429*c8dee2aaSAndroid Build Coastguard Worker // behind testT, + testParametricID <= combinedEdgeID?) 430*c8dee2aaSAndroid Build Coastguard Worker if (cosRotation >= cos(maxRotation)) { 431*c8dee2aaSAndroid Build Coastguard Worker // testParametricID is on or before the combinedEdgeID. Keep it! 432*c8dee2aaSAndroid Build Coastguard Worker lastParametricEdgeID = testParametricID; 433*c8dee2aaSAndroid Build Coastguard Worker } 434*c8dee2aaSAndroid Build Coastguard Worker } 435*c8dee2aaSAndroid Build Coastguard Worker } 436*c8dee2aaSAndroid Build Coastguard Worker 437*c8dee2aaSAndroid Build Coastguard Worker // Find the T value of the parametric edge at lastParametricEdgeID. 438*c8dee2aaSAndroid Build Coastguard Worker float parametricT = lastParametricEdgeID / numParametricSegments; 439*c8dee2aaSAndroid Build Coastguard Worker 440*c8dee2aaSAndroid Build Coastguard Worker // Now that we've identified the highest parametric edge on or before the 441*c8dee2aaSAndroid Build Coastguard Worker // combinedEdgeID, the highest radial edge is easy: 442*c8dee2aaSAndroid Build Coastguard Worker float lastRadialEdgeID = combinedEdgeID - lastParametricEdgeID; 443*c8dee2aaSAndroid Build Coastguard Worker 444*c8dee2aaSAndroid Build Coastguard Worker // Find the angle of tan0, i.e. the angle between tan0 and the positive x axis. 445*c8dee2aaSAndroid Build Coastguard Worker float angle0 = acos(clamp(tan0.x, -1.0, 1.0)); 446*c8dee2aaSAndroid Build Coastguard Worker angle0 = tan0.y >= 0.0 ? angle0 : -angle0; 447*c8dee2aaSAndroid Build Coastguard Worker 448*c8dee2aaSAndroid Build Coastguard Worker // Find the tangent vector on the edge at lastRadialEdgeID. By construction it is already 449*c8dee2aaSAndroid Build Coastguard Worker // normalized. 450*c8dee2aaSAndroid Build Coastguard Worker float radialAngle = fma(lastRadialEdgeID, radsPerSegment, angle0); 451*c8dee2aaSAndroid Build Coastguard Worker tangent = float2(cos(radialAngle), sin(radialAngle)); 452*c8dee2aaSAndroid Build Coastguard Worker float2 norm = float2(-tangent.y, tangent.x); 453*c8dee2aaSAndroid Build Coastguard Worker 454*c8dee2aaSAndroid Build Coastguard Worker // Find the T value where the tangent is orthogonal to norm. This is a quadratic: 455*c8dee2aaSAndroid Build Coastguard Worker // 456*c8dee2aaSAndroid Build Coastguard Worker // dot(norm, Tangent_Direction(T)) == 0 457*c8dee2aaSAndroid Build Coastguard Worker // 458*c8dee2aaSAndroid Build Coastguard Worker // |T^2| 459*c8dee2aaSAndroid Build Coastguard Worker // norm * |A 2B C| * |T | == 0 460*c8dee2aaSAndroid Build Coastguard Worker // |. . .| |1 | 461*c8dee2aaSAndroid Build Coastguard Worker // 462*c8dee2aaSAndroid Build Coastguard Worker float a=dot(norm,A), b_over_2=dot(norm,B), c=dot(norm,C); 463*c8dee2aaSAndroid Build Coastguard Worker float discr_over_4 = max(b_over_2*b_over_2 - a*c, 0.0); 464*c8dee2aaSAndroid Build Coastguard Worker float q = sqrt(discr_over_4); 465*c8dee2aaSAndroid Build Coastguard Worker if (b_over_2 > 0.0) { 466*c8dee2aaSAndroid Build Coastguard Worker q = -q; 467*c8dee2aaSAndroid Build Coastguard Worker } 468*c8dee2aaSAndroid Build Coastguard Worker q -= b_over_2; 469*c8dee2aaSAndroid Build Coastguard Worker 470*c8dee2aaSAndroid Build Coastguard Worker // Roots are q/a and c/q. Since each curve section does not inflect or rotate more than 180 471*c8dee2aaSAndroid Build Coastguard Worker // degrees, there can only be one tangent orthogonal to "norm" inside 0..1. Pick the root 472*c8dee2aaSAndroid Build Coastguard Worker // nearest .5. 473*c8dee2aaSAndroid Build Coastguard Worker float _5qa = -.5*q*a; 474*c8dee2aaSAndroid Build Coastguard Worker float2 root = (abs(fma(q,q,_5qa)) < abs(fma(a,c,_5qa))) ? float2(q,a) : float2(c,q); 475*c8dee2aaSAndroid Build Coastguard Worker 476*c8dee2aaSAndroid Build Coastguard Worker // The root finder above can become unstable when lastRadialEdgeID == 0 (e.g., if there are 477*c8dee2aaSAndroid Build Coastguard Worker // roots at exatly 0 and 1 both). radialT should always equal 0 in this case. 478*c8dee2aaSAndroid Build Coastguard Worker float radialT = (lastRadialEdgeID != 0.0 && root.t != 0.0) 479*c8dee2aaSAndroid Build Coastguard Worker ? saturate(root.s / root.t) 480*c8dee2aaSAndroid Build Coastguard Worker : 0.0; 481*c8dee2aaSAndroid Build Coastguard Worker 482*c8dee2aaSAndroid Build Coastguard Worker // Now that we've identified the T values of the last parametric and radial edges, our final 483*c8dee2aaSAndroid Build Coastguard Worker // T value for combinedEdgeID is whichever is larger. 484*c8dee2aaSAndroid Build Coastguard Worker float T = max(parametricT, radialT); 485*c8dee2aaSAndroid Build Coastguard Worker 486*c8dee2aaSAndroid Build Coastguard Worker // Evaluate the cubic at T. Use De Casteljau's for its accuracy and stability. 487*c8dee2aaSAndroid Build Coastguard Worker float2 ab = $unchecked_mix(p0, p1, T); 488*c8dee2aaSAndroid Build Coastguard Worker float2 bc = $unchecked_mix(p1, p2, T); 489*c8dee2aaSAndroid Build Coastguard Worker float2 cd = $unchecked_mix(p2, p3, T); 490*c8dee2aaSAndroid Build Coastguard Worker float2 abc = $unchecked_mix(ab, bc, T); 491*c8dee2aaSAndroid Build Coastguard Worker float2 bcd = $unchecked_mix(bc, cd, T); 492*c8dee2aaSAndroid Build Coastguard Worker float2 abcd = $unchecked_mix(abc, bcd, T); 493*c8dee2aaSAndroid Build Coastguard Worker 494*c8dee2aaSAndroid Build Coastguard Worker // Evaluate the conic weight at T. 495*c8dee2aaSAndroid Build Coastguard Worker float u = $unchecked_mix(1.0, w, T); 496*c8dee2aaSAndroid Build Coastguard Worker float v = w + 1 - u; // == mix(w, 1, T) 497*c8dee2aaSAndroid Build Coastguard Worker float uv = $unchecked_mix(u, v, T); 498*c8dee2aaSAndroid Build Coastguard Worker 499*c8dee2aaSAndroid Build Coastguard Worker // If we went with T=parametricT, then update the tangent. Otherwise leave it at the radial 500*c8dee2aaSAndroid Build Coastguard Worker // tangent found previously. (In the event that parametricT == radialT, we keep the radial 501*c8dee2aaSAndroid Build Coastguard Worker // tangent.) 502*c8dee2aaSAndroid Build Coastguard Worker if (T != radialT) { 503*c8dee2aaSAndroid Build Coastguard Worker // We must re-normalize here because the tangent is determined by the curve coefficients 504*c8dee2aaSAndroid Build Coastguard Worker tangent = w >= 0.0 ? $robust_normalize_diff(bc*u, ab*v) 505*c8dee2aaSAndroid Build Coastguard Worker : $robust_normalize_diff(bcd, abc); 506*c8dee2aaSAndroid Build Coastguard Worker } 507*c8dee2aaSAndroid Build Coastguard Worker 508*c8dee2aaSAndroid Build Coastguard Worker strokeCoord = (w >= 0.0) ? abc/uv : abcd; 509*c8dee2aaSAndroid Build Coastguard Worker } else { 510*c8dee2aaSAndroid Build Coastguard Worker // Edges at the beginning and end of the strip use exact endpoints and tangents. This 511*c8dee2aaSAndroid Build Coastguard Worker // ensures crack-free seaming between instances. 512*c8dee2aaSAndroid Build Coastguard Worker tangent = (combinedEdgeID == 0) ? tan0 : tan1; 513*c8dee2aaSAndroid Build Coastguard Worker strokeCoord = (combinedEdgeID == 0) ? p0 : p3; 514*c8dee2aaSAndroid Build Coastguard Worker } 515*c8dee2aaSAndroid Build Coastguard Worker 516*c8dee2aaSAndroid Build Coastguard Worker // At this point 'tangent' is normalized, so the orthogonal vector is also normalized. 517*c8dee2aaSAndroid Build Coastguard Worker float2 ortho = float2(tangent.y, -tangent.x); 518*c8dee2aaSAndroid Build Coastguard Worker strokeCoord += ortho * (strokeRadius * strokeOutset); 519*c8dee2aaSAndroid Build Coastguard Worker 520*c8dee2aaSAndroid Build Coastguard Worker if (isHairline) { 521*c8dee2aaSAndroid Build Coastguard Worker // Hairline case. The scale and skew already happened before tessellation. 522*c8dee2aaSAndroid Build Coastguard Worker // TODO: There's probably a more efficient way to tessellate the hairline that lets us 523*c8dee2aaSAndroid Build Coastguard Worker // avoid inverting the affine matrix to get back to local coords, but it's just a 2x2 so 524*c8dee2aaSAndroid Build Coastguard Worker // this works for now. 525*c8dee2aaSAndroid Build Coastguard Worker return float4(strokeCoord + translate, inverse(affineMatrix) * strokeCoord); 526*c8dee2aaSAndroid Build Coastguard Worker } else { 527*c8dee2aaSAndroid Build Coastguard Worker // Normal case. Do the transform after tessellation. 528*c8dee2aaSAndroid Build Coastguard Worker return float4(affineMatrix * strokeCoord + translate, strokeCoord); 529*c8dee2aaSAndroid Build Coastguard Worker } 530*c8dee2aaSAndroid Build Coastguard Worker} 531*c8dee2aaSAndroid Build Coastguard Worker 532*c8dee2aaSAndroid Build Coastguard Workerfloat4 analytic_rrect_vertex_fn(// Vertex Attributes 533*c8dee2aaSAndroid Build Coastguard Worker float2 position, 534*c8dee2aaSAndroid Build Coastguard Worker float2 normal, 535*c8dee2aaSAndroid Build Coastguard Worker float normalScale, 536*c8dee2aaSAndroid Build Coastguard Worker float centerWeight, 537*c8dee2aaSAndroid Build Coastguard Worker // Instance Attributes 538*c8dee2aaSAndroid Build Coastguard Worker float4 xRadiiOrFlags, 539*c8dee2aaSAndroid Build Coastguard Worker float4 radiiOrQuadXs, 540*c8dee2aaSAndroid Build Coastguard Worker float4 ltrbOrQuadYs, 541*c8dee2aaSAndroid Build Coastguard Worker float4 center, 542*c8dee2aaSAndroid Build Coastguard Worker float depth, 543*c8dee2aaSAndroid Build Coastguard Worker float3x3 localToDevice, 544*c8dee2aaSAndroid Build Coastguard Worker // Varyings 545*c8dee2aaSAndroid Build Coastguard Worker out float4 jacobian, 546*c8dee2aaSAndroid Build Coastguard Worker out float4 edgeDistances, 547*c8dee2aaSAndroid Build Coastguard Worker out float4 xRadii, 548*c8dee2aaSAndroid Build Coastguard Worker out float4 yRadii, 549*c8dee2aaSAndroid Build Coastguard Worker out float2 strokeParams, 550*c8dee2aaSAndroid Build Coastguard Worker out float2 perPixelControl, 551*c8dee2aaSAndroid Build Coastguard Worker // Render Step 552*c8dee2aaSAndroid Build Coastguard Worker out float2 stepLocalCoords) { 553*c8dee2aaSAndroid Build Coastguard Worker const uint kCornerVertexCount = 9; // KEEP IN SYNC WITH C++'s 554*c8dee2aaSAndroid Build Coastguard Worker // AnalyticRRectRenderStep::kCornerVertexCount 555*c8dee2aaSAndroid Build Coastguard Worker const float kMiterScale = 1.0; 556*c8dee2aaSAndroid Build Coastguard Worker const float kBevelScale = 0.0; 557*c8dee2aaSAndroid Build Coastguard Worker const float kRoundScale = 0.41421356237; // sqrt(2)-1 558*c8dee2aaSAndroid Build Coastguard Worker 559*c8dee2aaSAndroid Build Coastguard Worker const float kEpsilon = 0.00024; // SK_ScalarNearlyZero 560*c8dee2aaSAndroid Build Coastguard Worker 561*c8dee2aaSAndroid Build Coastguard Worker // Default to miter'ed vertex positioning. Corners with sufficiently large corner radii, or 562*c8dee2aaSAndroid Build Coastguard Worker // bevel'ed strokes will adjust vertex placement on a per corner basis. This will not affect 563*c8dee2aaSAndroid Build Coastguard Worker // the final coverage calculations in the fragment shader. 564*c8dee2aaSAndroid Build Coastguard Worker float joinScale = kMiterScale; 565*c8dee2aaSAndroid Build Coastguard Worker 566*c8dee2aaSAndroid Build Coastguard Worker // Unpack instance-level state that determines the vertex placement and style of shape. 567*c8dee2aaSAndroid Build Coastguard Worker bool bidirectionalCoverage = center.z <= 0.0; 568*c8dee2aaSAndroid Build Coastguard Worker bool deviceSpaceDistances = false; 569*c8dee2aaSAndroid Build Coastguard Worker float4 xs, ys; // ordered TL, TR, BR, BL 570*c8dee2aaSAndroid Build Coastguard Worker float4 edgeAA = float4(1.0); // ordered L,T,R,B. 1 = AA, 0 = no AA 571*c8dee2aaSAndroid Build Coastguard Worker bool strokedLine = false; 572*c8dee2aaSAndroid Build Coastguard Worker if (xRadiiOrFlags.x < -1.0) { 573*c8dee2aaSAndroid Build Coastguard Worker // Stroked [round] rect or line 574*c8dee2aaSAndroid Build Coastguard Worker // If y > 0, unpack the line end points, otherwise unpack the rect edges 575*c8dee2aaSAndroid Build Coastguard Worker strokedLine = xRadiiOrFlags.y > 0.0; 576*c8dee2aaSAndroid Build Coastguard Worker xs = strokedLine ? ltrbOrQuadYs.LLRR : ltrbOrQuadYs.LRRL; 577*c8dee2aaSAndroid Build Coastguard Worker ys = ltrbOrQuadYs.TTBB; 578*c8dee2aaSAndroid Build Coastguard Worker 579*c8dee2aaSAndroid Build Coastguard Worker if (xRadiiOrFlags.y < 0.0) { 580*c8dee2aaSAndroid Build Coastguard Worker // A hairline [r]rect so the X radii are encoded as negative values in this field, 581*c8dee2aaSAndroid Build Coastguard Worker // and Y radii are stored directly in the subsequent float4. 582*c8dee2aaSAndroid Build Coastguard Worker xRadii = -xRadiiOrFlags - 2.0; 583*c8dee2aaSAndroid Build Coastguard Worker yRadii = radiiOrQuadXs; 584*c8dee2aaSAndroid Build Coastguard Worker 585*c8dee2aaSAndroid Build Coastguard Worker // All hairlines use miter joins (join style > 0) 586*c8dee2aaSAndroid Build Coastguard Worker strokeParams = float2(0.0, 1.0); 587*c8dee2aaSAndroid Build Coastguard Worker } else { 588*c8dee2aaSAndroid Build Coastguard Worker xRadii = radiiOrQuadXs; 589*c8dee2aaSAndroid Build Coastguard Worker yRadii = xRadii; // regular strokes are circular 590*c8dee2aaSAndroid Build Coastguard Worker strokeParams = xRadiiOrFlags.zw; 591*c8dee2aaSAndroid Build Coastguard Worker 592*c8dee2aaSAndroid Build Coastguard Worker // `sign(strokeParams.y)` evaluates to kMiterScale (1.0) when the 593*c8dee2aaSAndroid Build Coastguard Worker // input is positive, and kBevelScale (0.0) when it is zero. 594*c8dee2aaSAndroid Build Coastguard Worker // kRoundScale uses the stroke radius to round rectangular corners. 595*c8dee2aaSAndroid Build Coastguard Worker joinScale = (strokeParams.y < 0.0) ? kRoundScale 596*c8dee2aaSAndroid Build Coastguard Worker : sign(strokeParams.y); 597*c8dee2aaSAndroid Build Coastguard Worker } 598*c8dee2aaSAndroid Build Coastguard Worker } else if (any(greaterThan(xRadiiOrFlags, float4(0.0)))) { 599*c8dee2aaSAndroid Build Coastguard Worker // Filled round rect 600*c8dee2aaSAndroid Build Coastguard Worker xs = ltrbOrQuadYs.LRRL; 601*c8dee2aaSAndroid Build Coastguard Worker ys = ltrbOrQuadYs.TTBB; 602*c8dee2aaSAndroid Build Coastguard Worker 603*c8dee2aaSAndroid Build Coastguard Worker xRadii = xRadiiOrFlags; 604*c8dee2aaSAndroid Build Coastguard Worker yRadii = radiiOrQuadXs; 605*c8dee2aaSAndroid Build Coastguard Worker 606*c8dee2aaSAndroid Build Coastguard Worker strokeParams = float2(0.0, -1.0); // A negative join style is "round" 607*c8dee2aaSAndroid Build Coastguard Worker } else { 608*c8dee2aaSAndroid Build Coastguard Worker // Per-edge quadrilateral, so we have to calculate the corner's basis from the 609*c8dee2aaSAndroid Build Coastguard Worker // quad's edges. 610*c8dee2aaSAndroid Build Coastguard Worker xs = radiiOrQuadXs; 611*c8dee2aaSAndroid Build Coastguard Worker ys = ltrbOrQuadYs; 612*c8dee2aaSAndroid Build Coastguard Worker edgeAA = -xRadiiOrFlags; // AA flags needed to be < 0 on upload, so flip the sign. 613*c8dee2aaSAndroid Build Coastguard Worker 614*c8dee2aaSAndroid Build Coastguard Worker xRadii = float4(0.0); 615*c8dee2aaSAndroid Build Coastguard Worker yRadii = float4(0.0); 616*c8dee2aaSAndroid Build Coastguard Worker 617*c8dee2aaSAndroid Build Coastguard Worker strokeParams = float2(0.0, 1.0); // Will be ignored, but set to a "miter" 618*c8dee2aaSAndroid Build Coastguard Worker deviceSpaceDistances = true; 619*c8dee2aaSAndroid Build Coastguard Worker } 620*c8dee2aaSAndroid Build Coastguard Worker 621*c8dee2aaSAndroid Build Coastguard Worker // Adjust state on a per-corner basis 622*c8dee2aaSAndroid Build Coastguard Worker uint cornerID = uint(sk_VertexID) / kCornerVertexCount; 623*c8dee2aaSAndroid Build Coastguard Worker float2 cornerRadii = float2(xRadii[cornerID], yRadii[cornerID]); 624*c8dee2aaSAndroid Build Coastguard Worker if (cornerID % 2 != 0) { 625*c8dee2aaSAndroid Build Coastguard Worker // Corner radii are uploaded in the local coordinate frame, but vertex placement happens 626*c8dee2aaSAndroid Build Coastguard Worker // in a consistent winding before transforming to final local coords, so swap the 627*c8dee2aaSAndroid Build Coastguard Worker // radii for odd corners. 628*c8dee2aaSAndroid Build Coastguard Worker cornerRadii = cornerRadii.yx; 629*c8dee2aaSAndroid Build Coastguard Worker } 630*c8dee2aaSAndroid Build Coastguard Worker 631*c8dee2aaSAndroid Build Coastguard Worker float2 cornerAspectRatio = float2(1.0); 632*c8dee2aaSAndroid Build Coastguard Worker if (all(greaterThan(cornerRadii, float2(0.0)))) { 633*c8dee2aaSAndroid Build Coastguard Worker // Position vertices for an elliptical corner; overriding any previous join style since 634*c8dee2aaSAndroid Build Coastguard Worker // that only applies when radii are 0. 635*c8dee2aaSAndroid Build Coastguard Worker joinScale = kRoundScale; 636*c8dee2aaSAndroid Build Coastguard Worker cornerAspectRatio = cornerRadii.yx; 637*c8dee2aaSAndroid Build Coastguard Worker } 638*c8dee2aaSAndroid Build Coastguard Worker 639*c8dee2aaSAndroid Build Coastguard Worker // Calculate the local edge vectors, ordered L, T, R, B starting from the bottom left point. 640*c8dee2aaSAndroid Build Coastguard Worker // For quadrilaterals these are not necessarily axis-aligned, but in all cases they orient 641*c8dee2aaSAndroid Build Coastguard Worker // the +X/+Y normalized vertex template for each corner. 642*c8dee2aaSAndroid Build Coastguard Worker float4 dx = xs - xs.wxyz; 643*c8dee2aaSAndroid Build Coastguard Worker float4 dy = ys - ys.wxyz; 644*c8dee2aaSAndroid Build Coastguard Worker float4 edgeSquaredLen = dx*dx + dy*dy; 645*c8dee2aaSAndroid Build Coastguard Worker 646*c8dee2aaSAndroid Build Coastguard Worker float4 edgeMask = sign(edgeSquaredLen); // 0 for zero-length edge, 1 for non-zero edge. 647*c8dee2aaSAndroid Build Coastguard Worker float4 edgeBias = float4(0.0); // adjustment to edge distance for butt cap correction 648*c8dee2aaSAndroid Build Coastguard Worker float2 strokeRadius = float2(strokeParams.x); 649*c8dee2aaSAndroid Build Coastguard Worker if (any(equal(edgeMask, float4(0.0)))) { 650*c8dee2aaSAndroid Build Coastguard Worker // Must clean up (dx,dy) depending on the empty edge configuration 651*c8dee2aaSAndroid Build Coastguard Worker if (all(equal(edgeMask, float4(0.0)))) { 652*c8dee2aaSAndroid Build Coastguard Worker // A point so use the canonical basis 653*c8dee2aaSAndroid Build Coastguard Worker dx = float4( 0.0, 1.0, 0.0, -1.0); 654*c8dee2aaSAndroid Build Coastguard Worker dy = float4(-1.0, 0.0, 1.0, 0.0); 655*c8dee2aaSAndroid Build Coastguard Worker edgeSquaredLen = float4(1.0); 656*c8dee2aaSAndroid Build Coastguard Worker } else { 657*c8dee2aaSAndroid Build Coastguard Worker // Triangles (3 non-zero edges) copy the adjacent edge. Otherwise it's a line so 658*c8dee2aaSAndroid Build Coastguard Worker // replace empty edges with the left-hand normal vector of the adjacent edge. 659*c8dee2aaSAndroid Build Coastguard Worker bool triangle = (edgeMask[0] + edgeMask[1] + edgeMask[2] + edgeMask[3]) > 2.5; 660*c8dee2aaSAndroid Build Coastguard Worker float4 edgeX = triangle ? dx.yzwx : dy.yzwx; 661*c8dee2aaSAndroid Build Coastguard Worker float4 edgeY = triangle ? dy.yzwx : -dx.yzwx; 662*c8dee2aaSAndroid Build Coastguard Worker 663*c8dee2aaSAndroid Build Coastguard Worker dx = mix(edgeX, dx, edgeMask); 664*c8dee2aaSAndroid Build Coastguard Worker dy = mix(edgeY, dy, edgeMask); 665*c8dee2aaSAndroid Build Coastguard Worker edgeSquaredLen = mix(edgeSquaredLen.yzwx, edgeSquaredLen, edgeMask); 666*c8dee2aaSAndroid Build Coastguard Worker edgeAA = mix(edgeAA.yzwx, edgeAA, edgeMask); 667*c8dee2aaSAndroid Build Coastguard Worker 668*c8dee2aaSAndroid Build Coastguard Worker if (!triangle && joinScale == kBevelScale) { 669*c8dee2aaSAndroid Build Coastguard Worker // Don't outset by stroke radius for butt caps on the zero-length edge, but 670*c8dee2aaSAndroid Build Coastguard Worker // adjust edgeBias and strokeParams to calculate an AA miter'ed shape with the 671*c8dee2aaSAndroid Build Coastguard Worker // non-uniform stroke outset. 672*c8dee2aaSAndroid Build Coastguard Worker strokeRadius *= float2(edgeMask[cornerID], edgeMask.yzwx[cornerID]); 673*c8dee2aaSAndroid Build Coastguard Worker edgeBias = (edgeMask - 1.0) * strokeParams.x; 674*c8dee2aaSAndroid Build Coastguard Worker strokeParams.y = 1.0; 675*c8dee2aaSAndroid Build Coastguard Worker joinScale = kMiterScale; 676*c8dee2aaSAndroid Build Coastguard Worker } 677*c8dee2aaSAndroid Build Coastguard Worker } 678*c8dee2aaSAndroid Build Coastguard Worker } 679*c8dee2aaSAndroid Build Coastguard Worker 680*c8dee2aaSAndroid Build Coastguard Worker float4 inverseEdgeLen = inversesqrt(edgeSquaredLen); 681*c8dee2aaSAndroid Build Coastguard Worker dx *= inverseEdgeLen; 682*c8dee2aaSAndroid Build Coastguard Worker dy *= inverseEdgeLen; 683*c8dee2aaSAndroid Build Coastguard Worker 684*c8dee2aaSAndroid Build Coastguard Worker // Calculate local coordinate for the vertex (relative to xAxis and yAxis at first). 685*c8dee2aaSAndroid Build Coastguard Worker float2 xAxis = -float2(dx.yzwx[cornerID], dy.yzwx[cornerID]); 686*c8dee2aaSAndroid Build Coastguard Worker float2 yAxis = float2(dx.xyzw[cornerID], dy.xyzw[cornerID]); 687*c8dee2aaSAndroid Build Coastguard Worker float2 localPos; 688*c8dee2aaSAndroid Build Coastguard Worker bool snapToCenter = false; 689*c8dee2aaSAndroid Build Coastguard Worker if (normalScale < 0.0) { 690*c8dee2aaSAndroid Build Coastguard Worker // Vertex is inset from the base shape, so we scale by (cornerRadii - strokeRadius) 691*c8dee2aaSAndroid Build Coastguard Worker // and have to check for the possibility of an inner miter. It is always inset by an 692*c8dee2aaSAndroid Build Coastguard Worker // additional conservative AA amount. 693*c8dee2aaSAndroid Build Coastguard Worker if (center.w < 0.0 || centerWeight * center.z != 0.0) { 694*c8dee2aaSAndroid Build Coastguard Worker snapToCenter = true; 695*c8dee2aaSAndroid Build Coastguard Worker } else { 696*c8dee2aaSAndroid Build Coastguard Worker float localAARadius = center.w; 697*c8dee2aaSAndroid Build Coastguard Worker float2 insetRadii = 698*c8dee2aaSAndroid Build Coastguard Worker cornerRadii + (bidirectionalCoverage ? -strokeRadius : strokeRadius); 699*c8dee2aaSAndroid Build Coastguard Worker if (joinScale == kMiterScale || 700*c8dee2aaSAndroid Build Coastguard Worker any(lessThanEqual(insetRadii, float2(localAARadius)))) { 701*c8dee2aaSAndroid Build Coastguard Worker // Miter the inset position 702*c8dee2aaSAndroid Build Coastguard Worker localPos = (insetRadii - localAARadius); 703*c8dee2aaSAndroid Build Coastguard Worker } else { 704*c8dee2aaSAndroid Build Coastguard Worker localPos = insetRadii*position - localAARadius*normal; 705*c8dee2aaSAndroid Build Coastguard Worker } 706*c8dee2aaSAndroid Build Coastguard Worker } 707*c8dee2aaSAndroid Build Coastguard Worker } else { 708*c8dee2aaSAndroid Build Coastguard Worker // Vertex is outset from the base shape (and possibly with an additional AA outset later 709*c8dee2aaSAndroid Build Coastguard Worker // in device space). 710*c8dee2aaSAndroid Build Coastguard Worker localPos = (cornerRadii + strokeRadius) * (position + joinScale*position.yx); 711*c8dee2aaSAndroid Build Coastguard Worker } 712*c8dee2aaSAndroid Build Coastguard Worker 713*c8dee2aaSAndroid Build Coastguard Worker if (snapToCenter) { 714*c8dee2aaSAndroid Build Coastguard Worker // Center is already relative to true local coords, not the corner basis. 715*c8dee2aaSAndroid Build Coastguard Worker localPos = center.xy; 716*c8dee2aaSAndroid Build Coastguard Worker } else { 717*c8dee2aaSAndroid Build Coastguard Worker // Transform from corner basis to true local coords. 718*c8dee2aaSAndroid Build Coastguard Worker localPos -= cornerRadii; 719*c8dee2aaSAndroid Build Coastguard Worker localPos = float2(xs[cornerID], ys[cornerID]) + xAxis*localPos.x + yAxis*localPos.y; 720*c8dee2aaSAndroid Build Coastguard Worker } 721*c8dee2aaSAndroid Build Coastguard Worker 722*c8dee2aaSAndroid Build Coastguard Worker // Calculate edge distances and device space coordinate for the vertex 723*c8dee2aaSAndroid Build Coastguard Worker edgeDistances = dy*(xs - localPos.x) - dx*(ys - localPos.y) + edgeBias; 724*c8dee2aaSAndroid Build Coastguard Worker 725*c8dee2aaSAndroid Build Coastguard Worker // NOTE: This 3x3 inverse is different than just taking the 1st two columns of the 4x4 726*c8dee2aaSAndroid Build Coastguard Worker // inverse of the original SkM44 local-to-device matrix. We could calculate the 3x3 inverse 727*c8dee2aaSAndroid Build Coastguard Worker // and upload it, but it does not seem to be a bottleneck and saves on bandwidth to 728*c8dee2aaSAndroid Build Coastguard Worker // calculate it here instead. 729*c8dee2aaSAndroid Build Coastguard Worker float3x3 deviceToLocal = inverse(localToDevice); 730*c8dee2aaSAndroid Build Coastguard Worker float3 devPos = localToDevice * localPos.xy1; 731*c8dee2aaSAndroid Build Coastguard Worker jacobian = float4(deviceToLocal[0].xy - deviceToLocal[0].z*localPos, 732*c8dee2aaSAndroid Build Coastguard Worker deviceToLocal[1].xy - deviceToLocal[1].z*localPos); 733*c8dee2aaSAndroid Build Coastguard Worker 734*c8dee2aaSAndroid Build Coastguard Worker if (deviceSpaceDistances) { 735*c8dee2aaSAndroid Build Coastguard Worker // Apply the Jacobian in the vertex shader so any quadrilateral normals do not have to 736*c8dee2aaSAndroid Build Coastguard Worker // be passed to the fragment shader. However, it's important to use the Jacobian at a 737*c8dee2aaSAndroid Build Coastguard Worker // vertex on the edge, not the current vertex's Jacobian. 738*c8dee2aaSAndroid Build Coastguard Worker float4 gx = -dy*(deviceToLocal[0].x - deviceToLocal[0].z*xs) + 739*c8dee2aaSAndroid Build Coastguard Worker dx*(deviceToLocal[0].y - deviceToLocal[0].z*ys); 740*c8dee2aaSAndroid Build Coastguard Worker float4 gy = -dy*(deviceToLocal[1].x - deviceToLocal[1].z*xs) + 741*c8dee2aaSAndroid Build Coastguard Worker dx*(deviceToLocal[1].y - deviceToLocal[1].z*ys); 742*c8dee2aaSAndroid Build Coastguard Worker // NOTE: The gradient is missing a W term so edgeDistances must still be multiplied by 743*c8dee2aaSAndroid Build Coastguard Worker // 1/w in the fragment shader. The same goes for the encoded coverage scale. 744*c8dee2aaSAndroid Build Coastguard Worker edgeDistances *= inversesqrt(gx*gx + gy*gy); 745*c8dee2aaSAndroid Build Coastguard Worker 746*c8dee2aaSAndroid Build Coastguard Worker // Bias non-AA edge distances by device W so its coverage contribution is >= 1.0 747*c8dee2aaSAndroid Build Coastguard Worker edgeDistances += (1 - edgeAA)*abs(devPos.z); 748*c8dee2aaSAndroid Build Coastguard Worker 749*c8dee2aaSAndroid Build Coastguard Worker // Mixed edge AA shapes do not use subpixel scale+bias for coverage, since they tile 750*c8dee2aaSAndroid Build Coastguard Worker // to a large shape of unknown--but likely not subpixel--size. Triangles and quads do 751*c8dee2aaSAndroid Build Coastguard Worker // not use subpixel coverage since the scale+bias is not constant over the shape, but 752*c8dee2aaSAndroid Build Coastguard Worker // we can't evaluate per-fragment since we aren't passing down their arbitrary normals. 753*c8dee2aaSAndroid Build Coastguard Worker bool subpixelCoverage = edgeAA == float4(1.0) && 754*c8dee2aaSAndroid Build Coastguard Worker dot(abs(dx*dx.yzwx + dy*dy.yzwx), float4(1.0)) < kEpsilon; 755*c8dee2aaSAndroid Build Coastguard Worker if (subpixelCoverage) { 756*c8dee2aaSAndroid Build Coastguard Worker // Reconstructs the actual device-space width and height for all rectangle vertices. 757*c8dee2aaSAndroid Build Coastguard Worker float2 dim = edgeDistances.xy + edgeDistances.zw; 758*c8dee2aaSAndroid Build Coastguard Worker perPixelControl.y = 1.0 + min(min(dim.x, dim.y), abs(devPos.z)); 759*c8dee2aaSAndroid Build Coastguard Worker } else { 760*c8dee2aaSAndroid Build Coastguard Worker perPixelControl.y = 1.0 + abs(devPos.z); // standard 1px width pre W division. 761*c8dee2aaSAndroid Build Coastguard Worker } 762*c8dee2aaSAndroid Build Coastguard Worker } 763*c8dee2aaSAndroid Build Coastguard Worker 764*c8dee2aaSAndroid Build Coastguard Worker // Only outset for a vertex that is in front of the w=0 plane to avoid dealing with outset 765*c8dee2aaSAndroid Build Coastguard Worker // triangles rasterizing differently from the main triangles as w crosses 0. 766*c8dee2aaSAndroid Build Coastguard Worker if (normalScale > 0.0 && devPos.z > 0.0) { 767*c8dee2aaSAndroid Build Coastguard Worker // Note that when there's no perspective, the jacobian is equivalent to the normal 768*c8dee2aaSAndroid Build Coastguard Worker // matrix (inverse transpose), but produces correct results when there's perspective 769*c8dee2aaSAndroid Build Coastguard Worker // because it accounts for the position's influence on a line's projected direction. 770*c8dee2aaSAndroid Build Coastguard Worker float2x2 J = float2x2(jacobian); 771*c8dee2aaSAndroid Build Coastguard Worker 772*c8dee2aaSAndroid Build Coastguard Worker float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA.yzwx[cornerID]) * normal; 773*c8dee2aaSAndroid Build Coastguard Worker float2 nx = cornerAspectRatio.x * edgeAANormal.x * perp(-yAxis) * J; 774*c8dee2aaSAndroid Build Coastguard Worker float2 ny = cornerAspectRatio.y * edgeAANormal.y * perp( xAxis) * J; 775*c8dee2aaSAndroid Build Coastguard Worker 776*c8dee2aaSAndroid Build Coastguard Worker bool isMidVertex = all(notEqual(edgeAANormal, float2(0))); 777*c8dee2aaSAndroid Build Coastguard Worker if (joinScale == kMiterScale && isMidVertex) { 778*c8dee2aaSAndroid Build Coastguard Worker // Produce a bisecting vector in device space. 779*c8dee2aaSAndroid Build Coastguard Worker nx = normalize(nx); 780*c8dee2aaSAndroid Build Coastguard Worker ny = normalize(ny); 781*c8dee2aaSAndroid Build Coastguard Worker if (dot(nx, ny) < -0.8) { 782*c8dee2aaSAndroid Build Coastguard Worker // Normals are in nearly opposite directions, so adjust to avoid float error. 783*c8dee2aaSAndroid Build Coastguard Worker float s = sign(cross_length_2d(nx, ny)); 784*c8dee2aaSAndroid Build Coastguard Worker nx = s*perp(nx); 785*c8dee2aaSAndroid Build Coastguard Worker ny = -s*perp(ny); 786*c8dee2aaSAndroid Build Coastguard Worker } 787*c8dee2aaSAndroid Build Coastguard Worker } 788*c8dee2aaSAndroid Build Coastguard Worker // Adding the normal components together directly results in what we'd have 789*c8dee2aaSAndroid Build Coastguard Worker // calculated if we'd just transformed 'normal' in one go, assuming they weren't 790*c8dee2aaSAndroid Build Coastguard Worker // normalized in the if-block above. If they were normalized, the sum equals the 791*c8dee2aaSAndroid Build Coastguard Worker // bisector between the original nx and ny. 792*c8dee2aaSAndroid Build Coastguard Worker // 793*c8dee2aaSAndroid Build Coastguard Worker // We multiply by W so that after perspective division the new point is offset by the 794*c8dee2aaSAndroid Build Coastguard Worker // now-unit normal. 795*c8dee2aaSAndroid Build Coastguard Worker // NOTE: (nx + ny) can become the zero vector if the device outset is for an edge 796*c8dee2aaSAndroid Build Coastguard Worker // marked as non-AA. In this case normalize() could produce the zero vector or NaN. 797*c8dee2aaSAndroid Build Coastguard Worker // Until a counter-example is found, GPUs seem to discard triangles with NaN vertices, 798*c8dee2aaSAndroid Build Coastguard Worker // which has the same effect as outsetting by the zero vector with this mesh, so we 799*c8dee2aaSAndroid Build Coastguard Worker // don't bother guarding the normalize() (yet). 800*c8dee2aaSAndroid Build Coastguard Worker devPos.xy += devPos.z * normalize(nx + ny); 801*c8dee2aaSAndroid Build Coastguard Worker 802*c8dee2aaSAndroid Build Coastguard Worker // By construction these points are 1px away from the outer edge in device space. 803*c8dee2aaSAndroid Build Coastguard Worker if (deviceSpaceDistances) { 804*c8dee2aaSAndroid Build Coastguard Worker // Apply directly to edgeDistances to save work per pixel later on. 805*c8dee2aaSAndroid Build Coastguard Worker edgeDistances -= devPos.z; 806*c8dee2aaSAndroid Build Coastguard Worker } else { 807*c8dee2aaSAndroid Build Coastguard Worker // Otherwise store separately so edgeDistances can be used to reconstruct corner pos 808*c8dee2aaSAndroid Build Coastguard Worker perPixelControl.y = -devPos.z; 809*c8dee2aaSAndroid Build Coastguard Worker } 810*c8dee2aaSAndroid Build Coastguard Worker } else if (!deviceSpaceDistances) { 811*c8dee2aaSAndroid Build Coastguard Worker // Triangles are within the original shape so there's no additional outsetting to 812*c8dee2aaSAndroid Build Coastguard Worker // take into account for coverage calculations. 813*c8dee2aaSAndroid Build Coastguard Worker perPixelControl.y = 0.0; 814*c8dee2aaSAndroid Build Coastguard Worker } 815*c8dee2aaSAndroid Build Coastguard Worker 816*c8dee2aaSAndroid Build Coastguard Worker perPixelControl.x = (centerWeight != 0.0) 817*c8dee2aaSAndroid Build Coastguard Worker // A positive value signals that a pixel is trivially full coverage. 818*c8dee2aaSAndroid Build Coastguard Worker ? 1.0 819*c8dee2aaSAndroid Build Coastguard Worker // A negative value signals bidirectional coverage, and a zero value signals a solid 820*c8dee2aaSAndroid Build Coastguard Worker // interior with per-pixel coverage. 821*c8dee2aaSAndroid Build Coastguard Worker : bidirectionalCoverage ? -1.0 : 0.0; 822*c8dee2aaSAndroid Build Coastguard Worker 823*c8dee2aaSAndroid Build Coastguard Worker // The fragment shader operates in a canonical basis (x-axis = (1,0), y-axis = (0,1)). For 824*c8dee2aaSAndroid Build Coastguard Worker // stroked lines, incorporate their local orientation into the Jacobian to preserve this. 825*c8dee2aaSAndroid Build Coastguard Worker if (strokedLine) { 826*c8dee2aaSAndroid Build Coastguard Worker // The updated Jacobian is J' = B^-1 * J, where B is float2x2(xAxis, yAxis) for the 827*c8dee2aaSAndroid Build Coastguard Worker // top-left corner (so that B^-1 is constant over the whole shape). Since it's a line 828*c8dee2aaSAndroid Build Coastguard Worker // the basis was constructed to be orthonormal, det(B) = 1 and B^-1 is trivial. 829*c8dee2aaSAndroid Build Coastguard Worker // NOTE: float2x2 is column-major. 830*c8dee2aaSAndroid Build Coastguard Worker jacobian = float4(float2x2(dy[0], -dy[1], -dx[0], dx[1]) * float2x2(jacobian)); 831*c8dee2aaSAndroid Build Coastguard Worker } 832*c8dee2aaSAndroid Build Coastguard Worker 833*c8dee2aaSAndroid Build Coastguard Worker // Write out final results 834*c8dee2aaSAndroid Build Coastguard Worker stepLocalCoords = localPos; 835*c8dee2aaSAndroid Build Coastguard Worker return float4(devPos.xy, devPos.z*depth, devPos.z); 836*c8dee2aaSAndroid Build Coastguard Worker} 837*c8dee2aaSAndroid Build Coastguard Worker 838*c8dee2aaSAndroid Build Coastguard Workerfloat4 per_edge_aa_quad_vertex_fn(// Vertex Attributes 839*c8dee2aaSAndroid Build Coastguard Worker float2 normal, 840*c8dee2aaSAndroid Build Coastguard Worker // Instance Attributes 841*c8dee2aaSAndroid Build Coastguard Worker float4 edgeAA, 842*c8dee2aaSAndroid Build Coastguard Worker float4 xs, // ordered TL, TR, BR, BL 843*c8dee2aaSAndroid Build Coastguard Worker float4 ys, 844*c8dee2aaSAndroid Build Coastguard Worker float depth, 845*c8dee2aaSAndroid Build Coastguard Worker float3x3 localToDevice, 846*c8dee2aaSAndroid Build Coastguard Worker // Varyings 847*c8dee2aaSAndroid Build Coastguard Worker out float4 edgeDistances, 848*c8dee2aaSAndroid Build Coastguard Worker // Render Step 849*c8dee2aaSAndroid Build Coastguard Worker out float2 stepLocalCoords) { 850*c8dee2aaSAndroid Build Coastguard Worker const uint kCornerVertexCount = 4; // KEEP IN SYNC WITH C++'s 851*c8dee2aaSAndroid Build Coastguard Worker // PerEdgeAAQuadRenderStep::kCornerVertexCount 852*c8dee2aaSAndroid Build Coastguard Worker 853*c8dee2aaSAndroid Build Coastguard Worker const float kEpsilon = 0.00024; // SK_ScalarNearlyZero 854*c8dee2aaSAndroid Build Coastguard Worker 855*c8dee2aaSAndroid Build Coastguard Worker // Calculate the local edge vectors, ordered L, T, R, B starting from the bottom left point. 856*c8dee2aaSAndroid Build Coastguard Worker // For quadrilaterals these are not necessarily axis-aligned, but in all cases they orient 857*c8dee2aaSAndroid Build Coastguard Worker // the +X/+Y normalized vertex template for each corner. 858*c8dee2aaSAndroid Build Coastguard Worker float4 dx = xs - xs.wxyz; 859*c8dee2aaSAndroid Build Coastguard Worker float4 dy = ys - ys.wxyz; 860*c8dee2aaSAndroid Build Coastguard Worker float4 edgeSquaredLen = dx*dx + dy*dy; 861*c8dee2aaSAndroid Build Coastguard Worker 862*c8dee2aaSAndroid Build Coastguard Worker float4 edgeMask = sign(edgeSquaredLen); // 0 for zero-length edge, 1 for non-zero edge. 863*c8dee2aaSAndroid Build Coastguard Worker if (any(equal(edgeMask, float4(0.0)))) { 864*c8dee2aaSAndroid Build Coastguard Worker // Must clean up (dx,dy) depending on the empty edge configuration 865*c8dee2aaSAndroid Build Coastguard Worker if (all(equal(edgeMask, float4(0.0)))) { 866*c8dee2aaSAndroid Build Coastguard Worker // A point so use the canonical basis 867*c8dee2aaSAndroid Build Coastguard Worker dx = float4( 0.0, 1.0, 0.0, -1.0); 868*c8dee2aaSAndroid Build Coastguard Worker dy = float4(-1.0, 0.0, 1.0, 0.0); 869*c8dee2aaSAndroid Build Coastguard Worker edgeSquaredLen = float4(1.0); 870*c8dee2aaSAndroid Build Coastguard Worker } else { 871*c8dee2aaSAndroid Build Coastguard Worker // Triangles (3 non-zero edges) copy the adjacent edge. Otherwise it's a line so 872*c8dee2aaSAndroid Build Coastguard Worker // replace empty edges with the left-hand normal vector of the adjacent edge. 873*c8dee2aaSAndroid Build Coastguard Worker bool triangle = (edgeMask[0] + edgeMask[1] + edgeMask[2] + edgeMask[3]) > 2.5; 874*c8dee2aaSAndroid Build Coastguard Worker float4 edgeX = triangle ? dx.yzwx : dy.yzwx; 875*c8dee2aaSAndroid Build Coastguard Worker float4 edgeY = triangle ? dy.yzwx : -dx.yzwx; 876*c8dee2aaSAndroid Build Coastguard Worker 877*c8dee2aaSAndroid Build Coastguard Worker dx = mix(edgeX, dx, edgeMask); 878*c8dee2aaSAndroid Build Coastguard Worker dy = mix(edgeY, dy, edgeMask); 879*c8dee2aaSAndroid Build Coastguard Worker edgeSquaredLen = mix(edgeSquaredLen.yzwx, edgeSquaredLen, edgeMask); 880*c8dee2aaSAndroid Build Coastguard Worker edgeAA = mix(edgeAA.yzwx, edgeAA, edgeMask); 881*c8dee2aaSAndroid Build Coastguard Worker } 882*c8dee2aaSAndroid Build Coastguard Worker } 883*c8dee2aaSAndroid Build Coastguard Worker 884*c8dee2aaSAndroid Build Coastguard Worker float4 inverseEdgeLen = inversesqrt(edgeSquaredLen); 885*c8dee2aaSAndroid Build Coastguard Worker dx *= inverseEdgeLen; 886*c8dee2aaSAndroid Build Coastguard Worker dy *= inverseEdgeLen; 887*c8dee2aaSAndroid Build Coastguard Worker 888*c8dee2aaSAndroid Build Coastguard Worker // Calculate local coordinate for the vertex (relative to xAxis and yAxis at first). 889*c8dee2aaSAndroid Build Coastguard Worker uint cornerID = uint(sk_VertexID) / kCornerVertexCount; 890*c8dee2aaSAndroid Build Coastguard Worker float2 xAxis = -float2(dx.yzwx[cornerID], dy.yzwx[cornerID]); 891*c8dee2aaSAndroid Build Coastguard Worker float2 yAxis = float2(dx.xyzw[cornerID], dy.xyzw[cornerID]); 892*c8dee2aaSAndroid Build Coastguard Worker 893*c8dee2aaSAndroid Build Coastguard Worker // Vertex is outset from the base shape (and possibly with an additional AA outset later 894*c8dee2aaSAndroid Build Coastguard Worker // in device space). 895*c8dee2aaSAndroid Build Coastguard Worker float2 localPos = float2(xs[cornerID], ys[cornerID]); 896*c8dee2aaSAndroid Build Coastguard Worker 897*c8dee2aaSAndroid Build Coastguard Worker // Calculate edge distances and device space coordinate for the vertex 898*c8dee2aaSAndroid Build Coastguard Worker edgeDistances = dy*(xs - localPos.x) - dx*(ys - localPos.y); 899*c8dee2aaSAndroid Build Coastguard Worker 900*c8dee2aaSAndroid Build Coastguard Worker // NOTE: This 3x3 inverse is different than just taking the 1st two columns of the 4x4 901*c8dee2aaSAndroid Build Coastguard Worker // inverse of the original SkM44 local-to-device matrix. We could calculate the 3x3 inverse 902*c8dee2aaSAndroid Build Coastguard Worker // and upload it, but it does not seem to be a bottleneck and saves on bandwidth to 903*c8dee2aaSAndroid Build Coastguard Worker // calculate it here instead. 904*c8dee2aaSAndroid Build Coastguard Worker float3x3 deviceToLocal = inverse(localToDevice); 905*c8dee2aaSAndroid Build Coastguard Worker float3 devPos = localToDevice * localPos.xy1; 906*c8dee2aaSAndroid Build Coastguard Worker 907*c8dee2aaSAndroid Build Coastguard Worker // Apply the Jacobian in the vertex shader so any quadrilateral normals do not have to 908*c8dee2aaSAndroid Build Coastguard Worker // be passed to the fragment shader. However, it's important to use the Jacobian at a 909*c8dee2aaSAndroid Build Coastguard Worker // vertex on the edge, not the current vertex's Jacobian. 910*c8dee2aaSAndroid Build Coastguard Worker float4 gx = -dy*(deviceToLocal[0].x - deviceToLocal[0].z*xs) + 911*c8dee2aaSAndroid Build Coastguard Worker dx*(deviceToLocal[0].y - deviceToLocal[0].z*ys); 912*c8dee2aaSAndroid Build Coastguard Worker float4 gy = -dy*(deviceToLocal[1].x - deviceToLocal[1].z*xs) + 913*c8dee2aaSAndroid Build Coastguard Worker dx*(deviceToLocal[1].y - deviceToLocal[1].z*ys); 914*c8dee2aaSAndroid Build Coastguard Worker // NOTE: The gradient is missing a W term so edgeDistances must still be multiplied by 915*c8dee2aaSAndroid Build Coastguard Worker // 1/w in the fragment shader. The same goes for the encoded coverage scale. 916*c8dee2aaSAndroid Build Coastguard Worker edgeDistances *= inversesqrt(gx*gx + gy*gy); 917*c8dee2aaSAndroid Build Coastguard Worker 918*c8dee2aaSAndroid Build Coastguard Worker // Bias non-AA edge distances by device W so its coverage contribution is >= 1.0 919*c8dee2aaSAndroid Build Coastguard Worker // Add additional 1/2 bias here so we don't have to do so in the fragment shader. 920*c8dee2aaSAndroid Build Coastguard Worker edgeDistances += (1.5 - edgeAA)*abs(devPos.z); 921*c8dee2aaSAndroid Build Coastguard Worker 922*c8dee2aaSAndroid Build Coastguard Worker // Only outset for a vertex that is in front of the w=0 plane to avoid dealing with outset 923*c8dee2aaSAndroid Build Coastguard Worker // triangles rasterizing differently from the main triangles as w crosses 0. 924*c8dee2aaSAndroid Build Coastguard Worker if (any(notEqual(normal, float2(0.0))) && devPos.z > 0.0) { 925*c8dee2aaSAndroid Build Coastguard Worker // Note that when there's no perspective, the jacobian is equivalent to the normal 926*c8dee2aaSAndroid Build Coastguard Worker // matrix (inverse transpose), but produces correct results when there's perspective 927*c8dee2aaSAndroid Build Coastguard Worker // because it accounts for the position's influence on a line's projected direction. 928*c8dee2aaSAndroid Build Coastguard Worker float2x2 J = float2x2(deviceToLocal[0].xy - deviceToLocal[0].z*localPos, 929*c8dee2aaSAndroid Build Coastguard Worker deviceToLocal[1].xy - deviceToLocal[1].z*localPos); 930*c8dee2aaSAndroid Build Coastguard Worker 931*c8dee2aaSAndroid Build Coastguard Worker float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA.yzwx[cornerID]) * normal; 932*c8dee2aaSAndroid Build Coastguard Worker float2 nx = edgeAANormal.x * perp(-yAxis) * J; 933*c8dee2aaSAndroid Build Coastguard Worker float2 ny = edgeAANormal.y * perp( xAxis) * J; 934*c8dee2aaSAndroid Build Coastguard Worker 935*c8dee2aaSAndroid Build Coastguard Worker bool isMidVertex = all(notEqual(edgeAANormal, float2(0))); 936*c8dee2aaSAndroid Build Coastguard Worker if (isMidVertex) { 937*c8dee2aaSAndroid Build Coastguard Worker // Produce a bisecting vector in device space. 938*c8dee2aaSAndroid Build Coastguard Worker nx = normalize(nx); 939*c8dee2aaSAndroid Build Coastguard Worker ny = normalize(ny); 940*c8dee2aaSAndroid Build Coastguard Worker if (dot(nx, ny) < -0.8) { 941*c8dee2aaSAndroid Build Coastguard Worker // Normals are in nearly opposite directions, so adjust to avoid float error. 942*c8dee2aaSAndroid Build Coastguard Worker float s = sign(cross_length_2d(nx, ny)); 943*c8dee2aaSAndroid Build Coastguard Worker nx = s*perp(nx); 944*c8dee2aaSAndroid Build Coastguard Worker ny = -s*perp(ny); 945*c8dee2aaSAndroid Build Coastguard Worker } 946*c8dee2aaSAndroid Build Coastguard Worker } 947*c8dee2aaSAndroid Build Coastguard Worker // Adding the normal components together directly results in what we'd have 948*c8dee2aaSAndroid Build Coastguard Worker // calculated if we'd just transformed 'normal' in one go, assuming they weren't 949*c8dee2aaSAndroid Build Coastguard Worker // normalized in the if-block above. If they were normalized, the sum equals the 950*c8dee2aaSAndroid Build Coastguard Worker // bisector between the original nx and ny. 951*c8dee2aaSAndroid Build Coastguard Worker // 952*c8dee2aaSAndroid Build Coastguard Worker // We multiply by W so that after perspective division the new point is offset by the 953*c8dee2aaSAndroid Build Coastguard Worker // now-unit normal. 954*c8dee2aaSAndroid Build Coastguard Worker // NOTE: (nx + ny) can become the zero vector if the device outset is for an edge 955*c8dee2aaSAndroid Build Coastguard Worker // marked as non-AA. In this case normalize() could produce the zero vector or NaN. 956*c8dee2aaSAndroid Build Coastguard Worker // Until a counter-example is found, GPUs seem to discard triangles with NaN vertices, 957*c8dee2aaSAndroid Build Coastguard Worker // which has the same effect as outsetting by the zero vector with this mesh, so we 958*c8dee2aaSAndroid Build Coastguard Worker // don't bother guarding the normalize() (yet). 959*c8dee2aaSAndroid Build Coastguard Worker devPos.xy += devPos.z * normalize(nx + ny); 960*c8dee2aaSAndroid Build Coastguard Worker 961*c8dee2aaSAndroid Build Coastguard Worker // By construction these points are 1px away from the outer edge in device space. 962*c8dee2aaSAndroid Build Coastguard Worker // Apply directly to edgeDistances to save work per pixel later on. 963*c8dee2aaSAndroid Build Coastguard Worker edgeDistances -= devPos.z; 964*c8dee2aaSAndroid Build Coastguard Worker } 965*c8dee2aaSAndroid Build Coastguard Worker 966*c8dee2aaSAndroid Build Coastguard Worker // Write out final results 967*c8dee2aaSAndroid Build Coastguard Worker stepLocalCoords = localPos; 968*c8dee2aaSAndroid Build Coastguard Worker return float4(devPos.xy, devPos.z*depth, devPos.z); 969*c8dee2aaSAndroid Build Coastguard Worker} 970*c8dee2aaSAndroid Build Coastguard Worker 971*c8dee2aaSAndroid Build Coastguard Workerfloat4 circular_arc_vertex_fn(float3 position, 972*c8dee2aaSAndroid Build Coastguard Worker // Instance Attributes 973*c8dee2aaSAndroid Build Coastguard Worker float4 centerScales, 974*c8dee2aaSAndroid Build Coastguard Worker float3 radiiAndFlags, 975*c8dee2aaSAndroid Build Coastguard Worker float3 geoClipPlane, 976*c8dee2aaSAndroid Build Coastguard Worker float3 fragClipPlane0, 977*c8dee2aaSAndroid Build Coastguard Worker float3 fragClipPlane1, 978*c8dee2aaSAndroid Build Coastguard Worker float4 inRoundCapPos, 979*c8dee2aaSAndroid Build Coastguard Worker float depth, 980*c8dee2aaSAndroid Build Coastguard Worker float3x3 localToDevice, 981*c8dee2aaSAndroid Build Coastguard Worker // Varyings 982*c8dee2aaSAndroid Build Coastguard Worker out float4 circleEdge, 983*c8dee2aaSAndroid Build Coastguard Worker out float3 clipPlane, 984*c8dee2aaSAndroid Build Coastguard Worker out float3 isectPlane, 985*c8dee2aaSAndroid Build Coastguard Worker out float3 unionPlane, 986*c8dee2aaSAndroid Build Coastguard Worker out float roundCapRadius, 987*c8dee2aaSAndroid Build Coastguard Worker out float4 roundCapPos, 988*c8dee2aaSAndroid Build Coastguard Worker // Render Step 989*c8dee2aaSAndroid Build Coastguard Worker out float2 stepLocalCoords) { 990*c8dee2aaSAndroid Build Coastguard Worker // TODO: clip offset against clip planes 991*c8dee2aaSAndroid Build Coastguard Worker float2 localCenter = centerScales.xy; 992*c8dee2aaSAndroid Build Coastguard Worker float2 localPos = localCenter; 993*c8dee2aaSAndroid Build Coastguard Worker // do geometric clip in normalized space 994*c8dee2aaSAndroid Build Coastguard Worker float dist = min(dot(position.xy, geoClipPlane.xy) + geoClipPlane.z, 0); 995*c8dee2aaSAndroid Build Coastguard Worker position.xy -= geoClipPlane.xy * dist; 996*c8dee2aaSAndroid Build Coastguard Worker // Get the new length to use below for scaling the offset 997*c8dee2aaSAndroid Build Coastguard Worker // (origLength is the initial length of position.xy). 998*c8dee2aaSAndroid Build Coastguard Worker float offsetScale = length(position.xy); 999*c8dee2aaSAndroid Build Coastguard Worker 1000*c8dee2aaSAndroid Build Coastguard Worker // scale and translate to local space 1001*c8dee2aaSAndroid Build Coastguard Worker if (position.z > 0) { 1002*c8dee2aaSAndroid Build Coastguard Worker localPos += position.xy * centerScales.z; 1003*c8dee2aaSAndroid Build Coastguard Worker } else { 1004*c8dee2aaSAndroid Build Coastguard Worker localPos += position.xy * centerScales.w; 1005*c8dee2aaSAndroid Build Coastguard Worker } 1006*c8dee2aaSAndroid Build Coastguard Worker 1007*c8dee2aaSAndroid Build Coastguard Worker float3 devPos = localToDevice * localPos.xy1; 1008*c8dee2aaSAndroid Build Coastguard Worker float3 devCenter = localToDevice * localCenter.xy1; 1009*c8dee2aaSAndroid Build Coastguard Worker float2 offset = devPos.xy - devCenter.xy; 1010*c8dee2aaSAndroid Build Coastguard Worker // offset for AA and correct length of offset 1011*c8dee2aaSAndroid Build Coastguard Worker if (offset != float2(0)) { 1012*c8dee2aaSAndroid Build Coastguard Worker offset = normalize(offset); 1013*c8dee2aaSAndroid Build Coastguard Worker devPos.xy += position.z*offset; 1014*c8dee2aaSAndroid Build Coastguard Worker if (position.z > 0) { 1015*c8dee2aaSAndroid Build Coastguard Worker // Scale using distance from center of unit octagon to the vertex 1016*c8dee2aaSAndroid Build Coastguard Worker // Because of geometry clipping we need to scale by 1.0823922*newLength/origLength 1017*c8dee2aaSAndroid Build Coastguard Worker // But the original length is 1.0823922 so the offsetScale is just newLength 1018*c8dee2aaSAndroid Build Coastguard Worker offset *= offsetScale; 1019*c8dee2aaSAndroid Build Coastguard Worker } else { 1020*c8dee2aaSAndroid Build Coastguard Worker // Because of geometry clipping we need to scale by innerRadius*newLength/origLength 1021*c8dee2aaSAndroid Build Coastguard Worker // But the original length is 1 so this is just innerRadius*newLength 1022*c8dee2aaSAndroid Build Coastguard Worker offset *= offsetScale*radiiAndFlags.y; 1023*c8dee2aaSAndroid Build Coastguard Worker } 1024*c8dee2aaSAndroid Build Coastguard Worker } 1025*c8dee2aaSAndroid Build Coastguard Worker 1026*c8dee2aaSAndroid Build Coastguard Worker circleEdge = float4(offset, radiiAndFlags.xy); 1027*c8dee2aaSAndroid Build Coastguard Worker if (radiiAndFlags.z > 0) { 1028*c8dee2aaSAndroid Build Coastguard Worker clipPlane = fragClipPlane0; 1029*c8dee2aaSAndroid Build Coastguard Worker isectPlane = fragClipPlane1; 1030*c8dee2aaSAndroid Build Coastguard Worker unionPlane = float3(0, 0, 0); 1031*c8dee2aaSAndroid Build Coastguard Worker } else { 1032*c8dee2aaSAndroid Build Coastguard Worker clipPlane = fragClipPlane0; 1033*c8dee2aaSAndroid Build Coastguard Worker isectPlane = float3(0, 0, 1); 1034*c8dee2aaSAndroid Build Coastguard Worker unionPlane = fragClipPlane1; 1035*c8dee2aaSAndroid Build Coastguard Worker } 1036*c8dee2aaSAndroid Build Coastguard Worker if (abs(radiiAndFlags.z) > 1) { 1037*c8dee2aaSAndroid Build Coastguard Worker // This is the cap radius in normalized space where the outer radius is 1 and 1038*c8dee2aaSAndroid Build Coastguard Worker // radii.y is the normalized inner radius. 1039*c8dee2aaSAndroid Build Coastguard Worker roundCapRadius = (1.0 - radiiAndFlags.y) / 2.0; 1040*c8dee2aaSAndroid Build Coastguard Worker } else { 1041*c8dee2aaSAndroid Build Coastguard Worker roundCapRadius = 0; 1042*c8dee2aaSAndroid Build Coastguard Worker } 1043*c8dee2aaSAndroid Build Coastguard Worker roundCapPos = inRoundCapPos; 1044*c8dee2aaSAndroid Build Coastguard Worker stepLocalCoords = localPos; 1045*c8dee2aaSAndroid Build Coastguard Worker 1046*c8dee2aaSAndroid Build Coastguard Worker // We assume no perspective 1047*c8dee2aaSAndroid Build Coastguard Worker return float4(devPos.xy, depth, 1); 1048*c8dee2aaSAndroid Build Coastguard Worker} 1049*c8dee2aaSAndroid Build Coastguard Worker 1050*c8dee2aaSAndroid Build Coastguard Workerfloat4 text_vertex_fn(float2 baseCoords, 1051*c8dee2aaSAndroid Build Coastguard Worker // Uniforms 1052*c8dee2aaSAndroid Build Coastguard Worker float4x4 subRunDeviceMatrix, 1053*c8dee2aaSAndroid Build Coastguard Worker float4x4 deviceToLocal, 1054*c8dee2aaSAndroid Build Coastguard Worker float2 atlasSizeInv, 1055*c8dee2aaSAndroid Build Coastguard Worker // Instance Attributes 1056*c8dee2aaSAndroid Build Coastguard Worker float2 size, 1057*c8dee2aaSAndroid Build Coastguard Worker float2 uvPos, 1058*c8dee2aaSAndroid Build Coastguard Worker float2 xyPos, 1059*c8dee2aaSAndroid Build Coastguard Worker float strikeToSourceScale, 1060*c8dee2aaSAndroid Build Coastguard Worker float depth, 1061*c8dee2aaSAndroid Build Coastguard Worker // Varyings 1062*c8dee2aaSAndroid Build Coastguard Worker out float2 textureCoords, 1063*c8dee2aaSAndroid Build Coastguard Worker out float2 unormTexCoords, // used as varying in SDFText 1064*c8dee2aaSAndroid Build Coastguard Worker // Render Step 1065*c8dee2aaSAndroid Build Coastguard Worker out float2 stepLocalCoords) { 1066*c8dee2aaSAndroid Build Coastguard Worker baseCoords.xy *= float2(size); 1067*c8dee2aaSAndroid Build Coastguard Worker 1068*c8dee2aaSAndroid Build Coastguard Worker // Sub runs have a decomposed transform and are sometimes already transformed into device 1069*c8dee2aaSAndroid Build Coastguard Worker // space, in which `subRunCoords` represents the bounds projected to device space without 1070*c8dee2aaSAndroid Build Coastguard Worker // the local-to-device translation and `subRunDeviceMatrix` contains the translation. 1071*c8dee2aaSAndroid Build Coastguard Worker float2 subRunCoords = strikeToSourceScale * baseCoords + xyPos; 1072*c8dee2aaSAndroid Build Coastguard Worker float4 position = subRunDeviceMatrix * subRunCoords.xy01; 1073*c8dee2aaSAndroid Build Coastguard Worker 1074*c8dee2aaSAndroid Build Coastguard Worker // Calculate the local coords used for shading. 1075*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/246963258): This is incorrect if the transform has perspective, which would 1076*c8dee2aaSAndroid Build Coastguard Worker // require a division + a valid z coordinate (which is currently set to 0). 1077*c8dee2aaSAndroid Build Coastguard Worker stepLocalCoords = (deviceToLocal * position).xy; 1078*c8dee2aaSAndroid Build Coastguard Worker 1079*c8dee2aaSAndroid Build Coastguard Worker unormTexCoords = baseCoords + uvPos; 1080*c8dee2aaSAndroid Build Coastguard Worker textureCoords = unormTexCoords * atlasSizeInv; 1081*c8dee2aaSAndroid Build Coastguard Worker 1082*c8dee2aaSAndroid Build Coastguard Worker return float4(position.xy, depth*position.w, position.w); 1083*c8dee2aaSAndroid Build Coastguard Worker} 1084*c8dee2aaSAndroid Build Coastguard Worker 1085*c8dee2aaSAndroid Build Coastguard Workerfloat4 coverage_mask_vertex_fn(float2 quadCoords, 1086*c8dee2aaSAndroid Build Coastguard Worker // Uniforms 1087*c8dee2aaSAndroid Build Coastguard Worker float3x3 maskToDeviceRemainder, 1088*c8dee2aaSAndroid Build Coastguard Worker // Instance Attributes 1089*c8dee2aaSAndroid Build Coastguard Worker float4 drawBounds, 1090*c8dee2aaSAndroid Build Coastguard Worker float4 maskBoundsIn, 1091*c8dee2aaSAndroid Build Coastguard Worker float2 deviceOrigin, 1092*c8dee2aaSAndroid Build Coastguard Worker float depth, 1093*c8dee2aaSAndroid Build Coastguard Worker float3x3 deviceToLocal, 1094*c8dee2aaSAndroid Build Coastguard Worker // Varyings 1095*c8dee2aaSAndroid Build Coastguard Worker out float4 maskBounds, 1096*c8dee2aaSAndroid Build Coastguard Worker out float2 textureCoords, 1097*c8dee2aaSAndroid Build Coastguard Worker out half invert, 1098*c8dee2aaSAndroid Build Coastguard Worker // Render Step 1099*c8dee2aaSAndroid Build Coastguard Worker out float2 stepLocalCoords) { 1100*c8dee2aaSAndroid Build Coastguard Worker // An atlas shape is an axis-aligned rectangle tessellated as a triangle strip. 1101*c8dee2aaSAndroid Build Coastguard Worker // 1102*c8dee2aaSAndroid Build Coastguard Worker // The bounds coordinates are in an intermediate space, pixel-aligned with the mask texture 1103*c8dee2aaSAndroid Build Coastguard Worker // that's sampled in the fragment shader. The coords must be transformed by both 1104*c8dee2aaSAndroid Build Coastguard Worker // maskToDeviceRemainder and translated by deviceOrigin to get device coords. 1105*c8dee2aaSAndroid Build Coastguard Worker textureCoords = mix(drawBounds.xy, drawBounds.zw, quadCoords); 1106*c8dee2aaSAndroid Build Coastguard Worker float3 drawCoords = maskToDeviceRemainder*((textureCoords + deviceOrigin).xy1); 1107*c8dee2aaSAndroid Build Coastguard Worker 1108*c8dee2aaSAndroid Build Coastguard Worker // Local coordinates used for shading are derived from the final device coords and the inverse 1109*c8dee2aaSAndroid Build Coastguard Worker // of the original local-to-device matrix. 1110*c8dee2aaSAndroid Build Coastguard Worker float3 localCoords = deviceToLocal * drawCoords; 1111*c8dee2aaSAndroid Build Coastguard Worker // TODO: Support float3 local coordinates if the matrix has perspective so that W is 1112*c8dee2aaSAndroid Build Coastguard Worker // interpolated correctly to the fragment shader. 1113*c8dee2aaSAndroid Build Coastguard Worker stepLocalCoords = localCoords.xy / localCoords.z; 1114*c8dee2aaSAndroid Build Coastguard Worker 1115*c8dee2aaSAndroid Build Coastguard Worker // For an inverse fill, `textureCoords` will get clamped to `maskBounds` and the edge pixels 1116*c8dee2aaSAndroid Build Coastguard Worker // will always land on a 0-coverage border pixel assuming the atlas was prepared with 1px 1117*c8dee2aaSAndroid Build Coastguard Worker // padding around each mask entry. This includes inverse fills where the mask was fully clipped 1118*c8dee2aaSAndroid Build Coastguard Worker // out, since then maskBounds.RBLT == (0,0,-1,-1) and we sample the top-left-most pixel of the 1119*c8dee2aaSAndroid Build Coastguard Worker // atlas, which is guaranteed to be transparent. 1120*c8dee2aaSAndroid Build Coastguard Worker if (all(lessThanEqual(maskBoundsIn.LT, maskBoundsIn.RB))) { 1121*c8dee2aaSAndroid Build Coastguard Worker // Regular fill 1122*c8dee2aaSAndroid Build Coastguard Worker maskBounds = maskBoundsIn; 1123*c8dee2aaSAndroid Build Coastguard Worker invert = 0; 1124*c8dee2aaSAndroid Build Coastguard Worker } else { 1125*c8dee2aaSAndroid Build Coastguard Worker // Re-arrange the mask bounds to sorted order for texture clamping in the fragment shader 1126*c8dee2aaSAndroid Build Coastguard Worker maskBounds = maskBoundsIn.RBLT; 1127*c8dee2aaSAndroid Build Coastguard Worker invert = 1; 1128*c8dee2aaSAndroid Build Coastguard Worker } 1129*c8dee2aaSAndroid Build Coastguard Worker 1130*c8dee2aaSAndroid Build Coastguard Worker return float4(drawCoords.xy, depth*drawCoords.z, drawCoords.z); 1131*c8dee2aaSAndroid Build Coastguard Worker} 1132*c8dee2aaSAndroid Build Coastguard Worker 1133*c8dee2aaSAndroid Build Coastguard Workerfloat4 cover_bounds_vertex_fn(float2 corner, 1134*c8dee2aaSAndroid Build Coastguard Worker float4 bounds, 1135*c8dee2aaSAndroid Build Coastguard Worker float depth, 1136*c8dee2aaSAndroid Build Coastguard Worker float3x3 matrix, 1137*c8dee2aaSAndroid Build Coastguard Worker out float2 stepLocalCoords) { 1138*c8dee2aaSAndroid Build Coastguard Worker if (all(lessThanEqual(bounds.LT, bounds.RB))) { 1139*c8dee2aaSAndroid Build Coastguard Worker // A regular fill 1140*c8dee2aaSAndroid Build Coastguard Worker corner = mix(bounds.LT, bounds.RB, corner); 1141*c8dee2aaSAndroid Build Coastguard Worker float3 devCorner = matrix * corner.xy1; 1142*c8dee2aaSAndroid Build Coastguard Worker stepLocalCoords = corner; 1143*c8dee2aaSAndroid Build Coastguard Worker return float4(devCorner.xy, depth*devCorner.z, devCorner.z); 1144*c8dee2aaSAndroid Build Coastguard Worker } else { 1145*c8dee2aaSAndroid Build Coastguard Worker // An inverse fill 1146*c8dee2aaSAndroid Build Coastguard Worker corner = mix(bounds.RB, bounds.LT, corner); 1147*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/351923375): Get the 3x3 inverse of the local-to-device transform from the CPU 1148*c8dee2aaSAndroid Build Coastguard Worker // if it can be computed fast enough on the CPU from the cached 4x4 inverse. 1149*c8dee2aaSAndroid Build Coastguard Worker float3 localCoords = inverse(matrix) * corner.xy1; 1150*c8dee2aaSAndroid Build Coastguard Worker // Dividing the inverse mapped local coords by its homogenous coordinate reconstructs the 1151*c8dee2aaSAndroid Build Coastguard Worker // original local coords. 1152*c8dee2aaSAndroid Build Coastguard Worker float invW = 1.0 / localCoords.z; 1153*c8dee2aaSAndroid Build Coastguard Worker stepLocalCoords = localCoords.xy * invW; 1154*c8dee2aaSAndroid Build Coastguard Worker 1155*c8dee2aaSAndroid Build Coastguard Worker // 1/W also happens to be equal to (matrix*stepLocalCoords.xy1).z, which is the device-space 1156*c8dee2aaSAndroid Build Coastguard Worker // homogenous coordinate we want perspective interpolation to respect. We multiply the 1157*c8dee2aaSAndroid Build Coastguard Worker // output position by 1/W and set the output position's homogenous coord to that same 1/W 1158*c8dee2aaSAndroid Build Coastguard Worker // which ensures the projected vertices are still the device-space corners, but 1159*c8dee2aaSAndroid Build Coastguard Worker // stepLocalCoords will be correctly perspective interpolated by HW. 1160*c8dee2aaSAndroid Build Coastguard Worker return float4(corner*invW, depth*invW, invW); 1161*c8dee2aaSAndroid Build Coastguard Worker } 1162*c8dee2aaSAndroid Build Coastguard Worker} 1163