blob: cdc49b32ffd5695dcd1440fc28945d50fe211104 [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;
Greg Kaiser2f3b0dd2019-03-12 06:27:52 -0700496 } else if (d1And2[0] || d1And2[1] || d1And2[2] || d1And2[3]) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500497 // 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,
Brian Salomon1d835422019-03-13 16:11:44 -0400627 CoverageMode mode, Sk4f coverage, SkPMColor4f color4f, const SkRect& domain,
Michael Ludwige6266a22019-03-07 11:24:32 -0500628 const Vertices& quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500629 static constexpr auto If = GrVertexWriter::If<float>;
630
Michael Ludwig553e9a92018-11-29 12:38:35 -0500631 for (int i = 0; i < 4; ++i) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500632 // save position, this is a float2 or float3 or float4 depending on the combination of
633 // perspective and coverage mode.
Michael Ludwige6266a22019-03-07 11:24:32 -0500634 vb->write(quad.fX[i], quad.fY[i],
635 If(spec.deviceQuadType() == GrQuadType::kPerspective, quad.fW[i]),
636 If(mode == CoverageMode::kWithPosition, coverage[i]));
Michael Ludwig4921dc32018-12-03 14:57:29 +0000637
Michael Ludwig93aeba02018-12-21 09:50:31 -0500638 // save color
639 if (spec.hasVertexColors()) {
Brian Salomon1d835422019-03-13 16:11:44 -0400640 bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
Michael Ludwige6266a22019-03-07 11:24:32 -0500641 vb->write(GrVertexColor(
Brian Salomon1d835422019-03-13 16:11:44 -0400642 color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wide));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500643 }
644
645 // save local position
646 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500647 vb->write(quad.fU[i], quad.fV[i],
648 If(spec.localQuadType() == GrQuadType::kPerspective, quad.fR[i]));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500649 }
650
651 // save the domain
652 if (spec.hasDomain()) {
653 vb->write(domain);
654 }
655 }
656}
657
658GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
659
660static const int kVertsPerAAFillRect = 8;
661static const int kIndicesPerAAFillRect = 30;
662
Brian Salomondbf70722019-02-07 11:31:24 -0500663static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500664 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
665
666 // clang-format off
667 static const uint16_t gFillAARectIdx[] = {
668 0, 1, 2, 1, 3, 2,
669 0, 4, 1, 4, 5, 1,
670 0, 6, 4, 0, 2, 6,
671 2, 3, 6, 3, 7, 6,
672 1, 5, 3, 3, 5, 7,
673 };
674 // clang-format on
675
676 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
677 return resourceProvider->findOrCreatePatternedIndexBuffer(
678 gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
679 kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
Michael Ludwig553e9a92018-11-29 12:38:35 -0500680}
681
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400682} // anonymous namespace
683
Michael Ludwigc182b942018-11-16 10:27:51 -0500684namespace GrQuadPerEdgeAA {
685
Brian Salomon1d835422019-03-13 16:11:44 -0400686ColorType MinColorType(SkPMColor4f color) {
687 if (color == SK_PMColor4fWHITE) {
688 return ColorType::kNone;
689 } else if (color.fitsInBytes()) {
690 return ColorType::kByte;
691 } else {
692 return ColorType::kHalf;
693 }
694}
695
Michael Ludwigc182b942018-11-16 10:27:51 -0500696////////////////// Tessellate Implementation
697
698void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
Brian Osman3d139a42018-11-19 10:42:10 -0500699 const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
Michael Ludwigc182b942018-11-16 10:27:51 -0500700 GrQuadAAFlags aaFlags) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500701 CoverageMode mode = get_mode_for_spec(spec);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400702
Michael Ludwigf995c052018-11-26 15:24:29 -0500703 // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
Michael Ludwige6266a22019-03-07 11:24:32 -0500704 Vertices outer;
705 outer.fX = deviceQuad.x4f();
706 outer.fY = deviceQuad.y4f();
707 outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400708
709 // Load local position data into Sk4fs (either none, just u,v or all three)
Michael Ludwige6266a22019-03-07 11:24:32 -0500710 outer.fUVRCount = spec.localDimensionality();
Michael Ludwigc182b942018-11-16 10:27:51 -0500711 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500712 outer.fU = localQuad.x4f();
713 outer.fV = localQuad.y4f();
714 outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400715 }
716
Michael Ludwigc182b942018-11-16 10:27:51 -0500717 GrVertexWriter vb{vertices};
Michael Ludwig93aeba02018-12-21 09:50:31 -0500718 if (spec.usesCoverageAA()) {
719 SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500720 // Must calculate two new quads, an outset and inset by .5 in projected device space, so
Michael Ludwige6266a22019-03-07 11:24:32 -0500721 // duplicate the original quad for the inner space
722 Vertices inner = outer;
Michael Ludwigc182b942018-11-16 10:27:51 -0500723
Michael Ludwige6266a22019-03-07 11:24:32 -0500724 Sk4f maxCoverage = 1.f;
725 if (spec.deviceQuadType() == GrQuadType::kPerspective) {
726 // For perspective, send quads with all edges non-AA through the tessellation to ensure
727 // their corners are processed the same as adjacent quads. This approach relies on
728 // solving edge equations to reconstruct corners, which can create seams if an inner
729 // fully non-AA quad is not similarly processed.
730 maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer);
731 } else if (aaFlags != GrQuadAAFlags::kNone) {
732 // In 2D, the simpler corner math does not cause issues with seaming against non-AA
733 // inner quads.
734 maxCoverage = compute_nested_quad_vertices(
735 aaFlags, spec.deviceQuadType() <= GrQuadType::kRectilinear, &inner, &outer);
736 }
737 // NOTE: could provide an even more optimized tessellation function for axis-aligned
738 // rects since the positions can be outset by constants without doing vector math,
739 // except it must handle identifying the winding of the quad vertices if the transform
740 // applied a mirror, etc. The current 2D case is already adequately fast.
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400741
Michael Ludwig93aeba02018-12-21 09:50:31 -0500742 // Write two quads for inner and outer, inner will use the
Brian Salomon1d835422019-03-13 16:11:44 -0400743 write_quad(&vb, spec, mode, maxCoverage, color4f, domain, inner);
744 write_quad(&vb, spec, mode, 0.f, color4f, domain, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500745 } else {
746 // No outsetting needed, just write a single quad with full coverage
747 SkASSERT(mode == CoverageMode::kNone);
Brian Salomon1d835422019-03-13 16:11:44 -0400748 write_quad(&vb, spec, mode, 1.f, color4f, domain, outer);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400749 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500750
751 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400752}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400753
Michael Ludwig93aeba02018-12-21 09:50:31 -0500754bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
755 int quadCount) {
756 if (spec.usesCoverageAA()) {
757 // AA quads use 8 vertices, basically nested rectangles
Brian Salomondbf70722019-02-07 11:31:24 -0500758 sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
Michael Ludwig93aeba02018-12-21 09:50:31 -0500759 if (!ibuffer) {
760 return false;
761 }
762
763 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500764 mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
765 quadCount, kNumAAQuadsInIndexBuffer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500766 } else {
767 // Non-AA quads use 4 vertices, and regular triangle strip layout
768 if (quadCount > 1) {
Brian Salomondbf70722019-02-07 11:31:24 -0500769 sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Michael Ludwig93aeba02018-12-21 09:50:31 -0500770 if (!ibuffer) {
771 return false;
772 }
773
774 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500775 mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500776 GrResourceProvider::QuadCountOfQuadBuffer());
777 } else {
778 mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
779 mesh->setNonIndexedNonInstanced(4);
780 }
781 }
782
783 return true;
784}
785
Michael Ludwigc182b942018-11-16 10:27:51 -0500786////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400787
Michael Ludwigc182b942018-11-16 10:27:51 -0500788int VertexSpec::deviceDimensionality() const {
789 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
790}
791
792int VertexSpec::localDimensionality() const {
793 return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
794}
795
Michael Ludwig467994d2018-12-03 14:58:31 +0000796////////////////// Geometry Processor Implementation
Michael Ludwigc182b942018-11-16 10:27:51 -0500797
Michael Ludwig467994d2018-12-03 14:58:31 +0000798class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
799public:
Michael Ludwig20e909e2018-10-30 10:43:57 -0400800
Michael Ludwig467994d2018-12-03 14:58:31 +0000801 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
802 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400803 }
804
Michael Ludwig467994d2018-12-03 14:58:31 +0000805 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
806 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500807 const GrSamplerState& samplerState,
808 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000809 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
810 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
Greg Daniel7a82edf2018-12-04 10:54:34 -0500811 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000812 std::move(textureColorSpaceXform)));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400813 }
814
Michael Ludwig467994d2018-12-03 14:58:31 +0000815 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
Michael Ludwig024e2622018-11-30 13:25:55 -0500816
Michael Ludwig467994d2018-12-03 14:58:31 +0000817 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500818 // domain, texturing, device-dimensions are single bit flags
819 uint32_t x = fDomain.isInitialized() ? 0 : 1;
820 x |= fSampler.isInitialized() ? 0 : 2;
821 x |= fNeedsPerspective ? 0 : 4;
Michael Ludwig467994d2018-12-03 14:58:31 +0000822 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
823 if (fLocalCoord.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500824 x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
Brian Osman78dc72c2018-12-03 13:20:43 +0000825 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000826 // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
Michael Ludwig93aeba02018-12-21 09:50:31 -0500827 if (fColor.isInitialized()) {
828 x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
829 }
830 // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor
831 if (fCoverageMode != CoverageMode::kNone) {
832 x |= CoverageMode::kWithPosition == fCoverageMode ? 128 : 256;
Michael Ludwig467994d2018-12-03 14:58:31 +0000833 }
834
835 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
836 b->add32(x);
Brian Osman78dc72c2018-12-03 13:20:43 +0000837 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000838
839 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
840 class GLSLProcessor : public GrGLSLGeometryProcessor {
841 public:
842 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
843 FPCoordTransformIter&& transformIter) override {
844 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
845 if (gp.fLocalCoord.isInitialized()) {
846 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
847 }
848 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
849 }
850
851 private:
852 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
853 using Interpolation = GrGLSLVaryingHandler::Interpolation;
854
855 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
856 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
857 gp.fTextureColorSpaceXform.get());
858
859 args.fVaryingHandler->emitAttributes(gp);
860
Michael Ludwig93aeba02018-12-21 09:50:31 -0500861 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
862 // Strip last channel from the vertex attribute to remove coverage and get the
863 // actual position
864 if (gp.fNeedsPerspective) {
865 args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
866 gp.fPosition.name());
867 } else {
868 args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
869 gp.fPosition.name());
870 }
871 gpArgs->fPositionVar = {"position",
872 gp.fNeedsPerspective ? kFloat3_GrSLType
873 : kFloat2_GrSLType,
874 GrShaderVar::kNone_TypeModifier};
Michael Ludwig467994d2018-12-03 14:58:31 +0000875 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500876 // No coverage to eliminate
877 gpArgs->fPositionVar = gp.fPosition.asShaderVar();
Michael Ludwig467994d2018-12-03 14:58:31 +0000878 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000879
880 // Handle local coordinates if they exist
881 if (gp.fLocalCoord.isInitialized()) {
882 // NOTE: If the only usage of local coordinates is for the inline texture fetch
883 // before FPs, then there are no registered FPCoordTransforms and this ends up
884 // emitting nothing, so there isn't a duplication of local coordinates
885 this->emitTransforms(args.fVertBuilder,
886 args.fVaryingHandler,
887 args.fUniformHandler,
888 gp.fLocalCoord.asShaderVar(),
889 args.fFPCoordTransformHandler);
890 }
891
892 // Solid color before any texturing gets modulated in
893 if (gp.fColor.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500894 // The color cannot be flat if the varying coverage has been modulated into it
Michael Ludwig467994d2018-12-03 14:58:31 +0000895 args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500896 gp.fCoverageMode == CoverageMode::kWithColor ?
897 Interpolation::kInterpolated : Interpolation::kCanBeFlat);
898 } else {
899 // Output color must be initialized to something
900 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
Michael Ludwig467994d2018-12-03 14:58:31 +0000901 }
902
903 // If there is a texture, must also handle texture coordinates and reading from
904 // the texture in the fragment shader before continuing to fragment processors.
905 if (gp.fSampler.isInitialized()) {
906 // Texture coordinates clamped by the domain on the fragment shader; if the GP
907 // has a texture, it's guaranteed to have local coordinates
908 args.fFragBuilder->codeAppend("float2 texCoord;");
909 if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
910 // Can't do a pass through since we need to perform perspective division
911 GrGLSLVarying v(gp.fLocalCoord.gpuType());
912 args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
913 args.fVertBuilder->codeAppendf("%s = %s;",
914 v.vsOut(), gp.fLocalCoord.name());
915 args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
916 v.fsIn(), v.fsIn());
917 } else {
918 args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
919 }
920
921 // Clamp the now 2D localCoordName variable by the domain if it is provided
922 if (gp.fDomain.isInitialized()) {
923 args.fFragBuilder->codeAppend("float4 domain;");
924 args.fVaryingHandler->addPassThroughAttribute(gp.fDomain, "domain",
925 Interpolation::kCanBeFlat);
926 args.fFragBuilder->codeAppend(
927 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
928 }
929
930 // Now modulate the starting output color by the texture lookup
931 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
932 args.fFragBuilder->appendTextureLookupAndModulate(
933 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
934 &fTextureColorSpaceXformHelper);
935 args.fFragBuilder->codeAppend(";");
936 }
937
938 // And lastly, output the coverage calculation code
Michael Ludwig93aeba02018-12-21 09:50:31 -0500939 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
940 GrGLSLVarying coverage(kFloat_GrSLType);
941 args.fVaryingHandler->addVarying("coverage", &coverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000942 if (gp.fNeedsPerspective) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500943 args.fVertBuilder->codeAppendf("%s = %s.w;",
944 coverage.vsOut(), gp.fPosition.name());
945 } else {
946 args.fVertBuilder->codeAppendf("%s = %s.z;",
947 coverage.vsOut(), gp.fPosition.name());
Michael Ludwig467994d2018-12-03 14:58:31 +0000948 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500949
Ethan Nicholase1f55022019-02-05 17:17:40 -0500950 args.fFragBuilder->codeAppendf("%s = half4(half(%s));",
Michael Ludwig93aeba02018-12-21 09:50:31 -0500951 args.fOutputCoverage, coverage.fsIn());
Michael Ludwig467994d2018-12-03 14:58:31 +0000952 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500953 // Set coverage to 1, since it's either non-AA or the coverage was already
954 // folded into the output color
Ethan Nicholase1f55022019-02-05 17:17:40 -0500955 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000956 }
957 }
958 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
959 };
960 return new GLSLProcessor;
961 }
962
963private:
964 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
965 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
966 , fTextureColorSpaceXform(nullptr) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500967 SkASSERT(!spec.hasDomain());
Michael Ludwig467994d2018-12-03 14:58:31 +0000968 this->initializeAttrs(spec);
969 this->setTextureSamplerCnt(0);
970 }
971
972 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
973 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500974 const GrSamplerState& samplerState,
975 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000976 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
977 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
978 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Greg Daniel7a82edf2018-12-04 10:54:34 -0500979 , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500980 SkASSERT(spec.hasLocalCoords());
Michael Ludwig467994d2018-12-03 14:58:31 +0000981 this->initializeAttrs(spec);
982 this->setTextureSamplerCnt(1);
983 }
984
985 void initializeAttrs(const VertexSpec& spec) {
986 fNeedsPerspective = spec.deviceDimensionality() == 3;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500987 fCoverageMode = get_mode_for_spec(spec);
988
989 if (fCoverageMode == CoverageMode::kWithPosition) {
990 if (fNeedsPerspective) {
991 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
992 } else {
993 fPosition = {"positionWithCoverage", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
994 }
995 } else {
996 if (fNeedsPerspective) {
997 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
998 } else {
999 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1000 }
1001 }
Michael Ludwig467994d2018-12-03 14:58:31 +00001002
1003 int localDim = spec.localDimensionality();
1004 if (localDim == 3) {
1005 fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1006 } else if (localDim == 2) {
1007 fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1008 } // else localDim == 0 and attribute remains uninitialized
1009
1010 if (ColorType::kByte == spec.colorType()) {
1011 fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
1012 } else if (ColorType::kHalf == spec.colorType()) {
1013 fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
1014 }
1015
1016 if (spec.hasDomain()) {
1017 fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1018 }
1019
Michael Ludwig93aeba02018-12-21 09:50:31 -05001020 this->setVertexAttributes(&fPosition, 4);
Michael Ludwig467994d2018-12-03 14:58:31 +00001021 }
1022
1023 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
1024
Michael Ludwig93aeba02018-12-21 09:50:31 -05001025 Attribute fPosition; // May contain coverage as last channel
1026 Attribute fColor; // May have coverage modulated in if the FPs support it
Michael Ludwig467994d2018-12-03 14:58:31 +00001027 Attribute fLocalCoord;
1028 Attribute fDomain;
Michael Ludwig467994d2018-12-03 14:58:31 +00001029
Michael Ludwig93aeba02018-12-21 09:50:31 -05001030 // The positions attribute may have coverage built into it, so float3 is an ambiguous type
1031 // and may mean 2d with coverage, or 3d with no coverage
Michael Ludwig467994d2018-12-03 14:58:31 +00001032 bool fNeedsPerspective;
Michael Ludwig93aeba02018-12-21 09:50:31 -05001033 CoverageMode fCoverageMode;
Michael Ludwig467994d2018-12-03 14:58:31 +00001034
1035 // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
1036 // to skip texturing.
1037 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1038 TextureSampler fSampler;
1039
1040 typedef GrGeometryProcessor INHERITED;
1041};
1042
1043sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
1044 return QuadPerEdgeAAGeometryProcessor::Make(spec);
1045}
1046
1047sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
1048 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -05001049 const GrSamplerState& samplerState, uint32_t extraSamplerKey,
1050 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1051 return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
1052 samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +00001053 std::move(textureColorSpaceXform));
Michael Ludwig20e909e2018-10-30 10:43:57 -04001054}
Michael Ludwigc182b942018-11-16 10:27:51 -05001055
1056} // namespace GrQuadPerEdgeAA