1/* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "GrCCPRCubicProcessor.h" 9 10#include "glsl/GrGLSLFragmentShaderBuilder.h" 11#include "glsl/GrGLSLGeometryShaderBuilder.h" 12#include "glsl/GrGLSLVertexShaderBuilder.h" 13 14void GrCCPRCubicProcessor::onEmitVertexShader(const GrCCPRCoverageProcessor& proc, 15 GrGLSLVertexBuilder* v, 16 const TexelBufferHandle& pointsBuffer, 17 const char* atlasOffset, const char* rtAdjust, 18 GrGPArgs* gpArgs) const { 19 float inset = 1 - kAABloatRadius; 20#ifdef SK_DEBUG 21 if (proc.debugVisualizations()) { 22 inset *= GrCCPRCoverageProcessor::kDebugBloat; 23 } 24#endif 25 26 // Fetch all 4 cubic bezier points. 27 v->codeAppendf("ivec4 indices = ivec4(%s.y, %s.x, %s.x + 1, %s.y + 1);", 28 proc.instanceAttrib(), proc.instanceAttrib(), proc.instanceAttrib(), 29 proc.instanceAttrib()); 30 v->codeAppend ("highp mat4x2 bezierpts = mat4x2("); 31 v->appendTexelFetch(pointsBuffer, "indices[sk_VertexID]"); 32 v->codeAppend (".xy, "); 33 v->appendTexelFetch(pointsBuffer, "indices[(sk_VertexID + 1) % 4]"); 34 v->codeAppend (".xy, "); 35 v->appendTexelFetch(pointsBuffer, "indices[(sk_VertexID + 2) % 4]"); 36 v->codeAppend (".xy, "); 37 v->appendTexelFetch(pointsBuffer, "indices[(sk_VertexID + 3) % 4]"); 38 v->codeAppend (".xy);"); 39 40 // Find the corner of the inset geometry that corresponds to this bezier vertex (bezierpts[0]). 41 v->codeAppend ("highp mat2 N = mat2(bezierpts[3].y - bezierpts[0].y, " 42 "bezierpts[0].x - bezierpts[3].x, " 43 "bezierpts[1].y - bezierpts[0].y, " 44 "bezierpts[0].x - bezierpts[1].x);"); 45 v->codeAppend ("highp mat2 P = mat2(bezierpts[3], bezierpts[1]);"); 46 v->codeAppend ("if (abs(determinant(N)) < 2) {"); // Area of [pts[3], pts[0], pts[1]] < 1px. 47 // The inset corner doesn't exist because we are effectively colinear with 48 // both neighbor vertices. Just duplicate a neighbor's inset corner. 49 v->codeAppend ( "int smallidx = (dot(N[0], N[0]) > dot(N[1], N[1])) ? 1 : 0;"); 50 v->codeAppend ( "N[smallidx] = vec2(bezierpts[2].y - bezierpts[3 - smallidx * 2].y, " 51 "bezierpts[3 - smallidx * 2].x - bezierpts[2].x);"); 52 v->codeAppend ( "P[smallidx] = bezierpts[2];"); 53 v->codeAppend ("}"); 54 v->codeAppend ("N[0] *= sign(dot(N[0], P[1] - P[0]));"); 55 v->codeAppend ("N[1] *= sign(dot(N[1], P[0] - P[1]));"); 56 57 v->codeAppendf("highp vec2 K = vec2(dot(N[0], P[0] + %f * sign(N[0])), " 58 "dot(N[1], P[1] + %f * sign(N[1])));", inset, inset); 59 v->codeAppendf("%s.xy = K * inverse(N) + %s;", fInset.vsOut(), atlasOffset); 60 v->codeAppendf("%s.xy = %s.xy * %s.xz + %s.yw;", 61 fInset.vsOut(), fInset.vsOut(), rtAdjust, rtAdjust); 62 63 // The z component tells the gemetry shader how "sharp" this corner is. 64 v->codeAppendf("%s.z = determinant(N) * sign(%s.x) * sign(%s.z);", 65 fInset.vsOut(), rtAdjust, rtAdjust); 66 67 // Fetch one of the t,s klm root values for the geometry shader. 68 v->codeAppendf("%s = ", fTS.vsOut()); 69 v->appendTexelFetch(pointsBuffer, 70 SkStringPrintf("%s.x + 2 + sk_VertexID/2", proc.instanceAttrib()).c_str()); 71 v->codeAppend ("[sk_VertexID % 2];"); 72 73 // Emit the vertex position. 74 v->codeAppendf("highp vec2 self = bezierpts[0] + %s;", atlasOffset); 75 gpArgs->fPositionVar.set(kVec2f_GrSLType, "self"); 76} 77 78void GrCCPRCubicProcessor::emitWind(GrGLSLGeometryBuilder* g, const char* rtAdjust, 79 const char* outputWind) const { 80 // We will define bezierpts in onEmitGeometryShader. 81 g->codeAppend ("highp float area_times_2 = determinant(mat3(1, bezierpts[0], " 82 "1, bezierpts[2], " 83 "0, bezierpts[3] - bezierpts[1]));"); 84 // Drop curves that are nearly flat. The KLM math becomes unstable in this case. 85 g->codeAppendf("if (2 * abs(area_times_2) < length((bezierpts[3] - bezierpts[0]) * %s.zx)) {", 86 rtAdjust); 87#ifndef SK_BUILD_FOR_MAC 88 g->codeAppend ( "return;"); 89#else 90 // Returning from this geometry shader makes Mac very unhappy. Instead we make wind 0. 91 g->codeAppend ( "area_times_2 = 0;"); 92#endif 93 g->codeAppend ("}"); 94 g->codeAppendf("%s = sign(area_times_2);", outputWind); 95} 96 97void GrCCPRCubicProcessor::onEmitGeometryShader(GrGLSLGeometryBuilder* g, const char* emitVertexFn, 98 const char* wind, const char* rtAdjust) const { 99 // Prepend bezierpts at the start of the shader. 100 g->codePrependf("highp mat4x2 bezierpts = mat4x2(sk_in[0].gl_Position.xy, " 101 "sk_in[1].gl_Position.xy, " 102 "sk_in[2].gl_Position.xy, " 103 "sk_in[3].gl_Position.xy);"); 104 105 // Evaluate the cubic at t=.5 for an approximate midpoint. 106 g->codeAppendf("highp vec2 midpoint = bezierpts * vec4(.125, .375, .375, .125);"); 107 108 // Finish finding the inset geometry we started in the vertex shader. The z component tells us 109 // how "sharp" an inset corner is. And the vertex shader already skips one corner if it is 110 // colinear with its neighbors. So at this point, if a corner is flat, it means the inset 111 // geometry is all empty (it should never be non-convex because the curve gets chopped into 112 // convex segments ahead of time). 113 g->codeAppendf("bool isempty = " 114 "any(lessThan(vec4(%s[0].z, %s[1].z, %s[2].z, %s[3].z) * %s, vec4(2)));", 115 fInset.gsIn(), fInset.gsIn(), fInset.gsIn(), fInset.gsIn(), wind); 116 g->codeAppendf("highp vec2 inset[4];"); 117 g->codeAppend ("for (int i = 0; i < 4; ++i) {"); 118 g->codeAppendf( "inset[i] = isempty ? midpoint : %s[i].xy;", fInset.gsIn()); 119 g->codeAppend ("}"); 120 121 // We determine crossover and/or degeneracy by how many inset edges run the opposite direction 122 // of their corresponding bezier edge. If there is one backwards edge, the inset geometry is 123 // actually triangle with a vertex at the crossover point. If there are >1 backwards edges, the 124 // inset geometry doesn't exist (i.e. the bezier quadrilateral isn't large enough) and we 125 // degenerate to the midpoint. 126 g->codeAppend ("lowp float backwards[4];"); 127 g->codeAppend ("lowp int numbackwards = 0;"); 128 g->codeAppend ("for (int i = 0; i < 4; ++i) {"); 129 g->codeAppend ( "lowp int j = (i + 1) % 4;"); 130 g->codeAppendf( "highp vec2 inner = inset[j] - inset[i];"); 131 g->codeAppendf( "highp vec2 outer = sk_in[j].gl_Position.xy - sk_in[i].gl_Position.xy;"); 132 g->codeAppendf( "backwards[i] = sign(dot(outer, inner));"); 133 g->codeAppendf( "numbackwards += backwards[i] < 0 ? 1 : 0;"); 134 g->codeAppend ("}"); 135 136 // Find the crossover point. If there actually isn't one, this math is meaningless and will get 137 // dropped on the floor later. 138 g->codeAppend ("lowp int x = (backwards[0] != backwards[2]) ? 1 : 0;"); 139 g->codeAppend ("lowp int x3 = (x + 3) % 4;"); 140 g->codeAppend ("highp mat2 X = mat2(inset[x].y - inset[x+1].y, " 141 "inset[x+1].x - inset[x].x, " 142 "inset[x+2].y - inset[x3].y, " 143 "inset[x3].x - inset[x+2].x);"); 144 g->codeAppend ("highp vec2 KK = vec2(dot(X[0], inset[x]), dot(X[1], inset[x+2]));"); 145 g->codeAppend ("highp vec2 crossoverpoint = KK * inverse(X);"); 146 147 // Determine what point backwards edges should collapse into. If there is one backwards edge, 148 // it should collapse to the crossover point. If >1, they should all collapse to the midpoint. 149 g->codeAppend ("highp vec2 collapsepoint = numbackwards == 1 ? crossoverpoint : midpoint;"); 150 151 // Collapse backwards egdes to the "collapse" point. 152 g->codeAppend ("for (int i = 0; i < 4; ++i) {"); 153 g->codeAppend ( "if (backwards[i] < 0) {"); 154 g->codeAppend ( "inset[i] = inset[(i + 1) % 4] = collapsepoint;"); 155 g->codeAppend ( "}"); 156 g->codeAppend ("}"); 157 158 // Calculate the KLM matrix. 159 g->declareGlobal(fKLMMatrix); 160 g->codeAppend ("highp vec4 K, L, M;"); 161 if (Type::kSerpentine == fType) { 162 g->codeAppend ("highp vec2 l,m;"); 163 g->codeAppendf("l.ts = vec2(%s[0], %s[1]);", fTS.gsIn(), fTS.gsIn()); 164 g->codeAppendf("m.ts = vec2(%s[2], %s[3]);", fTS.gsIn(), fTS.gsIn()); 165 g->codeAppend ("K = vec4(0, l.s * m.s, -l.t * m.s - m.t * l.s, l.t * m.t);"); 166 g->codeAppend ("L = vec4(-1,3,-3,1) * l.ssst * l.sstt * l.sttt;"); 167 g->codeAppend ("M = vec4(-1,3,-3,1) * m.ssst * m.sstt * m.sttt;"); 168 169 } else { 170 g->codeAppend ("highp vec2 d,e;"); 171 g->codeAppendf("d.ts = vec2(%s[0], %s[1]);", fTS.gsIn(), fTS.gsIn()); 172 g->codeAppendf("e.ts = vec2(%s[2], %s[3]);", fTS.gsIn(), fTS.gsIn()); 173 g->codeAppend ("highp vec4 dxe = vec4(d.s * e.s, d.s * e.t, d.t * e.s, d.t * e.t);"); 174 g->codeAppend ("K = vec4(0, dxe.x, -dxe.y - dxe.z, dxe.w);"); 175 g->codeAppend ("L = vec4(-1,1,-1,1) * d.sstt * (dxe.xyzw + vec4(0, 2*dxe.zy, 0));"); 176 g->codeAppend ("M = vec4(-1,1,-1,1) * e.sstt * (dxe.xzyw + vec4(0, 2*dxe.yz, 0));"); 177 } 178 179 g->codeAppend ("highp mat2x4 C = mat4(-1, 3, -3, 1, " 180 " 3, -6, 3, 0, " 181 "-3, 3, 0, 0, " 182 " 1, 0, 0, 0) * transpose(bezierpts);"); 183 184 g->codeAppend ("highp vec2 absdet = abs(C[0].xx * C[1].zy - C[1].xx * C[0].zy);"); 185 g->codeAppend ("lowp int middlerow = absdet[0] > absdet[1] ? 2 : 1;"); 186 187 g->codeAppend ("highp mat3 CI = inverse(mat3(C[0][0], C[0][middlerow], C[0][3], " 188 "C[1][0], C[1][middlerow], C[1][3], " 189 " 0, 0, 1));"); 190 g->codeAppendf("%s = CI * mat3(K[0], K[middlerow], K[3], " 191 "L[0], L[middlerow], L[3], " 192 "M[0], M[middlerow], M[3]);", fKLMMatrix.c_str()); 193 194 // Orient the KLM matrix so we fill the correct side of the curve. 195 g->codeAppendf("lowp vec2 orientation = sign(vec3(midpoint, 1) * mat2x3(%s[1], %s[2]));", 196 fKLMMatrix.c_str(), fKLMMatrix.c_str()); 197 g->codeAppendf("%s *= mat3(orientation[0] * orientation[1], 0, 0, " 198 "0, orientation[0], 0, " 199 "0, 0, orientation[1]);", fKLMMatrix.c_str()); 200 201 g->declareGlobal(fKLMDerivatives); 202 g->codeAppendf("%s[0] = %s[0].xy * %s.xz;", 203 fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust); 204 g->codeAppendf("%s[1] = %s[1].xy * %s.xz;", 205 fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust); 206 g->codeAppendf("%s[2] = %s[2].xy * %s.xz;", 207 fKLMDerivatives.c_str(), fKLMMatrix.c_str(), rtAdjust); 208 209 this->emitCubicGeometry(g, emitVertexFn, wind, rtAdjust); 210} 211 212void GrCCPRCubicInsetProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g, 213 const char* emitVertexFn, const char* wind, 214 const char* rtAdjust) const { 215 // FIXME: we should clip this geometry at the tip of the curve. 216 g->codeAppendf("%s(inset[0], 1);", emitVertexFn); 217 g->codeAppendf("%s(inset[1], 1);", emitVertexFn); 218 g->codeAppendf("%s(inset[3], 1);", emitVertexFn); 219 g->codeAppendf("%s(inset[2], 1);", emitVertexFn); 220 g->codeAppend ("EndPrimitive();"); 221 222 g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency, 223 GrGLSLGeometryBuilder::OutputType::kTriangleStrip, 224 4, 1); 225} 226 227void GrCCPRCubicInsetProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position, 228 const char* /*coverage*/, 229 const char* /*wind*/) const { 230 fnBody->appendf("highp vec3 klm = vec3(%s, 1) * %s;", position, fKLMMatrix.c_str()); 231 fnBody->appendf("%s = klm;", fKLM.gsOut()); 232 fnBody->appendf("%s[0] = 3 * klm[0] * %s[0];", fGradMatrix.gsOut(), fKLMDerivatives.c_str()); 233 fnBody->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;", 234 fGradMatrix.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str()); 235} 236 237void GrCCPRCubicInsetProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f, 238 const char* outputCoverage) const { 239 f->codeAppendf("highp float k = %s.x, l = %s.y, m = %s.z;", 240 fKLM.fsIn(), fKLM.fsIn(), fKLM.fsIn()); 241 f->codeAppend ("highp float f = k*k*k - l*m;"); 242 f->codeAppendf("highp vec2 grad = %s * vec2(k, 1);", fGradMatrix.fsIn()); 243 f->codeAppend ("highp float d = f * inversesqrt(dot(grad, grad));"); 244 f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage); 245} 246 247void GrCCPRCubicBorderProcessor::emitCubicGeometry(GrGLSLGeometryBuilder* g, 248 const char* emitVertexFn, const char* wind, 249 const char* rtAdjust) const { 250 // We defined bezierpts in onEmitGeometryShader. 251 g->declareGlobal(fEdgeDistanceEquation); 252 g->codeAppendf("int edgeidx0 = %s > 0 ? 3 : 0;", wind); 253 g->codeAppendf("highp vec2 edgept0 = bezierpts[edgeidx0];"); 254 g->codeAppendf("highp vec2 edgept1 = bezierpts[3 - edgeidx0];"); 255 this->emitEdgeDistanceEquation(g, "edgept0", "edgept1", fEdgeDistanceEquation.c_str()); 256 g->codeAppendf("%s.z += 0.5;", fEdgeDistanceEquation.c_str()); // outer = -.5, inner = .5 257 258 g->declareGlobal(fEdgeDistanceDerivatives); 259 g->codeAppendf("%s = %s.xy * %s.xz;", 260 fEdgeDistanceDerivatives.c_str(), fEdgeDistanceEquation.c_str(), rtAdjust); 261 262 g->declareGlobal(fEdgeSpaceTransform); 263 g->codeAppend ("highp vec4 edgebbox = vec4(min(bezierpts[0], bezierpts[3]) - bloat, " 264 "max(bezierpts[0], bezierpts[3]) + bloat);"); 265 g->codeAppendf("%s.xy = 2 / vec2(edgebbox.zw - edgebbox.xy);", fEdgeSpaceTransform.c_str()); 266 g->codeAppendf("%s.zw = -1 - %s.xy * edgebbox.xy;", 267 fEdgeSpaceTransform.c_str(), fEdgeSpaceTransform.c_str()); 268 269 int maxVertices = this->emitHullGeometry(g, emitVertexFn, "bezierpts", 4, "sk_InvocationID", 270 "inset"); 271 272 g->configure(GrGLSLGeometryBuilder::InputType::kLinesAdjacency, 273 GrGLSLGeometryBuilder::OutputType::kTriangleStrip, 274 maxVertices, 4); 275} 276 277void GrCCPRCubicBorderProcessor::emitPerVertexGeometryCode(SkString* fnBody, const char* position, 278 const char* /*coverage*/, 279 const char* /*wind*/) const { 280 fnBody->appendf("highp vec3 klm = vec3(%s, 1) * %s;", position, fKLMMatrix.c_str()); 281 fnBody->appendf("highp float d = dot(vec3(%s, 1), %s);", 282 position, fEdgeDistanceEquation.c_str()); 283 fnBody->appendf("%s = vec4(klm, d);", fKLMD.gsOut()); 284 fnBody->appendf("%s = vec4(%s[0].x, %s[1].x, %s[2].x, %s.x);", 285 fdKLMDdx.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(), 286 fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str()); 287 fnBody->appendf("%s = vec4(%s[0].y, %s[1].y, %s[2].y, %s.y);", 288 fdKLMDdy.gsOut(), fKLMDerivatives.c_str(), fKLMDerivatives.c_str(), 289 fKLMDerivatives.c_str(), fEdgeDistanceDerivatives.c_str()); 290 fnBody->appendf("%s = position * %s.xy + %s.zw;", fEdgeSpaceCoord.gsOut(), 291 fEdgeSpaceTransform.c_str(), fEdgeSpaceTransform.c_str()); 292 293 // Otherwise, fEdgeDistances = fEdgeDistances * sign(wind * rtAdjust.x * rdAdjust.z). 294 GR_STATIC_ASSERT(kTopLeft_GrSurfaceOrigin == GrCCPRCoverageProcessor::kAtlasOrigin); 295} 296 297void GrCCPRCubicBorderProcessor::emitShaderCoverage(GrGLSLFragmentBuilder* f, 298 const char* outputCoverage) const { 299 // Use software msaa to determine coverage. 300 const int sampleCount = this->defineSoftSampleLocations(f, "samples"); 301 302 // Along the shared edge, we start with distance-to-edge coverage, then subtract out the 303 // remaining pixel coverage that is still inside the shared edge, but outside the curve. 304 // Outside the shared edege, we just use standard msaa to count samples inside the curve. 305 f->codeAppendf("bool use_edge = all(lessThan(abs(%s), vec2(1)));", fEdgeSpaceCoord.fsIn()); 306 f->codeAppendf("%s = (use_edge ? clamp(%s.w + 0.5, 0, 1) : 0) * %i;", 307 outputCoverage, fKLMD.fsIn(), sampleCount); 308 309 f->codeAppendf("highp mat2x4 grad_klmd = mat2x4(%s, %s);", fdKLMDdx.fsIn(), fdKLMDdy.fsIn()); 310 311 f->codeAppendf("for (int i = 0; i < %i; ++i) {", sampleCount); 312 f->codeAppendf( "highp vec4 klmd = grad_klmd * samples[i] + %s;", fKLMD.fsIn()); 313 f->codeAppend ( "lowp float f = klmd.y * klmd.z - klmd.x * klmd.x * klmd.x;"); 314 // A sample is inside our cubic sub-section if it is inside the implicit AND L & M are both 315 // positive. This works because the sections get chopped at the K/L and K/M intersections. 316 f->codeAppend ( "bvec4 inside = greaterThan(vec4(f,klmd.yzw), vec4(0));"); 317 f->codeAppend ( "lowp float in_curve = all(inside.xyz) ? 1 : 0;"); 318 f->codeAppend ( "lowp float in_edge = inside.w ? 1 : 0;"); 319 f->codeAppendf( "%s += use_edge ? in_edge * (in_curve - 1) : in_curve;", outputCoverage); 320 f->codeAppend ("}"); 321 322 f->codeAppendf("%s *= %f;", outputCoverage, 1.0 / sampleCount); 323} 324