blob: de2641f2fd37864e41eaa4e3280f2aee06d7b1be [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 Ludwig93aeba02018-12-21 09:50:31 -050057// outset and outsetCW are provided separately to allow for different magnitude outsets for
58// with-edge and "perpendicular" edge shifts. This is needed when one axis cannot be inset the full
59// half pixel without crossing over the other side.
60static AI void outset_masked_vertices(const Sk4f& outset, const Sk4f& outsetCW, const Sk4f& xdiff,
61 const Sk4f& ydiff, const Sk4f& invLengths, const Sk4f& mask,
62 Sk4f* x, Sk4f* y, Sk4f* u, Sk4f* v, Sk4f* r, int uvrCount) {
63 // The mask is rotated compared to the outsets and edge vectors, since if the edge is "on"
64 // both its points need to be moved along their other edge vectors.
65 auto maskedOutset = -outset * nextCW(mask);
66 auto maskedOutsetCW = outsetCW * mask;
67 // x = x + outsetCW * mask * nextCW(xdiff) - outset * nextCW(mask) * xdiff
68 *x += fma(maskedOutsetCW, nextCW(xdiff), maskedOutset * xdiff);
69 *y += fma(maskedOutsetCW, nextCW(ydiff), maskedOutset * ydiff);
Michael Ludwigf995c052018-11-26 15:24:29 -050070 if (uvrCount > 0) {
71 // We want to extend the texture coords by the same proportion as the positions.
Michael Ludwig93aeba02018-12-21 09:50:31 -050072 maskedOutset *= invLengths;
73 maskedOutsetCW *= nextCW(invLengths);
Michael Ludwigf995c052018-11-26 15:24:29 -050074 Sk4f udiff = nextCCW(*u) - *u;
75 Sk4f vdiff = nextCCW(*v) - *v;
Michael Ludwig93aeba02018-12-21 09:50:31 -050076 *u += fma(maskedOutsetCW, nextCW(udiff), maskedOutset * udiff);
77 *v += fma(maskedOutsetCW, nextCW(vdiff), maskedOutset * vdiff);
Michael Ludwigf995c052018-11-26 15:24:29 -050078 if (uvrCount == 3) {
79 Sk4f rdiff = nextCCW(*r) - *r;
Michael Ludwig93aeba02018-12-21 09:50:31 -050080 *r += fma(maskedOutsetCW, nextCW(rdiff), maskedOutset * rdiff);
Michael Ludwigf995c052018-11-26 15:24:29 -050081 }
82 }
83}
84
Michael Ludwig93aeba02018-12-21 09:50:31 -050085static AI void outset_vertices(const Sk4f& outset, const Sk4f& outsetCW, const Sk4f& xdiff,
86 const Sk4f& ydiff, const Sk4f& invLengths,
Michael Ludwigf995c052018-11-26 15:24:29 -050087 Sk4f* x, Sk4f* y, Sk4f* u, Sk4f* v, Sk4f* r, int uvrCount) {
Michael Ludwig93aeba02018-12-21 09:50:31 -050088 // x = x + outsetCW * nextCW(xdiff) - outset * xdiff (as above, but where mask = (1,1,1,1))
89 *x += fma(outsetCW, nextCW(xdiff), -outset * xdiff);
90 *y += fma(outsetCW, nextCW(ydiff), -outset * ydiff);
Michael Ludwigf995c052018-11-26 15:24:29 -050091 if (uvrCount > 0) {
Michael Ludwig93aeba02018-12-21 09:50:31 -050092 Sk4f t = -outset * invLengths; // Bake minus sign in here
93 Sk4f tCW = outsetCW * nextCW(invLengths);
Michael Ludwigf995c052018-11-26 15:24:29 -050094 Sk4f udiff = nextCCW(*u) - *u;
95 Sk4f vdiff = nextCCW(*v) - *v;
Michael Ludwig93aeba02018-12-21 09:50:31 -050096 *u += fma(tCW, nextCW(udiff), t * udiff);
97 *v += fma(tCW, nextCW(vdiff), t * vdiff);
Michael Ludwigf995c052018-11-26 15:24:29 -050098 if (uvrCount == 3) {
99 Sk4f rdiff = nextCCW(*r) - *r;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500100 *r += fma(tCW, nextCW(rdiff), t * rdiff);
Michael Ludwigf995c052018-11-26 15:24:29 -0500101 }
102 }
103}
104
Michael Ludwig93aeba02018-12-21 09:50:31 -0500105// Updates outset in place to account for non-90 degree angles of the quad edges stored in
106// xdiff, ydiff (which are assumed to be normalized).
107static void adjust_non_rectilinear_outset(const Sk4f& xdiff, const Sk4f& ydiff, Sk4f* outset) {
108 // The distance the point needs to move is outset/sqrt(1-cos^2(theta)), where theta is the angle
109 // between the two edges at that point. cos(theta) is equal to dot(xydiff, nextCW(xydiff)),
110 Sk4f cosTheta = fma(xdiff, nextCW(xdiff), ydiff * nextCW(ydiff));
111 *outset *= (1.f - cosTheta * cosTheta).rsqrt();
112 // But clamp to make sure we don't expand by a giant amount if the sheer is really high
113 *outset = Sk4f::Max(-3.f, Sk4f::Min(*outset, 3.f));
Michael Ludwigf995c052018-11-26 15:24:29 -0500114}
115
Michael Ludwig93aeba02018-12-21 09:50:31 -0500116// Computes the vertices for the two nested quads used to create AA edges. The original single quad
117// should be duplicated as input in x1 and x2, y1 and y2, and possibly u1|u2, v1|v2, [r1|r2]
118// (controlled by uvrChannelCount). While the values should be duplicated, they should be separate
119// pointers. The outset quad is written in-place back to x1, y1, etc. and the inset inner quad is
120// written to x2, y2, etc.
121static float compute_nested_quad_vertices(GrQuadAAFlags aaFlags, Sk4f* x1, Sk4f* y1,
122 Sk4f* u1, Sk4f* v1, Sk4f* r1, Sk4f* x2, Sk4f* y2, Sk4f* u2, Sk4f* v2, Sk4f* r2,
123 int uvrCount, bool rectilinear) {
124 SkASSERT(uvrCount == 0 || uvrCount == 2 || uvrCount == 3);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400125
Michael Ludwigf995c052018-11-26 15:24:29 -0500126 // Compute edge vectors for the quad.
Michael Ludwig93aeba02018-12-21 09:50:31 -0500127 auto xnext = nextCCW(*x1);
128 auto ynext = nextCCW(*y1);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400129 // xdiff and ydiff will comprise the normalized vectors pointing along each quad edge.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000130 Sk4f xdiff, ydiff, invLengths;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500131 compute_edge_vectors(*x1, *y1, xnext, ynext, &xdiff, &ydiff, &invLengths);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400132
Michael Ludwig93aeba02018-12-21 09:50:31 -0500133 // When outsetting, we want the new edge to be .5px away from the old line, which means the
134 // corners may need to be adjusted by more than .5px if the matrix had sheer.
135 Sk4f outset = 0.5f;
136 if (!rectilinear) {
137 adjust_non_rectilinear_outset(xdiff, ydiff, &outset);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400138 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000139
Michael Ludwig93aeba02018-12-21 09:50:31 -0500140 // When insetting, cap the inset amount to be half of the edge length, except that each edge
141 // has to remain parallel, so we separately limit LR and TB to half of the smallest of the
142 // opposing edges.
Michael Ludwig4921dc32018-12-03 14:57:29 +0000143 Sk4f lengths = invLengths.invert();
Michael Ludwig93aeba02018-12-21 09:50:31 -0500144 Sk2f sides(SkMinScalar(lengths[0], lengths[3]), SkMinScalar(lengths[1], lengths[2]));
145 Sk4f edgeLimits = 0.5f * SkNx_shuffle<0, 1, 1, 0>(sides);
Michael Ludwigf995c052018-11-26 15:24:29 -0500146
Michael Ludwig93aeba02018-12-21 09:50:31 -0500147 if ((edgeLimits < 0.5f).anyTrue()) {
148 // Dealing with a subpixel rectangle, so must calculate clamped insets and padded outsets.
149 // The outsets are padded to ensure that the quad spans 2 pixels for improved interpolation.
150 Sk4f inset = -Sk4f::Min(outset, edgeLimits);
151 Sk4f insetCW = -Sk4f::Min(outset, nextCW(edgeLimits));
Michael Ludwigf995c052018-11-26 15:24:29 -0500152
Michael Ludwig93aeba02018-12-21 09:50:31 -0500153 // The parallel distance shift caused by outset is currently 0.5, but need to scale it up to
154 // 0.5*(2 - side) so that (side + 2*shift) = 2px. Thus scale outsets for thin edges by
155 // (2 - side) since it already has the 1/2.
156 Sk4f outsetScale = 2.f - 2.f * Sk4f::Min(edgeLimits, 0.5f); // == 1 for non-thin edges
157 Sk4f outsetCW = outset * nextCW(outsetScale);
158 outset *= outsetScale;
Michael Ludwigf995c052018-11-26 15:24:29 -0500159
Michael Ludwig93aeba02018-12-21 09:50:31 -0500160 if (aaFlags != GrQuadAAFlags::kAll) {
161 Sk4f mask = compute_edge_mask(aaFlags);
162 outset_masked_vertices(outset, outsetCW, xdiff, ydiff, invLengths, mask, x1, y1,
163 u1, v1, r1, uvrCount);
164 outset_masked_vertices(inset, insetCW, xdiff, ydiff, invLengths, mask, x2, y2,
165 u2, v2, r2, uvrCount);
166 } else {
167 outset_vertices(outset, outsetCW, xdiff, ydiff, invLengths, x1, y1, u1, v1, r1, uvrCount);
168 outset_vertices(inset, insetCW, xdiff, ydiff, invLengths, x2, y2, u2, v2, r2, uvrCount);
Michael Ludwigf995c052018-11-26 15:24:29 -0500169 }
Michael Ludwigf995c052018-11-26 15:24:29 -0500170 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500171 // Since it's not subpixel, the inset is just the opposite of the outset and there's no
172 // difference between CCW and CW behavior.
173 Sk4f inset = -outset;
174 if (aaFlags != GrQuadAAFlags::kAll) {
175 Sk4f mask = compute_edge_mask(aaFlags);
176 outset_masked_vertices(outset, outset, xdiff, ydiff, invLengths, mask, x1, y1,
177 u1, v1, r1, uvrCount);
178 outset_masked_vertices(inset, inset, xdiff, ydiff, invLengths, mask, x2, y2,
179 u2, v2, r2, uvrCount);
180 } else {
181 outset_vertices(outset, outset, xdiff, ydiff, invLengths, x1, y1, u1, v1, r1, uvrCount);
182 outset_vertices(inset, inset, xdiff, ydiff, invLengths, x2, y2, u2, v2, r2, uvrCount);
183 }
Michael Ludwigf995c052018-11-26 15:24:29 -0500184 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000185
Michael Ludwig93aeba02018-12-21 09:50:31 -0500186 // An approximation of the pixel area covered by the quad
187 sides = Sk2f::Min(1.f, sides);
188 return sides[0] * sides[1];
Michael Ludwigf995c052018-11-26 15:24:29 -0500189}
190
Michael Ludwig93aeba02018-12-21 09:50:31 -0500191// For each device space corner, devP, label its left/right or top/bottom opposite device space
192// point opDevPt. The new device space point is opDevPt + s (devPt - opDevPt) where s is
193// (length(devPt - opDevPt) + outset) / length(devPt - opDevPt); This returns the interpolant s,
194// adjusted for any subpixel corrections. If subpixel, it also updates the max coverage.
195static Sk4f get_projected_interpolant(const Sk4f& len, const Sk4f& outsets, float* maxCoverage) {
196 if ((len < 1.f).anyTrue()) {
197 *maxCoverage *= len.min();
198
199 // When insetting, the amount is clamped to be half the minimum edge length to prevent
200 // overlap. When outsetting, the amount is padded to cover 2 pixels.
201 if ((outsets < 0.f).anyTrue()) {
202 return (len - 0.5f * len.min()) / len;
203 } else {
204 return (len + outsets * (2.f - len.min())) / len;
205 }
206 } else {
207 return (len + outsets) / len;
208 }
209}
210
211// Generalizes compute_nested_quad_vertices to extrapolate local coords such that
Michael Ludwigf995c052018-11-26 15:24:29 -0500212// after perspective division of the device coordinate, the original local coordinate value is at
Michael Ludwig93aeba02018-12-21 09:50:31 -0500213// the original un-outset device position. r is the local coordinate's w component. However, since
214// the projected edges will be different for inner and outer quads, there isn't much reuse between
215// the calculations, so it's easier to just have this operate on one quad a time.
216static float compute_quad_persp_vertices(GrQuadAAFlags aaFlags, Sk4f* x, Sk4f* y,
217 Sk4f* w, Sk4f* u, Sk4f* v, Sk4f* r, int uvrCount, bool inset) {
218 SkASSERT(uvrCount == 0 || uvrCount == 2 || uvrCount == 3);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400219
220 auto iw = (*w).invert();
221 auto x2d = (*x) * iw;
222 auto y2d = (*y) * iw;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400223
Michael Ludwig93aeba02018-12-21 09:50:31 -0500224 // Must compute non-rectilinear outset quantity using the projected 2d edge vectors
225 Sk4f xdiff, ydiff, invLengths;
226 compute_edge_vectors(x2d, y2d, nextCCW(x2d), nextCCW(y2d), &xdiff, &ydiff, &invLengths);
227 Sk4f outset = inset ? -0.5f : 0.5f;
228 adjust_non_rectilinear_outset(xdiff, ydiff, &outset);
229
230 float maxProjectedCoverage = 1.f;
231
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400232 if ((GrQuadAAFlags::kLeft | GrQuadAAFlags::kRight) & aaFlags) {
233 // For each entry in x the equivalent entry in opX is the left/right opposite and so on.
234 Sk4f opX = SkNx_shuffle<2, 3, 0, 1>(*x);
235 Sk4f opW = SkNx_shuffle<2, 3, 0, 1>(*w);
236 Sk4f opY = SkNx_shuffle<2, 3, 0, 1>(*y);
237 // vx/vy holds the device space left-to-right vectors along top and bottom of the quad.
238 Sk2f vx = SkNx_shuffle<2, 3>(x2d) - SkNx_shuffle<0, 1>(x2d);
239 Sk2f vy = SkNx_shuffle<2, 3>(y2d) - SkNx_shuffle<0, 1>(y2d);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500240 Sk4f len = SkNx_shuffle<0, 1, 0, 1>(SkNx_fma(vx, vx, vy * vy).sqrt());
241
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400242 // Compute t in homogeneous space from s using similar triangles so that we can produce
243 // homogeneous outset vertices for perspective-correct interpolation.
Kevin Lubickf6e963e2018-12-26 16:05:14 -0500244 Sk4f s = get_projected_interpolant(len, outset, &maxProjectedCoverage);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400245 Sk4f sOpW = s * opW;
246 Sk4f t = sOpW / (sOpW + (1.f - s) * (*w));
247 // mask is used to make the t values be 1 when the left/right side is not antialiased.
248 Sk4f mask(GrQuadAAFlags::kLeft & aaFlags ? 1.f : 0.f,
249 GrQuadAAFlags::kLeft & aaFlags ? 1.f : 0.f,
250 GrQuadAAFlags::kRight & aaFlags ? 1.f : 0.f,
251 GrQuadAAFlags::kRight & aaFlags ? 1.f : 0.f);
252 t = t * mask + (1.f - mask);
253 *x = opX + t * (*x - opX);
254 *y = opY + t * (*y - opY);
255 *w = opW + t * (*w - opW);
256
Michael Ludwig93aeba02018-12-21 09:50:31 -0500257 if (uvrCount > 0) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400258 Sk4f opU = SkNx_shuffle<2, 3, 0, 1>(*u);
259 Sk4f opV = SkNx_shuffle<2, 3, 0, 1>(*v);
260 *u = opU + t * (*u - opU);
261 *v = opV + t * (*v - opV);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500262 if (uvrCount == 3) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400263 Sk4f opR = SkNx_shuffle<2, 3, 0, 1>(*r);
264 *r = opR + t * (*r - opR);
265 }
266 }
267
268 if ((GrQuadAAFlags::kTop | GrQuadAAFlags::kBottom) & aaFlags) {
269 // Update the 2D points for the top/bottom calculation.
270 iw = (*w).invert();
271 x2d = (*x) * iw;
272 y2d = (*y) * iw;
273 }
274 }
275
276 if ((GrQuadAAFlags::kTop | GrQuadAAFlags::kBottom) & aaFlags) {
277 // This operates the same as above but for top/bottom rather than left/right.
278 Sk4f opX = SkNx_shuffle<1, 0, 3, 2>(*x);
279 Sk4f opW = SkNx_shuffle<1, 0, 3, 2>(*w);
280 Sk4f opY = SkNx_shuffle<1, 0, 3, 2>(*y);
281
282 Sk2f vx = SkNx_shuffle<1, 3>(x2d) - SkNx_shuffle<0, 2>(x2d);
283 Sk2f vy = SkNx_shuffle<1, 3>(y2d) - SkNx_shuffle<0, 2>(y2d);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500284 Sk4f len = SkNx_shuffle<0, 0, 1, 1>(SkNx_fma(vx, vx, vy * vy).sqrt());
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400285
Kevin Lubickf6e963e2018-12-26 16:05:14 -0500286 Sk4f s = get_projected_interpolant(len, outset, &maxProjectedCoverage);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400287 Sk4f sOpW = s * opW;
288 Sk4f t = sOpW / (sOpW + (1.f - s) * (*w));
289
290 Sk4f mask(GrQuadAAFlags::kTop & aaFlags ? 1.f : 0.f,
291 GrQuadAAFlags::kBottom & aaFlags ? 1.f : 0.f,
292 GrQuadAAFlags::kTop & aaFlags ? 1.f : 0.f,
293 GrQuadAAFlags::kBottom & aaFlags ? 1.f : 0.f);
294 t = t * mask + (1.f - mask);
295 *x = opX + t * (*x - opX);
296 *y = opY + t * (*y - opY);
297 *w = opW + t * (*w - opW);
298
Michael Ludwig93aeba02018-12-21 09:50:31 -0500299 if (uvrCount > 0) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400300 Sk4f opU = SkNx_shuffle<1, 0, 3, 2>(*u);
301 Sk4f opV = SkNx_shuffle<1, 0, 3, 2>(*v);
302 *u = opU + t * (*u - opU);
303 *v = opV + t * (*v - opV);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500304 if (uvrCount == 3) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400305 Sk4f opR = SkNx_shuffle<1, 0, 3, 2>(*r);
306 *r = opR + t * (*r - opR);
307 }
308 }
309 }
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400310
Michael Ludwig4921dc32018-12-03 14:57:29 +0000311 return maxProjectedCoverage;
Eric Boren98cb1592018-11-26 18:38:05 +0000312}
313
Michael Ludwig93aeba02018-12-21 09:50:31 -0500314enum class CoverageMode {
315 kNone,
316 kWithPosition,
317 kWithColor
318};
319
320static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
321 if (spec.usesCoverageAA()) {
322 if (spec.compatibleWithAlphaAsCoverage() && spec.hasVertexColors()) {
323 return CoverageMode::kWithColor;
324 } else {
325 return CoverageMode::kWithPosition;
326 }
Michael Ludwig553e9a92018-11-29 12:38:35 -0500327 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500328 return CoverageMode::kNone;
Michael Ludwig553e9a92018-11-29 12:38:35 -0500329 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500330}
Michael Ludwig553e9a92018-11-29 12:38:35 -0500331
Michael Ludwig93aeba02018-12-21 09:50:31 -0500332// Writes four vertices in triangle strip order, including the additional data for local
333// coordinates, domain, color, and coverage as needed to satisfy the vertex spec.
334static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
335 CoverageMode mode, float coverage,
336 SkPMColor4f color4f, bool wideColor,
337 const SkRect& domain,
338 const Sk4f& x, const Sk4f& y, const Sk4f& w,
339 const Sk4f& u, const Sk4f& v, const Sk4f& r) {
340 static constexpr auto If = GrVertexWriter::If<float>;
341
342 if (mode == CoverageMode::kWithColor) {
343 // Multiply the color by the coverage up front
344 SkASSERT(spec.hasVertexColors());
345 color4f = color4f * coverage;
346 }
347 GrVertexColor color(color4f, wideColor);
348
Michael Ludwig553e9a92018-11-29 12:38:35 -0500349 for (int i = 0; i < 4; ++i) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500350 // save position, this is a float2 or float3 or float4 depending on the combination of
351 // perspective and coverage mode.
352 vb->write(x[i], y[i], If(spec.deviceQuadType() == GrQuadType::kPerspective, w[i]),
353 If(mode == CoverageMode::kWithPosition, coverage));
Michael Ludwig4921dc32018-12-03 14:57:29 +0000354
Michael Ludwig93aeba02018-12-21 09:50:31 -0500355 // save color
356 if (spec.hasVertexColors()) {
357 vb->write(color);
358 }
359
360 // save local position
361 if (spec.hasLocalCoords()) {
362 vb->write(u[i], v[i], If(spec.localQuadType() == GrQuadType::kPerspective, r[i]));
363 }
364
365 // save the domain
366 if (spec.hasDomain()) {
367 vb->write(domain);
368 }
369 }
370}
371
372GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
373
374static const int kVertsPerAAFillRect = 8;
375static const int kIndicesPerAAFillRect = 30;
376
Brian Salomondbf70722019-02-07 11:31:24 -0500377static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500378 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
379
380 // clang-format off
381 static const uint16_t gFillAARectIdx[] = {
382 0, 1, 2, 1, 3, 2,
383 0, 4, 1, 4, 5, 1,
384 0, 6, 4, 0, 2, 6,
385 2, 3, 6, 3, 7, 6,
386 1, 5, 3, 3, 5, 7,
387 };
388 // clang-format on
389
390 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
391 return resourceProvider->findOrCreatePatternedIndexBuffer(
392 gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
393 kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
Michael Ludwig553e9a92018-11-29 12:38:35 -0500394}
395
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400396} // anonymous namespace
397
Michael Ludwigc182b942018-11-16 10:27:51 -0500398namespace GrQuadPerEdgeAA {
399
400////////////////// Tessellate Implementation
401
402void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
Brian Osman3d139a42018-11-19 10:42:10 -0500403 const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
Michael Ludwigc182b942018-11-16 10:27:51 -0500404 GrQuadAAFlags aaFlags) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500405 bool wideColor = GrQuadPerEdgeAA::ColorType::kHalf == spec.colorType();
406 CoverageMode mode = get_mode_for_spec(spec);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400407
Michael Ludwigf995c052018-11-26 15:24:29 -0500408 // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
Michael Ludwig93aeba02018-12-21 09:50:31 -0500409 Sk4f oX = deviceQuad.x4f();
410 Sk4f oY = deviceQuad.y4f();
411 Sk4f oW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400412
413 // Load local position data into Sk4fs (either none, just u,v or all three)
Michael Ludwig93aeba02018-12-21 09:50:31 -0500414 Sk4f oU, oV, oR;
Michael Ludwigc182b942018-11-16 10:27:51 -0500415 if (spec.hasLocalCoords()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500416 oU = localQuad.x4f();
417 oV = localQuad.y4f();
418 oR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400419 }
420
Michael Ludwigc182b942018-11-16 10:27:51 -0500421 GrVertexWriter vb{vertices};
Michael Ludwig93aeba02018-12-21 09:50:31 -0500422 if (spec.usesCoverageAA()) {
423 SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400424
Michael Ludwig93aeba02018-12-21 09:50:31 -0500425 // Must calculate two new quads, an outset and inset by .5 in projected device space, so
426 // duplicate the original quad into new Sk4fs for the inset.
427 Sk4f iX = oX, iY = oY, iW = oW;
428 Sk4f iU = oU, iV = oV, iR = oR;
Michael Ludwigc182b942018-11-16 10:27:51 -0500429
Michael Ludwig93aeba02018-12-21 09:50:31 -0500430 float maxCoverage = 1.f;
431 if (aaFlags != GrQuadAAFlags::kNone) {
432 if (spec.deviceQuadType() == GrQuadType::kPerspective) {
433 // Outset and inset the quads independently because perspective makes each shift
434 // unique. Since iX copied pre-outset oX, this will compute the proper inset too.
435 compute_quad_persp_vertices(aaFlags, &oX, &oY, &oW, &oU, &oV, &oW,
436 spec.localDimensionality(), /* inset */ false);
437 // Save coverage limit when computing inset quad
438 maxCoverage = compute_quad_persp_vertices(aaFlags, &iX, &iY, &iW, &iU, &iV, &iW,
439 spec.localDimensionality(), true);
Michael Ludwigc182b942018-11-16 10:27:51 -0500440 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500441 // In the 2D case, insetting and outsetting can reuse the edge vectors, so the
442 // nested quads are computed together
443 maxCoverage = compute_nested_quad_vertices(aaFlags, &oX, &oY, &oU, &oV, &oR,
444 &iX, &iY, &iU, &iV, &iR, spec.localDimensionality(),
445 spec.deviceQuadType() <= GrQuadType::kRectilinear);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400446 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500447 // NOTE: could provide an even more optimized tessellation function for axis-aligned
448 // rects since the positions can be outset by constants without doing vector math,
449 // except it must handle identifying the winding of the quad vertices if the transform
450 // applied a mirror, etc. The current 2D case is already adequately fast.
451 } // else don't adjust any positions, let the outer quad form degenerate triangles
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400452
Michael Ludwig93aeba02018-12-21 09:50:31 -0500453 // Write two quads for inner and outer, inner will use the
454 write_quad(&vb, spec, mode, maxCoverage, color4f, wideColor, domain,
455 iX, iY, iW, iU, iV, iR);
456 write_quad(&vb, spec, mode, 0.f, color4f, wideColor, domain, oX, oY, oW, oU, oV, oR);
457 } else {
458 // No outsetting needed, just write a single quad with full coverage
459 SkASSERT(mode == CoverageMode::kNone);
460 write_quad(&vb, spec, mode, 1.f, color4f, wideColor, domain, oX, oY, oW, oU, oV, oR);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400461 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500462
463 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400464}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400465
Michael Ludwig93aeba02018-12-21 09:50:31 -0500466bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
467 int quadCount) {
468 if (spec.usesCoverageAA()) {
469 // AA quads use 8 vertices, basically nested rectangles
Brian Salomondbf70722019-02-07 11:31:24 -0500470 sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
Michael Ludwig93aeba02018-12-21 09:50:31 -0500471 if (!ibuffer) {
472 return false;
473 }
474
475 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500476 mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
477 quadCount, kNumAAQuadsInIndexBuffer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500478 } else {
479 // Non-AA quads use 4 vertices, and regular triangle strip layout
480 if (quadCount > 1) {
Brian Salomondbf70722019-02-07 11:31:24 -0500481 sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Michael Ludwig93aeba02018-12-21 09:50:31 -0500482 if (!ibuffer) {
483 return false;
484 }
485
486 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500487 mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500488 GrResourceProvider::QuadCountOfQuadBuffer());
489 } else {
490 mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
491 mesh->setNonIndexedNonInstanced(4);
492 }
493 }
494
495 return true;
496}
497
Michael Ludwigc182b942018-11-16 10:27:51 -0500498////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400499
Michael Ludwigc182b942018-11-16 10:27:51 -0500500int VertexSpec::deviceDimensionality() const {
501 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
502}
503
504int VertexSpec::localDimensionality() const {
505 return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
506}
507
Michael Ludwig467994d2018-12-03 14:58:31 +0000508////////////////// Geometry Processor Implementation
Michael Ludwigc182b942018-11-16 10:27:51 -0500509
Michael Ludwig467994d2018-12-03 14:58:31 +0000510class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
511public:
Michael Ludwig20e909e2018-10-30 10:43:57 -0400512
Michael Ludwig467994d2018-12-03 14:58:31 +0000513 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
514 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400515 }
516
Michael Ludwig467994d2018-12-03 14:58:31 +0000517 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
518 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500519 const GrSamplerState& samplerState,
520 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000521 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
522 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
Greg Daniel7a82edf2018-12-04 10:54:34 -0500523 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000524 std::move(textureColorSpaceXform)));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400525 }
526
Michael Ludwig467994d2018-12-03 14:58:31 +0000527 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
Michael Ludwig024e2622018-11-30 13:25:55 -0500528
Michael Ludwig467994d2018-12-03 14:58:31 +0000529 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500530 // domain, texturing, device-dimensions are single bit flags
531 uint32_t x = fDomain.isInitialized() ? 0 : 1;
532 x |= fSampler.isInitialized() ? 0 : 2;
533 x |= fNeedsPerspective ? 0 : 4;
Michael Ludwig467994d2018-12-03 14:58:31 +0000534 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
535 if (fLocalCoord.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500536 x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
Brian Osman78dc72c2018-12-03 13:20:43 +0000537 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000538 // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
Michael Ludwig93aeba02018-12-21 09:50:31 -0500539 if (fColor.isInitialized()) {
540 x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
541 }
542 // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor
543 if (fCoverageMode != CoverageMode::kNone) {
544 x |= CoverageMode::kWithPosition == fCoverageMode ? 128 : 256;
Michael Ludwig467994d2018-12-03 14:58:31 +0000545 }
546
547 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
548 b->add32(x);
Brian Osman78dc72c2018-12-03 13:20:43 +0000549 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000550
551 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
552 class GLSLProcessor : public GrGLSLGeometryProcessor {
553 public:
554 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
555 FPCoordTransformIter&& transformIter) override {
556 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
557 if (gp.fLocalCoord.isInitialized()) {
558 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
559 }
560 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
561 }
562
563 private:
564 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
565 using Interpolation = GrGLSLVaryingHandler::Interpolation;
566
567 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
568 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
569 gp.fTextureColorSpaceXform.get());
570
571 args.fVaryingHandler->emitAttributes(gp);
572
Michael Ludwig93aeba02018-12-21 09:50:31 -0500573 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
574 // Strip last channel from the vertex attribute to remove coverage and get the
575 // actual position
576 if (gp.fNeedsPerspective) {
577 args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
578 gp.fPosition.name());
579 } else {
580 args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
581 gp.fPosition.name());
582 }
583 gpArgs->fPositionVar = {"position",
584 gp.fNeedsPerspective ? kFloat3_GrSLType
585 : kFloat2_GrSLType,
586 GrShaderVar::kNone_TypeModifier};
Michael Ludwig467994d2018-12-03 14:58:31 +0000587 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500588 // No coverage to eliminate
589 gpArgs->fPositionVar = gp.fPosition.asShaderVar();
Michael Ludwig467994d2018-12-03 14:58:31 +0000590 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000591
592 // Handle local coordinates if they exist
593 if (gp.fLocalCoord.isInitialized()) {
594 // NOTE: If the only usage of local coordinates is for the inline texture fetch
595 // before FPs, then there are no registered FPCoordTransforms and this ends up
596 // emitting nothing, so there isn't a duplication of local coordinates
597 this->emitTransforms(args.fVertBuilder,
598 args.fVaryingHandler,
599 args.fUniformHandler,
600 gp.fLocalCoord.asShaderVar(),
601 args.fFPCoordTransformHandler);
602 }
603
604 // Solid color before any texturing gets modulated in
605 if (gp.fColor.isInitialized()) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500606 // The color cannot be flat if the varying coverage has been modulated into it
Michael Ludwig467994d2018-12-03 14:58:31 +0000607 args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500608 gp.fCoverageMode == CoverageMode::kWithColor ?
609 Interpolation::kInterpolated : Interpolation::kCanBeFlat);
610 } else {
611 // Output color must be initialized to something
612 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
Michael Ludwig467994d2018-12-03 14:58:31 +0000613 }
614
615 // If there is a texture, must also handle texture coordinates and reading from
616 // the texture in the fragment shader before continuing to fragment processors.
617 if (gp.fSampler.isInitialized()) {
618 // Texture coordinates clamped by the domain on the fragment shader; if the GP
619 // has a texture, it's guaranteed to have local coordinates
620 args.fFragBuilder->codeAppend("float2 texCoord;");
621 if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
622 // Can't do a pass through since we need to perform perspective division
623 GrGLSLVarying v(gp.fLocalCoord.gpuType());
624 args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
625 args.fVertBuilder->codeAppendf("%s = %s;",
626 v.vsOut(), gp.fLocalCoord.name());
627 args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
628 v.fsIn(), v.fsIn());
629 } else {
630 args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
631 }
632
633 // Clamp the now 2D localCoordName variable by the domain if it is provided
634 if (gp.fDomain.isInitialized()) {
635 args.fFragBuilder->codeAppend("float4 domain;");
636 args.fVaryingHandler->addPassThroughAttribute(gp.fDomain, "domain",
637 Interpolation::kCanBeFlat);
638 args.fFragBuilder->codeAppend(
639 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
640 }
641
642 // Now modulate the starting output color by the texture lookup
643 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
644 args.fFragBuilder->appendTextureLookupAndModulate(
645 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
646 &fTextureColorSpaceXformHelper);
647 args.fFragBuilder->codeAppend(";");
648 }
649
650 // And lastly, output the coverage calculation code
Michael Ludwig93aeba02018-12-21 09:50:31 -0500651 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
652 GrGLSLVarying coverage(kFloat_GrSLType);
653 args.fVaryingHandler->addVarying("coverage", &coverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000654 if (gp.fNeedsPerspective) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500655 args.fVertBuilder->codeAppendf("%s = %s.w;",
656 coverage.vsOut(), gp.fPosition.name());
657 } else {
658 args.fVertBuilder->codeAppendf("%s = %s.z;",
659 coverage.vsOut(), gp.fPosition.name());
Michael Ludwig467994d2018-12-03 14:58:31 +0000660 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500661
Ethan Nicholase1f55022019-02-05 17:17:40 -0500662 args.fFragBuilder->codeAppendf("%s = half4(half(%s));",
Michael Ludwig93aeba02018-12-21 09:50:31 -0500663 args.fOutputCoverage, coverage.fsIn());
Michael Ludwig467994d2018-12-03 14:58:31 +0000664 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500665 // Set coverage to 1, since it's either non-AA or the coverage was already
666 // folded into the output color
Ethan Nicholase1f55022019-02-05 17:17:40 -0500667 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000668 }
669 }
670 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
671 };
672 return new GLSLProcessor;
673 }
674
675private:
676 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
677 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
678 , fTextureColorSpaceXform(nullptr) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500679 SkASSERT(!spec.hasDomain());
Michael Ludwig467994d2018-12-03 14:58:31 +0000680 this->initializeAttrs(spec);
681 this->setTextureSamplerCnt(0);
682 }
683
684 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
685 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500686 const GrSamplerState& samplerState,
687 uint32_t extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000688 sk_sp<GrColorSpaceXform> textureColorSpaceXform)
689 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
690 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Greg Daniel7a82edf2018-12-04 10:54:34 -0500691 , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500692 SkASSERT(spec.hasLocalCoords());
Michael Ludwig467994d2018-12-03 14:58:31 +0000693 this->initializeAttrs(spec);
694 this->setTextureSamplerCnt(1);
695 }
696
697 void initializeAttrs(const VertexSpec& spec) {
698 fNeedsPerspective = spec.deviceDimensionality() == 3;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500699 fCoverageMode = get_mode_for_spec(spec);
700
701 if (fCoverageMode == CoverageMode::kWithPosition) {
702 if (fNeedsPerspective) {
703 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
704 } else {
705 fPosition = {"positionWithCoverage", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
706 }
707 } else {
708 if (fNeedsPerspective) {
709 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
710 } else {
711 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
712 }
713 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000714
715 int localDim = spec.localDimensionality();
716 if (localDim == 3) {
717 fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
718 } else if (localDim == 2) {
719 fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
720 } // else localDim == 0 and attribute remains uninitialized
721
722 if (ColorType::kByte == spec.colorType()) {
723 fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
724 } else if (ColorType::kHalf == spec.colorType()) {
725 fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
726 }
727
728 if (spec.hasDomain()) {
729 fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
730 }
731
Michael Ludwig93aeba02018-12-21 09:50:31 -0500732 this->setVertexAttributes(&fPosition, 4);
Michael Ludwig467994d2018-12-03 14:58:31 +0000733 }
734
735 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
736
Michael Ludwig93aeba02018-12-21 09:50:31 -0500737 Attribute fPosition; // May contain coverage as last channel
738 Attribute fColor; // May have coverage modulated in if the FPs support it
Michael Ludwig467994d2018-12-03 14:58:31 +0000739 Attribute fLocalCoord;
740 Attribute fDomain;
Michael Ludwig467994d2018-12-03 14:58:31 +0000741
Michael Ludwig93aeba02018-12-21 09:50:31 -0500742 // The positions attribute may have coverage built into it, so float3 is an ambiguous type
743 // and may mean 2d with coverage, or 3d with no coverage
Michael Ludwig467994d2018-12-03 14:58:31 +0000744 bool fNeedsPerspective;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500745 CoverageMode fCoverageMode;
Michael Ludwig467994d2018-12-03 14:58:31 +0000746
747 // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
748 // to skip texturing.
749 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
750 TextureSampler fSampler;
751
752 typedef GrGeometryProcessor INHERITED;
753};
754
755sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
756 return QuadPerEdgeAAGeometryProcessor::Make(spec);
757}
758
759sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
760 GrTextureType textureType, GrPixelConfig textureConfig,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500761 const GrSamplerState& samplerState, uint32_t extraSamplerKey,
762 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
763 return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
764 samplerState, extraSamplerKey,
Michael Ludwig467994d2018-12-03 14:58:31 +0000765 std::move(textureColorSpaceXform));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400766}
Michael Ludwigc182b942018-11-16 10:27:51 -0500767
768} // namespace GrQuadPerEdgeAA