• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
302 // Calculate area of intersection between quad (xs, ys) and a pixel at 'pixelCenter'.
303 // a, b, c are edge equations of the quad, flipped is true if the line equations had their normals
304 // reversed to correct for matrix transforms.
get_exact_coverage(const SkPoint & pixelCenter,const Vertices & quad,const Edges & edges)305 static float get_exact_coverage(const SkPoint& pixelCenter, const Vertices& quad,
306                                 const Edges& edges) {
307      // Ordering of vertices given default tri-strip that produces CCW points
308     static const int kCCW[] = {0, 1, 3, 2};
309     // Ordering of vertices given inverted tri-strip that produces CCW
310     static const int kFlippedCCW[] = {0, 2, 3, 1};
311 
312     // Edge boundaries of the pixel
313     float left = pixelCenter.fX - 0.5f;
314     float right = pixelCenter.fX + 0.5f;
315     float top = pixelCenter.fY - 0.5f;
316     float bot = pixelCenter.fY + 0.5f;
317 
318     // Whether or not the 4 corners of the pixel are inside the quad geometry. Variable names are
319     // intentional to work easily with the helper macros.
320     bool topleftInside = all((edges.fA * left + edges.fB * top + edges.fC) >= 0.f);
321     bool botleftInside = all((edges.fA * left + edges.fB * bot + edges.fC) >= 0.f);
322     bool botrightInside = all((edges.fA * right + edges.fB * bot + edges.fC) >= 0.f);
323     bool toprightInside = all((edges.fA * right + edges.fB * top + edges.fC) >= 0.f);
324     if (topleftInside && botleftInside && botrightInside && toprightInside) {
325         // Quad fully contains the pixel, so we know the area will be 1.f
326         return 1.f;
327     }
328 
329     // Track whether or not the quad vertices in (xs, ys) are on the proper sides of l, t, r, and b
330     M4f leftValid = quad.fX >= left;
331     M4f rightValid = quad.fX <= right;
332     M4f topValid = quad.fY >= top;
333     M4f botValid = quad.fY <= bot;
334 
335     // Intercepts of quad lines with the 4 pixel edges
336     V4f leftCross = -(edges.fC + edges.fA * left) / edges.fB;
337     V4f rightCross = -(edges.fC + edges.fA * right) / edges.fB;
338     V4f topCross = -(edges.fC + edges.fB * top) / edges.fA;
339     V4f botCross = -(edges.fC + edges.fB * bot) / edges.fA;
340 
341     // State for implicitly tracking the intersection boundary and area
342     SkPoint firstPoint = {0.f, 0.f};
343     SkPoint lastPoint = {0.f, 0.f};
344     bool intersected = false;
345     float area = 0.f;
346 
347     // Adds a point to the intersection hull, remembering first point (for closing) and the
348     // current point, and updates the running area total.
349     // See http://mathworld.wolfram.com/PolygonArea.html
350     auto accumulate = [&](const SkPoint& p) {
351         if (intersected) {
352             float da = lastPoint.fX * p.fY - p.fX * lastPoint.fY;
353             area += da;
354         } else {
355             firstPoint = p;
356             intersected = true;
357         }
358         lastPoint = p;
359     };
360 
361     // Used during iteration over the quad points to check if edge intersections are valid and
362     // should be accumulated.
363 #define ADD_EDGE_CROSSING_X(SIDE) \
364     do { \
365         if (SIDE##Cross[ei] >= top && SIDE##Cross[ei] <= bot) { \
366             accumulate({SIDE, SIDE##Cross[ei]}); \
367             addedIntersection = true; \
368         } \
369     } while(false)
370 #define ADD_EDGE_CROSSING_Y(SIDE) \
371     do { \
372         if (SIDE##Cross[ei] >= left && SIDE##Cross[ei] <= right) { \
373             accumulate({SIDE##Cross[ei], SIDE}); \
374             addedIntersection = true; \
375         } \
376     } while(false)
377 #define TEST_EDGES(SIDE, AXIS, I, NI) \
378     do { \
379         if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
380             ADD_EDGE_CROSSING_##AXIS(SIDE); \
381             crossedEdges = true; \
382         } \
383     } while(false)
384     // Used during iteration over the quad points to check if a pixel corner should be included
385     // in the intersection boundary
386 #define ADD_CORNER(CHECK, SIDE_LR, SIDE_TB) \
387     if (!CHECK##Valid[i] || !CHECK##Valid[ni]) { \
388         if (SIDE_TB##SIDE_LR##Inside) { \
389             accumulate({SIDE_LR, SIDE_TB}); \
390         } \
391     }
392 #define TEST_CORNER_X(SIDE, I, NI) \
393     do { \
394         if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
395             ADD_CORNER(top, SIDE, top) else ADD_CORNER(bot, SIDE, bot) \
396         } \
397     } while(false)
398 #define TEST_CORNER_Y(SIDE, I, NI) \
399     do { \
400         if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
401             ADD_CORNER(left, left, SIDE) else ADD_CORNER(right, right, SIDE) \
402         } \
403     } while(false)
404 
405     // Iterate over the 4 points of the quad, adding valid intersections with the pixel edges
406     // or adding interior pixel corners as it goes. This automatically keeps all accumulated points
407     // in CCW ordering so the area can be calculated on the fly and there's no need to store the
408     // list of hull points. This is somewhat inspired by the Sutherland-Hodgman algorithm but since
409     // there are only 4 points in each source polygon, there is no point list maintenance.
410     for (int j = 0; j < 4; ++j) {
411         // Current vertex
412         int i = edges.fFlipped ? kFlippedCCW[j] : kCCW[j];
413         // Moving to this vertex
414         int ni = edges.fFlipped ? kFlippedCCW[(j + 1) % 4] : kCCW[(j + 1) % 4];
415         // Index in edge vectors corresponding to move from i to ni
416         int ei = edges.fFlipped ? ni : i;
417 
418         bool crossedEdges = false;
419         bool addedIntersection = false;
420 
421         // First check if there are any outside -> inside edge crossings. There can be 0, 1, or 2.
422         // 2 can occur if one crossing is still outside the pixel, or if they both go through
423         // the corner (in which case a duplicate point is added, but that doesn't change area).
424 
425         // Outside to inside crossing
426         TEST_EDGES(left, X, i, ni);
427         TEST_EDGES(right, X, i, ni);
428         TEST_EDGES(top, Y, i, ni);
429         TEST_EDGES(bot, Y, i, ni);
430         // Inside to outside crossing (swapping ni and i in the boolean test)
431         TEST_EDGES(left, X, ni, i);
432         TEST_EDGES(right, X, ni, i);
433         TEST_EDGES(top, Y, ni, i);
434         TEST_EDGES(bot, Y, ni, i);
435 
436         // If we crossed edges but didn't add any intersections, check the corners of the pixel.
437         // If the pixel corners are inside the quad, include them in the boundary.
438         if (crossedEdges && !addedIntersection) {
439             // This can lead to repeated points, but those just accumulate zero area
440             TEST_CORNER_X(left, i, ni);
441             TEST_CORNER_X(right, i, ni);
442             TEST_CORNER_Y(top, i, ni);
443             TEST_CORNER_Y(bot, i, ni);
444 
445             TEST_CORNER_X(left, ni, i);
446             TEST_CORNER_X(right, ni, i);
447             TEST_CORNER_Y(top, ni, i);
448             TEST_CORNER_Y(bot, ni, i);
449         }
450 
451         // Lastly, if the next point is completely inside the pixel it gets included in the boundary
452         if (leftValid[ni] && rightValid[ni] && topValid[ni] && botValid[ni]) {
453             accumulate({quad.fX[ni], quad.fY[ni]});
454         }
455     }
456 
457 #undef TEST_CORNER_Y
458 #undef TEST_CORNER_X
459 #undef ADD_CORNER
460 
461 #undef TEST_EDGES
462 #undef ADD_EDGE_CROSSING_Y
463 #undef ADD_EDGE_CROSSING_X
464 
465     // After all points have been considered, close the boundary to get final area. If we never
466     // added any points, it means the quad didn't intersect the pixel rectangle.
467     if (intersected) {
468         // Final equation for area of convex polygon is to multiply by -1/2 (minus since the points
469         // were in CCW order).
470         accumulate(firstPoint);
471         return -0.5f * area;
472     } else {
473         return 0.f;
474     }
475 }
476 
477 // Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
478 // parallel, or edges are very short/zero-length. Returns coverage for each vertex.
479 // 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)480 static V4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const V4f& mask, const Edges& edges,
481                                     bool outset, Vertices* quad) {
482     // Move the edge 1/2 pixel in or out depending on 'outset'.
483     V4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
484 
485     // There are 6 points that we care about to determine the final shape of the polygon, which
486     // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
487     // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
488     V4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
489     V4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
490     V4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
491     correct_bad_coords(abs(denom) < kTolerance, &px, &py, nullptr);
492 
493     // Calculate the signed distances from these 4 corners to the other two edges that did not
494     // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
495     // p(3) to e0,e2
496     V4f dists1 = px * skvx::shuffle<3, 3, 0, 0>(edges.fA) +
497                  py * skvx::shuffle<3, 3, 0, 0>(edges.fB) +
498                  skvx::shuffle<3, 3, 0, 0>(oc);
499     V4f dists2 = px * skvx::shuffle<1, 2, 1, 2>(edges.fA) +
500                  py * skvx::shuffle<1, 2, 1, 2>(edges.fB) +
501                  skvx::shuffle<1, 2, 1, 2>(oc);
502 
503     // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
504     // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
505     // and we need to use a central point to represent it. If all four points are only on the
506     // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
507     // Otherwise, use a triangle that replaces the bad points with the intersections of
508     // (e1, e2) or (e0, e3) as needed.
509     M4f d1v0 = dists1 < kTolerance;
510     M4f d2v0 = dists2 < kTolerance;
511     M4f d1And2 = d1v0 & d2v0;
512     M4f d1Or2 = d1v0 | d2v0;
513 
514     V4f coverage;
515     if (!any(d1Or2)) {
516         // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
517         // and use full coverage
518         coverage = 1.f;
519     } else if (any(d1And2)) {
520         // A point failed against two edges, so reduce the shape to a single point, which we take as
521         // the center of the original quad to ensure it is contained in the intended geometry. Since
522         // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
523         SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
524                           0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
525         coverage = get_exact_coverage(center, *quad, edges);
526         px = center.fX;
527         py = center.fY;
528     } else if (all(d1Or2)) {
529         // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
530         // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
531         if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
532             // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
533             px = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px));
534             py = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py));
535             float mc02 = get_exact_coverage({px[0], py[0]}, *quad, edges);
536             float mc13 = get_exact_coverage({px[1], py[1]}, *quad, edges);
537             coverage = V4f{mc02, mc13, mc02, mc13};
538         } else {
539             // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
540             px = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px));
541             py = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py));
542             float mc01 = get_exact_coverage({px[0], py[0]}, *quad, edges);
543             float mc23 = get_exact_coverage({px[2], py[2]}, *quad, edges);
544             coverage = V4f{mc01, mc01, mc23, mc23};
545         }
546     } else {
547         // This turns into a triangle. Replace corners as needed with the intersections between
548         // (e0,e3) and (e1,e2), which must now be calculated
549         using V2f = skvx::Vec<2, float>;
550         V2f eDenom = skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(edges.fB) -
551                       skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(edges.fA);
552         V2f ex = (skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(oc) -
553                    skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fB)) / eDenom;
554         V2f ey = (skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fA) -
555                    skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(oc)) / eDenom;
556 
557         if (SkScalarAbs(eDenom[0]) > kTolerance) {
558             px = if_then_else(d1v0, V4f(ex[0]), px);
559             py = if_then_else(d1v0, V4f(ey[0]), py);
560         }
561         if (SkScalarAbs(eDenom[1]) > kTolerance) {
562             px = if_then_else(d2v0, V4f(ex[1]), px);
563             py = if_then_else(d2v0, V4f(ey[1]), py);
564         }
565 
566         coverage = 1.f;
567     }
568 
569     outset_projected_vertices(px, py, aaFlags, quad);
570     return coverage;
571 }
572 
573 // Computes the vertices for the two nested quads used to create AA edges. The original single quad
574 // should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
575 // 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)576 static V4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
577                                          Vertices* inner, Vertices* outer, SkRect* domain) {
578     SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
579     SkASSERT(outer->fUVRCount == inner->fUVRCount);
580 
581     QuadMetadata metadata = get_metadata(*inner, aaFlags);
582 
583     // Calculate domain first before updating vertices. It's only used when not rectilinear.
584     if (!rectilinear) {
585         SkASSERT(domain);
586         // The domain is the bounding box of the quad, outset by 0.5. Don't worry about edge masks
587         // since the FP only applies the domain on the exterior triangles, which are degenerate for
588         // non-AA edges.
589         domain->fLeft = min(outer->fX) - 0.5f;
590         domain->fRight = max(outer->fX) + 0.5f;
591         domain->fTop = min(outer->fY) - 0.5f;
592         domain->fBottom = max(outer->fY) + 0.5f;
593     }
594 
595     // When outsetting, we want the new edge to be .5px away from the old line, which means the
596     // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
597     // is only computed if there are no empty edges, and it may signal going through the slow path.
598     V4f outset = 0.5f;
599     if (get_optimized_outset(metadata, rectilinear, &outset)) {
600        // Since it's not subpixel, outsetting and insetting are trivial vector additions.
601         outset_vertices(outset, metadata, outer);
602         outset_vertices(-outset, metadata, inner);
603         return 1.f;
604     }
605 
606     // Only compute edge equations once since they are the same for inner and outer quads
607     Edges edges = get_edge_equations(metadata, *inner);
608 
609     // Calculate both outset and inset, returning the coverage reported for the inset, since the
610     // outset will always have 0.0f.
611     compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
612     return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
613 }
614 
615 // Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
616 // division of the device coordinates, the original local coordinate value is at the original
617 // un-outset device position.
compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags,Vertices * inner,Vertices * outer,SkRect * domain)618 static V4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
619                                                Vertices* outer, SkRect* domain) {
620     SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
621     SkASSERT(outer->fUVRCount == inner->fUVRCount);
622 
623     // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
624     V4f iw = 1.0f / inner->fW;
625     V4f x2d = inner->fX * iw;
626     V4f y2d = inner->fY * iw;
627 
628     Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
629     Vertices outer2D = inner2D;
630 
631     V4f coverage = compute_nested_quad_vertices(
632             aaFlags, /* rect */ false, &inner2D, &outer2D, domain);
633 
634     // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
635     outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
636     outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
637 
638     return coverage;
639 }
640 
641 enum class CoverageMode {
642     kNone,
643     kWithPosition,
644     kWithColor
645 };
646 
get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec & spec)647 static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
648     if (spec.usesCoverageAA()) {
649         if (spec.compatibleWithCoverageAsAlpha() && spec.hasVertexColors() &&
650             !spec.requiresGeometryDomain()) {
651             // Using a geometric domain acts as a second source of coverage and folding the original
652             // coverage into color makes it impossible to apply the color's alpha to the geometric
653             // domain's coverage when the original shape is clipped.
654             return CoverageMode::kWithColor;
655         } else {
656             return CoverageMode::kWithPosition;
657         }
658     } else {
659         return CoverageMode::kNone;
660     }
661 }
662 
663 // Writes four vertices in triangle strip order, including the additional data for local
664 // coordinates, geometry + texture domains, color, and coverage as needed to satisfy the vertex spec
write_quad(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,CoverageMode mode,const V4f & coverage,SkPMColor4f color4f,const SkRect & geomDomain,const SkRect & texDomain,const Vertices & quad)665 static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
666                        CoverageMode mode, const V4f& coverage, SkPMColor4f color4f,
667                        const SkRect& geomDomain, const SkRect& texDomain, const Vertices& quad) {
668     static constexpr auto If = GrVertexWriter::If<float>;
669 
670     for (int i = 0; i < 4; ++i) {
671         // save position, this is a float2 or float3 or float4 depending on the combination of
672         // perspective and coverage mode.
673         vb->write(quad.fX[i], quad.fY[i],
674                   If(spec.deviceQuadType() == GrQuad::Type::kPerspective, quad.fW[i]),
675                   If(mode == CoverageMode::kWithPosition, coverage[i]));
676 
677         // save color
678         if (spec.hasVertexColors()) {
679             bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
680             vb->write(GrVertexColor(
681                     color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wide));
682         }
683 
684         // save local position
685         if (spec.hasLocalCoords()) {
686             vb->write(quad.fU[i], quad.fV[i],
687                       If(spec.localQuadType() == GrQuad::Type::kPerspective, quad.fR[i]));
688         }
689 
690         // save the geometry domain
691         if (spec.requiresGeometryDomain()) {
692             vb->write(geomDomain);
693         }
694 
695         // save the texture domain
696         if (spec.hasDomain()) {
697             vb->write(texDomain);
698         }
699     }
700 }
701 
702 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
703 
704 static const int kVertsPerAAFillRect = 8;
705 static const int kIndicesPerAAFillRect = 30;
706 
get_index_buffer(GrResourceProvider * resourceProvider)707 static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
708     GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
709 
710     // clang-format off
711     static const uint16_t gFillAARectIdx[] = {
712         0, 1, 2, 1, 3, 2,
713         0, 4, 1, 4, 5, 1,
714         0, 6, 4, 0, 2, 6,
715         2, 3, 6, 3, 7, 6,
716         1, 5, 3, 3, 5, 7,
717     };
718     // clang-format on
719 
720     GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
721     return resourceProvider->findOrCreatePatternedIndexBuffer(
722             gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
723             kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
724 }
725 
726 } // anonymous namespace
727 
728 namespace GrQuadPerEdgeAA {
729 
730 // This is a more elaborate version of SkPMColor4fNeedsWideColor that allows "no color" for white
MinColorType(SkPMColor4f color,GrClampType clampType,const GrCaps & caps)731 ColorType MinColorType(SkPMColor4f color, GrClampType clampType, const GrCaps& caps) {
732     if (color == SK_PMColor4fWHITE) {
733         return ColorType::kNone;
734     } else {
735         return SkPMColor4fNeedsWideColor(color, clampType, caps) ? ColorType::kHalf
736                                                                  : ColorType::kByte;
737     }
738 }
739 
740 ////////////////// Tessellate Implementation
741 
Tessellate(void * vertices,const VertexSpec & spec,const GrQuad & deviceQuad,const SkPMColor4f & color4f,const GrQuad & localQuad,const SkRect & domain,GrQuadAAFlags aaFlags)742 void* Tessellate(void* vertices, const VertexSpec& spec, const GrQuad& deviceQuad,
743                  const SkPMColor4f& color4f, const GrQuad& localQuad, const SkRect& domain,
744                  GrQuadAAFlags aaFlags) {
745     SkASSERT(deviceQuad.quadType() <= spec.deviceQuadType());
746     SkASSERT(!spec.hasLocalCoords() || localQuad.quadType() <= spec.localQuadType());
747 
748     CoverageMode mode = get_mode_for_spec(spec);
749 
750     // Load position data into V4fs (always x, y, and load w to avoid branching down the road)
751     Vertices outer;
752     outer.fX = deviceQuad.x4f();
753     outer.fY = deviceQuad.y4f();
754     outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
755 
756     // Load local position data into V4fs (either none, just u,v or all three)
757     outer.fUVRCount = spec.localDimensionality();
758     if (spec.hasLocalCoords()) {
759         outer.fU = localQuad.x4f();
760         outer.fV = localQuad.y4f();
761         outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
762     }
763 
764     GrVertexWriter vb{vertices};
765     if (spec.usesCoverageAA()) {
766         SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
767         // Must calculate two new quads, an outset and inset by .5 in projected device space, so
768         // duplicate the original quad for the inner space
769         Vertices inner = outer;
770 
771         SkRect geomDomain;
772         V4f maxCoverage = 1.f;
773         if (spec.deviceQuadType() == GrQuad::Type::kPerspective) {
774             // For perspective, send quads with all edges non-AA through the tessellation to ensure
775             // their corners are processed the same as adjacent quads. This approach relies on
776             // solving edge equations to reconstruct corners, which can create seams if an inner
777             // fully non-AA quad is not similarly processed.
778             maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer, &geomDomain);
779         } else if (aaFlags != GrQuadAAFlags::kNone) {
780             // In 2D, the simpler corner math does not cause issues with seaming against non-AA
781             // inner quads.
782             maxCoverage = compute_nested_quad_vertices(
783                     aaFlags, spec.deviceQuadType() <= GrQuad::Type::kRectilinear, &inner, &outer,
784                     &geomDomain);
785         } else if (spec.requiresGeometryDomain()) {
786             // The quad itself wouldn't need a geometric domain, but the batch does, so set the
787             // domain to the bounds of the X/Y coords. Since it's non-AA, this won't actually be
788             // evaluated by the shader, but make sure not to upload uninitialized data.
789             geomDomain.fLeft = min(outer.fX);
790             geomDomain.fRight = max(outer.fX);
791             geomDomain.fTop = min(outer.fY);
792             geomDomain.fBottom = max(outer.fY);
793         }
794 
795         // Write two quads for inner and outer, inner will use the
796         write_quad(&vb, spec, mode, maxCoverage, color4f, geomDomain, domain, inner);
797         write_quad(&vb, spec, mode, 0.f, color4f, geomDomain, domain, outer);
798     } else {
799         // No outsetting needed, just write a single quad with full coverage
800         SkASSERT(mode == CoverageMode::kNone && !spec.requiresGeometryDomain());
801         write_quad(&vb, spec, mode, 1.f, color4f, SkRect::MakeEmpty(), domain, outer);
802     }
803 
804     return vb.fPtr;
805 }
806 
ConfigureMeshIndices(GrMeshDrawOp::Target * target,GrMesh * mesh,const VertexSpec & spec,int quadCount)807 bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
808                           int quadCount) {
809     if (spec.usesCoverageAA()) {
810         // AA quads use 8 vertices, basically nested rectangles
811         sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
812         if (!ibuffer) {
813             return false;
814         }
815 
816         mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
817         mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
818                                   quadCount, kNumAAQuadsInIndexBuffer);
819     } else {
820         // Non-AA quads use 4 vertices, and regular triangle strip layout
821         if (quadCount > 1) {
822             sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
823             if (!ibuffer) {
824                 return false;
825             }
826 
827             mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
828             mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
829                                       GrResourceProvider::QuadCountOfQuadBuffer());
830         } else {
831             mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
832             mesh->setNonIndexedNonInstanced(4);
833         }
834     }
835 
836     return true;
837 }
838 
839 ////////////////// VertexSpec Implementation
840 
deviceDimensionality() const841 int VertexSpec::deviceDimensionality() const {
842     return this->deviceQuadType() == GrQuad::Type::kPerspective ? 3 : 2;
843 }
844 
localDimensionality() const845 int VertexSpec::localDimensionality() const {
846     return fHasLocalCoords ? (this->localQuadType() == GrQuad::Type::kPerspective ? 3 : 2) : 0;
847 }
848 
849 ////////////////// Geometry Processor Implementation
850 
851 class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
852 public:
853 
Make(const VertexSpec & spec)854     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
855         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
856     }
857 
Make(const VertexSpec & vertexSpec,const GrShaderCaps & caps,GrTextureType textureType,const GrSamplerState & samplerState,const GrSwizzle & swizzle,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)858     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
859                                            GrTextureType textureType,
860                                            const GrSamplerState& samplerState,
861                                            const GrSwizzle& swizzle, uint32_t extraSamplerKey,
862                                            sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
863         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
864                 vertexSpec, caps, textureType, samplerState, swizzle, extraSamplerKey,
865                 std::move(textureColorSpaceXform)));
866     }
867 
name() const868     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
869 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const870     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
871         // texturing, device-dimensions are single bit flags
872         uint32_t x = fTexDomain.isInitialized() ? 0 : 1;
873         x |= fSampler.isInitialized() ? 0 : 2;
874         x |= fNeedsPerspective ? 0 : 4;
875         // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
876         if (fLocalCoord.isInitialized()) {
877             x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
878         }
879         // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
880         if (fColor.isInitialized()) {
881             x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
882         }
883         // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor, 11 for
884         // position+geomdomain
885         SkASSERT(!fGeomDomain.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
886         if (fCoverageMode != CoverageMode::kNone) {
887             x |= fGeomDomain.isInitialized() ?
888                     384 : (CoverageMode::kWithPosition == fCoverageMode ? 128 : 256);
889         }
890 
891         b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
892         b->add32(x);
893     }
894 
createGLSLInstance(const GrShaderCaps & caps) const895     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
896         class GLSLProcessor : public GrGLSLGeometryProcessor {
897         public:
898             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
899                          FPCoordTransformIter&& transformIter) override {
900                 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
901                 if (gp.fLocalCoord.isInitialized()) {
902                     this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
903                 }
904                 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
905             }
906 
907         private:
908             void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
909                 using Interpolation = GrGLSLVaryingHandler::Interpolation;
910 
911                 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
912                 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
913                                                        gp.fTextureColorSpaceXform.get());
914 
915                 args.fVaryingHandler->emitAttributes(gp);
916 
917                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
918                     // Strip last channel from the vertex attribute to remove coverage and get the
919                     // actual position
920                     if (gp.fNeedsPerspective) {
921                         args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
922                                                        gp.fPosition.name());
923                     } else {
924                         args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
925                                                        gp.fPosition.name());
926                     }
927                     gpArgs->fPositionVar = {"position",
928                                             gp.fNeedsPerspective ? kFloat3_GrSLType
929                                                                  : kFloat2_GrSLType,
930                                             GrShaderVar::kNone_TypeModifier};
931                 } else {
932                     // No coverage to eliminate
933                     gpArgs->fPositionVar = gp.fPosition.asShaderVar();
934                 }
935 
936                 // Handle local coordinates if they exist
937                 if (gp.fLocalCoord.isInitialized()) {
938                     // NOTE: If the only usage of local coordinates is for the inline texture fetch
939                     // before FPs, then there are no registered FPCoordTransforms and this ends up
940                     // emitting nothing, so there isn't a duplication of local coordinates
941                     this->emitTransforms(args.fVertBuilder,
942                                          args.fVaryingHandler,
943                                          args.fUniformHandler,
944                                          gp.fLocalCoord.asShaderVar(),
945                                          args.fFPCoordTransformHandler);
946                 }
947 
948                 // Solid color before any texturing gets modulated in
949                 if (gp.fColor.isInitialized()) {
950                     SkASSERT(gp.fCoverageMode != CoverageMode::kWithColor || !gp.fNeedsPerspective);
951                     // The color cannot be flat if the varying coverage has been modulated into it
952                     args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
953                             gp.fCoverageMode == CoverageMode::kWithColor ?
954                             Interpolation::kInterpolated : Interpolation::kCanBeFlat);
955                 } else {
956                     // Output color must be initialized to something
957                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
958                 }
959 
960                 // If there is a texture, must also handle texture coordinates and reading from
961                 // the texture in the fragment shader before continuing to fragment processors.
962                 if (gp.fSampler.isInitialized()) {
963                     // Texture coordinates clamped by the domain on the fragment shader; if the GP
964                     // has a texture, it's guaranteed to have local coordinates
965                     args.fFragBuilder->codeAppend("float2 texCoord;");
966                     if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
967                         // Can't do a pass through since we need to perform perspective division
968                         GrGLSLVarying v(gp.fLocalCoord.gpuType());
969                         args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
970                         args.fVertBuilder->codeAppendf("%s = %s;",
971                                                        v.vsOut(), gp.fLocalCoord.name());
972                         args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
973                                                        v.fsIn(), v.fsIn());
974                     } else {
975                         args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
976                     }
977 
978                     // Clamp the now 2D localCoordName variable by the domain if it is provided
979                     if (gp.fTexDomain.isInitialized()) {
980                         args.fFragBuilder->codeAppend("float4 domain;");
981                         args.fVaryingHandler->addPassThroughAttribute(gp.fTexDomain, "domain",
982                                                                       Interpolation::kCanBeFlat);
983                         args.fFragBuilder->codeAppend(
984                                 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
985                     }
986 
987                     // Now modulate the starting output color by the texture lookup
988                     args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
989                     args.fFragBuilder->appendTextureLookupAndModulate(
990                         args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
991                         &fTextureColorSpaceXformHelper);
992                     args.fFragBuilder->codeAppend(";");
993                 }
994 
995                 // And lastly, output the coverage calculation code
996                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
997                     GrGLSLVarying coverage(kFloat_GrSLType);
998                     args.fVaryingHandler->addVarying("coverage", &coverage);
999                     if (gp.fNeedsPerspective) {
1000                         // Multiply by "W" in the vertex shader, then by 1/w (sk_FragCoord.w) in
1001                         // the fragment shader to get screen-space linear coverage.
1002                         args.fVertBuilder->codeAppendf("%s = %s.w * %s.z;",
1003                                                        coverage.vsOut(), gp.fPosition.name(),
1004                                                        gp.fPosition.name());
1005                         args.fFragBuilder->codeAppendf("float coverage = %s * sk_FragCoord.w;",
1006                                                         coverage.fsIn());
1007                     } else {
1008                         args.fVertBuilder->codeAppendf("%s = %s;",
1009                                                        coverage.vsOut(), gp.fCoverage.name());
1010                         args.fFragBuilder->codeAppendf("float coverage = %s;", coverage.fsIn());
1011                     }
1012 
1013                     if (gp.fGeomDomain.isInitialized()) {
1014                         // Calculate distance from sk_FragCoord to the 4 edges of the domain
1015                         // and clamp them to (0, 1). Use the minimum of these and the original
1016                         // coverage. This only has to be done in the exterior triangles, the
1017                         // interior of the quad geometry can never be clipped by the domain box.
1018                         args.fFragBuilder->codeAppend("float4 geoDomain;");
1019                         args.fVaryingHandler->addPassThroughAttribute(gp.fGeomDomain, "geoDomain",
1020                                         Interpolation::kCanBeFlat);
1021                         args.fFragBuilder->codeAppend(
1022                                 "if (coverage < 0.5) {"
1023                                 "   float4 dists4 = clamp(float4(1, 1, -1, -1) * "
1024                                         "(sk_FragCoord.xyxy - geoDomain), 0, 1);"
1025                                 "   float2 dists2 = dists4.xy * dists4.zw;"
1026                                 "   coverage = min(coverage, dists2.x * dists2.y);"
1027                                 "}");
1028                     }
1029 
1030                     args.fFragBuilder->codeAppendf("%s = half4(half(coverage));",
1031                                                    args.fOutputCoverage);
1032                 } else {
1033                     // Set coverage to 1, since it's either non-AA or the coverage was already
1034                     // folded into the output color
1035                     SkASSERT(!gp.fGeomDomain.isInitialized());
1036                     args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
1037                 }
1038             }
1039             GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
1040         };
1041         return new GLSLProcessor;
1042     }
1043 
1044 private:
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec)1045     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
1046             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
1047             , fTextureColorSpaceXform(nullptr) {
1048         SkASSERT(!spec.hasDomain());
1049         this->initializeAttrs(spec);
1050         this->setTextureSamplerCnt(0);
1051     }
1052 
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,const GrSamplerState & samplerState,const GrSwizzle & swizzle,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)1053     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec,
1054                                    const GrShaderCaps& caps,
1055                                    GrTextureType textureType,
1056                                    const GrSamplerState& samplerState,
1057                                    const GrSwizzle& swizzle,
1058                                    uint32_t extraSamplerKey,
1059                                    sk_sp<GrColorSpaceXform> textureColorSpaceXform)
1060             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
1061             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
1062             , fSampler(textureType, samplerState, swizzle, extraSamplerKey) {
1063         SkASSERT(spec.hasLocalCoords());
1064         this->initializeAttrs(spec);
1065         this->setTextureSamplerCnt(1);
1066     }
1067 
initializeAttrs(const VertexSpec & spec)1068     void initializeAttrs(const VertexSpec& spec) {
1069         fNeedsPerspective = spec.deviceDimensionality() == 3;
1070         fCoverageMode = get_mode_for_spec(spec);
1071 
1072         if (fCoverageMode == CoverageMode::kWithPosition) {
1073             if (fNeedsPerspective) {
1074                 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1075             } else {
1076                 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1077                 fCoverage = {"coverage", kFloat_GrVertexAttribType, kFloat_GrSLType};
1078             }
1079         } else {
1080             if (fNeedsPerspective) {
1081                 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1082             } else {
1083                 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1084             }
1085         }
1086 
1087         // Need a geometry domain when the quads are AA and not rectilinear, since their AA
1088         // outsetting can go beyond a half pixel.
1089         if (spec.requiresGeometryDomain()) {
1090             fGeomDomain = {"geomDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1091         }
1092 
1093         int localDim = spec.localDimensionality();
1094         if (localDim == 3) {
1095             fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1096         } else if (localDim == 2) {
1097             fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1098         } // else localDim == 0 and attribute remains uninitialized
1099 
1100         if (ColorType::kByte == spec.colorType()) {
1101             fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
1102         } else if (ColorType::kHalf == spec.colorType()) {
1103             fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
1104         }
1105 
1106         if (spec.hasDomain()) {
1107             fTexDomain = {"texDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1108         }
1109 
1110         this->setVertexAttributes(&fPosition, 6);
1111     }
1112 
onTextureSampler(int) const1113     const TextureSampler& onTextureSampler(int) const override { return fSampler; }
1114 
1115     Attribute fPosition; // May contain coverage as last channel
1116     Attribute fCoverage; // Used for non-perspective position to avoid Intel Metal issues
1117     Attribute fColor; // May have coverage modulated in if the FPs support it
1118     Attribute fLocalCoord;
1119     Attribute fGeomDomain; // Screen-space bounding box on geometry+aa outset
1120     Attribute fTexDomain; // Texture-space bounding box on local coords
1121 
1122     // The positions attribute may have coverage built into it, so float3 is an ambiguous type
1123     // and may mean 2d with coverage, or 3d with no coverage
1124     bool fNeedsPerspective;
1125     CoverageMode fCoverageMode;
1126 
1127     // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
1128     // to skip texturing.
1129     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1130     TextureSampler fSampler;
1131 
1132     typedef GrGeometryProcessor INHERITED;
1133 };
1134 
MakeProcessor(const VertexSpec & spec)1135 sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
1136     return QuadPerEdgeAAGeometryProcessor::Make(spec);
1137 }
1138 
MakeTexturedProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,const GrSamplerState & samplerState,const GrSwizzle & swizzle,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)1139 sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
1140                                                  GrTextureType textureType,
1141                                                  const GrSamplerState& samplerState,
1142                                                  const GrSwizzle& swizzle, uint32_t extraSamplerKey,
1143                                                  sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1144     return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, samplerState, swizzle,
1145                                                 extraSamplerKey, std::move(textureColorSpaceXform));
1146 }
1147 
1148 } // namespace GrQuadPerEdgeAA
1149