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