blob: 9716e31a7a2711611370c0f0699aeb3b8bfaddae [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
Michael Ludwiga89cc052019-03-14 14:26:47 -0400305 Sk4f left4f = quad.fX >= left;
306 Sk4f right4f = quad.fX <= right;
307 Sk4f top4f = quad.fY >= top;
308 Sk4f bot4f = quad.fY <= bot;
309 // Use bit casting so that overflows don't occur on WASM (will be cleaned up in SkVx port)
310 Sk4i leftValid = Sk4i::Load(&left4f);
311 Sk4i rightValid = Sk4i::Load(&right4f);
312 Sk4i topValid = Sk4i::Load(&top4f);
313 Sk4i botValid = Sk4i::Load(&bot4f);
Michael Ludwige6266a22019-03-07 11:24:32 -0500314
315 // Intercepts of quad lines with the 4 pixel edges
316 Sk4f leftCross = -(edges.fC + edges.fA * left) / edges.fB;
317 Sk4f rightCross = -(edges.fC + edges.fA * right) / edges.fB;
318 Sk4f topCross = -(edges.fC + edges.fB * top) / edges.fA;
319 Sk4f botCross = -(edges.fC + edges.fB * bot) / edges.fA;
320
321 // State for implicitly tracking the intersection boundary and area
322 SkPoint firstPoint = {0.f, 0.f};
323 SkPoint lastPoint = {0.f, 0.f};
324 bool intersected = false;
325 float area = 0.f;
326
327 // Adds a point to the intersection hull, remembering first point (for closing) and the
328 // current point, and updates the running area total.
329 // See http://mathworld.wolfram.com/PolygonArea.html
330 auto accumulate = [&](const SkPoint& p) {
331 if (intersected) {
332 float da = lastPoint.fX * p.fY - p.fX * lastPoint.fY;
333 area += da;
334 } else {
335 firstPoint = p;
336 intersected = true;
337 }
338 lastPoint = p;
339 };
340
341 // Used during iteration over the quad points to check if edge intersections are valid and
342 // should be accumulated.
343#define ADD_EDGE_CROSSING_X(SIDE) \
344 do { \
345 if (SIDE##Cross[ei] >= top && SIDE##Cross[ei] <= bot) { \
346 accumulate({SIDE, SIDE##Cross[ei]}); \
347 addedIntersection = true; \
348 } \
349 } while(false)
350#define ADD_EDGE_CROSSING_Y(SIDE) \
351 do { \
352 if (SIDE##Cross[ei] >= left && SIDE##Cross[ei] <= right) { \
353 accumulate({SIDE##Cross[ei], SIDE}); \
354 addedIntersection = true; \
355 } \
356 } while(false)
357#define TEST_EDGES(SIDE, AXIS, I, NI) \
358 do { \
359 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
360 ADD_EDGE_CROSSING_##AXIS(SIDE); \
361 crossedEdges = true; \
362 } \
363 } while(false)
364 // Used during iteration over the quad points to check if a pixel corner should be included
365 // in the intersection boundary
366#define ADD_CORNER(CHECK, SIDE_LR, SIDE_TB) \
367 if (!CHECK##Valid[i] || !CHECK##Valid[ni]) { \
368 if (SIDE_TB##SIDE_LR##Inside) { \
369 accumulate({SIDE_LR, SIDE_TB}); \
370 } \
371 }
372#define TEST_CORNER_X(SIDE, I, NI) \
373 do { \
374 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
375 ADD_CORNER(top, SIDE, top) else ADD_CORNER(bot, SIDE, bot) \
376 } \
377 } while(false)
378#define TEST_CORNER_Y(SIDE, I, NI) \
379 do { \
380 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
381 ADD_CORNER(left, left, SIDE) else ADD_CORNER(right, right, SIDE) \
382 } \
383 } while(false)
384
385 // Iterate over the 4 points of the quad, adding valid intersections with the pixel edges
386 // or adding interior pixel corners as it goes. This automatically keeps all accumulated points
387 // in CCW ordering so the area can be calculated on the fly and there's no need to store the
388 // list of hull points. This is somewhat inspired by the Sutherland-Hodgman algorithm but since
389 // there are only 4 points in each source polygon, there is no point list maintenance.
390 for (int j = 0; j < 4; ++j) {
391 // Current vertex
392 int i = edges.fFlipped ? kFlippedCCW[j] : kCCW[j];
393 // Moving to this vertex
394 int ni = edges.fFlipped ? kFlippedCCW[(j + 1) % 4] : kCCW[(j + 1) % 4];
395 // Index in edge vectors corresponding to move from i to ni
396 int ei = edges.fFlipped ? ni : i;
397
398 bool crossedEdges = false;
399 bool addedIntersection = false;
400
401 // First check if there are any outside -> inside edge crossings. There can be 0, 1, or 2.
402 // 2 can occur if one crossing is still outside the pixel, or if they both go through
403 // the corner (in which case a duplicate point is added, but that doesn't change area).
404
405 // Outside to inside crossing
406 TEST_EDGES(left, X, i, ni);
407 TEST_EDGES(right, X, i, ni);
408 TEST_EDGES(top, Y, i, ni);
409 TEST_EDGES(bot, Y, i, ni);
410 // Inside to outside crossing (swapping ni and i in the boolean test)
411 TEST_EDGES(left, X, ni, i);
412 TEST_EDGES(right, X, ni, i);
413 TEST_EDGES(top, Y, ni, i);
414 TEST_EDGES(bot, Y, ni, i);
415
416 // If we crossed edges but didn't add any intersections, check the corners of the pixel.
417 // If the pixel corners are inside the quad, include them in the boundary.
418 if (crossedEdges && !addedIntersection) {
419 // This can lead to repeated points, but those just accumulate zero area
420 TEST_CORNER_X(left, i, ni);
421 TEST_CORNER_X(right, i, ni);
422 TEST_CORNER_Y(top, i, ni);
423 TEST_CORNER_Y(bot, i, ni);
424
425 TEST_CORNER_X(left, ni, i);
426 TEST_CORNER_X(right, ni, i);
427 TEST_CORNER_Y(top, ni, i);
428 TEST_CORNER_Y(bot, ni, i);
429 }
430
431 // Lastly, if the next point is completely inside the pixel it gets included in the boundary
432 if (leftValid[ni] && rightValid[ni] && topValid[ni] && botValid[ni]) {
433 accumulate({quad.fX[ni], quad.fY[ni]});
434 }
435 }
436
437#undef TEST_CORNER_Y
438#undef TEST_CORNER_X
439#undef ADD_CORNER
440
441#undef TEST_EDGES
442#undef ADD_EDGE_CROSSING_Y
443#undef ADD_EDGE_CROSSING_X
444
445 // After all points have been considered, close the boundary to get final area. If we never
446 // added any points, it means the quad didn't intersect the pixel rectangle.
447 if (intersected) {
448 // Final equation for area of convex polygon is to multiply by -1/2 (minus since the points
449 // were in CCW order).
450 accumulate(firstPoint);
451 return -0.5f * area;
452 } else {
453 return 0.f;
454 }
455}
456
457// Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
458// parallel, or edges are very short/zero-length. Returns coverage for each vertex.
459// Requires (dx, dy) to already be fixed for empty edges.
460static Sk4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const Sk4f& mask, const Edges& edges,
461 bool outset, Vertices* quad) {
462 // Move the edge 1/2 pixel in or out depending on 'outset'.
463 Sk4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
464
465 // There are 6 points that we care about to determine the final shape of the polygon, which
466 // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
467 // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
468 Sk4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
469 Sk4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
470 Sk4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
471 correct_bad_coords(denom.abs() < kTolerance, &px, &py, nullptr);
472
473 // Calculate the signed distances from these 4 corners to the other two edges that did not
474 // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
475 // p(3) to e0,e2
476 Sk4f dists1 = px * SkNx_shuffle<3, 3, 0, 0>(edges.fA) +
477 py * SkNx_shuffle<3, 3, 0, 0>(edges.fB) +
478 SkNx_shuffle<3, 3, 0, 0>(oc);
479 Sk4f dists2 = px * SkNx_shuffle<1, 2, 1, 2>(edges.fA) +
480 py * SkNx_shuffle<1, 2, 1, 2>(edges.fB) +
481 SkNx_shuffle<1, 2, 1, 2>(oc);
482
483 // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
484 // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
485 // and we need to use a central point to represent it. If all four points are only on the
486 // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
487 // Otherwise, use a triangle that replaces the bad points with the intersections of
488 // (e1, e2) or (e0, e3) as needed.
489 Sk4f d1v0 = dists1 < kTolerance;
490 Sk4f d2v0 = dists2 < kTolerance;
491 // FIXME(michaelludwig): Sk4f has anyTrue() and allTrue(), but not & or |. Sk4i has & or | but
492 // not anyTrue() and allTrue(). Moving to SkVx from SkNx will clean this up.
Michael Ludwiga89cc052019-03-14 14:26:47 -0400493 Sk4i d1And2 = Sk4i::Load(&d1v0) & Sk4i::Load(&d2v0);
494 Sk4i d1Or2 = Sk4i::Load(&d1v0) | Sk4i::Load(&d2v0);
Michael Ludwige6266a22019-03-07 11:24:32 -0500495
496 Sk4f coverage;
497 if (!d1Or2[0] && !d1Or2[1] && !d1Or2[2] && !d1Or2[3]) {
498 // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
499 // and use full coverage
500 coverage = 1.f;
Greg Kaiser2f3b0dd2019-03-12 06:27:52 -0700501 } else if (d1And2[0] || d1And2[1] || d1And2[2] || d1And2[3]) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500502 // A point failed against two edges, so reduce the shape to a single point, which we take as
503 // the center of the original quad to ensure it is contained in the intended geometry. Since
504 // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
505 SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
506 0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
507 coverage = get_exact_coverage(center, *quad, edges);
508 px = center.fX;
509 py = center.fY;
510 } else if (d1Or2[0] && d1Or2[1] && d1Or2[2] && d1Or2[3]) {
511 // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
512 // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
513 if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
514 // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
515 px = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(px) + SkNx_shuffle<2, 3, 2, 3>(px));
516 py = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(py) + SkNx_shuffle<2, 3, 2, 3>(py));
517 float mc02 = get_exact_coverage({px[0], py[0]}, *quad, edges);
518 float mc13 = get_exact_coverage({px[1], py[1]}, *quad, edges);
519 coverage = Sk4f(mc02, mc13, mc02, mc13);
520 } else {
521 // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
522 px = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(px) + SkNx_shuffle<1, 1, 3, 3>(px));
523 py = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(py) + SkNx_shuffle<1, 1, 3, 3>(py));
524 float mc01 = get_exact_coverage({px[0], py[0]}, *quad, edges);
525 float mc23 = get_exact_coverage({px[2], py[2]}, *quad, edges);
526 coverage = Sk4f(mc01, mc01, mc23, mc23);
527 }
528 } else {
529 // This turns into a triangle. Replace corners as needed with the intersections between
530 // (e0,e3) and (e1,e2), which must now be calculated
531 Sk2f eDenom = SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(edges.fB) -
532 SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(edges.fA);
533 Sk2f ex = (SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(oc) -
534 SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fB)) / eDenom;
535 Sk2f ey = (SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fA) -
536 SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(oc)) / eDenom;
537
538 if (SkScalarAbs(eDenom[0]) > kTolerance) {
539 px = d1v0.thenElse(ex[0], px);
540 py = d1v0.thenElse(ey[0], py);
541 }
542 if (SkScalarAbs(eDenom[1]) > kTolerance) {
543 px = d2v0.thenElse(ex[1], px);
544 py = d2v0.thenElse(ey[1], py);
545 }
546
547 coverage = 1.f;
548 }
549
550 outset_projected_vertices(px, py, aaFlags, quad);
551 return coverage;
Michael Ludwigf995c052018-11-26 15:24:29 -0500552}
553
Michael Ludwig93aeba02018-12-21 09:50:31 -0500554// Computes the vertices for the two nested quads used to create AA edges. The original single quad
Michael Ludwige6266a22019-03-07 11:24:32 -0500555// should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
556// stored in-place on return. Returns per-vertex coverage for the inner vertices.
557static Sk4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
558 Vertices* inner, Vertices* outer) {
559 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
560 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400561
Michael Ludwige6266a22019-03-07 11:24:32 -0500562 QuadMetadata metadata = get_metadata(*inner, aaFlags);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400563
Michael Ludwig93aeba02018-12-21 09:50:31 -0500564 // 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 -0500565 // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
566 // 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 -0500567 Sk4f outset = 0.5f;
Michael Ludwige6266a22019-03-07 11:24:32 -0500568 if (get_optimized_outset(metadata, rectilinear, &outset)) {
569 // Since it's not subpixel, outsetting and insetting are trivial vector additions.
570 outset_vertices(outset, metadata, outer);
571 outset_vertices(-outset, metadata, inner);
572 return 1.f;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400573 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000574
Michael Ludwige6266a22019-03-07 11:24:32 -0500575 // Only compute edge equations once since they are the same for inner and outer quads
576 Edges edges = get_edge_equations(metadata, *inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500577
Michael Ludwige6266a22019-03-07 11:24:32 -0500578 // Calculate both outset and inset, returning the coverage reported for the inset, since the
579 // outset will always have 0.0f.
580 compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
581 return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500582}
583
Michael Ludwige6266a22019-03-07 11:24:32 -0500584// Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
585// division of the device coordinates, the original local coordinate value is at the original
586// un-outset device position.
587static Sk4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
588 Vertices* outer) {
589 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
590 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500591
Michael Ludwige6266a22019-03-07 11:24:32 -0500592 // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
593 // Don't use Sk4f.invert() here because it does not preserve 1/1 == 1, which creates rendering
594 // mismatches for 2D content that was batched into a 3D op, vs. 2D on its own.
595 Sk4f iw = 1.0f / inner->fW;
596 Sk4f x2d = inner->fX * iw;
597 Sk4f y2d = inner->fY * iw;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500598
Michael Ludwige6266a22019-03-07 11:24:32 -0500599 Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
600 Vertices outer2D = inner2D;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400601
Michael Ludwige6266a22019-03-07 11:24:32 -0500602 Sk4f coverage = compute_nested_quad_vertices(aaFlags, /* rect */ false, &inner2D, &outer2D);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400603
Michael Ludwige6266a22019-03-07 11:24:32 -0500604 // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
605 outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
606 outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500607
Michael Ludwige6266a22019-03-07 11:24:32 -0500608 return coverage;
Eric Boren98cb1592018-11-26 18:38:05 +0000609}
610
Michael Ludwig93aeba02018-12-21 09:50:31 -0500611enum class CoverageMode {
612 kNone,
613 kWithPosition,
614 kWithColor
615};
616
617static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
618 if (spec.usesCoverageAA()) {
Brian Osman605c6d52019-03-15 12:10:35 -0400619 if (spec.compatibleWithCoverageAsAlpha() && spec.hasVertexColors()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500620 return CoverageMode::kWithColor;
621 } else {
622 return CoverageMode::kWithPosition;
623 }
Michael Ludwig553e9a92018-11-29 12:38:35 -0500624 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500625 return CoverageMode::kNone;
Michael Ludwig553e9a92018-11-29 12:38:35 -0500626 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500627}
Michael Ludwig553e9a92018-11-29 12:38:35 -0500628
Michael Ludwig93aeba02018-12-21 09:50:31 -0500629// Writes four vertices in triangle strip order, including the additional data for local
630// coordinates, domain, color, and coverage as needed to satisfy the vertex spec.
631static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
Brian Salomon1d835422019-03-13 16:11:44 -0400632 CoverageMode mode, Sk4f coverage, SkPMColor4f color4f, const SkRect& domain,
Michael Ludwige6266a22019-03-07 11:24:32 -0500633 const Vertices& quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500634 static constexpr auto If = GrVertexWriter::If<float>;
635
Michael Ludwig553e9a92018-11-29 12:38:35 -0500636 for (int i = 0; i < 4; ++i) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500637 // save position, this is a float2 or float3 or float4 depending on the combination of
638 // perspective and coverage mode.
Michael Ludwige6266a22019-03-07 11:24:32 -0500639 vb->write(quad.fX[i], quad.fY[i],
640 If(spec.deviceQuadType() == GrQuadType::kPerspective, quad.fW[i]),
641 If(mode == CoverageMode::kWithPosition, coverage[i]));
Michael Ludwig4921dc32018-12-03 14:57:29 +0000642
Michael Ludwig93aeba02018-12-21 09:50:31 -0500643 // save color
644 if (spec.hasVertexColors()) {
Brian Salomon1d835422019-03-13 16:11:44 -0400645 bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
Michael Ludwige6266a22019-03-07 11:24:32 -0500646 vb->write(GrVertexColor(
Brian Salomon1d835422019-03-13 16:11:44 -0400647 color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wide));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500648 }
649
650 // save local position
651 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500652 vb->write(quad.fU[i], quad.fV[i],
653 If(spec.localQuadType() == GrQuadType::kPerspective, quad.fR[i]));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500654 }
655
656 // save the domain
657 if (spec.hasDomain()) {
658 vb->write(domain);
659 }
660 }
661}
662
663GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
664
665static const int kVertsPerAAFillRect = 8;
666static const int kIndicesPerAAFillRect = 30;
667
Brian Salomondbf70722019-02-07 11:31:24 -0500668static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500669 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
670
671 // clang-format off
672 static const uint16_t gFillAARectIdx[] = {
673 0, 1, 2, 1, 3, 2,
674 0, 4, 1, 4, 5, 1,
675 0, 6, 4, 0, 2, 6,
676 2, 3, 6, 3, 7, 6,
677 1, 5, 3, 3, 5, 7,
678 };
679 // clang-format on
680
681 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
682 return resourceProvider->findOrCreatePatternedIndexBuffer(
683 gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
684 kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
Michael Ludwig553e9a92018-11-29 12:38:35 -0500685}
686
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400687} // anonymous namespace
688
Michael Ludwigc182b942018-11-16 10:27:51 -0500689namespace GrQuadPerEdgeAA {
690
Brian Salomon1d835422019-03-13 16:11:44 -0400691ColorType MinColorType(SkPMColor4f color) {
692 if (color == SK_PMColor4fWHITE) {
693 return ColorType::kNone;
694 } else if (color.fitsInBytes()) {
695 return ColorType::kByte;
696 } else {
697 return ColorType::kHalf;
698 }
699}
700
Michael Ludwigc182b942018-11-16 10:27:51 -0500701////////////////// Tessellate Implementation
702
703void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
Brian Osman3d139a42018-11-19 10:42:10 -0500704 const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
Michael Ludwigc182b942018-11-16 10:27:51 -0500705 GrQuadAAFlags aaFlags) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500706 CoverageMode mode = get_mode_for_spec(spec);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400707
Michael Ludwigf995c052018-11-26 15:24:29 -0500708 // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
Michael Ludwige6266a22019-03-07 11:24:32 -0500709 Vertices outer;
710 outer.fX = deviceQuad.x4f();
711 outer.fY = deviceQuad.y4f();
712 outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400713
714 // Load local position data into Sk4fs (either none, just u,v or all three)
Michael Ludwige6266a22019-03-07 11:24:32 -0500715 outer.fUVRCount = spec.localDimensionality();
Michael Ludwigc182b942018-11-16 10:27:51 -0500716 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500717 outer.fU = localQuad.x4f();
718 outer.fV = localQuad.y4f();
719 outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400720 }
721
Michael Ludwigc182b942018-11-16 10:27:51 -0500722 GrVertexWriter vb{vertices};
Michael Ludwig93aeba02018-12-21 09:50:31 -0500723 if (spec.usesCoverageAA()) {
724 SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500725 // Must calculate two new quads, an outset and inset by .5 in projected device space, so
Michael Ludwige6266a22019-03-07 11:24:32 -0500726 // duplicate the original quad for the inner space
727 Vertices inner = outer;
Michael Ludwigc182b942018-11-16 10:27:51 -0500728
Michael Ludwige6266a22019-03-07 11:24:32 -0500729 Sk4f maxCoverage = 1.f;
730 if (spec.deviceQuadType() == GrQuadType::kPerspective) {
731 // For perspective, send quads with all edges non-AA through the tessellation to ensure
732 // their corners are processed the same as adjacent quads. This approach relies on
733 // solving edge equations to reconstruct corners, which can create seams if an inner
734 // fully non-AA quad is not similarly processed.
735 maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer);
736 } else if (aaFlags != GrQuadAAFlags::kNone) {
737 // In 2D, the simpler corner math does not cause issues with seaming against non-AA
738 // inner quads.
739 maxCoverage = compute_nested_quad_vertices(
740 aaFlags, spec.deviceQuadType() <= GrQuadType::kRectilinear, &inner, &outer);
741 }
742 // NOTE: could provide an even more optimized tessellation function for axis-aligned
743 // rects since the positions can be outset by constants without doing vector math,
744 // except it must handle identifying the winding of the quad vertices if the transform
745 // applied a mirror, etc. The current 2D case is already adequately fast.
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400746
Michael Ludwig93aeba02018-12-21 09:50:31 -0500747 // Write two quads for inner and outer, inner will use the
Brian Salomon1d835422019-03-13 16:11:44 -0400748 write_quad(&vb, spec, mode, maxCoverage, color4f, domain, inner);
749 write_quad(&vb, spec, mode, 0.f, color4f, domain, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500750 } else {
751 // No outsetting needed, just write a single quad with full coverage
752 SkASSERT(mode == CoverageMode::kNone);
Brian Salomon1d835422019-03-13 16:11:44 -0400753 write_quad(&vb, spec, mode, 1.f, color4f, domain, outer);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400754 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500755
756 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400757}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400758
Michael Ludwig93aeba02018-12-21 09:50:31 -0500759bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
760 int quadCount) {
761 if (spec.usesCoverageAA()) {
762 // AA quads use 8 vertices, basically nested rectangles
Brian Salomondbf70722019-02-07 11:31:24 -0500763 sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
Michael Ludwig93aeba02018-12-21 09:50:31 -0500764 if (!ibuffer) {
765 return false;
766 }
767
768 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500769 mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
770 quadCount, kNumAAQuadsInIndexBuffer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500771 } else {
772 // Non-AA quads use 4 vertices, and regular triangle strip layout
773 if (quadCount > 1) {
Brian Salomondbf70722019-02-07 11:31:24 -0500774 sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Michael Ludwig93aeba02018-12-21 09:50:31 -0500775 if (!ibuffer) {
776 return false;
777 }
778
779 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500780 mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500781 GrResourceProvider::QuadCountOfQuadBuffer());
782 } else {
783 mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
784 mesh->setNonIndexedNonInstanced(4);
785 }
786 }
787
788 return true;
789}
790
Michael Ludwigc182b942018-11-16 10:27:51 -0500791////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400792
Michael Ludwigc182b942018-11-16 10:27:51 -0500793int VertexSpec::deviceDimensionality() const {
794 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
795}
796
797int VertexSpec::localDimensionality() const {
798 return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
799}
800
Michael Ludwig467994d2018-12-03 14:58:31 +0000801////////////////// Geometry Processor Implementation
Michael Ludwigc182b942018-11-16 10:27:51 -0500802
Michael Ludwig467994d2018-12-03 14:58:31 +0000803class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
804public:
Michael Ludwig20e909e2018-10-30 10:43:57 -0400805
Michael Ludwig467994d2018-12-03 14:58:31 +0000806 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
807 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400808 }
809
Michael Ludwig467994d2018-12-03 14:58:31 +0000810 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
811 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500812 const GrSamplerState& samplerState,
813 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000814 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
815 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
Greg Daniel7a82edf2018-12-04 10:54:34 -0500816 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000817 std::move(textureColorSpaceXform)));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400818 }
819
Michael Ludwig467994d2018-12-03 14:58:31 +0000820 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
Michael Ludwig024e2622018-11-30 13:25:55 -0500821
Michael Ludwig467994d2018-12-03 14:58:31 +0000822 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500823 // domain, texturing, device-dimensions are single bit flags
824 uint32_t x = fDomain.isInitialized() ? 0 : 1;
825 x |= fSampler.isInitialized() ? 0 : 2;
826 x |= fNeedsPerspective ? 0 : 4;
Michael Ludwig467994d2018-12-03 14:58:31 +0000827 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
828 if (fLocalCoord.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500829 x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
Brian Osman78dc72c2018-12-03 13:20:43 +0000830 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000831 // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
Michael Ludwig93aeba02018-12-21 09:50:31 -0500832 if (fColor.isInitialized()) {
833 x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
834 }
835 // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor
836 if (fCoverageMode != CoverageMode::kNone) {
837 x |= CoverageMode::kWithPosition == fCoverageMode ? 128 : 256;
Michael Ludwig467994d2018-12-03 14:58:31 +0000838 }
839
840 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
841 b->add32(x);
Brian Osman78dc72c2018-12-03 13:20:43 +0000842 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000843
844 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
845 class GLSLProcessor : public GrGLSLGeometryProcessor {
846 public:
847 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
848 FPCoordTransformIter&& transformIter) override {
849 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
850 if (gp.fLocalCoord.isInitialized()) {
851 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
852 }
853 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
854 }
855
856 private:
857 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
858 using Interpolation = GrGLSLVaryingHandler::Interpolation;
859
860 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
861 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
862 gp.fTextureColorSpaceXform.get());
863
864 args.fVaryingHandler->emitAttributes(gp);
865
Michael Ludwig93aeba02018-12-21 09:50:31 -0500866 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
867 // Strip last channel from the vertex attribute to remove coverage and get the
868 // actual position
869 if (gp.fNeedsPerspective) {
870 args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
871 gp.fPosition.name());
872 } else {
873 args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
874 gp.fPosition.name());
875 }
876 gpArgs->fPositionVar = {"position",
877 gp.fNeedsPerspective ? kFloat3_GrSLType
878 : kFloat2_GrSLType,
879 GrShaderVar::kNone_TypeModifier};
Michael Ludwig467994d2018-12-03 14:58:31 +0000880 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500881 // No coverage to eliminate
882 gpArgs->fPositionVar = gp.fPosition.asShaderVar();
Michael Ludwig467994d2018-12-03 14:58:31 +0000883 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000884
885 // Handle local coordinates if they exist
886 if (gp.fLocalCoord.isInitialized()) {
887 // NOTE: If the only usage of local coordinates is for the inline texture fetch
888 // before FPs, then there are no registered FPCoordTransforms and this ends up
889 // emitting nothing, so there isn't a duplication of local coordinates
890 this->emitTransforms(args.fVertBuilder,
891 args.fVaryingHandler,
892 args.fUniformHandler,
893 gp.fLocalCoord.asShaderVar(),
894 args.fFPCoordTransformHandler);
895 }
896
897 // Solid color before any texturing gets modulated in
898 if (gp.fColor.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500899 // The color cannot be flat if the varying coverage has been modulated into it
Michael Ludwig467994d2018-12-03 14:58:31 +0000900 args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500901 gp.fCoverageMode == CoverageMode::kWithColor ?
902 Interpolation::kInterpolated : Interpolation::kCanBeFlat);
903 } else {
904 // Output color must be initialized to something
905 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
Michael Ludwig467994d2018-12-03 14:58:31 +0000906 }
907
908 // If there is a texture, must also handle texture coordinates and reading from
909 // the texture in the fragment shader before continuing to fragment processors.
910 if (gp.fSampler.isInitialized()) {
911 // Texture coordinates clamped by the domain on the fragment shader; if the GP
912 // has a texture, it's guaranteed to have local coordinates
913 args.fFragBuilder->codeAppend("float2 texCoord;");
914 if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
915 // Can't do a pass through since we need to perform perspective division
916 GrGLSLVarying v(gp.fLocalCoord.gpuType());
917 args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
918 args.fVertBuilder->codeAppendf("%s = %s;",
919 v.vsOut(), gp.fLocalCoord.name());
920 args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
921 v.fsIn(), v.fsIn());
922 } else {
923 args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
924 }
925
926 // Clamp the now 2D localCoordName variable by the domain if it is provided
927 if (gp.fDomain.isInitialized()) {
928 args.fFragBuilder->codeAppend("float4 domain;");
929 args.fVaryingHandler->addPassThroughAttribute(gp.fDomain, "domain",
930 Interpolation::kCanBeFlat);
931 args.fFragBuilder->codeAppend(
932 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
933 }
934
935 // Now modulate the starting output color by the texture lookup
936 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
937 args.fFragBuilder->appendTextureLookupAndModulate(
938 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
939 &fTextureColorSpaceXformHelper);
940 args.fFragBuilder->codeAppend(";");
941 }
942
943 // And lastly, output the coverage calculation code
Michael Ludwig93aeba02018-12-21 09:50:31 -0500944 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
945 GrGLSLVarying coverage(kFloat_GrSLType);
946 args.fVaryingHandler->addVarying("coverage", &coverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000947 if (gp.fNeedsPerspective) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500948 args.fVertBuilder->codeAppendf("%s = %s.w;",
949 coverage.vsOut(), gp.fPosition.name());
950 } else {
951 args.fVertBuilder->codeAppendf("%s = %s.z;",
952 coverage.vsOut(), gp.fPosition.name());
Michael Ludwig467994d2018-12-03 14:58:31 +0000953 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500954
Ethan Nicholase1f55022019-02-05 17:17:40 -0500955 args.fFragBuilder->codeAppendf("%s = half4(half(%s));",
Michael Ludwig93aeba02018-12-21 09:50:31 -0500956 args.fOutputCoverage, coverage.fsIn());
Michael Ludwig467994d2018-12-03 14:58:31 +0000957 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500958 // Set coverage to 1, since it's either non-AA or the coverage was already
959 // folded into the output color
Ethan Nicholase1f55022019-02-05 17:17:40 -0500960 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000961 }
962 }
963 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
964 };
965 return new GLSLProcessor;
966 }
967
968private:
969 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
970 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
971 , fTextureColorSpaceXform(nullptr) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500972 SkASSERT(!spec.hasDomain());
Michael Ludwig467994d2018-12-03 14:58:31 +0000973 this->initializeAttrs(spec);
974 this->setTextureSamplerCnt(0);
975 }
976
977 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
978 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500979 const GrSamplerState& samplerState,
980 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000981 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
982 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
983 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Greg Daniel7a82edf2018-12-04 10:54:34 -0500984 , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500985 SkASSERT(spec.hasLocalCoords());
Michael Ludwig467994d2018-12-03 14:58:31 +0000986 this->initializeAttrs(spec);
987 this->setTextureSamplerCnt(1);
988 }
989
990 void initializeAttrs(const VertexSpec& spec) {
991 fNeedsPerspective = spec.deviceDimensionality() == 3;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500992 fCoverageMode = get_mode_for_spec(spec);
993
994 if (fCoverageMode == CoverageMode::kWithPosition) {
995 if (fNeedsPerspective) {
996 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
997 } else {
998 fPosition = {"positionWithCoverage", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
999 }
1000 } else {
1001 if (fNeedsPerspective) {
1002 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1003 } else {
1004 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1005 }
1006 }
Michael Ludwig467994d2018-12-03 14:58:31 +00001007
1008 int localDim = spec.localDimensionality();
1009 if (localDim == 3) {
1010 fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1011 } else if (localDim == 2) {
1012 fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1013 } // else localDim == 0 and attribute remains uninitialized
1014
1015 if (ColorType::kByte == spec.colorType()) {
1016 fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
1017 } else if (ColorType::kHalf == spec.colorType()) {
1018 fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
1019 }
1020
1021 if (spec.hasDomain()) {
1022 fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1023 }
1024
Michael Ludwig93aeba02018-12-21 09:50:31 -05001025 this->setVertexAttributes(&fPosition, 4);
Michael Ludwig467994d2018-12-03 14:58:31 +00001026 }
1027
1028 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
1029
Michael Ludwig93aeba02018-12-21 09:50:31 -05001030 Attribute fPosition; // May contain coverage as last channel
1031 Attribute fColor; // May have coverage modulated in if the FPs support it
Michael Ludwig467994d2018-12-03 14:58:31 +00001032 Attribute fLocalCoord;
1033 Attribute fDomain;
Michael Ludwig467994d2018-12-03 14:58:31 +00001034
Michael Ludwig93aeba02018-12-21 09:50:31 -05001035 // The positions attribute may have coverage built into it, so float3 is an ambiguous type
1036 // and may mean 2d with coverage, or 3d with no coverage
Michael Ludwig467994d2018-12-03 14:58:31 +00001037 bool fNeedsPerspective;
Michael Ludwig93aeba02018-12-21 09:50:31 -05001038 CoverageMode fCoverageMode;
Michael Ludwig467994d2018-12-03 14:58:31 +00001039
1040 // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
1041 // to skip texturing.
1042 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1043 TextureSampler fSampler;
1044
1045 typedef GrGeometryProcessor INHERITED;
1046};
1047
1048sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
1049 return QuadPerEdgeAAGeometryProcessor::Make(spec);
1050}
1051
1052sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
1053 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -05001054 const GrSamplerState& samplerState, uint32_t extraSamplerKey,
1055 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1056 return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
1057 samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +00001058 std::move(textureColorSpaceXform));
Michael Ludwig20e909e2018-10-30 10:43:57 -04001059}
Michael Ludwigc182b942018-11-16 10:27:51 -05001060
1061} // namespace GrQuadPerEdgeAA