blob: f70f953a14b6ee37af6ec1815c45741862f728d8 [file] [log] [blame]
Michael Ludwig460eb5e2018-10-29 11:09:29 -04001/*
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 "GrQuadPerEdgeAA.h"
9#include "GrQuad.h"
Michael Ludwigc182b942018-11-16 10:27:51 -050010#include "GrVertexWriter.h"
Michael Ludwig20e909e2018-10-30 10:43:57 -040011#include "glsl/GrGLSLColorSpaceXformHelper.h"
Michael Ludwig467994d2018-12-03 14:58:31 +000012#include "glsl/GrGLSLGeometryProcessor.h"
Michael Ludwig20e909e2018-10-30 10:43:57 -040013#include "glsl/GrGLSLPrimitiveProcessor.h"
14#include "glsl/GrGLSLFragmentShaderBuilder.h"
15#include "glsl/GrGLSLVarying.h"
16#include "glsl/GrGLSLVertexGeoBuilder.h"
Michael Ludwig460eb5e2018-10-29 11:09:29 -040017#include "SkNx.h"
18
Michael Ludwigf995c052018-11-26 15:24:29 -050019#define AI SK_ALWAYS_INLINE
20
Michael Ludwig460eb5e2018-10-29 11:09:29 -040021namespace {
22
Michael Ludwige6266a22019-03-07 11:24:32 -050023// Helper data types since there is a lot of information that needs to be passed around to
24// avoid recalculation in the different procedures for tessellating an AA quad.
25
26struct Vertices {
27 // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
28 Sk4f fX, fY, fW;
29 // U, V, and R coordinates representing local quad. Ignored depending on uvrCount (0, 1, 2).
30 Sk4f fU, fV, fR;
31 int fUVRCount;
32};
33
34struct QuadMetadata {
35 // Normalized edge vectors of the device space quad, ordered L, B, T, R (i.e. nextCCW(x) - x).
36 Sk4f fDX, fDY;
37 // 1 / edge length of the device space quad
38 Sk4f fInvLengths;
39 // Edge mask (set to all 1s if aa flags is kAll), otherwise 1.f if edge was AA, 0.f if non-AA.
40 Sk4f fMask;
41};
42
43struct Edges {
44 // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
45 Sk4f fA, fB, fC;
46 // Whether or not the edge normals had to be flipped to preserve positive distance on the inside
47 bool fFlipped;
48};
49
50static constexpr float kTolerance = 1e-2f;
51
Michael Ludwigf995c052018-11-26 15:24:29 -050052static AI Sk4f fma(const Sk4f& f, const Sk4f& m, const Sk4f& a) {
53 return SkNx_fma<4, float>(f, m, a);
54}
55
56// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
57// order.
58static AI Sk4f nextCW(const Sk4f& v) {
59 return SkNx_shuffle<2, 0, 3, 1>(v);
60}
61
62static AI Sk4f nextCCW(const Sk4f& v) {
63 return SkNx_shuffle<1, 3, 0, 2>(v);
64}
65
Michael Ludwige6266a22019-03-07 11:24:32 -050066// Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector.
67// e3 may be null if only 2D edges need to be corrected for.
68static AI void correct_bad_edges(const Sk4f& bad, Sk4f* e1, Sk4f* e2, Sk4f* e3) {
69 if (bad.anyTrue()) {
70 // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding
71 *e1 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e1), *e1);
72 *e2 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e2), *e2);
73 if (e3) {
74 *e3 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e3), *e3);
75 }
76 }
Michael Ludwigf995c052018-11-26 15:24:29 -050077}
78
Michael Ludwige6266a22019-03-07 11:24:32 -050079// Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points.
80static AI void correct_bad_coords(const Sk4f& bad, Sk4f* c1, Sk4f* c2, Sk4f* c3) {
81 if (bad.anyTrue()) {
82 *c1 = bad.thenElse(nextCCW(*c1), *c1);
83 *c2 = bad.thenElse(nextCCW(*c2), *c2);
84 if (c3) {
85 *c3 = bad.thenElse(nextCCW(*c3), *c3);
86 }
87 }
Michael Ludwig4921dc32018-12-03 14:57:29 +000088}
89
Michael Ludwige6266a22019-03-07 11:24:32 -050090static AI QuadMetadata get_metadata(const Vertices& vertices, GrQuadAAFlags aaFlags) {
91 Sk4f dx = nextCCW(vertices.fX) - vertices.fX;
92 Sk4f dy = nextCCW(vertices.fY) - vertices.fY;
93 Sk4f invLengths = fma(dx, dx, dy * dy).rsqrt();
94
95 Sk4f mask = aaFlags == GrQuadAAFlags::kAll ? Sk4f(1.f) :
96 Sk4f((GrQuadAAFlags::kLeft & aaFlags) ? 1.f : 0.f,
97 (GrQuadAAFlags::kBottom & aaFlags) ? 1.f : 0.f,
98 (GrQuadAAFlags::kTop & aaFlags) ? 1.f : 0.f,
99 (GrQuadAAFlags::kRight & aaFlags) ? 1.f : 0.f);
100 return { dx * invLengths, dy * invLengths, invLengths, mask };
101}
102
103static AI Edges get_edge_equations(const QuadMetadata& metadata, const Vertices& vertices) {
104 Sk4f dx = metadata.fDX;
105 Sk4f dy = metadata.fDY;
106 // Correct for bad edges by copying adjacent edge information into the bad component
107 correct_bad_edges(metadata.fInvLengths >= 1.f / kTolerance, &dx, &dy, nullptr);
108
109 Sk4f c = fma(dx, vertices.fY, -dy * vertices.fX);
110 // Make sure normals point into the shape
111 Sk4f test = fma(dy, nextCW(vertices.fX), fma(-dx, nextCW(vertices.fY), c));
112 if ((test < -kTolerance).anyTrue()) {
113 return {-dy, dx, -c, true};
114 } else {
115 return {dy, -dx, c, false};
116 }
117}
118
119// Sets 'outset' to the magnitude of outset/inset to adjust each corner of a quad given the
120// edge angles and lengths. If the quad is too small, has empty edges, or too sharp of angles,
121// false is returned and the degenerate slow-path should be used.
122static bool get_optimized_outset(const QuadMetadata& metadata, bool rectilinear, Sk4f* outset) {
123 if (rectilinear) {
124 *outset = 0.5f;
125 // Stay in the fast path as long as all edges are at least a pixel long (so 1/len <= 1)
126 return (metadata.fInvLengths <= 1.f).allTrue();
127 }
128
129 if ((metadata.fInvLengths >= 1.f / kTolerance).anyTrue()) {
130 // Have an empty edge from a degenerate quad, so there's no hope
131 return false;
132 }
133
134 // The distance the point needs to move is 1/2sin(theta), where theta is the angle between the
135 // two edges at that point. cos(theta) is equal to dot(dxy, nextCW(dxy))
136 Sk4f cosTheta = fma(metadata.fDX, nextCW(metadata.fDX), metadata.fDY * nextCW(metadata.fDY));
137 // If the angle is too shallow between edges, go through the degenerate path, otherwise adding
138 // and subtracting very large vectors in almost opposite directions leads to float errors
139 if ((cosTheta.abs() >= 0.9f).anyTrue()) {
140 return false;
141 }
142 *outset = 0.5f * (1.f - cosTheta * cosTheta).rsqrt(); // 1/2sin(theta)
143
144 // When outsetting or insetting, the current edge's AA adds to the length:
145 // cos(pi - theta)/2sin(theta) + cos(pi-ccw(theta))/2sin(ccw(theta))
146 // Moving an adjacent edge updates the length by 1/2sin(theta|ccw(theta))
147 Sk4f halfTanTheta = -cosTheta * (*outset); // cos(pi - theta) = -cos(theta)
148 Sk4f edgeAdjust = metadata.fMask * (halfTanTheta + nextCCW(halfTanTheta)) +
149 nextCCW(metadata.fMask) * nextCCW(*outset) +
150 nextCW(metadata.fMask) * (*outset);
151 // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make edgeLen negative
152 // then use the slow path
153 Sk4f threshold = 0.1f - metadata.fInvLengths.invert();
154 return (edgeAdjust > threshold).allTrue() && (edgeAdjust < -threshold).allTrue();
155}
156
157// Ignores the quad's fW, use outset_projected_vertices if it's known to need 3D.
158static AI void outset_vertices(const Sk4f& outset, const QuadMetadata& metadata, Vertices* quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500159 // The mask is rotated compared to the outsets and edge vectors, since if the edge is "on"
160 // both its points need to be moved along their other edge vectors.
Michael Ludwige6266a22019-03-07 11:24:32 -0500161 auto maskedOutset = -outset * nextCW(metadata.fMask);
162 auto maskedOutsetCW = outset * metadata.fMask;
163 // x = x + outset * mask * nextCW(xdiff) - outset * nextCW(mask) * xdiff
164 quad->fX += fma(maskedOutsetCW, nextCW(metadata.fDX), maskedOutset * metadata.fDX);
165 quad->fY += fma(maskedOutsetCW, nextCW(metadata.fDY), maskedOutset * metadata.fDY);
166 if (quad->fUVRCount > 0) {
Michael Ludwigf995c052018-11-26 15:24:29 -0500167 // We want to extend the texture coords by the same proportion as the positions.
Michael Ludwige6266a22019-03-07 11:24:32 -0500168 maskedOutset *= metadata.fInvLengths;
169 maskedOutsetCW *= nextCW(metadata.fInvLengths);
170 Sk4f du = nextCCW(quad->fU) - quad->fU;
171 Sk4f dv = nextCCW(quad->fV) - quad->fV;
172 quad->fU += fma(maskedOutsetCW, nextCW(du), maskedOutset * du);
173 quad->fV += fma(maskedOutsetCW, nextCW(dv), maskedOutset * dv);
174 if (quad->fUVRCount == 3) {
175 Sk4f dr = nextCCW(quad->fR) - quad->fR;
176 quad->fR += fma(maskedOutsetCW, nextCW(dr), maskedOutset * dr);
Michael Ludwigf995c052018-11-26 15:24:29 -0500177 }
178 }
179}
180
Michael Ludwige6266a22019-03-07 11:24:32 -0500181// Updates (x,y,w) to be at (x2d,y2d) once projected. Updates (u,v,r) to match if provided.
182// Gracefully handles 2D content if *w holds all 1s.
183static void outset_projected_vertices(const Sk4f& x2d, const Sk4f& y2d,
184 GrQuadAAFlags aaFlags, Vertices* quad) {
185 // Left to right, in device space, for each point
186 Sk4f e1x = SkNx_shuffle<2, 3, 2, 3>(quad->fX) - SkNx_shuffle<0, 1, 0, 1>(quad->fX);
187 Sk4f e1y = SkNx_shuffle<2, 3, 2, 3>(quad->fY) - SkNx_shuffle<0, 1, 0, 1>(quad->fY);
188 Sk4f e1w = SkNx_shuffle<2, 3, 2, 3>(quad->fW) - SkNx_shuffle<0, 1, 0, 1>(quad->fW);
189 correct_bad_edges(fma(e1x, e1x, e1y * e1y) < kTolerance * kTolerance, &e1x, &e1y, &e1w);
190
191 // // Top to bottom, in device space, for each point
192 Sk4f e2x = SkNx_shuffle<1, 1, 3, 3>(quad->fX) - SkNx_shuffle<0, 0, 2, 2>(quad->fX);
193 Sk4f e2y = SkNx_shuffle<1, 1, 3, 3>(quad->fY) - SkNx_shuffle<0, 0, 2, 2>(quad->fY);
194 Sk4f e2w = SkNx_shuffle<1, 1, 3, 3>(quad->fW) - SkNx_shuffle<0, 0, 2, 2>(quad->fW);
195 correct_bad_edges(fma(e2x, e2x, e2y * e2y) < kTolerance * kTolerance, &e2x, &e2y, &e2w);
196
197 // Can only move along e1 and e2 to reach the new 2D point, so we have
198 // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and
199 // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b
200 // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where
201 // the cNx and cNy coefficients are:
202 Sk4f c1x = e1w * x2d - e1x;
203 Sk4f c1y = e1w * y2d - e1y;
204 Sk4f c2x = e2w * x2d - e2x;
205 Sk4f c2y = e2w * y2d - e2y;
206 Sk4f c3x = quad->fW * x2d - quad->fX;
207 Sk4f c3y = quad->fW * y2d - quad->fY;
208
209 // Solve for a and b
210 Sk4f a, b, denom;
211 if (aaFlags == GrQuadAAFlags::kAll) {
212 // When every edge is outset/inset, each corner can use both edge vectors
213 denom = c1x * c2y - c2x * c1y;
214 a = (c2x * c3y - c3x * c2y) / denom;
215 b = (c3x * c1y - c1x * c3y) / denom;
216 } else {
217 // Force a or b to be 0 if that edge cannot be used due to non-AA
218 // FIXME requires the extra > 0.f, since Sk4f's thenElse only works if true values have
219 // all their bits set to 1.
220 Sk4f aMask = Sk4f((aaFlags & GrQuadAAFlags::kLeft) ? 1.f : 0.f,
221 (aaFlags & GrQuadAAFlags::kLeft) ? 1.f : 0.f,
222 (aaFlags & GrQuadAAFlags::kRight) ? 1.f : 0.f,
223 (aaFlags & GrQuadAAFlags::kRight) ? 1.f : 0.f) > 0.f;
224 Sk4f bMask = Sk4f((aaFlags & GrQuadAAFlags::kTop) ? 1.f : 0.f,
225 (aaFlags & GrQuadAAFlags::kBottom) ? 1.f : 0.f,
226 (aaFlags & GrQuadAAFlags::kTop) ? 1.f : 0.f,
227 (aaFlags & GrQuadAAFlags::kBottom) ? 1.f : 0.f) > 0.f;
228
229 // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case.
230 // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom
231 // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, ""
232 // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0
233 Sk4f useC1x = c1x.abs() > c1y.abs();
234 Sk4f useC2x = c2x.abs() > c2y.abs();
235 // -------- A & B ------ --------- A & !B ---------
236 denom = aMask.thenElse(bMask.thenElse(c1x * c2y - c2x * c1y, useC1x.thenElse(c1x, c1y)),
237 // ------- !A & B ---------- - !A & !B -
238 bMask.thenElse(useC2x.thenElse(c2x, c2y), 1.0f));
239 // -------- A & B ------ ---------- A & !B ----------
240 a = aMask.thenElse(bMask.thenElse(c2x * c3y - c3x * c2y, useC1x.thenElse(-c3x, -c3y)),
241 // - !A -
242 0.0f) / denom;
243 // -------- A & B ------ ---------- !A & B ----------
244 b = bMask.thenElse(aMask.thenElse(c3x * c1y - c1x * c3y, useC2x.thenElse(-c3x, -c3y)),
245 // - !B -
246 0.0f) / denom;
247 }
248
249 quad->fX += a * e1x + b * e2x;
250 quad->fY += a * e1y + b * e2y;
251 quad->fW += a * e1w + b * e2w;
252 correct_bad_coords(denom.abs() < kTolerance, &quad->fX, &quad->fY, &quad->fW);
253
254 if (quad->fUVRCount > 0) {
255 // Calculate R here so it can be corrected with U and V in case it's needed later
256 Sk4f e1u = SkNx_shuffle<2, 3, 2, 3>(quad->fU) - SkNx_shuffle<0, 1, 0, 1>(quad->fU);
257 Sk4f e1v = SkNx_shuffle<2, 3, 2, 3>(quad->fV) - SkNx_shuffle<0, 1, 0, 1>(quad->fV);
258 Sk4f e1r = SkNx_shuffle<2, 3, 2, 3>(quad->fR) - SkNx_shuffle<0, 1, 0, 1>(quad->fR);
259 correct_bad_edges(fma(e1u, e1u, e1v * e1v) < kTolerance * kTolerance, &e1u, &e1v, &e1r);
260
261 Sk4f e2u = SkNx_shuffle<1, 1, 3, 3>(quad->fU) - SkNx_shuffle<0, 0, 2, 2>(quad->fU);
262 Sk4f e2v = SkNx_shuffle<1, 1, 3, 3>(quad->fV) - SkNx_shuffle<0, 0, 2, 2>(quad->fV);
263 Sk4f e2r = SkNx_shuffle<1, 1, 3, 3>(quad->fR) - SkNx_shuffle<0, 0, 2, 2>(quad->fR);
264 correct_bad_edges(fma(e2u, e2u, e2v * e2v) < kTolerance * kTolerance, &e2u, &e2v, &e2r);
265
266 quad->fU += a * e1u + b * e2u;
267 quad->fV += a * e1v + b * e2v;
268 if (quad->fUVRCount == 3) {
269 quad->fR += a * e1r + b * e2r;
270 correct_bad_coords(denom.abs() < kTolerance, &quad->fU, &quad->fV, &quad->fR);
271 } else {
272 correct_bad_coords(denom.abs() < kTolerance, &quad->fU, &quad->fV, nullptr);
Michael Ludwigf995c052018-11-26 15:24:29 -0500273 }
274 }
275}
276
Michael Ludwige6266a22019-03-07 11:24:32 -0500277// Calculate area of intersection between quad (xs, ys) and a pixel at 'pixelCenter'.
278// a, b, c are edge equations of the quad, flipped is true if the line equations had their normals
279// reversed to correct for matrix transforms.
280static float get_exact_coverage(const SkPoint& pixelCenter, const Vertices& quad,
281 const Edges& edges) {
282 // Ordering of vertices given default tri-strip that produces CCW points
283 static const int kCCW[] = {0, 1, 3, 2};
284 // Ordering of vertices given inverted tri-strip that produces CCW
285 static const int kFlippedCCW[] = {0, 2, 3, 1};
286
287 // Edge boundaries of the pixel
288 float left = pixelCenter.fX - 0.5f;
289 float right = pixelCenter.fX + 0.5f;
290 float top = pixelCenter.fY - 0.5f;
291 float bot = pixelCenter.fY + 0.5f;
292
293 // Whether or not the 4 corners of the pixel are inside the quad geometry. Variable names are
294 // intentional to work easily with the helper macros.
295 bool topleftInside = ((edges.fA * left + edges.fB * top + edges.fC) >= 0.f).allTrue();
296 bool botleftInside = ((edges.fA * left + edges.fB * bot + edges.fC) >= 0.f).allTrue();
297 bool botrightInside = ((edges.fA * right + edges.fB * bot + edges.fC) >= 0.f).allTrue();
298 bool toprightInside = ((edges.fA * right + edges.fB * top + edges.fC) >= 0.f).allTrue();
299 if (topleftInside && botleftInside && botrightInside && toprightInside) {
300 // Quad fully contains the pixel, so we know the area will be 1.f
301 return 1.f;
302 }
303
304 // Track whether or not the quad vertices in (xs, ys) are on the proper sides of l, t, r, and b
305 Sk4i leftValid = SkNx_cast<int32_t>(quad.fX >= left);
306 Sk4i rightValid = SkNx_cast<int32_t>(quad.fX <= right);
307 Sk4i topValid = SkNx_cast<int32_t>(quad.fY >= top);
308 Sk4i botValid = SkNx_cast<int32_t>(quad.fY <= bot);
309
310 // Intercepts of quad lines with the 4 pixel edges
311 Sk4f leftCross = -(edges.fC + edges.fA * left) / edges.fB;
312 Sk4f rightCross = -(edges.fC + edges.fA * right) / edges.fB;
313 Sk4f topCross = -(edges.fC + edges.fB * top) / edges.fA;
314 Sk4f botCross = -(edges.fC + edges.fB * bot) / edges.fA;
315
316 // State for implicitly tracking the intersection boundary and area
317 SkPoint firstPoint = {0.f, 0.f};
318 SkPoint lastPoint = {0.f, 0.f};
319 bool intersected = false;
320 float area = 0.f;
321
322 // Adds a point to the intersection hull, remembering first point (for closing) and the
323 // current point, and updates the running area total.
324 // See http://mathworld.wolfram.com/PolygonArea.html
325 auto accumulate = [&](const SkPoint& p) {
326 if (intersected) {
327 float da = lastPoint.fX * p.fY - p.fX * lastPoint.fY;
328 area += da;
329 } else {
330 firstPoint = p;
331 intersected = true;
332 }
333 lastPoint = p;
334 };
335
336 // Used during iteration over the quad points to check if edge intersections are valid and
337 // should be accumulated.
338#define ADD_EDGE_CROSSING_X(SIDE) \
339 do { \
340 if (SIDE##Cross[ei] >= top && SIDE##Cross[ei] <= bot) { \
341 accumulate({SIDE, SIDE##Cross[ei]}); \
342 addedIntersection = true; \
343 } \
344 } while(false)
345#define ADD_EDGE_CROSSING_Y(SIDE) \
346 do { \
347 if (SIDE##Cross[ei] >= left && SIDE##Cross[ei] <= right) { \
348 accumulate({SIDE##Cross[ei], SIDE}); \
349 addedIntersection = true; \
350 } \
351 } while(false)
352#define TEST_EDGES(SIDE, AXIS, I, NI) \
353 do { \
354 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
355 ADD_EDGE_CROSSING_##AXIS(SIDE); \
356 crossedEdges = true; \
357 } \
358 } while(false)
359 // Used during iteration over the quad points to check if a pixel corner should be included
360 // in the intersection boundary
361#define ADD_CORNER(CHECK, SIDE_LR, SIDE_TB) \
362 if (!CHECK##Valid[i] || !CHECK##Valid[ni]) { \
363 if (SIDE_TB##SIDE_LR##Inside) { \
364 accumulate({SIDE_LR, SIDE_TB}); \
365 } \
366 }
367#define TEST_CORNER_X(SIDE, I, NI) \
368 do { \
369 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
370 ADD_CORNER(top, SIDE, top) else ADD_CORNER(bot, SIDE, bot) \
371 } \
372 } while(false)
373#define TEST_CORNER_Y(SIDE, I, NI) \
374 do { \
375 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
376 ADD_CORNER(left, left, SIDE) else ADD_CORNER(right, right, SIDE) \
377 } \
378 } while(false)
379
380 // Iterate over the 4 points of the quad, adding valid intersections with the pixel edges
381 // or adding interior pixel corners as it goes. This automatically keeps all accumulated points
382 // in CCW ordering so the area can be calculated on the fly and there's no need to store the
383 // list of hull points. This is somewhat inspired by the Sutherland-Hodgman algorithm but since
384 // there are only 4 points in each source polygon, there is no point list maintenance.
385 for (int j = 0; j < 4; ++j) {
386 // Current vertex
387 int i = edges.fFlipped ? kFlippedCCW[j] : kCCW[j];
388 // Moving to this vertex
389 int ni = edges.fFlipped ? kFlippedCCW[(j + 1) % 4] : kCCW[(j + 1) % 4];
390 // Index in edge vectors corresponding to move from i to ni
391 int ei = edges.fFlipped ? ni : i;
392
393 bool crossedEdges = false;
394 bool addedIntersection = false;
395
396 // First check if there are any outside -> inside edge crossings. There can be 0, 1, or 2.
397 // 2 can occur if one crossing is still outside the pixel, or if they both go through
398 // the corner (in which case a duplicate point is added, but that doesn't change area).
399
400 // Outside to inside crossing
401 TEST_EDGES(left, X, i, ni);
402 TEST_EDGES(right, X, i, ni);
403 TEST_EDGES(top, Y, i, ni);
404 TEST_EDGES(bot, Y, i, ni);
405 // Inside to outside crossing (swapping ni and i in the boolean test)
406 TEST_EDGES(left, X, ni, i);
407 TEST_EDGES(right, X, ni, i);
408 TEST_EDGES(top, Y, ni, i);
409 TEST_EDGES(bot, Y, ni, i);
410
411 // If we crossed edges but didn't add any intersections, check the corners of the pixel.
412 // If the pixel corners are inside the quad, include them in the boundary.
413 if (crossedEdges && !addedIntersection) {
414 // This can lead to repeated points, but those just accumulate zero area
415 TEST_CORNER_X(left, i, ni);
416 TEST_CORNER_X(right, i, ni);
417 TEST_CORNER_Y(top, i, ni);
418 TEST_CORNER_Y(bot, i, ni);
419
420 TEST_CORNER_X(left, ni, i);
421 TEST_CORNER_X(right, ni, i);
422 TEST_CORNER_Y(top, ni, i);
423 TEST_CORNER_Y(bot, ni, i);
424 }
425
426 // Lastly, if the next point is completely inside the pixel it gets included in the boundary
427 if (leftValid[ni] && rightValid[ni] && topValid[ni] && botValid[ni]) {
428 accumulate({quad.fX[ni], quad.fY[ni]});
429 }
430 }
431
432#undef TEST_CORNER_Y
433#undef TEST_CORNER_X
434#undef ADD_CORNER
435
436#undef TEST_EDGES
437#undef ADD_EDGE_CROSSING_Y
438#undef ADD_EDGE_CROSSING_X
439
440 // After all points have been considered, close the boundary to get final area. If we never
441 // added any points, it means the quad didn't intersect the pixel rectangle.
442 if (intersected) {
443 // Final equation for area of convex polygon is to multiply by -1/2 (minus since the points
444 // were in CCW order).
445 accumulate(firstPoint);
446 return -0.5f * area;
447 } else {
448 return 0.f;
449 }
450}
451
452// Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
453// parallel, or edges are very short/zero-length. Returns coverage for each vertex.
454// Requires (dx, dy) to already be fixed for empty edges.
455static Sk4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const Sk4f& mask, const Edges& edges,
456 bool outset, Vertices* quad) {
457 // Move the edge 1/2 pixel in or out depending on 'outset'.
458 Sk4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
459
460 // There are 6 points that we care about to determine the final shape of the polygon, which
461 // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
462 // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
463 Sk4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
464 Sk4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
465 Sk4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
466 correct_bad_coords(denom.abs() < kTolerance, &px, &py, nullptr);
467
468 // Calculate the signed distances from these 4 corners to the other two edges that did not
469 // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
470 // p(3) to e0,e2
471 Sk4f dists1 = px * SkNx_shuffle<3, 3, 0, 0>(edges.fA) +
472 py * SkNx_shuffle<3, 3, 0, 0>(edges.fB) +
473 SkNx_shuffle<3, 3, 0, 0>(oc);
474 Sk4f dists2 = px * SkNx_shuffle<1, 2, 1, 2>(edges.fA) +
475 py * SkNx_shuffle<1, 2, 1, 2>(edges.fB) +
476 SkNx_shuffle<1, 2, 1, 2>(oc);
477
478 // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
479 // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
480 // and we need to use a central point to represent it. If all four points are only on the
481 // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
482 // Otherwise, use a triangle that replaces the bad points with the intersections of
483 // (e1, e2) or (e0, e3) as needed.
484 Sk4f d1v0 = dists1 < kTolerance;
485 Sk4f d2v0 = dists2 < kTolerance;
486 // FIXME(michaelludwig): Sk4f has anyTrue() and allTrue(), but not & or |. Sk4i has & or | but
487 // not anyTrue() and allTrue(). Moving to SkVx from SkNx will clean this up.
488 Sk4i d1And2 = SkNx_cast<int32_t>(d1v0) & SkNx_cast<int32_t>(d2v0);
489 Sk4i d1Or2 = SkNx_cast<int32_t>(d1v0) | SkNx_cast<int32_t>(d2v0);
490
491 Sk4f coverage;
492 if (!d1Or2[0] && !d1Or2[1] && !d1Or2[2] && !d1Or2[3]) {
493 // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
494 // and use full coverage
495 coverage = 1.f;
496 } else if (d1And2[0] || d1And2[1] || d1And2[2] || d1And2[2]) {
497 // A point failed against two edges, so reduce the shape to a single point, which we take as
498 // the center of the original quad to ensure it is contained in the intended geometry. Since
499 // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
500 SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
501 0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
502 coverage = get_exact_coverage(center, *quad, edges);
503 px = center.fX;
504 py = center.fY;
505 } else if (d1Or2[0] && d1Or2[1] && d1Or2[2] && d1Or2[3]) {
506 // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
507 // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
508 if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
509 // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
510 px = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(px) + SkNx_shuffle<2, 3, 2, 3>(px));
511 py = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(py) + SkNx_shuffle<2, 3, 2, 3>(py));
512 float mc02 = get_exact_coverage({px[0], py[0]}, *quad, edges);
513 float mc13 = get_exact_coverage({px[1], py[1]}, *quad, edges);
514 coverage = Sk4f(mc02, mc13, mc02, mc13);
515 } else {
516 // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
517 px = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(px) + SkNx_shuffle<1, 1, 3, 3>(px));
518 py = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(py) + SkNx_shuffle<1, 1, 3, 3>(py));
519 float mc01 = get_exact_coverage({px[0], py[0]}, *quad, edges);
520 float mc23 = get_exact_coverage({px[2], py[2]}, *quad, edges);
521 coverage = Sk4f(mc01, mc01, mc23, mc23);
522 }
523 } else {
524 // This turns into a triangle. Replace corners as needed with the intersections between
525 // (e0,e3) and (e1,e2), which must now be calculated
526 Sk2f eDenom = SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(edges.fB) -
527 SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(edges.fA);
528 Sk2f ex = (SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(oc) -
529 SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fB)) / eDenom;
530 Sk2f ey = (SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fA) -
531 SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(oc)) / eDenom;
532
533 if (SkScalarAbs(eDenom[0]) > kTolerance) {
534 px = d1v0.thenElse(ex[0], px);
535 py = d1v0.thenElse(ey[0], py);
536 }
537 if (SkScalarAbs(eDenom[1]) > kTolerance) {
538 px = d2v0.thenElse(ex[1], px);
539 py = d2v0.thenElse(ey[1], py);
540 }
541
542 coverage = 1.f;
543 }
544
545 outset_projected_vertices(px, py, aaFlags, quad);
546 return coverage;
Michael Ludwigf995c052018-11-26 15:24:29 -0500547}
548
Michael Ludwig93aeba02018-12-21 09:50:31 -0500549// Computes the vertices for the two nested quads used to create AA edges. The original single quad
Michael Ludwige6266a22019-03-07 11:24:32 -0500550// should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
551// stored in-place on return. Returns per-vertex coverage for the inner vertices.
552static Sk4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
553 Vertices* inner, Vertices* outer) {
554 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
555 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400556
Michael Ludwige6266a22019-03-07 11:24:32 -0500557 QuadMetadata metadata = get_metadata(*inner, aaFlags);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400558
Michael Ludwig93aeba02018-12-21 09:50:31 -0500559 // When outsetting, we want the new edge to be .5px away from the old line, which means the
Michael Ludwige6266a22019-03-07 11:24:32 -0500560 // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
561 // is only computed if there are no empty edges, and it may signal going through the slow path.
Michael Ludwig93aeba02018-12-21 09:50:31 -0500562 Sk4f outset = 0.5f;
Michael Ludwige6266a22019-03-07 11:24:32 -0500563 if (get_optimized_outset(metadata, rectilinear, &outset)) {
564 // Since it's not subpixel, outsetting and insetting are trivial vector additions.
565 outset_vertices(outset, metadata, outer);
566 outset_vertices(-outset, metadata, inner);
567 return 1.f;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400568 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000569
Michael Ludwige6266a22019-03-07 11:24:32 -0500570 // Only compute edge equations once since they are the same for inner and outer quads
571 Edges edges = get_edge_equations(metadata, *inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500572
Michael Ludwige6266a22019-03-07 11:24:32 -0500573 // Calculate both outset and inset, returning the coverage reported for the inset, since the
574 // outset will always have 0.0f.
575 compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
576 return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500577}
578
Michael Ludwige6266a22019-03-07 11:24:32 -0500579// Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
580// division of the device coordinates, the original local coordinate value is at the original
581// un-outset device position.
582static Sk4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
583 Vertices* outer) {
584 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
585 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500586
Michael Ludwige6266a22019-03-07 11:24:32 -0500587 // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
588 // Don't use Sk4f.invert() here because it does not preserve 1/1 == 1, which creates rendering
589 // mismatches for 2D content that was batched into a 3D op, vs. 2D on its own.
590 Sk4f iw = 1.0f / inner->fW;
591 Sk4f x2d = inner->fX * iw;
592 Sk4f y2d = inner->fY * iw;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500593
Michael Ludwige6266a22019-03-07 11:24:32 -0500594 Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
595 Vertices outer2D = inner2D;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400596
Michael Ludwige6266a22019-03-07 11:24:32 -0500597 Sk4f coverage = compute_nested_quad_vertices(aaFlags, /* rect */ false, &inner2D, &outer2D);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400598
Michael Ludwige6266a22019-03-07 11:24:32 -0500599 // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
600 outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
601 outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500602
Michael Ludwige6266a22019-03-07 11:24:32 -0500603 return coverage;
Eric Boren98cb1592018-11-26 18:38:05 +0000604}
605
Michael Ludwig93aeba02018-12-21 09:50:31 -0500606enum class CoverageMode {
607 kNone,
608 kWithPosition,
609 kWithColor
610};
611
612static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
613 if (spec.usesCoverageAA()) {
614 if (spec.compatibleWithAlphaAsCoverage() && spec.hasVertexColors()) {
615 return CoverageMode::kWithColor;
616 } else {
617 return CoverageMode::kWithPosition;
618 }
Michael Ludwig553e9a92018-11-29 12:38:35 -0500619 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500620 return CoverageMode::kNone;
Michael Ludwig553e9a92018-11-29 12:38:35 -0500621 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500622}
Michael Ludwig553e9a92018-11-29 12:38:35 -0500623
Michael Ludwig93aeba02018-12-21 09:50:31 -0500624// Writes four vertices in triangle strip order, including the additional data for local
625// coordinates, domain, color, and coverage as needed to satisfy the vertex spec.
626static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
Michael Ludwige6266a22019-03-07 11:24:32 -0500627 CoverageMode mode, Sk4f coverage,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500628 SkPMColor4f color4f, bool wideColor,
629 const SkRect& domain,
Michael Ludwige6266a22019-03-07 11:24:32 -0500630 const Vertices& quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500631 static constexpr auto If = GrVertexWriter::If<float>;
632
Michael Ludwig553e9a92018-11-29 12:38:35 -0500633 for (int i = 0; i < 4; ++i) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500634 // save position, this is a float2 or float3 or float4 depending on the combination of
635 // perspective and coverage mode.
Michael Ludwige6266a22019-03-07 11:24:32 -0500636 vb->write(quad.fX[i], quad.fY[i],
637 If(spec.deviceQuadType() == GrQuadType::kPerspective, quad.fW[i]),
638 If(mode == CoverageMode::kWithPosition, coverage[i]));
Michael Ludwig4921dc32018-12-03 14:57:29 +0000639
Michael Ludwig93aeba02018-12-21 09:50:31 -0500640 // save color
641 if (spec.hasVertexColors()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500642 vb->write(GrVertexColor(
643 color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wideColor));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500644 }
645
646 // save local position
647 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500648 vb->write(quad.fU[i], quad.fV[i],
649 If(spec.localQuadType() == GrQuadType::kPerspective, quad.fR[i]));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500650 }
651
652 // save the domain
653 if (spec.hasDomain()) {
654 vb->write(domain);
655 }
656 }
657}
658
659GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
660
661static const int kVertsPerAAFillRect = 8;
662static const int kIndicesPerAAFillRect = 30;
663
Brian Salomondbf70722019-02-07 11:31:24 -0500664static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500665 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
666
667 // clang-format off
668 static const uint16_t gFillAARectIdx[] = {
669 0, 1, 2, 1, 3, 2,
670 0, 4, 1, 4, 5, 1,
671 0, 6, 4, 0, 2, 6,
672 2, 3, 6, 3, 7, 6,
673 1, 5, 3, 3, 5, 7,
674 };
675 // clang-format on
676
677 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
678 return resourceProvider->findOrCreatePatternedIndexBuffer(
679 gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
680 kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
Michael Ludwig553e9a92018-11-29 12:38:35 -0500681}
682
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400683} // anonymous namespace
684
Michael Ludwigc182b942018-11-16 10:27:51 -0500685namespace GrQuadPerEdgeAA {
686
687////////////////// Tessellate Implementation
688
689void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
Brian Osman3d139a42018-11-19 10:42:10 -0500690 const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
Michael Ludwigc182b942018-11-16 10:27:51 -0500691 GrQuadAAFlags aaFlags) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500692 bool wideColor = GrQuadPerEdgeAA::ColorType::kHalf == spec.colorType();
693 CoverageMode mode = get_mode_for_spec(spec);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400694
Michael Ludwigf995c052018-11-26 15:24:29 -0500695 // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
Michael Ludwige6266a22019-03-07 11:24:32 -0500696 Vertices outer;
697 outer.fX = deviceQuad.x4f();
698 outer.fY = deviceQuad.y4f();
699 outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400700
701 // Load local position data into Sk4fs (either none, just u,v or all three)
Michael Ludwige6266a22019-03-07 11:24:32 -0500702 outer.fUVRCount = spec.localDimensionality();
Michael Ludwigc182b942018-11-16 10:27:51 -0500703 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500704 outer.fU = localQuad.x4f();
705 outer.fV = localQuad.y4f();
706 outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400707 }
708
Michael Ludwigc182b942018-11-16 10:27:51 -0500709 GrVertexWriter vb{vertices};
Michael Ludwig93aeba02018-12-21 09:50:31 -0500710 if (spec.usesCoverageAA()) {
711 SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500712 // Must calculate two new quads, an outset and inset by .5 in projected device space, so
Michael Ludwige6266a22019-03-07 11:24:32 -0500713 // duplicate the original quad for the inner space
714 Vertices inner = outer;
Michael Ludwigc182b942018-11-16 10:27:51 -0500715
Michael Ludwige6266a22019-03-07 11:24:32 -0500716 Sk4f maxCoverage = 1.f;
717 if (spec.deviceQuadType() == GrQuadType::kPerspective) {
718 // For perspective, send quads with all edges non-AA through the tessellation to ensure
719 // their corners are processed the same as adjacent quads. This approach relies on
720 // solving edge equations to reconstruct corners, which can create seams if an inner
721 // fully non-AA quad is not similarly processed.
722 maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer);
723 } else if (aaFlags != GrQuadAAFlags::kNone) {
724 // In 2D, the simpler corner math does not cause issues with seaming against non-AA
725 // inner quads.
726 maxCoverage = compute_nested_quad_vertices(
727 aaFlags, spec.deviceQuadType() <= GrQuadType::kRectilinear, &inner, &outer);
728 }
729 // NOTE: could provide an even more optimized tessellation function for axis-aligned
730 // rects since the positions can be outset by constants without doing vector math,
731 // except it must handle identifying the winding of the quad vertices if the transform
732 // applied a mirror, etc. The current 2D case is already adequately fast.
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400733
Michael Ludwig93aeba02018-12-21 09:50:31 -0500734 // Write two quads for inner and outer, inner will use the
Michael Ludwige6266a22019-03-07 11:24:32 -0500735 write_quad(&vb, spec, mode, maxCoverage, color4f, wideColor, domain, inner);
736 write_quad(&vb, spec, mode, 0.f, color4f, wideColor, domain, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500737 } else {
738 // No outsetting needed, just write a single quad with full coverage
739 SkASSERT(mode == CoverageMode::kNone);
Michael Ludwige6266a22019-03-07 11:24:32 -0500740 write_quad(&vb, spec, mode, 1.f, color4f, wideColor, domain, outer);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400741 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500742
743 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400744}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400745
Michael Ludwig93aeba02018-12-21 09:50:31 -0500746bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
747 int quadCount) {
748 if (spec.usesCoverageAA()) {
749 // AA quads use 8 vertices, basically nested rectangles
Brian Salomondbf70722019-02-07 11:31:24 -0500750 sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
Michael Ludwig93aeba02018-12-21 09:50:31 -0500751 if (!ibuffer) {
752 return false;
753 }
754
755 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500756 mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
757 quadCount, kNumAAQuadsInIndexBuffer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500758 } else {
759 // Non-AA quads use 4 vertices, and regular triangle strip layout
760 if (quadCount > 1) {
Brian Salomondbf70722019-02-07 11:31:24 -0500761 sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Michael Ludwig93aeba02018-12-21 09:50:31 -0500762 if (!ibuffer) {
763 return false;
764 }
765
766 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500767 mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500768 GrResourceProvider::QuadCountOfQuadBuffer());
769 } else {
770 mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
771 mesh->setNonIndexedNonInstanced(4);
772 }
773 }
774
775 return true;
776}
777
Michael Ludwigc182b942018-11-16 10:27:51 -0500778////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400779
Michael Ludwigc182b942018-11-16 10:27:51 -0500780int VertexSpec::deviceDimensionality() const {
781 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
782}
783
784int VertexSpec::localDimensionality() const {
785 return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
786}
787
Michael Ludwig467994d2018-12-03 14:58:31 +0000788////////////////// Geometry Processor Implementation
Michael Ludwigc182b942018-11-16 10:27:51 -0500789
Michael Ludwig467994d2018-12-03 14:58:31 +0000790class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
791public:
Michael Ludwig20e909e2018-10-30 10:43:57 -0400792
Michael Ludwig467994d2018-12-03 14:58:31 +0000793 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
794 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400795 }
796
Michael Ludwig467994d2018-12-03 14:58:31 +0000797 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
798 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500799 const GrSamplerState& samplerState,
800 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000801 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
802 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
Greg Daniel7a82edf2018-12-04 10:54:34 -0500803 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000804 std::move(textureColorSpaceXform)));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400805 }
806
Michael Ludwig467994d2018-12-03 14:58:31 +0000807 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
Michael Ludwig024e2622018-11-30 13:25:55 -0500808
Michael Ludwig467994d2018-12-03 14:58:31 +0000809 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500810 // domain, texturing, device-dimensions are single bit flags
811 uint32_t x = fDomain.isInitialized() ? 0 : 1;
812 x |= fSampler.isInitialized() ? 0 : 2;
813 x |= fNeedsPerspective ? 0 : 4;
Michael Ludwig467994d2018-12-03 14:58:31 +0000814 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
815 if (fLocalCoord.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500816 x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
Brian Osman78dc72c2018-12-03 13:20:43 +0000817 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000818 // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
Michael Ludwig93aeba02018-12-21 09:50:31 -0500819 if (fColor.isInitialized()) {
820 x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
821 }
822 // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor
823 if (fCoverageMode != CoverageMode::kNone) {
824 x |= CoverageMode::kWithPosition == fCoverageMode ? 128 : 256;
Michael Ludwig467994d2018-12-03 14:58:31 +0000825 }
826
827 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
828 b->add32(x);
Brian Osman78dc72c2018-12-03 13:20:43 +0000829 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000830
831 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
832 class GLSLProcessor : public GrGLSLGeometryProcessor {
833 public:
834 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
835 FPCoordTransformIter&& transformIter) override {
836 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
837 if (gp.fLocalCoord.isInitialized()) {
838 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
839 }
840 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
841 }
842
843 private:
844 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
845 using Interpolation = GrGLSLVaryingHandler::Interpolation;
846
847 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
848 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
849 gp.fTextureColorSpaceXform.get());
850
851 args.fVaryingHandler->emitAttributes(gp);
852
Michael Ludwig93aeba02018-12-21 09:50:31 -0500853 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
854 // Strip last channel from the vertex attribute to remove coverage and get the
855 // actual position
856 if (gp.fNeedsPerspective) {
857 args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
858 gp.fPosition.name());
859 } else {
860 args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
861 gp.fPosition.name());
862 }
863 gpArgs->fPositionVar = {"position",
864 gp.fNeedsPerspective ? kFloat3_GrSLType
865 : kFloat2_GrSLType,
866 GrShaderVar::kNone_TypeModifier};
Michael Ludwig467994d2018-12-03 14:58:31 +0000867 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500868 // No coverage to eliminate
869 gpArgs->fPositionVar = gp.fPosition.asShaderVar();
Michael Ludwig467994d2018-12-03 14:58:31 +0000870 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000871
872 // Handle local coordinates if they exist
873 if (gp.fLocalCoord.isInitialized()) {
874 // NOTE: If the only usage of local coordinates is for the inline texture fetch
875 // before FPs, then there are no registered FPCoordTransforms and this ends up
876 // emitting nothing, so there isn't a duplication of local coordinates
877 this->emitTransforms(args.fVertBuilder,
878 args.fVaryingHandler,
879 args.fUniformHandler,
880 gp.fLocalCoord.asShaderVar(),
881 args.fFPCoordTransformHandler);
882 }
883
884 // Solid color before any texturing gets modulated in
885 if (gp.fColor.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500886 // The color cannot be flat if the varying coverage has been modulated into it
Michael Ludwig467994d2018-12-03 14:58:31 +0000887 args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500888 gp.fCoverageMode == CoverageMode::kWithColor ?
889 Interpolation::kInterpolated : Interpolation::kCanBeFlat);
890 } else {
891 // Output color must be initialized to something
892 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
Michael Ludwig467994d2018-12-03 14:58:31 +0000893 }
894
895 // If there is a texture, must also handle texture coordinates and reading from
896 // the texture in the fragment shader before continuing to fragment processors.
897 if (gp.fSampler.isInitialized()) {
898 // Texture coordinates clamped by the domain on the fragment shader; if the GP
899 // has a texture, it's guaranteed to have local coordinates
900 args.fFragBuilder->codeAppend("float2 texCoord;");
901 if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
902 // Can't do a pass through since we need to perform perspective division
903 GrGLSLVarying v(gp.fLocalCoord.gpuType());
904 args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
905 args.fVertBuilder->codeAppendf("%s = %s;",
906 v.vsOut(), gp.fLocalCoord.name());
907 args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
908 v.fsIn(), v.fsIn());
909 } else {
910 args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
911 }
912
913 // Clamp the now 2D localCoordName variable by the domain if it is provided
914 if (gp.fDomain.isInitialized()) {
915 args.fFragBuilder->codeAppend("float4 domain;");
916 args.fVaryingHandler->addPassThroughAttribute(gp.fDomain, "domain",
917 Interpolation::kCanBeFlat);
918 args.fFragBuilder->codeAppend(
919 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
920 }
921
922 // Now modulate the starting output color by the texture lookup
923 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
924 args.fFragBuilder->appendTextureLookupAndModulate(
925 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
926 &fTextureColorSpaceXformHelper);
927 args.fFragBuilder->codeAppend(";");
928 }
929
930 // And lastly, output the coverage calculation code
Michael Ludwig93aeba02018-12-21 09:50:31 -0500931 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
932 GrGLSLVarying coverage(kFloat_GrSLType);
933 args.fVaryingHandler->addVarying("coverage", &coverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000934 if (gp.fNeedsPerspective) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500935 args.fVertBuilder->codeAppendf("%s = %s.w;",
936 coverage.vsOut(), gp.fPosition.name());
937 } else {
938 args.fVertBuilder->codeAppendf("%s = %s.z;",
939 coverage.vsOut(), gp.fPosition.name());
Michael Ludwig467994d2018-12-03 14:58:31 +0000940 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500941
Ethan Nicholase1f55022019-02-05 17:17:40 -0500942 args.fFragBuilder->codeAppendf("%s = half4(half(%s));",
Michael Ludwig93aeba02018-12-21 09:50:31 -0500943 args.fOutputCoverage, coverage.fsIn());
Michael Ludwig467994d2018-12-03 14:58:31 +0000944 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500945 // Set coverage to 1, since it's either non-AA or the coverage was already
946 // folded into the output color
Ethan Nicholase1f55022019-02-05 17:17:40 -0500947 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000948 }
949 }
950 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
951 };
952 return new GLSLProcessor;
953 }
954
955private:
956 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
957 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
958 , fTextureColorSpaceXform(nullptr) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500959 SkASSERT(!spec.hasDomain());
Michael Ludwig467994d2018-12-03 14:58:31 +0000960 this->initializeAttrs(spec);
961 this->setTextureSamplerCnt(0);
962 }
963
964 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
965 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500966 const GrSamplerState& samplerState,
967 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000968 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
969 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
970 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Greg Daniel7a82edf2018-12-04 10:54:34 -0500971 , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500972 SkASSERT(spec.hasLocalCoords());
Michael Ludwig467994d2018-12-03 14:58:31 +0000973 this->initializeAttrs(spec);
974 this->setTextureSamplerCnt(1);
975 }
976
977 void initializeAttrs(const VertexSpec& spec) {
978 fNeedsPerspective = spec.deviceDimensionality() == 3;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500979 fCoverageMode = get_mode_for_spec(spec);
980
981 if (fCoverageMode == CoverageMode::kWithPosition) {
982 if (fNeedsPerspective) {
983 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
984 } else {
985 fPosition = {"positionWithCoverage", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
986 }
987 } else {
988 if (fNeedsPerspective) {
989 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
990 } else {
991 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
992 }
993 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000994
995 int localDim = spec.localDimensionality();
996 if (localDim == 3) {
997 fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
998 } else if (localDim == 2) {
999 fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1000 } // else localDim == 0 and attribute remains uninitialized
1001
1002 if (ColorType::kByte == spec.colorType()) {
1003 fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
1004 } else if (ColorType::kHalf == spec.colorType()) {
1005 fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
1006 }
1007
1008 if (spec.hasDomain()) {
1009 fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1010 }
1011
Michael Ludwig93aeba02018-12-21 09:50:31 -05001012 this->setVertexAttributes(&fPosition, 4);
Michael Ludwig467994d2018-12-03 14:58:31 +00001013 }
1014
1015 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
1016
Michael Ludwig93aeba02018-12-21 09:50:31 -05001017 Attribute fPosition; // May contain coverage as last channel
1018 Attribute fColor; // May have coverage modulated in if the FPs support it
Michael Ludwig467994d2018-12-03 14:58:31 +00001019 Attribute fLocalCoord;
1020 Attribute fDomain;
Michael Ludwig467994d2018-12-03 14:58:31 +00001021
Michael Ludwig93aeba02018-12-21 09:50:31 -05001022 // The positions attribute may have coverage built into it, so float3 is an ambiguous type
1023 // and may mean 2d with coverage, or 3d with no coverage
Michael Ludwig467994d2018-12-03 14:58:31 +00001024 bool fNeedsPerspective;
Michael Ludwig93aeba02018-12-21 09:50:31 -05001025 CoverageMode fCoverageMode;
Michael Ludwig467994d2018-12-03 14:58:31 +00001026
1027 // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
1028 // to skip texturing.
1029 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1030 TextureSampler fSampler;
1031
1032 typedef GrGeometryProcessor INHERITED;
1033};
1034
1035sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
1036 return QuadPerEdgeAAGeometryProcessor::Make(spec);
1037}
1038
1039sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
1040 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -05001041 const GrSamplerState& samplerState, uint32_t extraSamplerKey,
1042 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1043 return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
1044 samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +00001045 std::move(textureColorSpaceXform));
Michael Ludwig20e909e2018-10-30 10:43:57 -04001046}
Michael Ludwigc182b942018-11-16 10:27:51 -05001047
1048} // namespace GrQuadPerEdgeAA