1 /*
2  * Copyright 2018 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 "src/gpu/ops/GrQuadPerEdgeAA.h"
9 
10 #include "include/private/SkNx.h"
11 #include "src/gpu/GrVertexWriter.h"
12 #include "src/gpu/SkGr.h"
13 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
14 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
16 #include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
17 #include "src/gpu/glsl/GrGLSLVarying.h"
18 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
19 
20 #define AI SK_ALWAYS_INLINE
21 
22 namespace {
23 
24 // Helper data types since there is a lot of information that needs to be passed around to
25 // avoid recalculation in the different procedures for tessellating an AA quad.
26 
27 using V4f = skvx::Vec<4, float>;
28 using M4f = skvx::Vec<4, int32_t>;
29 
30 struct Vertices {
31     // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
32     V4f fX, fY, fW;
33     // U, V, and R coordinates representing local quad. Ignored depending on uvrCount (0, 1, 2).
34     V4f fU, fV, fR;
35     int fUVRCount;
36 };
37 
38 struct QuadMetadata {
39     // Normalized edge vectors of the device space quad, ordered L, B, T, R (i.e. nextCCW(x) - x).
40     V4f fDX, fDY;
41     // 1 / edge length of the device space quad
42     V4f fInvLengths;
43     // Edge mask (set to all 1s if aa flags is kAll), otherwise 1.f if edge was AA, 0.f if non-AA.
44     V4f fMask;
45 };
46 
47 struct Edges {
48     // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
49     V4f fA, fB, fC;
50     // Whether or not the edge normals had to be flipped to preserve positive distance on the inside
51     bool fFlipped;
52 };
53 
54 static constexpr float kTolerance = 1e-2f;
55 // True/false bit masks for initializing an M4f
56 static constexpr int32_t kTrue    = ~0;
57 static constexpr int32_t kFalse   = 0;
58 
fma(const V4f & f,const V4f & m,const V4f & a)59 static AI V4f fma(const V4f& f, const V4f& m, const V4f& a) {
60     return mad(f, m, a);
61 }
62 
63 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
64 // order.
nextCW(const V4f & v)65 static AI V4f nextCW(const V4f& v) {
66     return skvx::shuffle<2, 0, 3, 1>(v);
67 }
68 
nextCCW(const V4f & v)69 static AI V4f nextCCW(const V4f& v) {
70     return skvx::shuffle<1, 3, 0, 2>(v);
71 }
72 
73 // Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector.
74 // e3 may be null if only 2D edges need to be corrected for.
correct_bad_edges(const M4f & bad,V4f * e1,V4f * e2,V4f * e3)75 static AI void correct_bad_edges(const M4f& bad, V4f* e1, V4f* e2, V4f* e3) {
76     if (any(bad)) {
77         // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding
78         *e1 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e1), *e1);
79         *e2 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e2), *e2);
80         if (e3) {
81             *e3 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e3), *e3);
82         }
83     }
84 }
85 
86 // Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points.
correct_bad_coords(const M4f & bad,V4f * c1,V4f * c2,V4f * c3)87 static AI void correct_bad_coords(const M4f& bad, V4f* c1, V4f* c2, V4f* c3) {
88     if (any(bad)) {
89         *c1 = if_then_else(bad, nextCCW(*c1), *c1);
90         *c2 = if_then_else(bad, nextCCW(*c2), *c2);
91         if (c3) {
92             *c3 = if_then_else(bad, nextCCW(*c3), *c3);
93         }
94     }
95 }
96 
get_metadata(const Vertices & vertices,GrQuadAAFlags aaFlags)97 static AI QuadMetadata get_metadata(const Vertices& vertices, GrQuadAAFlags aaFlags) {
98     V4f dx = nextCCW(vertices.fX) - vertices.fX;
99     V4f dy = nextCCW(vertices.fY) - vertices.fY;
100     V4f invLengths = rsqrt(fma(dx, dx, dy * dy));
101 
102     V4f mask = aaFlags == GrQuadAAFlags::kAll ? V4f(1.f) :
103             V4f{(GrQuadAAFlags::kLeft & aaFlags) ? 1.f : 0.f,
104                  (GrQuadAAFlags::kBottom & aaFlags) ? 1.f : 0.f,
105                  (GrQuadAAFlags::kTop & aaFlags) ? 1.f : 0.f,
106                  (GrQuadAAFlags::kRight & aaFlags) ? 1.f : 0.f};
107     return { dx * invLengths, dy * invLengths, invLengths, mask };
108 }
109 
get_edge_equations(const QuadMetadata & metadata,const Vertices & vertices)110 static AI Edges get_edge_equations(const QuadMetadata& metadata, const Vertices& vertices) {
111     V4f dx = metadata.fDX;
112     V4f dy = metadata.fDY;
113     // Correct for bad edges by copying adjacent edge information into the bad component
114     correct_bad_edges(metadata.fInvLengths >= 1.f / kTolerance, &dx, &dy, nullptr);
115 
116     V4f c = fma(dx, vertices.fY, -dy * vertices.fX);
117     // Make sure normals point into the shape
118     V4f test = fma(dy, nextCW(vertices.fX), fma(-dx, nextCW(vertices.fY), c));
119     if (any(test < -kTolerance)) {
120         return {-dy, dx, -c, true};
121     } else {
122         return {dy, -dx, c, false};
123     }
124 }
125 
126 // Sets 'outset' to the magnitude of outset/inset to adjust each corner of a quad given the
127 // edge angles and lengths. If the quad is too small, has empty edges, or too sharp of angles,
128 // false is returned and the degenerate slow-path should be used.
get_optimized_outset(const QuadMetadata & metadata,bool rectilinear,V4f * outset)129 static bool get_optimized_outset(const QuadMetadata& metadata, bool rectilinear, V4f* outset) {
130     if (rectilinear) {
131         *outset = 0.5f;
132         // Stay in the fast path as long as all edges are at least a pixel long (so 1/len <= 1)
133         return all(metadata.fInvLengths <= 1.f);
134     }
135 
136     if (any(metadata.fInvLengths >= 1.f / kTolerance)) {
137         // Have an empty edge from a degenerate quad, so there's no hope
138         return false;
139     }
140 
141     // The distance the point needs to move is 1/2sin(theta), where theta is the angle between the
142     // two edges at that point. cos(theta) is equal to dot(dxy, nextCW(dxy))
143     V4f cosTheta = fma(metadata.fDX, nextCW(metadata.fDX), metadata.fDY * nextCW(metadata.fDY));
144     // If the angle is too shallow between edges, go through the degenerate path, otherwise adding
145     // and subtracting very large vectors in almost opposite directions leads to float errors
146     if (any(abs(cosTheta) >= 0.9f)) {
147         return false;
148     }
149     *outset = 0.5f * rsqrt(1.f - cosTheta * cosTheta); // 1/2sin(theta)
150 
151     // When outsetting or insetting, the current edge's AA adds to the length:
152     //   cos(pi - theta)/2sin(theta) + cos(pi-ccw(theta))/2sin(ccw(theta))
153     // Moving an adjacent edge updates the length by 1/2sin(theta|ccw(theta))
154     V4f halfTanTheta = -cosTheta * (*outset); // cos(pi - theta) = -cos(theta)
155     V4f edgeAdjust = metadata.fMask * (halfTanTheta + nextCCW(halfTanTheta)) +
156                       nextCCW(metadata.fMask) * nextCCW(*outset) +
157                       nextCW(metadata.fMask) * (*outset);
158     // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make edgeLen negative
159     // then use the slow path
160     V4f threshold = 0.1f - (1.f / metadata.fInvLengths);
161     return all(edgeAdjust > threshold) && all(edgeAdjust < -threshold);
162 }
163 
164 // Ignores the quad's fW, use outset_projected_vertices if it's known to need 3D.
outset_vertices(const V4f & outset,const QuadMetadata & metadata,Vertices * quad)165 static AI void outset_vertices(const V4f& outset, const QuadMetadata& metadata, Vertices* quad) {
166     // The mask is rotated compared to the outsets and edge vectors, since if the edge is "on"
167     // both its points need to be moved along their other edge vectors.
168     auto maskedOutset = -outset * nextCW(metadata.fMask);
169     auto maskedOutsetCW = outset * metadata.fMask;
170     // x = x + outset * mask * nextCW(xdiff) - outset * nextCW(mask) * xdiff
171     quad->fX += fma(maskedOutsetCW, nextCW(metadata.fDX), maskedOutset * metadata.fDX);
172     quad->fY += fma(maskedOutsetCW, nextCW(metadata.fDY), maskedOutset * metadata.fDY);
173     if (quad->fUVRCount > 0) {
174         // We want to extend the texture coords by the same proportion as the positions.
175         maskedOutset *= metadata.fInvLengths;
176         maskedOutsetCW *= nextCW(metadata.fInvLengths);
177         V4f du = nextCCW(quad->fU) - quad->fU;
178         V4f dv = nextCCW(quad->fV) - quad->fV;
179         quad->fU += fma(maskedOutsetCW, nextCW(du), maskedOutset * du);
180         quad->fV += fma(maskedOutsetCW, nextCW(dv), maskedOutset * dv);
181         if (quad->fUVRCount == 3) {
182             V4f dr = nextCCW(quad->fR) - quad->fR;
183             quad->fR += fma(maskedOutsetCW, nextCW(dr), maskedOutset * dr);
184         }
185     }
186 }
187 
188 // Updates (x,y,w) to be at (x2d,y2d) once projected. Updates (u,v,r) to match if provided.
189 // Gracefully handles 2D content if *w holds all 1s.
outset_projected_vertices(const V4f & x2d,const V4f & y2d,GrQuadAAFlags aaFlags,Vertices * quad)190 static void outset_projected_vertices(const V4f& x2d, const V4f& y2d,
191                                       GrQuadAAFlags aaFlags, Vertices* quad) {
192     // Left to right, in device space, for each point
193     V4f e1x = skvx::shuffle<2, 3, 2, 3>(quad->fX) - skvx::shuffle<0, 1, 0, 1>(quad->fX);
194     V4f e1y = skvx::shuffle<2, 3, 2, 3>(quad->fY) - skvx::shuffle<0, 1, 0, 1>(quad->fY);
195     V4f e1w = skvx::shuffle<2, 3, 2, 3>(quad->fW) - skvx::shuffle<0, 1, 0, 1>(quad->fW);
196     correct_bad_edges(fma(e1x, e1x, e1y * e1y) < kTolerance * kTolerance, &e1x, &e1y, &e1w);
197 
198     // // Top to bottom, in device space, for each point
199     V4f e2x = skvx::shuffle<1, 1, 3, 3>(quad->fX) - skvx::shuffle<0, 0, 2, 2>(quad->fX);
200     V4f e2y = skvx::shuffle<1, 1, 3, 3>(quad->fY) - skvx::shuffle<0, 0, 2, 2>(quad->fY);
201     V4f e2w = skvx::shuffle<1, 1, 3, 3>(quad->fW) - skvx::shuffle<0, 0, 2, 2>(quad->fW);
202     correct_bad_edges(fma(e2x, e2x, e2y * e2y) < kTolerance * kTolerance, &e2x, &e2y, &e2w);
203 
204     // Can only move along e1 and e2 to reach the new 2D point, so we have
205     // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and
206     // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b
207     // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where
208     // the cNx and cNy coefficients are:
209     V4f c1x = e1w * x2d - e1x;
210     V4f c1y = e1w * y2d - e1y;
211     V4f c2x = e2w * x2d - e2x;
212     V4f c2y = e2w * y2d - e2y;
213     V4f c3x = quad->fW * x2d - quad->fX;
214     V4f c3y = quad->fW * y2d - quad->fY;
215 
216     // Solve for a and b
217     V4f a, b, denom;
218     if (aaFlags == GrQuadAAFlags::kAll) {
219         // When every edge is outset/inset, each corner can use both edge vectors
220         denom = c1x * c2y - c2x * c1y;
221         a = (c2x * c3y - c3x * c2y) / denom;
222         b = (c3x * c1y - c1x * c3y) / denom;
223     } else {
224         // Force a or b to be 0 if that edge cannot be used due to non-AA
225         M4f aMask = M4f{(aaFlags & GrQuadAAFlags::kLeft)   ? kTrue : kFalse,
226                         (aaFlags & GrQuadAAFlags::kLeft)   ? kTrue : kFalse,
227                         (aaFlags & GrQuadAAFlags::kRight)  ? kTrue : kFalse,
228                         (aaFlags & GrQuadAAFlags::kRight)  ? kTrue : kFalse};
229         M4f bMask = M4f{(aaFlags & GrQuadAAFlags::kTop)    ? kTrue : kFalse,
230                         (aaFlags & GrQuadAAFlags::kBottom) ? kTrue : kFalse,
231                         (aaFlags & GrQuadAAFlags::kTop)    ? kTrue : kFalse,
232                         (aaFlags & GrQuadAAFlags::kBottom) ? kTrue : kFalse};
233 
234         // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case.
235         // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom
236         // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, ""
237         // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0
238         M4f useC1x = abs(c1x) > abs(c1y);
239         M4f useC2x = abs(c2x) > abs(c2y);
240 
241         denom = if_then_else(aMask,
242                         if_then_else(bMask,
243                                 c1x * c2y - c2x * c1y,            /* A & B   */
244                                 if_then_else(useC1x, c1x, c1y)),  /* A & !B  */
245                         if_then_else(bMask,
246                                 if_then_else(useC2x, c2x, c2y),   /* !A & B  */
247                                 V4f(1.f)));                       /* !A & !B */
248 
249         a = if_then_else(aMask,
250                     if_then_else(bMask,
251                             c2x * c3y - c3x * c2y,                /* A & B   */
252                             if_then_else(useC1x, -c3x, -c3y)),    /* A & !B  */
253                     V4f(0.f)) / denom;                            /* !A      */
254         b = if_then_else(bMask,
255                     if_then_else(aMask,
256                             c3x * c1y - c1x * c3y,                /* A & B   */
257                             if_then_else(useC2x, -c3x, -c3y)),    /* !A & B  */
258                     V4f(0.f)) / denom;                            /* !B      */
259     }
260 
261     V4f newW = quad->fW + a * e1w + b * e2w;
262     // If newW < 0, scale a and b such that the point reaches the infinity plane instead of crossing
263     // This breaks orthogonality of inset/outsets, but GPUs don't handle negative Ws well so this
264     // is far less visually disturbing (likely not noticeable since it's at extreme perspective).
265     // The alternative correction (multiply xyw by -1) has the disadvantage of changing how local
266     // coordinates would be interpolated.
267     static const float kMinW = 1e-6f;
268     if (any(newW < 0.f)) {
269         V4f scale = if_then_else(newW < kMinW, (kMinW - quad->fW) / (newW - quad->fW), V4f(1.f));
270         a *= scale;
271         b *= scale;
272     }
273 
274     quad->fX += a * e1x + b * e2x;
275     quad->fY += a * e1y + b * e2y;
276     quad->fW += a * e1w + b * e2w;
277     correct_bad_coords(abs(denom) < kTolerance, &quad->fX, &quad->fY, &quad->fW);
278 
279     if (quad->fUVRCount > 0) {
280         // Calculate R here so it can be corrected with U and V in case it's needed later
281         V4f e1u = skvx::shuffle<2, 3, 2, 3>(quad->fU) - skvx::shuffle<0, 1, 0, 1>(quad->fU);
282         V4f e1v = skvx::shuffle<2, 3, 2, 3>(quad->fV) - skvx::shuffle<0, 1, 0, 1>(quad->fV);
283         V4f e1r = skvx::shuffle<2, 3, 2, 3>(quad->fR) - skvx::shuffle<0, 1, 0, 1>(quad->fR);
284         correct_bad_edges(fma(e1u, e1u, e1v * e1v) < kTolerance * kTolerance, &e1u, &e1v, &e1r);
285 
286         V4f e2u = skvx::shuffle<1, 1, 3, 3>(quad->fU) - skvx::shuffle<0, 0, 2, 2>(quad->fU);
287         V4f e2v = skvx::shuffle<1, 1, 3, 3>(quad->fV) - skvx::shuffle<0, 0, 2, 2>(quad->fV);
288         V4f e2r = skvx::shuffle<1, 1, 3, 3>(quad->fR) - skvx::shuffle<0, 0, 2, 2>(quad->fR);
289         correct_bad_edges(fma(e2u, e2u, e2v * e2v) < kTolerance * kTolerance, &e2u, &e2v, &e2r);
290 
291         quad->fU += a * e1u + b * e2u;
292         quad->fV += a * e1v + b * e2v;
293         if (quad->fUVRCount == 3) {
294             quad->fR += a * e1r + b * e2r;
295             correct_bad_coords(abs(denom) < kTolerance, &quad->fU, &quad->fV, &quad->fR);
296         } else {
297             correct_bad_coords(abs(denom) < kTolerance, &quad->fU, &quad->fV, nullptr);
298         }
299     }
300 }
301 
degenerate_coverage(const V4f & px,const V4f & py,const Edges & edges)302 static V4f degenerate_coverage(const V4f& px, const V4f& py, const Edges& edges) {
303     // Calculate distance of the 4 inset points (px, py) to the 4 edges
304     V4f d0 = fma(edges.fA[0], px, fma(edges.fB[0], py, edges.fC[0]));
305     V4f d1 = fma(edges.fA[1], px, fma(edges.fB[1], py, edges.fC[1]));
306     V4f d2 = fma(edges.fA[2], px, fma(edges.fB[2], py, edges.fC[2]));
307     V4f d3 = fma(edges.fA[3], px, fma(edges.fB[3], py, edges.fC[3]));
308 
309     // For each point, pretend that there's a rectangle that touches e0 and e3 on the horizontal
310     // axis, so its width is "approximately" d0 + d3, and it touches e1 and e2 on the vertical axis
311     // so its height is d1 + d2. Pin each of these dimensions to [0, 1] and approximate the coverage
312     // at each point as clamp(d0+d3, 0, 1) x clamp(d1+d2, 0, 1). For rectilinear quads this is an
313     // accurate calculation of its area clipped to an aligned pixel. For arbitrary quads it is not
314     // mathematically accurate but qualitatively provides a stable value proportional to the size of
315     // the shape.
316     V4f w = max(0.f, min(1.f, d0 + d3));
317     V4f h = max(0.f, min(1.f, d1 + d2));
318     return w * h;
319 }
320 
321 // Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
322 // parallel, or edges are very short/zero-length. Returns coverage for each vertex.
323 // Requires (dx, dy) to already be fixed for empty edges.
compute_degenerate_quad(GrQuadAAFlags aaFlags,const V4f & mask,const Edges & edges,bool outset,Vertices * quad)324 static V4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const V4f& mask, const Edges& edges,
325                                    bool outset, Vertices* quad) {
326     // Move the edge 1/2 pixel in or out depending on 'outset'.
327     V4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
328 
329     // There are 6 points that we care about to determine the final shape of the polygon, which
330     // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
331     // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
332     V4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
333     V4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
334     V4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
335     correct_bad_coords(abs(denom) < kTolerance, &px, &py, nullptr);
336 
337     // Calculate the signed distances from these 4 corners to the other two edges that did not
338     // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
339     // p(3) to e0,e2
340     V4f dists1 = px * skvx::shuffle<3, 3, 0, 0>(edges.fA) +
341                  py * skvx::shuffle<3, 3, 0, 0>(edges.fB) +
342                  skvx::shuffle<3, 3, 0, 0>(oc);
343     V4f dists2 = px * skvx::shuffle<1, 2, 1, 2>(edges.fA) +
344                  py * skvx::shuffle<1, 2, 1, 2>(edges.fB) +
345                  skvx::shuffle<1, 2, 1, 2>(oc);
346 
347     // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
348     // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
349     // and we need to use a central point to represent it. If all four points are only on the
350     // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
351     // Otherwise, use a triangle that replaces the bad points with the intersections of
352     // (e1, e2) or (e0, e3) as needed.
353     M4f d1v0 = dists1 < kTolerance;
354     M4f d2v0 = dists2 < kTolerance;
355     M4f d1And2 = d1v0 & d2v0;
356     M4f d1Or2 = d1v0 | d2v0;
357 
358     V4f coverage;
359     if (!any(d1Or2)) {
360         // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
361         // and use full coverage
362         coverage = 1.f;
363     } else if (any(d1And2)) {
364         // A point failed against two edges, so reduce the shape to a single point, which we take as
365         // the center of the original quad to ensure it is contained in the intended geometry. Since
366         // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
367         SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
368                           0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
369         px = center.fX;
370         py = center.fY;
371         coverage = degenerate_coverage(px, py, edges);
372     } else if (all(d1Or2)) {
373         // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
374         // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
375         if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
376             // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
377             px = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px));
378             py = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py));
379         } else {
380             // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
381             px = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px));
382             py = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py));
383         }
384         coverage = degenerate_coverage(px, py, edges);
385     } else {
386         // This turns into a triangle. Replace corners as needed with the intersections between
387         // (e0,e3) and (e1,e2), which must now be calculated
388         using V2f = skvx::Vec<2, float>;
389         V2f eDenom = skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(edges.fB) -
390                       skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(edges.fA);
391         V2f ex = (skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(oc) -
392                    skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fB)) / eDenom;
393         V2f ey = (skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fA) -
394                    skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(oc)) / eDenom;
395 
396         if (SkScalarAbs(eDenom[0]) > kTolerance) {
397             px = if_then_else(d1v0, V4f(ex[0]), px);
398             py = if_then_else(d1v0, V4f(ey[0]), py);
399         }
400         if (SkScalarAbs(eDenom[1]) > kTolerance) {
401             px = if_then_else(d2v0, V4f(ex[1]), px);
402             py = if_then_else(d2v0, V4f(ey[1]), py);
403         }
404 
405         coverage = 1.f;
406     }
407 
408     outset_projected_vertices(px, py, aaFlags, quad);
409     return coverage;
410 }
411 
412 // Computes the vertices for the two nested quads used to create AA edges. The original single quad
413 // should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
414 // stored in-place on return. Returns per-vertex coverage for the inner vertices.
compute_nested_quad_vertices(GrQuadAAFlags aaFlags,bool rectilinear,Vertices * inner,Vertices * outer,SkRect * domain)415 static V4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
416                                          Vertices* inner, Vertices* outer, SkRect* domain) {
417     SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
418     SkASSERT(outer->fUVRCount == inner->fUVRCount);
419 
420     QuadMetadata metadata = get_metadata(*inner, aaFlags);
421 
422     // Calculate domain first before updating vertices. It's only used when not rectilinear.
423     if (!rectilinear) {
424         SkASSERT(domain);
425         // The domain is the bounding box of the quad, outset by 0.5. Don't worry about edge masks
426         // since the FP only applies the domain on the exterior triangles, which are degenerate for
427         // non-AA edges.
428         domain->fLeft = min(outer->fX) - 0.5f;
429         domain->fRight = max(outer->fX) + 0.5f;
430         domain->fTop = min(outer->fY) - 0.5f;
431         domain->fBottom = max(outer->fY) + 0.5f;
432     }
433 
434     // When outsetting, we want the new edge to be .5px away from the old line, which means the
435     // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
436     // is only computed if there are no empty edges, and it may signal going through the slow path.
437     V4f outset = 0.5f;
438     if (get_optimized_outset(metadata, rectilinear, &outset)) {
439        // Since it's not subpixel, outsetting and insetting are trivial vector additions.
440         outset_vertices(outset, metadata, outer);
441         outset_vertices(-outset, metadata, inner);
442         return 1.f;
443     }
444 
445     // Only compute edge equations once since they are the same for inner and outer quads
446     Edges edges = get_edge_equations(metadata, *inner);
447 
448     // Calculate both outset and inset, returning the coverage reported for the inset, since the
449     // outset will always have 0.0f.
450     compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
451     return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
452 }
453 
454 // Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
455 // division of the device coordinates, the original local coordinate value is at the original
456 // un-outset device position.
compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags,Vertices * inner,Vertices * outer,SkRect * domain)457 static V4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
458                                                Vertices* outer, SkRect* domain) {
459     SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
460     SkASSERT(outer->fUVRCount == inner->fUVRCount);
461 
462     // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
463     V4f iw = 1.0f / inner->fW;
464     V4f x2d = inner->fX * iw;
465     V4f y2d = inner->fY * iw;
466 
467     Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
468     Vertices outer2D = inner2D;
469 
470     V4f coverage = compute_nested_quad_vertices(
471             aaFlags, /* rect */ false, &inner2D, &outer2D, domain);
472 
473     // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
474     outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
475     outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
476 
477     return coverage;
478 }
479 
480 // Writes four vertices in triangle strip order, including the additional data for local
481 // coordinates, geometry + texture domains, color, and coverage as needed to satisfy the vertex spec
write_quad(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,GrQuadPerEdgeAA::CoverageMode mode,const V4f & coverage,SkPMColor4f color4f,const SkRect & geomDomain,const SkRect & texDomain,const Vertices & quad)482 static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
483                        GrQuadPerEdgeAA::CoverageMode mode, const V4f& coverage, SkPMColor4f color4f,
484                        const SkRect& geomDomain, const SkRect& texDomain, const Vertices& quad) {
485     static constexpr auto If = GrVertexWriter::If<float>;
486 
487     for (int i = 0; i < 4; ++i) {
488         // save position, this is a float2 or float3 or float4 depending on the combination of
489         // perspective and coverage mode.
490         vb->write(quad.fX[i], quad.fY[i],
491                   If(spec.deviceQuadType() == GrQuad::Type::kPerspective, quad.fW[i]),
492                   If(mode == GrQuadPerEdgeAA::CoverageMode::kWithPosition, coverage[i]));
493 
494         // save color
495         if (spec.hasVertexColors()) {
496             bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
497             vb->write(GrVertexColor(
498                 color4f * (mode == GrQuadPerEdgeAA::CoverageMode::kWithColor ? coverage[i] : 1.f),
499                 wide));
500         }
501 
502         // save local position
503         if (spec.hasLocalCoords()) {
504             vb->write(quad.fU[i], quad.fV[i],
505                       If(spec.localQuadType() == GrQuad::Type::kPerspective, quad.fR[i]));
506         }
507 
508         // save the geometry domain
509         if (spec.requiresGeometryDomain()) {
510             vb->write(geomDomain);
511         }
512 
513         // save the texture domain
514         if (spec.hasDomain()) {
515             vb->write(texDomain);
516         }
517     }
518 }
519 
520 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
521 
522 static const int kVertsPerAAFillRect = 8;
523 static const int kIndicesPerAAFillRect = 30;
524 
get_index_buffer(GrResourceProvider * resourceProvider)525 static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
526     GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
527 
528     // clang-format off
529     static const uint16_t gFillAARectIdx[] = {
530         0, 1, 2, 1, 3, 2,
531         0, 4, 1, 4, 5, 1,
532         0, 6, 4, 0, 2, 6,
533         2, 3, 6, 3, 7, 6,
534         1, 5, 3, 3, 5, 7,
535     };
536     // clang-format on
537 
538     GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
539     return resourceProvider->findOrCreatePatternedIndexBuffer(
540             gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
541             kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
542 }
543 
544 } // anonymous namespace
545 
546 namespace GrQuadPerEdgeAA {
547 
548 // This is a more elaborate version of SkPMColor4fNeedsWideColor that allows "no color" for white
MinColorType(SkPMColor4f color,GrClampType clampType,const GrCaps & caps)549 ColorType MinColorType(SkPMColor4f color, GrClampType clampType, const GrCaps& caps) {
550     if (color == SK_PMColor4fWHITE) {
551         return ColorType::kNone;
552     } else {
553         return SkPMColor4fNeedsWideColor(color, clampType, caps) ? ColorType::kHalf
554                                                                  : ColorType::kByte;
555     }
556 }
557 
558 ////////////////// Tessellate Implementation
559 
Tessellate(void * vertices,const VertexSpec & spec,const GrQuad & deviceQuad,const SkPMColor4f & color4f,const GrQuad & localQuad,const SkRect & domain,GrQuadAAFlags aaFlags)560 void* Tessellate(void* vertices, const VertexSpec& spec, const GrQuad& deviceQuad,
561                  const SkPMColor4f& color4f, const GrQuad& localQuad, const SkRect& domain,
562                  GrQuadAAFlags aaFlags) {
563     SkASSERT(deviceQuad.quadType() <= spec.deviceQuadType());
564     SkASSERT(!spec.hasLocalCoords() || localQuad.quadType() <= spec.localQuadType());
565 
566     GrQuadPerEdgeAA::CoverageMode mode = spec.coverageMode();
567 
568     // Load position data into V4fs (always x, y, and load w to avoid branching down the road)
569     Vertices outer;
570     outer.fX = deviceQuad.x4f();
571     outer.fY = deviceQuad.y4f();
572     outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
573 
574     // Load local position data into V4fs (either none, just u,v or all three)
575     outer.fUVRCount = spec.localDimensionality();
576     if (spec.hasLocalCoords()) {
577         outer.fU = localQuad.x4f();
578         outer.fV = localQuad.y4f();
579         outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
580     }
581 
582     GrVertexWriter vb{vertices};
583     if (spec.usesCoverageAA()) {
584         SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
585         // Must calculate two new quads, an outset and inset by .5 in projected device space, so
586         // duplicate the original quad for the inner space
587         Vertices inner = outer;
588 
589         SkRect geomDomain;
590         V4f maxCoverage = 1.f;
591         if (spec.deviceQuadType() == GrQuad::Type::kPerspective) {
592             // For perspective, send quads with all edges non-AA through the tessellation to ensure
593             // their corners are processed the same as adjacent quads. This approach relies on
594             // solving edge equations to reconstruct corners, which can create seams if an inner
595             // fully non-AA quad is not similarly processed.
596             maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer, &geomDomain);
597         } else if (aaFlags != GrQuadAAFlags::kNone) {
598             // In 2D, the simpler corner math does not cause issues with seaming against non-AA
599             // inner quads.
600             maxCoverage = compute_nested_quad_vertices(
601                     aaFlags, spec.deviceQuadType() <= GrQuad::Type::kRectilinear, &inner, &outer,
602                     &geomDomain);
603         } else if (spec.requiresGeometryDomain()) {
604             // The quad itself wouldn't need a geometric domain, but the batch does, so set the
605             // domain to the bounds of the X/Y coords. Since it's non-AA, this won't actually be
606             // evaluated by the shader, but make sure not to upload uninitialized data.
607             geomDomain.fLeft = min(outer.fX);
608             geomDomain.fRight = max(outer.fX);
609             geomDomain.fTop = min(outer.fY);
610             geomDomain.fBottom = max(outer.fY);
611         }
612 
613         // Write two quads for inner and outer, inner will use the
614         write_quad(&vb, spec, mode, maxCoverage, color4f, geomDomain, domain, inner);
615         write_quad(&vb, spec, mode, 0.f, color4f, geomDomain, domain, outer);
616     } else {
617         // No outsetting needed, just write a single quad with full coverage
618         SkASSERT(mode == CoverageMode::kNone && !spec.requiresGeometryDomain());
619         write_quad(&vb, spec, mode, 1.f, color4f, SkRect::MakeEmpty(), domain, outer);
620     }
621 
622     return vb.fPtr;
623 }
624 
ConfigureMeshIndices(GrMeshDrawOp::Target * target,GrMesh * mesh,const VertexSpec & spec,int quadCount)625 bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
626                           int quadCount) {
627     if (spec.usesCoverageAA()) {
628         // AA quads use 8 vertices, basically nested rectangles
629         sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
630         if (!ibuffer) {
631             return false;
632         }
633 
634         mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
635         mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
636                                   quadCount, kNumAAQuadsInIndexBuffer);
637     } else {
638         // Non-AA quads use 4 vertices, and regular triangle strip layout
639         if (quadCount > 1) {
640             sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
641             if (!ibuffer) {
642                 return false;
643             }
644 
645             mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
646             mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
647                                       GrResourceProvider::QuadCountOfQuadBuffer());
648         } else {
649             mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
650             mesh->setNonIndexedNonInstanced(4);
651         }
652     }
653 
654     return true;
655 }
656 
657 ////////////////// VertexSpec Implementation
658 
deviceDimensionality() const659 int VertexSpec::deviceDimensionality() const {
660     return this->deviceQuadType() == GrQuad::Type::kPerspective ? 3 : 2;
661 }
662 
localDimensionality() const663 int VertexSpec::localDimensionality() const {
664     return fHasLocalCoords ? (this->localQuadType() == GrQuad::Type::kPerspective ? 3 : 2) : 0;
665 }
666 
coverageMode() const667 CoverageMode VertexSpec::coverageMode() const {
668     if (this->usesCoverageAA()) {
669         if (this->compatibleWithCoverageAsAlpha() && this->hasVertexColors() &&
670             !this->requiresGeometryDomain()) {
671             // Using a geometric domain acts as a second source of coverage and folding
672             // the original coverage into color makes it impossible to apply the color's
673             // alpha to the geometric domain's coverage when the original shape is clipped.
674             return CoverageMode::kWithColor;
675         } else {
676             return CoverageMode::kWithPosition;
677         }
678     } else {
679         return CoverageMode::kNone;
680     }
681 }
682 
683 // This needs to stay in sync w/ QuadPerEdgeAAGeometryProcessor::initializeAttrs
vertexSize() const684 size_t VertexSpec::vertexSize() const {
685     bool needsPerspective = (this->deviceDimensionality() == 3);
686     CoverageMode coverageMode = this->coverageMode();
687 
688     size_t count = 0;
689 
690     if (coverageMode == CoverageMode::kWithPosition) {
691         if (needsPerspective) {
692             count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
693         } else {
694             count += GrVertexAttribTypeSize(kFloat2_GrVertexAttribType) +
695                      GrVertexAttribTypeSize(kFloat_GrVertexAttribType);
696         }
697     } else {
698         if (needsPerspective) {
699             count += GrVertexAttribTypeSize(kFloat3_GrVertexAttribType);
700         } else {
701             count += GrVertexAttribTypeSize(kFloat2_GrVertexAttribType);
702         }
703     }
704 
705     if (this->requiresGeometryDomain()) {
706         count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
707     }
708 
709     count += this->localDimensionality() * GrVertexAttribTypeSize(kFloat_GrVertexAttribType);
710 
711     if (ColorType::kByte == this->colorType()) {
712         count += GrVertexAttribTypeSize(kUByte4_norm_GrVertexAttribType);
713     } else if (ColorType::kHalf == this->colorType()) {
714         count += GrVertexAttribTypeSize(kHalf4_GrVertexAttribType);
715     }
716 
717     if (this->hasDomain()) {
718         count += GrVertexAttribTypeSize(kFloat4_GrVertexAttribType);
719     }
720 
721     return count;
722 }
723 
724 ////////////////// Geometry Processor Implementation
725 
726 class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
727 public:
728     using Saturate = GrTextureOp::Saturate;
729 
Make(const VertexSpec & spec)730     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
731         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
732     }
733 
Make(const VertexSpec & vertexSpec,const GrShaderCaps & caps,GrTextureType textureType,const GrSamplerState & samplerState,const GrSwizzle & swizzle,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform,Saturate saturate)734     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
735                                            GrTextureType textureType,
736                                            const GrSamplerState& samplerState,
737                                            const GrSwizzle& swizzle, uint32_t extraSamplerKey,
738                                            sk_sp<GrColorSpaceXform> textureColorSpaceXform,
739                                            Saturate saturate) {
740         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
741                 vertexSpec, caps, textureType, samplerState, swizzle, extraSamplerKey,
742                 std::move(textureColorSpaceXform), saturate));
743     }
744 
name() const745     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
746 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const747     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
748         // texturing, device-dimensions are single bit flags
749         uint32_t x = (fTexDomain.isInitialized() ? 0 : 0x1)
750                    | (fSampler.isInitialized()   ? 0 : 0x2)
751                    | (fNeedsPerspective          ? 0 : 0x4)
752                    | (fSaturate == Saturate::kNo ? 0 : 0x8);
753         // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
754         if (fLocalCoord.isInitialized()) {
755             x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 0x10 : 0x20;
756         }
757         // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
758         if (fColor.isInitialized()) {
759             x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 0x40 : 0x80;
760         }
761         // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor, 11 for
762         // position+geomdomain
763         SkASSERT(!fGeomDomain.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
764         if (fCoverageMode != CoverageMode::kNone) {
765             x |= fGeomDomain.isInitialized()
766                          ? 0x300
767                          : (CoverageMode::kWithPosition == fCoverageMode ? 0x100 : 0x200);
768         }
769 
770         b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
771         b->add32(x);
772     }
773 
createGLSLInstance(const GrShaderCaps & caps) const774     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
775         class GLSLProcessor : public GrGLSLGeometryProcessor {
776         public:
777             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
778                          FPCoordTransformIter&& transformIter) override {
779                 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
780                 if (gp.fLocalCoord.isInitialized()) {
781                     this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
782                 }
783                 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
784             }
785 
786         private:
787             void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
788                 using Interpolation = GrGLSLVaryingHandler::Interpolation;
789 
790                 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
791                 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
792                                                        gp.fTextureColorSpaceXform.get());
793 
794                 args.fVaryingHandler->emitAttributes(gp);
795 
796                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
797                     // Strip last channel from the vertex attribute to remove coverage and get the
798                     // actual position
799                     if (gp.fNeedsPerspective) {
800                         args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
801                                                        gp.fPosition.name());
802                     } else {
803                         args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
804                                                        gp.fPosition.name());
805                     }
806                     gpArgs->fPositionVar = {"position",
807                                             gp.fNeedsPerspective ? kFloat3_GrSLType
808                                                                  : kFloat2_GrSLType,
809                                             GrShaderVar::kNone_TypeModifier};
810                 } else {
811                     // No coverage to eliminate
812                     gpArgs->fPositionVar = gp.fPosition.asShaderVar();
813                 }
814 
815                 // Handle local coordinates if they exist
816                 if (gp.fLocalCoord.isInitialized()) {
817                     // NOTE: If the only usage of local coordinates is for the inline texture fetch
818                     // before FPs, then there are no registered FPCoordTransforms and this ends up
819                     // emitting nothing, so there isn't a duplication of local coordinates
820                     this->emitTransforms(args.fVertBuilder,
821                                          args.fVaryingHandler,
822                                          args.fUniformHandler,
823                                          gp.fLocalCoord.asShaderVar(),
824                                          args.fFPCoordTransformHandler);
825                 }
826 
827                 // Solid color before any texturing gets modulated in
828                 if (gp.fColor.isInitialized()) {
829                     SkASSERT(gp.fCoverageMode != CoverageMode::kWithColor || !gp.fNeedsPerspective);
830                     // The color cannot be flat if the varying coverage has been modulated into it
831                     args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
832                             gp.fCoverageMode == CoverageMode::kWithColor ?
833                             Interpolation::kInterpolated : Interpolation::kCanBeFlat);
834                 } else {
835                     // Output color must be initialized to something
836                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
837                 }
838 
839                 // If there is a texture, must also handle texture coordinates and reading from
840                 // the texture in the fragment shader before continuing to fragment processors.
841                 if (gp.fSampler.isInitialized()) {
842                     // Texture coordinates clamped by the domain on the fragment shader; if the GP
843                     // has a texture, it's guaranteed to have local coordinates
844                     args.fFragBuilder->codeAppend("float2 texCoord;");
845                     if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
846                         // Can't do a pass through since we need to perform perspective division
847                         GrGLSLVarying v(gp.fLocalCoord.gpuType());
848                         args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
849                         args.fVertBuilder->codeAppendf("%s = %s;",
850                                                        v.vsOut(), gp.fLocalCoord.name());
851                         args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
852                                                        v.fsIn(), v.fsIn());
853                     } else {
854                         args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
855                     }
856 
857                     // Clamp the now 2D localCoordName variable by the domain if it is provided
858                     if (gp.fTexDomain.isInitialized()) {
859                         args.fFragBuilder->codeAppend("float4 domain;");
860                         args.fVaryingHandler->addPassThroughAttribute(gp.fTexDomain, "domain",
861                                                                       Interpolation::kCanBeFlat);
862                         args.fFragBuilder->codeAppend(
863                                 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
864                     }
865 
866                     // Now modulate the starting output color by the texture lookup
867                     args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
868                     args.fFragBuilder->appendTextureLookupAndModulate(
869                         args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
870                         &fTextureColorSpaceXformHelper);
871                     args.fFragBuilder->codeAppend(";");
872                     if (gp.fSaturate == Saturate::kYes) {
873                         args.fFragBuilder->codeAppendf("%s = saturate(%s);",
874                                                        args.fOutputColor, args.fOutputColor);
875                     }
876                 } else {
877                     // Saturate is only intended for use with a proxy to account for the fact
878                     // that GrTextureOp skips SkPaint conversion, which normally handles this.
879                     SkASSERT(gp.fSaturate == Saturate::kNo);
880                 }
881 
882                 // And lastly, output the coverage calculation code
883                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
884                     GrGLSLVarying coverage(kFloat_GrSLType);
885                     args.fVaryingHandler->addVarying("coverage", &coverage);
886                     if (gp.fNeedsPerspective) {
887                         // Multiply by "W" in the vertex shader, then by 1/w (sk_FragCoord.w) in
888                         // the fragment shader to get screen-space linear coverage.
889                         args.fVertBuilder->codeAppendf("%s = %s.w * %s.z;",
890                                                        coverage.vsOut(), gp.fPosition.name(),
891                                                        gp.fPosition.name());
892                         args.fFragBuilder->codeAppendf("float coverage = %s * sk_FragCoord.w;",
893                                                         coverage.fsIn());
894                     } else {
895                         args.fVertBuilder->codeAppendf("%s = %s;",
896                                                        coverage.vsOut(), gp.fCoverage.name());
897                         args.fFragBuilder->codeAppendf("float coverage = %s;", coverage.fsIn());
898                     }
899 
900                     if (gp.fGeomDomain.isInitialized()) {
901                         // Calculate distance from sk_FragCoord to the 4 edges of the domain
902                         // and clamp them to (0, 1). Use the minimum of these and the original
903                         // coverage. This only has to be done in the exterior triangles, the
904                         // interior of the quad geometry can never be clipped by the domain box.
905                         args.fFragBuilder->codeAppend("float4 geoDomain;");
906                         args.fVaryingHandler->addPassThroughAttribute(gp.fGeomDomain, "geoDomain",
907                                         Interpolation::kCanBeFlat);
908                         args.fFragBuilder->codeAppend(
909                                 "if (coverage < 0.5) {"
910                                 "   float4 dists4 = clamp(float4(1, 1, -1, -1) * "
911                                         "(sk_FragCoord.xyxy - geoDomain), 0, 1);"
912                                 "   float2 dists2 = dists4.xy * dists4.zw;"
913                                 "   coverage = min(coverage, dists2.x * dists2.y);"
914                                 "}");
915                     }
916 
917                     args.fFragBuilder->codeAppendf("%s = half4(half(coverage));",
918                                                    args.fOutputCoverage);
919                 } else {
920                     // Set coverage to 1, since it's either non-AA or the coverage was already
921                     // folded into the output color
922                     SkASSERT(!gp.fGeomDomain.isInitialized());
923                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
924                 }
925             }
926             GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
927         };
928         return new GLSLProcessor;
929     }
930 
931 private:
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec)932     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
933             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
934             , fTextureColorSpaceXform(nullptr) {
935         SkASSERT(!spec.hasDomain());
936         this->initializeAttrs(spec);
937         this->setTextureSamplerCnt(0);
938     }
939 
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,const GrSamplerState & samplerState,const GrSwizzle & swizzle,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform,Saturate saturate)940     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec,
941                                    const GrShaderCaps& caps,
942                                    GrTextureType textureType,
943                                    const GrSamplerState& samplerState,
944                                    const GrSwizzle& swizzle,
945                                    uint32_t extraSamplerKey,
946                                    sk_sp<GrColorSpaceXform> textureColorSpaceXform,
947                                    Saturate saturate)
948             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
949             , fSaturate(saturate)
950             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
951             , fSampler(textureType, samplerState, swizzle, extraSamplerKey) {
952         SkASSERT(spec.hasLocalCoords());
953         this->initializeAttrs(spec);
954         this->setTextureSamplerCnt(1);
955     }
956 
957     // This needs to stay in sync w/ VertexSpec::vertexSize
initializeAttrs(const VertexSpec & spec)958     void initializeAttrs(const VertexSpec& spec) {
959         fNeedsPerspective = spec.deviceDimensionality() == 3;
960         fCoverageMode = spec.coverageMode();
961 
962         if (fCoverageMode == CoverageMode::kWithPosition) {
963             if (fNeedsPerspective) {
964                 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
965             } else {
966                 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
967                 fCoverage = {"coverage", kFloat_GrVertexAttribType, kFloat_GrSLType};
968             }
969         } else {
970             if (fNeedsPerspective) {
971                 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
972             } else {
973                 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
974             }
975         }
976 
977         // Need a geometry domain when the quads are AA and not rectilinear, since their AA
978         // outsetting can go beyond a half pixel.
979         if (spec.requiresGeometryDomain()) {
980             fGeomDomain = {"geomDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
981         }
982 
983         int localDim = spec.localDimensionality();
984         if (localDim == 3) {
985             fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
986         } else if (localDim == 2) {
987             fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
988         } // else localDim == 0 and attribute remains uninitialized
989 
990         if (ColorType::kByte == spec.colorType()) {
991             fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
992         } else if (ColorType::kHalf == spec.colorType()) {
993             fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
994         }
995 
996         if (spec.hasDomain()) {
997             fTexDomain = {"texDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
998         }
999 
1000         this->setVertexAttributes(&fPosition, 6);
1001     }
1002 
onTextureSampler(int) const1003     const TextureSampler& onTextureSampler(int) const override { return fSampler; }
1004 
1005     Attribute fPosition; // May contain coverage as last channel
1006     Attribute fCoverage; // Used for non-perspective position to avoid Intel Metal issues
1007     Attribute fColor; // May have coverage modulated in if the FPs support it
1008     Attribute fLocalCoord;
1009     Attribute fGeomDomain; // Screen-space bounding box on geometry+aa outset
1010     Attribute fTexDomain; // Texture-space bounding box on local coords
1011 
1012     // The positions attribute may have coverage built into it, so float3 is an ambiguous type
1013     // and may mean 2d with coverage, or 3d with no coverage
1014     bool fNeedsPerspective;
1015     // Should saturate() be called on the color? Only relevant when created with a texture.
1016     Saturate fSaturate = Saturate::kNo;
1017     CoverageMode fCoverageMode;
1018 
1019     // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
1020     // to skip texturing.
1021     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1022     TextureSampler fSampler;
1023 
1024     typedef GrGeometryProcessor INHERITED;
1025 };
1026 
MakeProcessor(const VertexSpec & spec)1027 sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
1028     return QuadPerEdgeAAGeometryProcessor::Make(spec);
1029 }
1030 
MakeTexturedProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,const GrSamplerState & samplerState,const GrSwizzle & swizzle,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform,Saturate saturate)1031 sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
1032                                                  GrTextureType textureType,
1033                                                  const GrSamplerState& samplerState,
1034                                                  const GrSwizzle& swizzle, uint32_t extraSamplerKey,
1035                                                  sk_sp<GrColorSpaceXform> textureColorSpaceXform,
1036                                                  Saturate saturate) {
1037     return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, samplerState, swizzle,
1038                                                 extraSamplerKey, std::move(textureColorSpaceXform),
1039                                                 saturate);
1040 }
1041 
1042 } // namespace GrQuadPerEdgeAA
1043