blob: f893a919f9bf4c4182c0948cd143f701b74a6627 [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 Ludwigf995c052018-11-26 15:24:29 -050023static AI Sk4f fma(const Sk4f& f, const Sk4f& m, const Sk4f& a) {
24 return SkNx_fma<4, float>(f, m, a);
25}
26
27// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
28// order.
29static AI Sk4f nextCW(const Sk4f& v) {
30 return SkNx_shuffle<2, 0, 3, 1>(v);
31}
32
33static AI Sk4f nextCCW(const Sk4f& v) {
34 return SkNx_shuffle<1, 3, 0, 2>(v);
35}
36
37// Fills Sk4f with 1f if edge bit is set, 0f otherwise. Edges are ordered LBTR to match CCW ordering
38// of vertices in the quad.
39static AI Sk4f compute_edge_mask(GrQuadAAFlags aaFlags) {
40 return Sk4f((GrQuadAAFlags::kLeft & aaFlags) ? 1.f : 0.f,
41 (GrQuadAAFlags::kBottom & aaFlags) ? 1.f : 0.f,
42 (GrQuadAAFlags::kTop & aaFlags) ? 1.f : 0.f,
43 (GrQuadAAFlags::kRight & aaFlags) ? 1.f : 0.f);
44}
45
Michael Ludwig4921dc32018-12-03 14:57:29 +000046// Outputs normalized edge vectors in xdiff and ydiff, as well as the reciprocal of the original
47// edge lengths in invLengths
48static AI void compute_edge_vectors(const Sk4f& x, const Sk4f& y, const Sk4f& xnext,
49 const Sk4f& ynext, Sk4f* xdiff, Sk4f* ydiff, Sk4f* invLengths) {
50 *xdiff = xnext - x;
51 *ydiff = ynext - y;
52 *invLengths = fma(*xdiff, *xdiff, *ydiff * *ydiff).rsqrt();
53 *xdiff *= *invLengths;
54 *ydiff *= *invLengths;
55}
56
Michael Ludwigf995c052018-11-26 15:24:29 -050057static AI void outset_masked_vertices(const Sk4f& xdiff, const Sk4f& ydiff, const Sk4f& invLengths,
58 const Sk4f& mask, Sk4f* x, Sk4f* y, Sk4f* u, Sk4f* v, Sk4f* r,
59 int uvrCount) {
60 auto halfMask = 0.5f * mask;
61 auto maskCW = nextCW(halfMask);
62 *x += maskCW * -xdiff + halfMask * nextCW(xdiff);
63 *y += maskCW * -ydiff + halfMask * nextCW(ydiff);
64 if (uvrCount > 0) {
65 // We want to extend the texture coords by the same proportion as the positions.
66 maskCW *= invLengths;
67 halfMask *= nextCW(invLengths);
68 Sk4f udiff = nextCCW(*u) - *u;
69 Sk4f vdiff = nextCCW(*v) - *v;
70 *u += maskCW * -udiff + halfMask * nextCW(udiff);
71 *v += maskCW * -vdiff + halfMask * nextCW(vdiff);
72 if (uvrCount == 3) {
73 Sk4f rdiff = nextCCW(*r) - *r;
74 *r += maskCW * -rdiff + halfMask * nextCW(rdiff);
75 }
76 }
77}
78
79static AI void outset_vertices(const Sk4f& xdiff, const Sk4f& ydiff, const Sk4f& invLengths,
80 Sk4f* x, Sk4f* y, Sk4f* u, Sk4f* v, Sk4f* r, int uvrCount) {
81 *x += 0.5f * (-xdiff + nextCW(xdiff));
82 *y += 0.5f * (-ydiff + nextCW(ydiff));
83 if (uvrCount > 0) {
84 Sk4f t = 0.5f * invLengths;
85 Sk4f udiff = nextCCW(*u) - *u;
86 Sk4f vdiff = nextCCW(*v) - *v;
87 *u += t * -udiff + nextCW(t) * nextCW(udiff);
88 *v += t * -vdiff + nextCW(t) * nextCW(vdiff);
89 if (uvrCount == 3) {
90 Sk4f rdiff = nextCCW(*r) - *r;
91 *r += t * -rdiff + nextCW(t) * nextCW(rdiff);
92 }
93 }
94}
95
96static AI void compute_edge_distances(const Sk4f& a, const Sk4f& b, const Sk4f& c, const Sk4f& x,
97 const Sk4f& y, const Sk4f& w, Sk4f edgeDistances[]) {
98 for (int i = 0; i < 4; ++i) {
99 edgeDistances[i] = a * x[i] + b * y[i] + c * w[i];
100 }
101}
102
Michael Ludwig4921dc32018-12-03 14:57:29 +0000103static AI float get_max_coverage(const Sk4f& lengths) {
104 float minWidth = SkMinScalar(lengths[0], lengths[3]);
105 float minHeight = SkMinScalar(lengths[1], lengths[2]);
106 // Calculate approximate area of the quad, pinning dimensions to 1 in case the quad is larger
107 // than a pixel. Sub-pixel quads that are rotated may in fact have a different true maximum
108 // coverage than this calculation, but this will be close and is stable.
109 return SkMinScalar(minWidth, 1.f) * SkMinScalar(minHeight, 1.f);
110}
111
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400112// This computes the four edge equations for a quad, then outsets them and optionally computes a new
113// quad as the intersection points of the outset edges. 'x' and 'y' contain the original points as
Michael Ludwigf995c052018-11-26 15:24:29 -0500114// input and the outset points as output. In order to be used as a component of perspective edge
115// distance calculation, this exports edge equations in 'a', 'b', and 'c'. Use
116// compute_edge_distances to turn these equations into the distances needed by the shader. The
117// values in x, y, u, v, and r are possibly updated if outsetting is needed. r is the local
118// position's w component if it exists.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000119//
120// Returns maximum coverage allowed for any given pixel.
121static float compute_quad_edges_and_outset_vertices(GrQuadAAFlags aaFlags, Sk4f* x, Sk4f* y,
Michael Ludwigf995c052018-11-26 15:24:29 -0500122 Sk4f* a, Sk4f* b, Sk4f* c, Sk4f* u, Sk4f* v, Sk4f* r, int uvrChannelCount, bool outset) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400123 SkASSERT(uvrChannelCount == 0 || uvrChannelCount == 2 || uvrChannelCount == 3);
124
Michael Ludwigf995c052018-11-26 15:24:29 -0500125 // Compute edge vectors for the quad.
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400126 auto xnext = nextCCW(*x);
127 auto ynext = nextCCW(*y);
128 // xdiff and ydiff will comprise the normalized vectors pointing along each quad edge.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000129 Sk4f xdiff, ydiff, invLengths;
130 compute_edge_vectors(*x, *y, xnext, ynext, &xdiff, &ydiff, &invLengths);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400131
Michael Ludwigf995c052018-11-26 15:24:29 -0500132 // Use above vectors to compute edge equations (importantly before we outset positions).
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400133 *c = fma(xnext, *y, -ynext * *x) * invLengths;
134 // Make sure the edge equations have their normals facing into the quad in device space.
135 auto test = fma(ydiff, nextCW(*x), fma(-xdiff, nextCW(*y), *c));
136 if ((test < Sk4f(0)).anyTrue()) {
137 *a = -ydiff;
138 *b = xdiff;
139 *c = -*c;
140 } else {
141 *a = ydiff;
142 *b = -xdiff;
143 }
144 // Outset the edge equations so aa coverage evaluates to zero half a pixel away from the
145 // original quad edge.
146 *c += 0.5f;
147
148 if (aaFlags != GrQuadAAFlags::kAll) {
149 // This order is the same order the edges appear in xdiff/ydiff and therefore as the
150 // edges in a/b/c.
Michael Ludwigf995c052018-11-26 15:24:29 -0500151 Sk4f mask = compute_edge_mask(aaFlags);
152
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400153 // Outset edge equations for masked out edges another pixel so that they always evaluate
154 // >= 1.
155 *c += (1.f - mask);
Michael Ludwigf995c052018-11-26 15:24:29 -0500156 if (outset) {
157 outset_masked_vertices(xdiff, ydiff, invLengths, mask, x, y, u, v, r, uvrChannelCount);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400158 }
Michael Ludwigf995c052018-11-26 15:24:29 -0500159 } else if (outset) {
160 outset_vertices(xdiff, ydiff, invLengths, x, y, u, v, r, uvrChannelCount);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400161 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000162
163 return get_max_coverage(invLengths.invert());
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400164}
165
Michael Ludwigf995c052018-11-26 15:24:29 -0500166// A specialization of the above function that can compute edge distances very quickly when it knows
167// that the edges intersect at right angles, i.e. any transform other than skew and perspective
168// (GrQuadType::kRectilinear). Unlike the above function, this always outsets the corners since it
169// cannot be reused in the perspective case.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000170static float compute_rectilinear_dists_and_outset_vertices(GrQuadAAFlags aaFlags, Sk4f* x,
Michael Ludwigf995c052018-11-26 15:24:29 -0500171 Sk4f* y, Sk4f edgeDistances[4], Sk4f* u, Sk4f* v, Sk4f* r, int uvrChannelCount) {
172 SkASSERT(uvrChannelCount == 0 || uvrChannelCount == 2 || uvrChannelCount == 3);
Michael Ludwigf995c052018-11-26 15:24:29 -0500173 // xdiff and ydiff will comprise the normalized vectors pointing along each quad edge.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000174 Sk4f xdiff, ydiff, invLengths;
175 compute_edge_vectors(*x, *y, nextCCW(*x), nextCCW(*y), &xdiff, &ydiff, &invLengths);
176 Sk4f lengths = invLengths.invert();
Michael Ludwigf995c052018-11-26 15:24:29 -0500177
178 // Since the quad is rectilinear, the edge distances are predictable and independent of the
179 // actual orientation of the quad. The lengths vector stores |p1-p0|, |p3-p1|, |p0-p2|, |p2-p3|,
180 // matching the CCW order. For instance, edge distances for p0 are 0 for e0 and e2 since they
181 // intersect at p0. Distance to e1 is the same as p0 to p1. Distance to e3 is p0 to p2 since
182 // e3 goes through p2 and since the quad is rectilinear, we know that's the shortest distance.
183 edgeDistances[0] = Sk4f(0.f, lengths[0], 0.f, lengths[2]);
184 edgeDistances[1] = Sk4f(0.f, 0.f, lengths[0], lengths[1]);
185 edgeDistances[2] = Sk4f(lengths[2], lengths[3], 0.f, 0.f);
186 edgeDistances[3] = Sk4f(lengths[1], 0.f, lengths[3], 0.f);
187
188 if (aaFlags != GrQuadAAFlags::kAll) {
189 // This order is the same order the edges appear in xdiff/ydiff and therefore as the
190 // edges in a/b/c.
191 Sk4f mask = compute_edge_mask(aaFlags);
192
193 // Update opposite corner distances by 1 (when enabled by the mask). The distance
194 // calculations used in compute_quad_edges_... calculates the edge equations from original
Michael Ludwig553e9a92018-11-29 12:38:35 -0500195 // positions and then shifts the coefficient by 0.5. If the opposite edges are also outset
196 // then must add an additional 0.5 to account for its shift away from that edge.
197 Sk4f maskWithOpposites = mask + SkNx_shuffle<3, 2, 1, 0>(mask);
198 edgeDistances[0] += Sk4f(0.f, 0.5f, 0.f, 0.5f) * maskWithOpposites;
199 edgeDistances[1] += Sk4f(0.f, 0.f, 0.5f, 0.5f) * maskWithOpposites;
200 edgeDistances[2] += Sk4f(0.5f, 0.5f, 0.f, 0.f) * maskWithOpposites;
201 edgeDistances[3] += Sk4f(0.5f, 0.f, 0.5f, 0.f) * maskWithOpposites;
Michael Ludwigf995c052018-11-26 15:24:29 -0500202
203 // Outset edge equations for masked out edges another pixel so that they always evaluate
204 // So add 1-mask to each point's edge distances vector so that coverage >= 1 on non-aa
205 for (int i = 0; i < 4; ++i) {
206 edgeDistances[i] += (1.f - mask);
207 }
208 outset_masked_vertices(xdiff, ydiff, invLengths, mask, x, y, u, v, r, uvrChannelCount);
209 } else {
210 // Update opposite corner distances by 0.5 pixel and 0.5 edge shift, skipping the need for
211 // mask since that's 1s
212 edgeDistances[0] += Sk4f(0.f, 1.f, 0.f, 1.f);
213 edgeDistances[1] += Sk4f(0.f, 0.f, 1.f, 1.f);
214 edgeDistances[2] += Sk4f(1.f, 1.f, 0.f, 0.f);
215 edgeDistances[3] += Sk4f(1.f, 0.f, 1.f, 0.f);
216
217 outset_vertices(xdiff, ydiff, invLengths, x, y, u, v, r, uvrChannelCount);
218 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000219
220 return get_max_coverage(lengths);
Michael Ludwigf995c052018-11-26 15:24:29 -0500221}
222
223// Generalizes compute_quad_edge_distances_and_outset_vertices to extrapolate local coords such that
224// after perspective division of the device coordinate, the original local coordinate value is at
225// the original un-outset device position. r is the local coordinate's w component.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000226static float compute_quad_dists_and_outset_persp_vertices(GrQuadAAFlags aaFlags, Sk4f* x,
Michael Ludwigf995c052018-11-26 15:24:29 -0500227 Sk4f* y, Sk4f* w, Sk4f edgeDistances[4], Sk4f* u, Sk4f* v, Sk4f* r, int uvrChannelCount) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400228 SkASSERT(uvrChannelCount == 0 || uvrChannelCount == 2 || uvrChannelCount == 3);
229
230 auto iw = (*w).invert();
231 auto x2d = (*x) * iw;
232 auto y2d = (*y) * iw;
Michael Ludwigf995c052018-11-26 15:24:29 -0500233 Sk4f a, b, c;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400234 // Don't compute outset corners in the normalized space, which means u, v, and r don't need
Michael Ludwigf995c052018-11-26 15:24:29 -0500235 // to be provided here (outset separately below). Since this is computing distances for a
236 // projected quad, there is a very good chance it's not rectilinear so use the general 2D path.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000237 float maxProjectedCoverage = compute_quad_edges_and_outset_vertices(aaFlags, &x2d, &y2d,
238 &a, &b, &c, nullptr, nullptr, nullptr, /* uvr ct */ 0, /* outsetCorners */ false);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400239
240 static const float kOutset = 0.5f;
241 if ((GrQuadAAFlags::kLeft | GrQuadAAFlags::kRight) & aaFlags) {
242 // For each entry in x the equivalent entry in opX is the left/right opposite and so on.
243 Sk4f opX = SkNx_shuffle<2, 3, 0, 1>(*x);
244 Sk4f opW = SkNx_shuffle<2, 3, 0, 1>(*w);
245 Sk4f opY = SkNx_shuffle<2, 3, 0, 1>(*y);
246 // vx/vy holds the device space left-to-right vectors along top and bottom of the quad.
247 Sk2f vx = SkNx_shuffle<2, 3>(x2d) - SkNx_shuffle<0, 1>(x2d);
248 Sk2f vy = SkNx_shuffle<2, 3>(y2d) - SkNx_shuffle<0, 1>(y2d);
249 Sk2f len = SkNx_fma(vx, vx, vy * vy).sqrt();
250 // For each device space corner, devP, label its left/right opposite device space point
251 // opDevPt. The new device space point is opDevPt + s (devPt - opDevPt) where s is
252 // (length(devPt - opDevPt) + 0.5) / length(devPt - opDevPt);
253 Sk4f s = SkNx_shuffle<0, 1, 0, 1>((len + kOutset) / len);
254 // Compute t in homogeneous space from s using similar triangles so that we can produce
255 // homogeneous outset vertices for perspective-correct interpolation.
256 Sk4f sOpW = s * opW;
257 Sk4f t = sOpW / (sOpW + (1.f - s) * (*w));
258 // mask is used to make the t values be 1 when the left/right side is not antialiased.
259 Sk4f mask(GrQuadAAFlags::kLeft & aaFlags ? 1.f : 0.f,
260 GrQuadAAFlags::kLeft & aaFlags ? 1.f : 0.f,
261 GrQuadAAFlags::kRight & aaFlags ? 1.f : 0.f,
262 GrQuadAAFlags::kRight & aaFlags ? 1.f : 0.f);
263 t = t * mask + (1.f - mask);
264 *x = opX + t * (*x - opX);
265 *y = opY + t * (*y - opY);
266 *w = opW + t * (*w - opW);
267
268 if (uvrChannelCount > 0) {
269 Sk4f opU = SkNx_shuffle<2, 3, 0, 1>(*u);
270 Sk4f opV = SkNx_shuffle<2, 3, 0, 1>(*v);
271 *u = opU + t * (*u - opU);
272 *v = opV + t * (*v - opV);
273 if (uvrChannelCount == 3) {
274 Sk4f opR = SkNx_shuffle<2, 3, 0, 1>(*r);
275 *r = opR + t * (*r - opR);
276 }
277 }
278
279 if ((GrQuadAAFlags::kTop | GrQuadAAFlags::kBottom) & aaFlags) {
280 // Update the 2D points for the top/bottom calculation.
281 iw = (*w).invert();
282 x2d = (*x) * iw;
283 y2d = (*y) * iw;
284 }
285 }
286
287 if ((GrQuadAAFlags::kTop | GrQuadAAFlags::kBottom) & aaFlags) {
288 // This operates the same as above but for top/bottom rather than left/right.
289 Sk4f opX = SkNx_shuffle<1, 0, 3, 2>(*x);
290 Sk4f opW = SkNx_shuffle<1, 0, 3, 2>(*w);
291 Sk4f opY = SkNx_shuffle<1, 0, 3, 2>(*y);
292
293 Sk2f vx = SkNx_shuffle<1, 3>(x2d) - SkNx_shuffle<0, 2>(x2d);
294 Sk2f vy = SkNx_shuffle<1, 3>(y2d) - SkNx_shuffle<0, 2>(y2d);
295 Sk2f len = SkNx_fma(vx, vx, vy * vy).sqrt();
296
297 Sk4f s = SkNx_shuffle<0, 0, 1, 1>((len + kOutset) / len);
298
299 Sk4f sOpW = s * opW;
300 Sk4f t = sOpW / (sOpW + (1.f - s) * (*w));
301
302 Sk4f mask(GrQuadAAFlags::kTop & aaFlags ? 1.f : 0.f,
303 GrQuadAAFlags::kBottom & aaFlags ? 1.f : 0.f,
304 GrQuadAAFlags::kTop & aaFlags ? 1.f : 0.f,
305 GrQuadAAFlags::kBottom & aaFlags ? 1.f : 0.f);
306 t = t * mask + (1.f - mask);
307 *x = opX + t * (*x - opX);
308 *y = opY + t * (*y - opY);
309 *w = opW + t * (*w - opW);
310
311 if (uvrChannelCount > 0) {
312 Sk4f opU = SkNx_shuffle<1, 0, 3, 2>(*u);
313 Sk4f opV = SkNx_shuffle<1, 0, 3, 2>(*v);
314 *u = opU + t * (*u - opU);
315 *v = opV + t * (*v - opV);
316 if (uvrChannelCount == 3) {
317 Sk4f opR = SkNx_shuffle<1, 0, 3, 2>(*r);
318 *r = opR + t * (*r - opR);
319 }
320 }
321 }
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400322
Michael Ludwigf995c052018-11-26 15:24:29 -0500323 // Use the original edge equations with the outset homogeneous coordinates to get the edge
324 // distance (technically multiplied by w, so that the fragment shader can do perspective
325 // interpolation when it multiplies by 1/w later).
326 compute_edge_distances(a, b, c, *x, *y, *w, edgeDistances);
Michael Ludwig4921dc32018-12-03 14:57:29 +0000327
328 return maxProjectedCoverage;
Eric Boren98cb1592018-11-26 18:38:05 +0000329}
330
Michael Ludwig553e9a92018-11-29 12:38:35 -0500331// Calculate safe edge distances for non-aa quads that have been batched with aa quads. Since the
332// fragment shader multiples by 1/w, so the edge distance cannot just be set to 1. It cannot just
333// be set to w either due to interpolation across the triangle. If iA, iB, and iC are the
334// barycentric weights of the triangle, and we set the edge distance to w, the fragment shader
335// actually sees d = (iA*wA + iB*wB + iC*wC) * (iA/wA + iB/wB + iC/wC). Without perspective this
336// simplifies to 1 as necessary, but we must choose something other than w when there is perspective
337// to ensure that d >= 1 and the edge shows as non-aa.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000338static float compute_nonaa_edge_distances(const Sk4f& w, bool hasPersp, Sk4f edgeDistances[4]) {
Michael Ludwig553e9a92018-11-29 12:38:35 -0500339 // Let n = min(w1,w2,w3,w4) and m = max(w1,w2,w3,w4) and rewrite
340 // d = (iA*wA + iB*wB + iC*wC) * (iA*wB*wC + iB*wA*wC + iC*wA*wB) / (wA*wB*wC)
341 // | e=attr from VS | | fragCoord.w = 1/w |
342 // Since the weights are the interior of the primitive then we have:
343 // n <= (iA*wA + iB*wB + iC*wC) <= m and
344 // n^2 <= (iA*wB*wC + iB*wA*wC + iC*wA*wB) <= m^2 and
345 // n^3 <= wA*wB*wC <= m^3 regardless of the choice of A, B, and C verts in the quad
346 // Thus if we set e = m^3/n^3, it guarantees d >= 1 for any perspective.
347 float e;
348 if (hasPersp) {
349 float m = w.max();
350 float n = w.min();
351 e = (m * m * m) / (n * n * n);
352 } else {
353 e = 1.f;
354 }
355
356 // All edge distances set to the same
357 for (int i = 0; i < 4; ++i) {
358 edgeDistances[i] = e;
359 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000360
361 // Non-aa, so always use full coverage
362 return 1.f;
Michael Ludwig553e9a92018-11-29 12:38:35 -0500363}
364
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400365} // anonymous namespace
366
Michael Ludwigc182b942018-11-16 10:27:51 -0500367namespace GrQuadPerEdgeAA {
368
369////////////////// Tessellate Implementation
370
371void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
Brian Osman3d139a42018-11-19 10:42:10 -0500372 const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
Michael Ludwigc182b942018-11-16 10:27:51 -0500373 GrQuadAAFlags aaFlags) {
374 bool deviceHasPerspective = spec.deviceQuadType() == GrQuadType::kPerspective;
375 bool localHasPerspective = spec.localQuadType() == GrQuadType::kPerspective;
Brian Osman3d139a42018-11-19 10:42:10 -0500376 GrVertexColor color(color4f, GrQuadPerEdgeAA::ColorType::kHalf == spec.colorType());
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400377
Michael Ludwigf995c052018-11-26 15:24:29 -0500378 // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400379 Sk4f x = deviceQuad.x4f();
380 Sk4f y = deviceQuad.y4f();
Michael Ludwigf995c052018-11-26 15:24:29 -0500381 Sk4f w = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400382
383 // Load local position data into Sk4fs (either none, just u,v or all three)
384 Sk4f u, v, r;
Michael Ludwigc182b942018-11-16 10:27:51 -0500385 if (spec.hasLocalCoords()) {
386 u = localQuad.x4f();
387 v = localQuad.y4f();
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400388
Michael Ludwigc182b942018-11-16 10:27:51 -0500389 if (localHasPerspective) {
390 r = localQuad.w4f();
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400391 }
392 }
393
Michael Ludwigf995c052018-11-26 15:24:29 -0500394 // Index into array refers to vertex. Index into particular Sk4f refers to edge.
395 Sk4f edgeDistances[4];
Michael Ludwig4921dc32018-12-03 14:57:29 +0000396 float maxCoverage = 1.f;
Michael Ludwigc182b942018-11-16 10:27:51 -0500397 if (spec.usesCoverageAA()) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400398 // Must calculate edges and possibly outside the positions
399 if (aaFlags == GrQuadAAFlags::kNone) {
Michael Ludwigf995c052018-11-26 15:24:29 -0500400 // A non-AA quad that got batched into an AA group, so it should have full coverage
Michael Ludwig4921dc32018-12-03 14:57:29 +0000401 maxCoverage = compute_nonaa_edge_distances(w, deviceHasPerspective, edgeDistances);
Michael Ludwigc182b942018-11-16 10:27:51 -0500402 } else if (deviceHasPerspective) {
403 // For simplicity, pointers to u, v, and r are always provided, but the local dim param
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400404 // ensures that only loaded Sk4fs are modified in the compute functions.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000405 maxCoverage = compute_quad_dists_and_outset_persp_vertices(aaFlags, &x, &y, &w,
406 edgeDistances, &u, &v, &r, spec.localDimensionality());
Michael Ludwigf995c052018-11-26 15:24:29 -0500407 } else if (spec.deviceQuadType() <= GrQuadType::kRectilinear) {
Michael Ludwig4921dc32018-12-03 14:57:29 +0000408 maxCoverage = compute_rectilinear_dists_and_outset_vertices(aaFlags, &x, &y,
409 edgeDistances, &u, &v, &r, spec.localDimensionality());
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400410 } else {
Michael Ludwigf995c052018-11-26 15:24:29 -0500411 Sk4f a, b, c;
Michael Ludwig4921dc32018-12-03 14:57:29 +0000412 maxCoverage = compute_quad_edges_and_outset_vertices(aaFlags, &x, &y, &a, &b, &c,
413 &u, &v, &r, spec.localDimensionality(), /*outset*/ true);
Michael Ludwigf995c052018-11-26 15:24:29 -0500414 compute_edge_distances(a, b, c, x, y, w, edgeDistances); // w holds 1.f as desired
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400415 }
416 }
417
Michael Ludwigc182b942018-11-16 10:27:51 -0500418 // Now rearrange the Sk4fs into the interleaved vertex layout:
419 // i.e. x1x2x3x4 y1y2y3y4 -> x1y1 x2y2 x3y3 x4y
420 GrVertexWriter vb{vertices};
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400421 for (int i = 0; i < 4; ++i) {
Michael Ludwig4921dc32018-12-03 14:57:29 +0000422 // save position, always send a vec4 because we embed max coverage in the last component.
423 // For 2D quads, we know w holds the correct 1.f, so just write it out without branching
424 vb.write(x[i], y[i], w[i], maxCoverage);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400425
Michael Ludwigc182b942018-11-16 10:27:51 -0500426 // save color
427 if (spec.hasVertexColors()) {
Brian Osman3d139a42018-11-19 10:42:10 -0500428 vb.write(color);
Michael Ludwigc182b942018-11-16 10:27:51 -0500429 }
430
431 // save local position
432 if (spec.hasLocalCoords()) {
433 if (localHasPerspective) {
434 vb.write<SkPoint3>({u[i], v[i], r[i]});
435 } else {
436 vb.write<SkPoint>({u[i], v[i]});
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400437 }
438 }
439
Michael Ludwigc182b942018-11-16 10:27:51 -0500440 // save the domain
441 if (spec.hasDomain()) {
Michael Ludwigf995c052018-11-26 15:24:29 -0500442 vb.write(domain);
Michael Ludwigc182b942018-11-16 10:27:51 -0500443 }
444
445 // save the edges
446 if (spec.usesCoverageAA()) {
Michael Ludwigf995c052018-11-26 15:24:29 -0500447 vb.write(edgeDistances[i]);
Michael Ludwigc182b942018-11-16 10:27:51 -0500448 }
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400449 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500450
451 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400452}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400453
Michael Ludwigc182b942018-11-16 10:27:51 -0500454////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400455
Michael Ludwigc182b942018-11-16 10:27:51 -0500456int VertexSpec::deviceDimensionality() const {
457 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
458}
459
460int VertexSpec::localDimensionality() const {
461 return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
462}
463
Michael Ludwig467994d2018-12-03 14:58:31 +0000464////////////////// Geometry Processor Implementation
Michael Ludwigc182b942018-11-16 10:27:51 -0500465
Michael Ludwig467994d2018-12-03 14:58:31 +0000466class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
467public:
Michael Ludwig20e909e2018-10-30 10:43:57 -0400468
Michael Ludwig467994d2018-12-03 14:58:31 +0000469 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
470 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400471 }
472
Michael Ludwig467994d2018-12-03 14:58:31 +0000473 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
474 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500475 const GrSamplerState& samplerState,
476 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000477 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
478 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
Greg Daniel7a82edf2018-12-04 10:54:34 -0500479 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000480 std::move(textureColorSpaceXform)));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400481 }
482
Michael Ludwig467994d2018-12-03 14:58:31 +0000483 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
Michael Ludwig024e2622018-11-30 13:25:55 -0500484
Michael Ludwig467994d2018-12-03 14:58:31 +0000485 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
486 // aa, domain, texturing are single bit flags
487 uint32_t x = fAAEdgeDistances.isInitialized() ? 0 : 1;
488 x |= fDomain.isInitialized() ? 0 : 2;
489 x |= fSampler.isInitialized() ? 0 : 4;
490 // regular position has two options as well
491 x |= fNeedsPerspective ? 0 : 8;
492 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
493 if (fLocalCoord.isInitialized()) {
494 x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 16 : 32;
Brian Osman78dc72c2018-12-03 13:20:43 +0000495 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000496 // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
497 if (this->fColor.isInitialized()) {
498 x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 64 : 128;
499 }
500
501 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
502 b->add32(x);
Brian Osman78dc72c2018-12-03 13:20:43 +0000503 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000504
505 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
506 class GLSLProcessor : public GrGLSLGeometryProcessor {
507 public:
508 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
509 FPCoordTransformIter&& transformIter) override {
510 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
511 if (gp.fLocalCoord.isInitialized()) {
512 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
513 }
514 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
515 }
516
517 private:
518 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
519 using Interpolation = GrGLSLVaryingHandler::Interpolation;
520
521 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
522 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
523 gp.fTextureColorSpaceXform.get());
524
525 args.fVaryingHandler->emitAttributes(gp);
526
527 // Extract effective position out of vec4 as a local variable in the vertex shader
528 if (gp.fNeedsPerspective) {
529 args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
530 gp.fPositionWithCoverage.name());
531 } else {
532 args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
533 gp.fPositionWithCoverage.name());
534 }
535 gpArgs->fPositionVar = {"position",
536 gp.fNeedsPerspective ? kFloat3_GrSLType : kFloat2_GrSLType,
537 GrShaderVar::kNone_TypeModifier};
538
539 // Handle local coordinates if they exist
540 if (gp.fLocalCoord.isInitialized()) {
541 // NOTE: If the only usage of local coordinates is for the inline texture fetch
542 // before FPs, then there are no registered FPCoordTransforms and this ends up
543 // emitting nothing, so there isn't a duplication of local coordinates
544 this->emitTransforms(args.fVertBuilder,
545 args.fVaryingHandler,
546 args.fUniformHandler,
547 gp.fLocalCoord.asShaderVar(),
548 args.fFPCoordTransformHandler);
549 }
550
551 // Solid color before any texturing gets modulated in
552 if (gp.fColor.isInitialized()) {
553 args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
554 Interpolation::kCanBeFlat);
555 }
556
557 // If there is a texture, must also handle texture coordinates and reading from
558 // the texture in the fragment shader before continuing to fragment processors.
559 if (gp.fSampler.isInitialized()) {
560 // Texture coordinates clamped by the domain on the fragment shader; if the GP
561 // has a texture, it's guaranteed to have local coordinates
562 args.fFragBuilder->codeAppend("float2 texCoord;");
563 if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
564 // Can't do a pass through since we need to perform perspective division
565 GrGLSLVarying v(gp.fLocalCoord.gpuType());
566 args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
567 args.fVertBuilder->codeAppendf("%s = %s;",
568 v.vsOut(), gp.fLocalCoord.name());
569 args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
570 v.fsIn(), v.fsIn());
571 } else {
572 args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
573 }
574
575 // Clamp the now 2D localCoordName variable by the domain if it is provided
576 if (gp.fDomain.isInitialized()) {
577 args.fFragBuilder->codeAppend("float4 domain;");
578 args.fVaryingHandler->addPassThroughAttribute(gp.fDomain, "domain",
579 Interpolation::kCanBeFlat);
580 args.fFragBuilder->codeAppend(
581 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
582 }
583
584 // Now modulate the starting output color by the texture lookup
585 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
586 args.fFragBuilder->appendTextureLookupAndModulate(
587 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
588 &fTextureColorSpaceXformHelper);
589 args.fFragBuilder->codeAppend(";");
590 }
591
592 // And lastly, output the coverage calculation code
593 if (gp.fAAEdgeDistances.isInitialized()) {
594 GrGLSLVarying maxCoverage(kFloat_GrSLType);
595 args.fVaryingHandler->addVarying("maxCoverage", &maxCoverage);
596 args.fVertBuilder->codeAppendf("%s = %s.w;",
597 maxCoverage.vsOut(), gp.fPositionWithCoverage.name());
598
599 args.fFragBuilder->codeAppend("float4 edgeDists;");
600 args.fVaryingHandler->addPassThroughAttribute(gp.fAAEdgeDistances, "edgeDists");
601
602 args.fFragBuilder->codeAppend(
603 "float minDist = min(min(edgeDists.x, edgeDists.y),"
604 " min(edgeDists.z, edgeDists.w));");
605 if (gp.fNeedsPerspective) {
606 // The distance from edge equation e to homogeneous point p=sk_Position is
607 // e.x*p.x/p.w + e.y*p.y/p.w + e.z. However, we want screen space
608 // interpolation of this distance. We can do this by multiplying the vertex
609 // attribute by p.w and then multiplying by sk_FragCoord.w in the FS. So we
610 // output e.x*p.x + e.y*p.y + e.z * p.w
611 args.fFragBuilder->codeAppend("minDist *= sk_FragCoord.w;");
612 }
613 // Clamp to max coverage after the perspective divide since perspective quads
614 // calculated the max coverage in projected space.
615 args.fFragBuilder->codeAppendf("%s = float4(clamp(minDist, 0.0, %s));",
616 args.fOutputCoverage, maxCoverage.fsIn());
617 } else {
618 // Set coverage to 1
619 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
620 }
621 }
622 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
623 };
624 return new GLSLProcessor;
625 }
626
627private:
628 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
629 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
630 , fTextureColorSpaceXform(nullptr) {
631 SkASSERT(spec.hasVertexColors() && !spec.hasDomain());
632 this->initializeAttrs(spec);
633 this->setTextureSamplerCnt(0);
634 }
635
636 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
637 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500638 const GrSamplerState& samplerState,
639 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000640 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
641 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
642 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Greg Daniel7a82edf2018-12-04 10:54:34 -0500643 , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
Michael Ludwig467994d2018-12-03 14:58:31 +0000644 SkASSERT(spec.hasVertexColors() && spec.hasLocalCoords());
645 this->initializeAttrs(spec);
646 this->setTextureSamplerCnt(1);
647 }
648
649 void initializeAttrs(const VertexSpec& spec) {
650 fNeedsPerspective = spec.deviceDimensionality() == 3;
651 fPositionWithCoverage = {"posAndCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
652
653 int localDim = spec.localDimensionality();
654 if (localDim == 3) {
655 fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
656 } else if (localDim == 2) {
657 fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
658 } // else localDim == 0 and attribute remains uninitialized
659
660 if (ColorType::kByte == spec.colorType()) {
661 fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
662 } else if (ColorType::kHalf == spec.colorType()) {
663 fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
664 }
665
666 if (spec.hasDomain()) {
667 fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
668 }
669
670 if (spec.usesCoverageAA()) {
671 fAAEdgeDistances = {"aaEdgeDist", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
672 }
673 this->setVertexAttributes(&fPositionWithCoverage, 5);
674 }
675
676 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
677
678 Attribute fPositionWithCoverage;
679 Attribute fColor;
680 Attribute fLocalCoord;
681 Attribute fDomain;
682 Attribute fAAEdgeDistances;
683
684 // The positions attribute is always a vec4 and can't be used to encode perspectiveness
685 bool fNeedsPerspective;
686
687 // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
688 // to skip texturing.
689 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
690 TextureSampler fSampler;
691
692 typedef GrGeometryProcessor INHERITED;
693};
694
695sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
696 return QuadPerEdgeAAGeometryProcessor::Make(spec);
697}
698
699sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
700 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500701 const GrSamplerState& samplerState, uint32_t extraSamplerKey,
702 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
703 return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
704 samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000705 std::move(textureColorSpaceXform));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400706}
Michael Ludwigc182b942018-11-16 10:27:51 -0500707
708} // namespace GrQuadPerEdgeAA