blob: 2a53dd910db035f9ff2109be3ee2b150d7953727 [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"
Brian Osman8fa7ab42019-03-18 10:22:42 -040017#include "SkGr.h"
Michael Ludwig460eb5e2018-10-29 11:09:29 -040018#include "SkNx.h"
19
Michael Ludwigf995c052018-11-26 15:24:29 -050020#define AI SK_ALWAYS_INLINE
21
Michael Ludwig460eb5e2018-10-29 11:09:29 -040022namespace {
23
Michael Ludwige6266a22019-03-07 11:24:32 -050024// Helper data types since there is a lot of information that needs to be passed around to
25// avoid recalculation in the different procedures for tessellating an AA quad.
26
27struct Vertices {
28 // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
29 Sk4f fX, fY, fW;
30 // U, V, and R coordinates representing local quad. Ignored depending on uvrCount (0, 1, 2).
31 Sk4f fU, fV, fR;
32 int fUVRCount;
33};
34
35struct QuadMetadata {
36 // Normalized edge vectors of the device space quad, ordered L, B, T, R (i.e. nextCCW(x) - x).
37 Sk4f fDX, fDY;
38 // 1 / edge length of the device space quad
39 Sk4f fInvLengths;
40 // Edge mask (set to all 1s if aa flags is kAll), otherwise 1.f if edge was AA, 0.f if non-AA.
41 Sk4f fMask;
42};
43
44struct Edges {
45 // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
46 Sk4f fA, fB, fC;
47 // Whether or not the edge normals had to be flipped to preserve positive distance on the inside
48 bool fFlipped;
49};
50
51static constexpr float kTolerance = 1e-2f;
52
Michael Ludwigf995c052018-11-26 15:24:29 -050053static AI Sk4f fma(const Sk4f& f, const Sk4f& m, const Sk4f& a) {
54 return SkNx_fma<4, float>(f, m, a);
55}
56
57// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
58// order.
59static AI Sk4f nextCW(const Sk4f& v) {
60 return SkNx_shuffle<2, 0, 3, 1>(v);
61}
62
63static AI Sk4f nextCCW(const Sk4f& v) {
64 return SkNx_shuffle<1, 3, 0, 2>(v);
65}
66
Michael Ludwige6266a22019-03-07 11:24:32 -050067// Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector.
68// e3 may be null if only 2D edges need to be corrected for.
69static AI void correct_bad_edges(const Sk4f& bad, Sk4f* e1, Sk4f* e2, Sk4f* e3) {
70 if (bad.anyTrue()) {
71 // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding
72 *e1 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e1), *e1);
73 *e2 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e2), *e2);
74 if (e3) {
75 *e3 = bad.thenElse(-SkNx_shuffle<3, 2, 1, 0>(*e3), *e3);
76 }
77 }
Michael Ludwigf995c052018-11-26 15:24:29 -050078}
79
Michael Ludwige6266a22019-03-07 11:24:32 -050080// Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points.
81static AI void correct_bad_coords(const Sk4f& bad, Sk4f* c1, Sk4f* c2, Sk4f* c3) {
82 if (bad.anyTrue()) {
83 *c1 = bad.thenElse(nextCCW(*c1), *c1);
84 *c2 = bad.thenElse(nextCCW(*c2), *c2);
85 if (c3) {
86 *c3 = bad.thenElse(nextCCW(*c3), *c3);
87 }
88 }
Michael Ludwig4921dc32018-12-03 14:57:29 +000089}
90
Michael Ludwige6266a22019-03-07 11:24:32 -050091static AI QuadMetadata get_metadata(const Vertices& vertices, GrQuadAAFlags aaFlags) {
92 Sk4f dx = nextCCW(vertices.fX) - vertices.fX;
93 Sk4f dy = nextCCW(vertices.fY) - vertices.fY;
94 Sk4f invLengths = fma(dx, dx, dy * dy).rsqrt();
95
96 Sk4f mask = aaFlags == GrQuadAAFlags::kAll ? Sk4f(1.f) :
97 Sk4f((GrQuadAAFlags::kLeft & aaFlags) ? 1.f : 0.f,
98 (GrQuadAAFlags::kBottom & aaFlags) ? 1.f : 0.f,
99 (GrQuadAAFlags::kTop & aaFlags) ? 1.f : 0.f,
100 (GrQuadAAFlags::kRight & aaFlags) ? 1.f : 0.f);
101 return { dx * invLengths, dy * invLengths, invLengths, mask };
102}
103
104static AI Edges get_edge_equations(const QuadMetadata& metadata, const Vertices& vertices) {
105 Sk4f dx = metadata.fDX;
106 Sk4f dy = metadata.fDY;
107 // Correct for bad edges by copying adjacent edge information into the bad component
108 correct_bad_edges(metadata.fInvLengths >= 1.f / kTolerance, &dx, &dy, nullptr);
109
110 Sk4f c = fma(dx, vertices.fY, -dy * vertices.fX);
111 // Make sure normals point into the shape
112 Sk4f test = fma(dy, nextCW(vertices.fX), fma(-dx, nextCW(vertices.fY), c));
113 if ((test < -kTolerance).anyTrue()) {
114 return {-dy, dx, -c, true};
115 } else {
116 return {dy, -dx, c, false};
117 }
118}
119
120// Sets 'outset' to the magnitude of outset/inset to adjust each corner of a quad given the
121// edge angles and lengths. If the quad is too small, has empty edges, or too sharp of angles,
122// false is returned and the degenerate slow-path should be used.
123static bool get_optimized_outset(const QuadMetadata& metadata, bool rectilinear, Sk4f* outset) {
124 if (rectilinear) {
125 *outset = 0.5f;
126 // Stay in the fast path as long as all edges are at least a pixel long (so 1/len <= 1)
127 return (metadata.fInvLengths <= 1.f).allTrue();
128 }
129
130 if ((metadata.fInvLengths >= 1.f / kTolerance).anyTrue()) {
131 // Have an empty edge from a degenerate quad, so there's no hope
132 return false;
133 }
134
135 // The distance the point needs to move is 1/2sin(theta), where theta is the angle between the
136 // two edges at that point. cos(theta) is equal to dot(dxy, nextCW(dxy))
137 Sk4f cosTheta = fma(metadata.fDX, nextCW(metadata.fDX), metadata.fDY * nextCW(metadata.fDY));
138 // If the angle is too shallow between edges, go through the degenerate path, otherwise adding
139 // and subtracting very large vectors in almost opposite directions leads to float errors
140 if ((cosTheta.abs() >= 0.9f).anyTrue()) {
141 return false;
142 }
143 *outset = 0.5f * (1.f - cosTheta * cosTheta).rsqrt(); // 1/2sin(theta)
144
145 // When outsetting or insetting, the current edge's AA adds to the length:
146 // cos(pi - theta)/2sin(theta) + cos(pi-ccw(theta))/2sin(ccw(theta))
147 // Moving an adjacent edge updates the length by 1/2sin(theta|ccw(theta))
148 Sk4f halfTanTheta = -cosTheta * (*outset); // cos(pi - theta) = -cos(theta)
149 Sk4f edgeAdjust = metadata.fMask * (halfTanTheta + nextCCW(halfTanTheta)) +
150 nextCCW(metadata.fMask) * nextCCW(*outset) +
151 nextCW(metadata.fMask) * (*outset);
152 // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make edgeLen negative
153 // then use the slow path
154 Sk4f threshold = 0.1f - metadata.fInvLengths.invert();
155 return (edgeAdjust > threshold).allTrue() && (edgeAdjust < -threshold).allTrue();
156}
157
158// Ignores the quad's fW, use outset_projected_vertices if it's known to need 3D.
159static AI void outset_vertices(const Sk4f& outset, const QuadMetadata& metadata, Vertices* quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500160 // The mask is rotated compared to the outsets and edge vectors, since if the edge is "on"
161 // both its points need to be moved along their other edge vectors.
Michael Ludwige6266a22019-03-07 11:24:32 -0500162 auto maskedOutset = -outset * nextCW(metadata.fMask);
163 auto maskedOutsetCW = outset * metadata.fMask;
164 // x = x + outset * mask * nextCW(xdiff) - outset * nextCW(mask) * xdiff
165 quad->fX += fma(maskedOutsetCW, nextCW(metadata.fDX), maskedOutset * metadata.fDX);
166 quad->fY += fma(maskedOutsetCW, nextCW(metadata.fDY), maskedOutset * metadata.fDY);
167 if (quad->fUVRCount > 0) {
Michael Ludwigf995c052018-11-26 15:24:29 -0500168 // We want to extend the texture coords by the same proportion as the positions.
Michael Ludwige6266a22019-03-07 11:24:32 -0500169 maskedOutset *= metadata.fInvLengths;
170 maskedOutsetCW *= nextCW(metadata.fInvLengths);
171 Sk4f du = nextCCW(quad->fU) - quad->fU;
172 Sk4f dv = nextCCW(quad->fV) - quad->fV;
173 quad->fU += fma(maskedOutsetCW, nextCW(du), maskedOutset * du);
174 quad->fV += fma(maskedOutsetCW, nextCW(dv), maskedOutset * dv);
175 if (quad->fUVRCount == 3) {
176 Sk4f dr = nextCCW(quad->fR) - quad->fR;
177 quad->fR += fma(maskedOutsetCW, nextCW(dr), maskedOutset * dr);
Michael Ludwigf995c052018-11-26 15:24:29 -0500178 }
179 }
180}
181
Michael Ludwige6266a22019-03-07 11:24:32 -0500182// Updates (x,y,w) to be at (x2d,y2d) once projected. Updates (u,v,r) to match if provided.
183// Gracefully handles 2D content if *w holds all 1s.
184static void outset_projected_vertices(const Sk4f& x2d, const Sk4f& y2d,
185 GrQuadAAFlags aaFlags, Vertices* quad) {
186 // Left to right, in device space, for each point
187 Sk4f e1x = SkNx_shuffle<2, 3, 2, 3>(quad->fX) - SkNx_shuffle<0, 1, 0, 1>(quad->fX);
188 Sk4f e1y = SkNx_shuffle<2, 3, 2, 3>(quad->fY) - SkNx_shuffle<0, 1, 0, 1>(quad->fY);
189 Sk4f e1w = SkNx_shuffle<2, 3, 2, 3>(quad->fW) - SkNx_shuffle<0, 1, 0, 1>(quad->fW);
190 correct_bad_edges(fma(e1x, e1x, e1y * e1y) < kTolerance * kTolerance, &e1x, &e1y, &e1w);
191
192 // // Top to bottom, in device space, for each point
193 Sk4f e2x = SkNx_shuffle<1, 1, 3, 3>(quad->fX) - SkNx_shuffle<0, 0, 2, 2>(quad->fX);
194 Sk4f e2y = SkNx_shuffle<1, 1, 3, 3>(quad->fY) - SkNx_shuffle<0, 0, 2, 2>(quad->fY);
195 Sk4f e2w = SkNx_shuffle<1, 1, 3, 3>(quad->fW) - SkNx_shuffle<0, 0, 2, 2>(quad->fW);
196 correct_bad_edges(fma(e2x, e2x, e2y * e2y) < kTolerance * kTolerance, &e2x, &e2y, &e2w);
197
198 // Can only move along e1 and e2 to reach the new 2D point, so we have
199 // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and
200 // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b
201 // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where
202 // the cNx and cNy coefficients are:
203 Sk4f c1x = e1w * x2d - e1x;
204 Sk4f c1y = e1w * y2d - e1y;
205 Sk4f c2x = e2w * x2d - e2x;
206 Sk4f c2y = e2w * y2d - e2y;
207 Sk4f c3x = quad->fW * x2d - quad->fX;
208 Sk4f c3y = quad->fW * y2d - quad->fY;
209
210 // Solve for a and b
211 Sk4f a, b, denom;
212 if (aaFlags == GrQuadAAFlags::kAll) {
213 // When every edge is outset/inset, each corner can use both edge vectors
214 denom = c1x * c2y - c2x * c1y;
215 a = (c2x * c3y - c3x * c2y) / denom;
216 b = (c3x * c1y - c1x * c3y) / denom;
217 } else {
218 // Force a or b to be 0 if that edge cannot be used due to non-AA
219 // FIXME requires the extra > 0.f, since Sk4f's thenElse only works if true values have
220 // all their bits set to 1.
221 Sk4f aMask = Sk4f((aaFlags & GrQuadAAFlags::kLeft) ? 1.f : 0.f,
222 (aaFlags & GrQuadAAFlags::kLeft) ? 1.f : 0.f,
223 (aaFlags & GrQuadAAFlags::kRight) ? 1.f : 0.f,
224 (aaFlags & GrQuadAAFlags::kRight) ? 1.f : 0.f) > 0.f;
225 Sk4f bMask = Sk4f((aaFlags & GrQuadAAFlags::kTop) ? 1.f : 0.f,
226 (aaFlags & GrQuadAAFlags::kBottom) ? 1.f : 0.f,
227 (aaFlags & GrQuadAAFlags::kTop) ? 1.f : 0.f,
228 (aaFlags & GrQuadAAFlags::kBottom) ? 1.f : 0.f) > 0.f;
229
230 // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case.
231 // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom
232 // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, ""
233 // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0
234 Sk4f useC1x = c1x.abs() > c1y.abs();
235 Sk4f useC2x = c2x.abs() > c2y.abs();
236 // -------- A & B ------ --------- A & !B ---------
237 denom = aMask.thenElse(bMask.thenElse(c1x * c2y - c2x * c1y, useC1x.thenElse(c1x, c1y)),
238 // ------- !A & B ---------- - !A & !B -
239 bMask.thenElse(useC2x.thenElse(c2x, c2y), 1.0f));
240 // -------- A & B ------ ---------- A & !B ----------
241 a = aMask.thenElse(bMask.thenElse(c2x * c3y - c3x * c2y, useC1x.thenElse(-c3x, -c3y)),
242 // - !A -
243 0.0f) / denom;
244 // -------- A & B ------ ---------- !A & B ----------
245 b = bMask.thenElse(aMask.thenElse(c3x * c1y - c1x * c3y, useC2x.thenElse(-c3x, -c3y)),
246 // - !B -
247 0.0f) / denom;
248 }
249
250 quad->fX += a * e1x + b * e2x;
251 quad->fY += a * e1y + b * e2y;
252 quad->fW += a * e1w + b * e2w;
253 correct_bad_coords(denom.abs() < kTolerance, &quad->fX, &quad->fY, &quad->fW);
254
255 if (quad->fUVRCount > 0) {
256 // Calculate R here so it can be corrected with U and V in case it's needed later
257 Sk4f e1u = SkNx_shuffle<2, 3, 2, 3>(quad->fU) - SkNx_shuffle<0, 1, 0, 1>(quad->fU);
258 Sk4f e1v = SkNx_shuffle<2, 3, 2, 3>(quad->fV) - SkNx_shuffle<0, 1, 0, 1>(quad->fV);
259 Sk4f e1r = SkNx_shuffle<2, 3, 2, 3>(quad->fR) - SkNx_shuffle<0, 1, 0, 1>(quad->fR);
260 correct_bad_edges(fma(e1u, e1u, e1v * e1v) < kTolerance * kTolerance, &e1u, &e1v, &e1r);
261
262 Sk4f e2u = SkNx_shuffle<1, 1, 3, 3>(quad->fU) - SkNx_shuffle<0, 0, 2, 2>(quad->fU);
263 Sk4f e2v = SkNx_shuffle<1, 1, 3, 3>(quad->fV) - SkNx_shuffle<0, 0, 2, 2>(quad->fV);
264 Sk4f e2r = SkNx_shuffle<1, 1, 3, 3>(quad->fR) - SkNx_shuffle<0, 0, 2, 2>(quad->fR);
265 correct_bad_edges(fma(e2u, e2u, e2v * e2v) < kTolerance * kTolerance, &e2u, &e2v, &e2r);
266
267 quad->fU += a * e1u + b * e2u;
268 quad->fV += a * e1v + b * e2v;
269 if (quad->fUVRCount == 3) {
270 quad->fR += a * e1r + b * e2r;
271 correct_bad_coords(denom.abs() < kTolerance, &quad->fU, &quad->fV, &quad->fR);
272 } else {
273 correct_bad_coords(denom.abs() < kTolerance, &quad->fU, &quad->fV, nullptr);
Michael Ludwigf995c052018-11-26 15:24:29 -0500274 }
275 }
276}
277
Michael Ludwige6266a22019-03-07 11:24:32 -0500278// Calculate area of intersection between quad (xs, ys) and a pixel at 'pixelCenter'.
279// a, b, c are edge equations of the quad, flipped is true if the line equations had their normals
280// reversed to correct for matrix transforms.
281static float get_exact_coverage(const SkPoint& pixelCenter, const Vertices& quad,
282 const Edges& edges) {
283 // Ordering of vertices given default tri-strip that produces CCW points
284 static const int kCCW[] = {0, 1, 3, 2};
285 // Ordering of vertices given inverted tri-strip that produces CCW
286 static const int kFlippedCCW[] = {0, 2, 3, 1};
287
288 // Edge boundaries of the pixel
289 float left = pixelCenter.fX - 0.5f;
290 float right = pixelCenter.fX + 0.5f;
291 float top = pixelCenter.fY - 0.5f;
292 float bot = pixelCenter.fY + 0.5f;
293
294 // Whether or not the 4 corners of the pixel are inside the quad geometry. Variable names are
295 // intentional to work easily with the helper macros.
296 bool topleftInside = ((edges.fA * left + edges.fB * top + edges.fC) >= 0.f).allTrue();
297 bool botleftInside = ((edges.fA * left + edges.fB * bot + edges.fC) >= 0.f).allTrue();
298 bool botrightInside = ((edges.fA * right + edges.fB * bot + edges.fC) >= 0.f).allTrue();
299 bool toprightInside = ((edges.fA * right + edges.fB * top + edges.fC) >= 0.f).allTrue();
300 if (topleftInside && botleftInside && botrightInside && toprightInside) {
301 // Quad fully contains the pixel, so we know the area will be 1.f
302 return 1.f;
303 }
304
305 // 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 -0400306 Sk4f left4f = quad.fX >= left;
307 Sk4f right4f = quad.fX <= right;
308 Sk4f top4f = quad.fY >= top;
309 Sk4f bot4f = quad.fY <= bot;
310 // Use bit casting so that overflows don't occur on WASM (will be cleaned up in SkVx port)
311 Sk4i leftValid = Sk4i::Load(&left4f);
312 Sk4i rightValid = Sk4i::Load(&right4f);
313 Sk4i topValid = Sk4i::Load(&top4f);
314 Sk4i botValid = Sk4i::Load(&bot4f);
Michael Ludwige6266a22019-03-07 11:24:32 -0500315
316 // Intercepts of quad lines with the 4 pixel edges
317 Sk4f leftCross = -(edges.fC + edges.fA * left) / edges.fB;
318 Sk4f rightCross = -(edges.fC + edges.fA * right) / edges.fB;
319 Sk4f topCross = -(edges.fC + edges.fB * top) / edges.fA;
320 Sk4f botCross = -(edges.fC + edges.fB * bot) / edges.fA;
321
322 // State for implicitly tracking the intersection boundary and area
323 SkPoint firstPoint = {0.f, 0.f};
324 SkPoint lastPoint = {0.f, 0.f};
325 bool intersected = false;
326 float area = 0.f;
327
328 // Adds a point to the intersection hull, remembering first point (for closing) and the
329 // current point, and updates the running area total.
330 // See http://mathworld.wolfram.com/PolygonArea.html
331 auto accumulate = [&](const SkPoint& p) {
332 if (intersected) {
333 float da = lastPoint.fX * p.fY - p.fX * lastPoint.fY;
334 area += da;
335 } else {
336 firstPoint = p;
337 intersected = true;
338 }
339 lastPoint = p;
340 };
341
342 // Used during iteration over the quad points to check if edge intersections are valid and
343 // should be accumulated.
344#define ADD_EDGE_CROSSING_X(SIDE) \
345 do { \
346 if (SIDE##Cross[ei] >= top && SIDE##Cross[ei] <= bot) { \
347 accumulate({SIDE, SIDE##Cross[ei]}); \
348 addedIntersection = true; \
349 } \
350 } while(false)
351#define ADD_EDGE_CROSSING_Y(SIDE) \
352 do { \
353 if (SIDE##Cross[ei] >= left && SIDE##Cross[ei] <= right) { \
354 accumulate({SIDE##Cross[ei], SIDE}); \
355 addedIntersection = true; \
356 } \
357 } while(false)
358#define TEST_EDGES(SIDE, AXIS, I, NI) \
359 do { \
360 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
361 ADD_EDGE_CROSSING_##AXIS(SIDE); \
362 crossedEdges = true; \
363 } \
364 } while(false)
365 // Used during iteration over the quad points to check if a pixel corner should be included
366 // in the intersection boundary
367#define ADD_CORNER(CHECK, SIDE_LR, SIDE_TB) \
368 if (!CHECK##Valid[i] || !CHECK##Valid[ni]) { \
369 if (SIDE_TB##SIDE_LR##Inside) { \
370 accumulate({SIDE_LR, SIDE_TB}); \
371 } \
372 }
373#define TEST_CORNER_X(SIDE, I, NI) \
374 do { \
375 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
376 ADD_CORNER(top, SIDE, top) else ADD_CORNER(bot, SIDE, bot) \
377 } \
378 } while(false)
379#define TEST_CORNER_Y(SIDE, I, NI) \
380 do { \
381 if (!SIDE##Valid[I] && SIDE##Valid[NI]) { \
382 ADD_CORNER(left, left, SIDE) else ADD_CORNER(right, right, SIDE) \
383 } \
384 } while(false)
385
386 // Iterate over the 4 points of the quad, adding valid intersections with the pixel edges
387 // or adding interior pixel corners as it goes. This automatically keeps all accumulated points
388 // in CCW ordering so the area can be calculated on the fly and there's no need to store the
389 // list of hull points. This is somewhat inspired by the Sutherland-Hodgman algorithm but since
390 // there are only 4 points in each source polygon, there is no point list maintenance.
391 for (int j = 0; j < 4; ++j) {
392 // Current vertex
393 int i = edges.fFlipped ? kFlippedCCW[j] : kCCW[j];
394 // Moving to this vertex
395 int ni = edges.fFlipped ? kFlippedCCW[(j + 1) % 4] : kCCW[(j + 1) % 4];
396 // Index in edge vectors corresponding to move from i to ni
397 int ei = edges.fFlipped ? ni : i;
398
399 bool crossedEdges = false;
400 bool addedIntersection = false;
401
402 // First check if there are any outside -> inside edge crossings. There can be 0, 1, or 2.
403 // 2 can occur if one crossing is still outside the pixel, or if they both go through
404 // the corner (in which case a duplicate point is added, but that doesn't change area).
405
406 // Outside to inside crossing
407 TEST_EDGES(left, X, i, ni);
408 TEST_EDGES(right, X, i, ni);
409 TEST_EDGES(top, Y, i, ni);
410 TEST_EDGES(bot, Y, i, ni);
411 // Inside to outside crossing (swapping ni and i in the boolean test)
412 TEST_EDGES(left, X, ni, i);
413 TEST_EDGES(right, X, ni, i);
414 TEST_EDGES(top, Y, ni, i);
415 TEST_EDGES(bot, Y, ni, i);
416
417 // If we crossed edges but didn't add any intersections, check the corners of the pixel.
418 // If the pixel corners are inside the quad, include them in the boundary.
419 if (crossedEdges && !addedIntersection) {
420 // This can lead to repeated points, but those just accumulate zero area
421 TEST_CORNER_X(left, i, ni);
422 TEST_CORNER_X(right, i, ni);
423 TEST_CORNER_Y(top, i, ni);
424 TEST_CORNER_Y(bot, i, ni);
425
426 TEST_CORNER_X(left, ni, i);
427 TEST_CORNER_X(right, ni, i);
428 TEST_CORNER_Y(top, ni, i);
429 TEST_CORNER_Y(bot, ni, i);
430 }
431
432 // Lastly, if the next point is completely inside the pixel it gets included in the boundary
433 if (leftValid[ni] && rightValid[ni] && topValid[ni] && botValid[ni]) {
434 accumulate({quad.fX[ni], quad.fY[ni]});
435 }
436 }
437
438#undef TEST_CORNER_Y
439#undef TEST_CORNER_X
440#undef ADD_CORNER
441
442#undef TEST_EDGES
443#undef ADD_EDGE_CROSSING_Y
444#undef ADD_EDGE_CROSSING_X
445
446 // After all points have been considered, close the boundary to get final area. If we never
447 // added any points, it means the quad didn't intersect the pixel rectangle.
448 if (intersected) {
449 // Final equation for area of convex polygon is to multiply by -1/2 (minus since the points
450 // were in CCW order).
451 accumulate(firstPoint);
452 return -0.5f * area;
453 } else {
454 return 0.f;
455 }
456}
457
458// Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
459// parallel, or edges are very short/zero-length. Returns coverage for each vertex.
460// Requires (dx, dy) to already be fixed for empty edges.
461static Sk4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const Sk4f& mask, const Edges& edges,
462 bool outset, Vertices* quad) {
463 // Move the edge 1/2 pixel in or out depending on 'outset'.
464 Sk4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
465
466 // There are 6 points that we care about to determine the final shape of the polygon, which
467 // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
468 // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
469 Sk4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
470 Sk4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
471 Sk4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
472 correct_bad_coords(denom.abs() < kTolerance, &px, &py, nullptr);
473
474 // Calculate the signed distances from these 4 corners to the other two edges that did not
475 // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
476 // p(3) to e0,e2
477 Sk4f dists1 = px * SkNx_shuffle<3, 3, 0, 0>(edges.fA) +
478 py * SkNx_shuffle<3, 3, 0, 0>(edges.fB) +
479 SkNx_shuffle<3, 3, 0, 0>(oc);
480 Sk4f dists2 = px * SkNx_shuffle<1, 2, 1, 2>(edges.fA) +
481 py * SkNx_shuffle<1, 2, 1, 2>(edges.fB) +
482 SkNx_shuffle<1, 2, 1, 2>(oc);
483
484 // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
485 // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
486 // and we need to use a central point to represent it. If all four points are only on the
487 // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
488 // Otherwise, use a triangle that replaces the bad points with the intersections of
489 // (e1, e2) or (e0, e3) as needed.
490 Sk4f d1v0 = dists1 < kTolerance;
491 Sk4f d2v0 = dists2 < kTolerance;
492 // FIXME(michaelludwig): Sk4f has anyTrue() and allTrue(), but not & or |. Sk4i has & or | but
493 // not anyTrue() and allTrue(). Moving to SkVx from SkNx will clean this up.
Michael Ludwiga89cc052019-03-14 14:26:47 -0400494 Sk4i d1And2 = Sk4i::Load(&d1v0) & Sk4i::Load(&d2v0);
495 Sk4i d1Or2 = Sk4i::Load(&d1v0) | Sk4i::Load(&d2v0);
Michael Ludwige6266a22019-03-07 11:24:32 -0500496
497 Sk4f coverage;
498 if (!d1Or2[0] && !d1Or2[1] && !d1Or2[2] && !d1Or2[3]) {
499 // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
500 // and use full coverage
501 coverage = 1.f;
Greg Kaiser2f3b0dd2019-03-12 06:27:52 -0700502 } else if (d1And2[0] || d1And2[1] || d1And2[2] || d1And2[3]) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500503 // A point failed against two edges, so reduce the shape to a single point, which we take as
504 // the center of the original quad to ensure it is contained in the intended geometry. Since
505 // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
506 SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
507 0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
508 coverage = get_exact_coverage(center, *quad, edges);
509 px = center.fX;
510 py = center.fY;
511 } else if (d1Or2[0] && d1Or2[1] && d1Or2[2] && d1Or2[3]) {
512 // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
513 // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
514 if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
515 // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
516 px = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(px) + SkNx_shuffle<2, 3, 2, 3>(px));
517 py = 0.5f * (SkNx_shuffle<0, 1, 0, 1>(py) + SkNx_shuffle<2, 3, 2, 3>(py));
518 float mc02 = get_exact_coverage({px[0], py[0]}, *quad, edges);
519 float mc13 = get_exact_coverage({px[1], py[1]}, *quad, edges);
520 coverage = Sk4f(mc02, mc13, mc02, mc13);
521 } else {
522 // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
523 px = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(px) + SkNx_shuffle<1, 1, 3, 3>(px));
524 py = 0.5f * (SkNx_shuffle<0, 0, 2, 2>(py) + SkNx_shuffle<1, 1, 3, 3>(py));
525 float mc01 = get_exact_coverage({px[0], py[0]}, *quad, edges);
526 float mc23 = get_exact_coverage({px[2], py[2]}, *quad, edges);
527 coverage = Sk4f(mc01, mc01, mc23, mc23);
528 }
529 } else {
530 // This turns into a triangle. Replace corners as needed with the intersections between
531 // (e0,e3) and (e1,e2), which must now be calculated
532 Sk2f eDenom = SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(edges.fB) -
533 SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(edges.fA);
534 Sk2f ex = (SkNx_shuffle<0, 1>(edges.fB) * SkNx_shuffle<3, 2>(oc) -
535 SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fB)) / eDenom;
536 Sk2f ey = (SkNx_shuffle<0, 1>(oc) * SkNx_shuffle<3, 2>(edges.fA) -
537 SkNx_shuffle<0, 1>(edges.fA) * SkNx_shuffle<3, 2>(oc)) / eDenom;
538
539 if (SkScalarAbs(eDenom[0]) > kTolerance) {
540 px = d1v0.thenElse(ex[0], px);
541 py = d1v0.thenElse(ey[0], py);
542 }
543 if (SkScalarAbs(eDenom[1]) > kTolerance) {
544 px = d2v0.thenElse(ex[1], px);
545 py = d2v0.thenElse(ey[1], py);
546 }
547
548 coverage = 1.f;
549 }
550
551 outset_projected_vertices(px, py, aaFlags, quad);
552 return coverage;
Michael Ludwigf995c052018-11-26 15:24:29 -0500553}
554
Michael Ludwig93aeba02018-12-21 09:50:31 -0500555// Computes the vertices for the two nested quads used to create AA edges. The original single quad
Michael Ludwige6266a22019-03-07 11:24:32 -0500556// should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
557// stored in-place on return. Returns per-vertex coverage for the inner vertices.
558static Sk4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
559 Vertices* inner, Vertices* outer) {
560 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
561 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400562
Michael Ludwige6266a22019-03-07 11:24:32 -0500563 QuadMetadata metadata = get_metadata(*inner, aaFlags);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400564
Michael Ludwig93aeba02018-12-21 09:50:31 -0500565 // 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 -0500566 // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
567 // 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 -0500568 Sk4f outset = 0.5f;
Michael Ludwige6266a22019-03-07 11:24:32 -0500569 if (get_optimized_outset(metadata, rectilinear, &outset)) {
570 // Since it's not subpixel, outsetting and insetting are trivial vector additions.
571 outset_vertices(outset, metadata, outer);
572 outset_vertices(-outset, metadata, inner);
573 return 1.f;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400574 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000575
Michael Ludwige6266a22019-03-07 11:24:32 -0500576 // Only compute edge equations once since they are the same for inner and outer quads
577 Edges edges = get_edge_equations(metadata, *inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500578
Michael Ludwige6266a22019-03-07 11:24:32 -0500579 // Calculate both outset and inset, returning the coverage reported for the inset, since the
580 // outset will always have 0.0f.
581 compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
582 return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500583}
584
Michael Ludwige6266a22019-03-07 11:24:32 -0500585// Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
586// division of the device coordinates, the original local coordinate value is at the original
587// un-outset device position.
588static Sk4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
589 Vertices* outer) {
590 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
591 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500592
Michael Ludwige6266a22019-03-07 11:24:32 -0500593 // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
594 // Don't use Sk4f.invert() here because it does not preserve 1/1 == 1, which creates rendering
595 // mismatches for 2D content that was batched into a 3D op, vs. 2D on its own.
596 Sk4f iw = 1.0f / inner->fW;
597 Sk4f x2d = inner->fX * iw;
598 Sk4f y2d = inner->fY * iw;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500599
Michael Ludwige6266a22019-03-07 11:24:32 -0500600 Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
601 Vertices outer2D = inner2D;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400602
Michael Ludwige6266a22019-03-07 11:24:32 -0500603 Sk4f coverage = compute_nested_quad_vertices(aaFlags, /* rect */ false, &inner2D, &outer2D);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400604
Michael Ludwige6266a22019-03-07 11:24:32 -0500605 // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
606 outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
607 outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500608
Michael Ludwige6266a22019-03-07 11:24:32 -0500609 return coverage;
Eric Boren98cb1592018-11-26 18:38:05 +0000610}
611
Michael Ludwig93aeba02018-12-21 09:50:31 -0500612enum class CoverageMode {
613 kNone,
614 kWithPosition,
615 kWithColor
616};
617
618static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
619 if (spec.usesCoverageAA()) {
Brian Osman605c6d52019-03-15 12:10:35 -0400620 if (spec.compatibleWithCoverageAsAlpha() && spec.hasVertexColors()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500621 return CoverageMode::kWithColor;
622 } else {
623 return CoverageMode::kWithPosition;
624 }
Michael Ludwig553e9a92018-11-29 12:38:35 -0500625 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500626 return CoverageMode::kNone;
Michael Ludwig553e9a92018-11-29 12:38:35 -0500627 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500628}
Michael Ludwig553e9a92018-11-29 12:38:35 -0500629
Michael Ludwig93aeba02018-12-21 09:50:31 -0500630// Writes four vertices in triangle strip order, including the additional data for local
631// coordinates, domain, color, and coverage as needed to satisfy the vertex spec.
632static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
Brian Salomon1d835422019-03-13 16:11:44 -0400633 CoverageMode mode, Sk4f coverage, SkPMColor4f color4f, const SkRect& domain,
Michael Ludwige6266a22019-03-07 11:24:32 -0500634 const Vertices& quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500635 static constexpr auto If = GrVertexWriter::If<float>;
636
Michael Ludwig553e9a92018-11-29 12:38:35 -0500637 for (int i = 0; i < 4; ++i) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500638 // save position, this is a float2 or float3 or float4 depending on the combination of
639 // perspective and coverage mode.
Michael Ludwige6266a22019-03-07 11:24:32 -0500640 vb->write(quad.fX[i], quad.fY[i],
641 If(spec.deviceQuadType() == GrQuadType::kPerspective, quad.fW[i]),
642 If(mode == CoverageMode::kWithPosition, coverage[i]));
Michael Ludwig4921dc32018-12-03 14:57:29 +0000643
Michael Ludwig93aeba02018-12-21 09:50:31 -0500644 // save color
645 if (spec.hasVertexColors()) {
Brian Salomon1d835422019-03-13 16:11:44 -0400646 bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
Michael Ludwige6266a22019-03-07 11:24:32 -0500647 vb->write(GrVertexColor(
Brian Salomon1d835422019-03-13 16:11:44 -0400648 color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wide));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500649 }
650
651 // save local position
652 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500653 vb->write(quad.fU[i], quad.fV[i],
654 If(spec.localQuadType() == GrQuadType::kPerspective, quad.fR[i]));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500655 }
656
657 // save the domain
658 if (spec.hasDomain()) {
659 vb->write(domain);
660 }
661 }
662}
663
664GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
665
666static const int kVertsPerAAFillRect = 8;
667static const int kIndicesPerAAFillRect = 30;
668
Brian Salomondbf70722019-02-07 11:31:24 -0500669static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500670 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
671
672 // clang-format off
673 static const uint16_t gFillAARectIdx[] = {
674 0, 1, 2, 1, 3, 2,
675 0, 4, 1, 4, 5, 1,
676 0, 6, 4, 0, 2, 6,
677 2, 3, 6, 3, 7, 6,
678 1, 5, 3, 3, 5, 7,
679 };
680 // clang-format on
681
682 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
683 return resourceProvider->findOrCreatePatternedIndexBuffer(
684 gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
685 kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
Michael Ludwig553e9a92018-11-29 12:38:35 -0500686}
687
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400688} // anonymous namespace
689
Michael Ludwigc182b942018-11-16 10:27:51 -0500690namespace GrQuadPerEdgeAA {
691
Brian Osman8fa7ab42019-03-18 10:22:42 -0400692// This is a more elaborate version of SkPMColor4fNeedsWideColor that allows "no color" for white
693ColorType MinColorType(SkPMColor4f color, GrClampType clampType, const GrCaps& caps) {
Brian Salomon1d835422019-03-13 16:11:44 -0400694 if (color == SK_PMColor4fWHITE) {
695 return ColorType::kNone;
Brian Salomon1d835422019-03-13 16:11:44 -0400696 } else {
Brian Osman8fa7ab42019-03-18 10:22:42 -0400697 return SkPMColor4fNeedsWideColor(color, clampType, caps) ? ColorType::kHalf
698 : ColorType::kByte;
Brian Salomon1d835422019-03-13 16:11:44 -0400699 }
700}
701
Michael Ludwigc182b942018-11-16 10:27:51 -0500702////////////////// Tessellate Implementation
703
704void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
Brian Osman3d139a42018-11-19 10:42:10 -0500705 const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
Michael Ludwigc182b942018-11-16 10:27:51 -0500706 GrQuadAAFlags aaFlags) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500707 CoverageMode mode = get_mode_for_spec(spec);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400708
Michael Ludwigf995c052018-11-26 15:24:29 -0500709 // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
Michael Ludwige6266a22019-03-07 11:24:32 -0500710 Vertices outer;
711 outer.fX = deviceQuad.x4f();
712 outer.fY = deviceQuad.y4f();
713 outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400714
715 // Load local position data into Sk4fs (either none, just u,v or all three)
Michael Ludwige6266a22019-03-07 11:24:32 -0500716 outer.fUVRCount = spec.localDimensionality();
Michael Ludwigc182b942018-11-16 10:27:51 -0500717 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500718 outer.fU = localQuad.x4f();
719 outer.fV = localQuad.y4f();
720 outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400721 }
722
Michael Ludwigc182b942018-11-16 10:27:51 -0500723 GrVertexWriter vb{vertices};
Michael Ludwig93aeba02018-12-21 09:50:31 -0500724 if (spec.usesCoverageAA()) {
725 SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500726 // Must calculate two new quads, an outset and inset by .5 in projected device space, so
Michael Ludwige6266a22019-03-07 11:24:32 -0500727 // duplicate the original quad for the inner space
728 Vertices inner = outer;
Michael Ludwigc182b942018-11-16 10:27:51 -0500729
Michael Ludwige6266a22019-03-07 11:24:32 -0500730 Sk4f maxCoverage = 1.f;
731 if (spec.deviceQuadType() == GrQuadType::kPerspective) {
732 // For perspective, send quads with all edges non-AA through the tessellation to ensure
733 // their corners are processed the same as adjacent quads. This approach relies on
734 // solving edge equations to reconstruct corners, which can create seams if an inner
735 // fully non-AA quad is not similarly processed.
736 maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer);
737 } else if (aaFlags != GrQuadAAFlags::kNone) {
738 // In 2D, the simpler corner math does not cause issues with seaming against non-AA
739 // inner quads.
740 maxCoverage = compute_nested_quad_vertices(
741 aaFlags, spec.deviceQuadType() <= GrQuadType::kRectilinear, &inner, &outer);
742 }
743 // NOTE: could provide an even more optimized tessellation function for axis-aligned
744 // rects since the positions can be outset by constants without doing vector math,
745 // except it must handle identifying the winding of the quad vertices if the transform
746 // applied a mirror, etc. The current 2D case is already adequately fast.
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400747
Michael Ludwig93aeba02018-12-21 09:50:31 -0500748 // Write two quads for inner and outer, inner will use the
Brian Salomon1d835422019-03-13 16:11:44 -0400749 write_quad(&vb, spec, mode, maxCoverage, color4f, domain, inner);
750 write_quad(&vb, spec, mode, 0.f, color4f, domain, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500751 } else {
752 // No outsetting needed, just write a single quad with full coverage
753 SkASSERT(mode == CoverageMode::kNone);
Brian Salomon1d835422019-03-13 16:11:44 -0400754 write_quad(&vb, spec, mode, 1.f, color4f, domain, outer);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400755 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500756
757 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400758}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400759
Michael Ludwig93aeba02018-12-21 09:50:31 -0500760bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
761 int quadCount) {
762 if (spec.usesCoverageAA()) {
763 // AA quads use 8 vertices, basically nested rectangles
Brian Salomondbf70722019-02-07 11:31:24 -0500764 sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
Michael Ludwig93aeba02018-12-21 09:50:31 -0500765 if (!ibuffer) {
766 return false;
767 }
768
769 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500770 mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
771 quadCount, kNumAAQuadsInIndexBuffer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500772 } else {
773 // Non-AA quads use 4 vertices, and regular triangle strip layout
774 if (quadCount > 1) {
Brian Salomondbf70722019-02-07 11:31:24 -0500775 sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Michael Ludwig93aeba02018-12-21 09:50:31 -0500776 if (!ibuffer) {
777 return false;
778 }
779
780 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500781 mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500782 GrResourceProvider::QuadCountOfQuadBuffer());
783 } else {
784 mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
785 mesh->setNonIndexedNonInstanced(4);
786 }
787 }
788
789 return true;
790}
791
Michael Ludwigc182b942018-11-16 10:27:51 -0500792////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400793
Michael Ludwigc182b942018-11-16 10:27:51 -0500794int VertexSpec::deviceDimensionality() const {
795 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
796}
797
798int VertexSpec::localDimensionality() const {
799 return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
800}
801
Michael Ludwig467994d2018-12-03 14:58:31 +0000802////////////////// Geometry Processor Implementation
Michael Ludwigc182b942018-11-16 10:27:51 -0500803
Michael Ludwig467994d2018-12-03 14:58:31 +0000804class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
805public:
Michael Ludwig20e909e2018-10-30 10:43:57 -0400806
Michael Ludwig467994d2018-12-03 14:58:31 +0000807 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
808 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400809 }
810
Michael Ludwig467994d2018-12-03 14:58:31 +0000811 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
812 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500813 const GrSamplerState& samplerState,
814 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000815 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
816 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
Greg Daniel7a82edf2018-12-04 10:54:34 -0500817 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000818 std::move(textureColorSpaceXform)));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400819 }
820
Michael Ludwig467994d2018-12-03 14:58:31 +0000821 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
Michael Ludwig024e2622018-11-30 13:25:55 -0500822
Michael Ludwig467994d2018-12-03 14:58:31 +0000823 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500824 // domain, texturing, device-dimensions are single bit flags
825 uint32_t x = fDomain.isInitialized() ? 0 : 1;
826 x |= fSampler.isInitialized() ? 0 : 2;
827 x |= fNeedsPerspective ? 0 : 4;
Michael Ludwig467994d2018-12-03 14:58:31 +0000828 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
829 if (fLocalCoord.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500830 x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
Brian Osman78dc72c2018-12-03 13:20:43 +0000831 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000832 // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
Michael Ludwig93aeba02018-12-21 09:50:31 -0500833 if (fColor.isInitialized()) {
834 x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
835 }
836 // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor
837 if (fCoverageMode != CoverageMode::kNone) {
838 x |= CoverageMode::kWithPosition == fCoverageMode ? 128 : 256;
Michael Ludwig467994d2018-12-03 14:58:31 +0000839 }
840
841 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
842 b->add32(x);
Brian Osman78dc72c2018-12-03 13:20:43 +0000843 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000844
845 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
846 class GLSLProcessor : public GrGLSLGeometryProcessor {
847 public:
848 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
849 FPCoordTransformIter&& transformIter) override {
850 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
851 if (gp.fLocalCoord.isInitialized()) {
852 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
853 }
854 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
855 }
856
857 private:
858 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
859 using Interpolation = GrGLSLVaryingHandler::Interpolation;
860
861 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
862 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
863 gp.fTextureColorSpaceXform.get());
864
865 args.fVaryingHandler->emitAttributes(gp);
866
Michael Ludwig93aeba02018-12-21 09:50:31 -0500867 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
868 // Strip last channel from the vertex attribute to remove coverage and get the
869 // actual position
870 if (gp.fNeedsPerspective) {
871 args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
872 gp.fPosition.name());
873 } else {
874 args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
875 gp.fPosition.name());
876 }
877 gpArgs->fPositionVar = {"position",
878 gp.fNeedsPerspective ? kFloat3_GrSLType
879 : kFloat2_GrSLType,
880 GrShaderVar::kNone_TypeModifier};
Michael Ludwig467994d2018-12-03 14:58:31 +0000881 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500882 // No coverage to eliminate
883 gpArgs->fPositionVar = gp.fPosition.asShaderVar();
Michael Ludwig467994d2018-12-03 14:58:31 +0000884 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000885
886 // Handle local coordinates if they exist
887 if (gp.fLocalCoord.isInitialized()) {
888 // NOTE: If the only usage of local coordinates is for the inline texture fetch
889 // before FPs, then there are no registered FPCoordTransforms and this ends up
890 // emitting nothing, so there isn't a duplication of local coordinates
891 this->emitTransforms(args.fVertBuilder,
892 args.fVaryingHandler,
893 args.fUniformHandler,
894 gp.fLocalCoord.asShaderVar(),
895 args.fFPCoordTransformHandler);
896 }
897
898 // Solid color before any texturing gets modulated in
899 if (gp.fColor.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500900 // The color cannot be flat if the varying coverage has been modulated into it
Michael Ludwig467994d2018-12-03 14:58:31 +0000901 args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500902 gp.fCoverageMode == CoverageMode::kWithColor ?
903 Interpolation::kInterpolated : Interpolation::kCanBeFlat);
904 } else {
905 // Output color must be initialized to something
906 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
Michael Ludwig467994d2018-12-03 14:58:31 +0000907 }
908
909 // If there is a texture, must also handle texture coordinates and reading from
910 // the texture in the fragment shader before continuing to fragment processors.
911 if (gp.fSampler.isInitialized()) {
912 // Texture coordinates clamped by the domain on the fragment shader; if the GP
913 // has a texture, it's guaranteed to have local coordinates
914 args.fFragBuilder->codeAppend("float2 texCoord;");
915 if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
916 // Can't do a pass through since we need to perform perspective division
917 GrGLSLVarying v(gp.fLocalCoord.gpuType());
918 args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
919 args.fVertBuilder->codeAppendf("%s = %s;",
920 v.vsOut(), gp.fLocalCoord.name());
921 args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
922 v.fsIn(), v.fsIn());
923 } else {
924 args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
925 }
926
927 // Clamp the now 2D localCoordName variable by the domain if it is provided
928 if (gp.fDomain.isInitialized()) {
929 args.fFragBuilder->codeAppend("float4 domain;");
930 args.fVaryingHandler->addPassThroughAttribute(gp.fDomain, "domain",
931 Interpolation::kCanBeFlat);
932 args.fFragBuilder->codeAppend(
933 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
934 }
935
936 // Now modulate the starting output color by the texture lookup
937 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
938 args.fFragBuilder->appendTextureLookupAndModulate(
939 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
940 &fTextureColorSpaceXformHelper);
941 args.fFragBuilder->codeAppend(";");
942 }
943
944 // And lastly, output the coverage calculation code
Michael Ludwig93aeba02018-12-21 09:50:31 -0500945 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
946 GrGLSLVarying coverage(kFloat_GrSLType);
947 args.fVaryingHandler->addVarying("coverage", &coverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000948 if (gp.fNeedsPerspective) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500949 args.fVertBuilder->codeAppendf("%s = %s.w;",
950 coverage.vsOut(), gp.fPosition.name());
951 } else {
952 args.fVertBuilder->codeAppendf("%s = %s.z;",
953 coverage.vsOut(), gp.fPosition.name());
Michael Ludwig467994d2018-12-03 14:58:31 +0000954 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500955
Ethan Nicholase1f55022019-02-05 17:17:40 -0500956 args.fFragBuilder->codeAppendf("%s = half4(half(%s));",
Michael Ludwig93aeba02018-12-21 09:50:31 -0500957 args.fOutputCoverage, coverage.fsIn());
Michael Ludwig467994d2018-12-03 14:58:31 +0000958 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500959 // Set coverage to 1, since it's either non-AA or the coverage was already
960 // folded into the output color
Ethan Nicholase1f55022019-02-05 17:17:40 -0500961 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000962 }
963 }
964 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
965 };
966 return new GLSLProcessor;
967 }
968
969private:
970 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
971 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
972 , fTextureColorSpaceXform(nullptr) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500973 SkASSERT(!spec.hasDomain());
Michael Ludwig467994d2018-12-03 14:58:31 +0000974 this->initializeAttrs(spec);
975 this->setTextureSamplerCnt(0);
976 }
977
978 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
979 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500980 const GrSamplerState& samplerState,
981 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000982 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
983 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
984 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Greg Daniel7a82edf2018-12-04 10:54:34 -0500985 , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500986 SkASSERT(spec.hasLocalCoords());
Michael Ludwig467994d2018-12-03 14:58:31 +0000987 this->initializeAttrs(spec);
988 this->setTextureSamplerCnt(1);
989 }
990
991 void initializeAttrs(const VertexSpec& spec) {
992 fNeedsPerspective = spec.deviceDimensionality() == 3;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500993 fCoverageMode = get_mode_for_spec(spec);
994
995 if (fCoverageMode == CoverageMode::kWithPosition) {
996 if (fNeedsPerspective) {
997 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
998 } else {
999 fPosition = {"positionWithCoverage", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1000 }
1001 } else {
1002 if (fNeedsPerspective) {
1003 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1004 } else {
1005 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1006 }
1007 }
Michael Ludwig467994d2018-12-03 14:58:31 +00001008
1009 int localDim = spec.localDimensionality();
1010 if (localDim == 3) {
1011 fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
1012 } else if (localDim == 2) {
1013 fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
1014 } // else localDim == 0 and attribute remains uninitialized
1015
1016 if (ColorType::kByte == spec.colorType()) {
1017 fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
1018 } else if (ColorType::kHalf == spec.colorType()) {
1019 fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
1020 }
1021
1022 if (spec.hasDomain()) {
1023 fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
1024 }
1025
Michael Ludwig93aeba02018-12-21 09:50:31 -05001026 this->setVertexAttributes(&fPosition, 4);
Michael Ludwig467994d2018-12-03 14:58:31 +00001027 }
1028
1029 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
1030
Michael Ludwig93aeba02018-12-21 09:50:31 -05001031 Attribute fPosition; // May contain coverage as last channel
1032 Attribute fColor; // May have coverage modulated in if the FPs support it
Michael Ludwig467994d2018-12-03 14:58:31 +00001033 Attribute fLocalCoord;
1034 Attribute fDomain;
Michael Ludwig467994d2018-12-03 14:58:31 +00001035
Michael Ludwig93aeba02018-12-21 09:50:31 -05001036 // The positions attribute may have coverage built into it, so float3 is an ambiguous type
1037 // and may mean 2d with coverage, or 3d with no coverage
Michael Ludwig467994d2018-12-03 14:58:31 +00001038 bool fNeedsPerspective;
Michael Ludwig93aeba02018-12-21 09:50:31 -05001039 CoverageMode fCoverageMode;
Michael Ludwig467994d2018-12-03 14:58:31 +00001040
1041 // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
1042 // to skip texturing.
1043 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1044 TextureSampler fSampler;
1045
1046 typedef GrGeometryProcessor INHERITED;
1047};
1048
1049sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
1050 return QuadPerEdgeAAGeometryProcessor::Make(spec);
1051}
1052
1053sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
1054 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -05001055 const GrSamplerState& samplerState, uint32_t extraSamplerKey,
1056 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1057 return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
1058 samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +00001059 std::move(textureColorSpaceXform));
Michael Ludwig20e909e2018-10-30 10:43:57 -04001060}
Michael Ludwigc182b942018-11-16 10:27:51 -05001061
1062} // namespace GrQuadPerEdgeAA