blob: ad060dd59f4f84154bd17dc7e1bccc39feb19201 [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
Michael Ludwigfd4f4df2019-05-29 09:51:09 -04008#include "src/gpu/ops/GrQuadPerEdgeAA.h"
9
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/private/SkNx.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/gpu/GrVertexWriter.h"
12#include "src/gpu/SkGr.h"
13#include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
14#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
15#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
16#include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
17#include "src/gpu/glsl/GrGLSLVarying.h"
18#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Michael Ludwig460eb5e2018-10-29 11:09:29 -040019
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
Michael Ludwigb3461fa2019-04-30 11:50:55 -040027using V4f = skvx::Vec<4, float>;
28using M4f = skvx::Vec<4, int32_t>;
29
Michael Ludwige6266a22019-03-07 11:24:32 -050030struct Vertices {
31 // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
Michael Ludwigb3461fa2019-04-30 11:50:55 -040032 V4f fX, fY, fW;
Michael Ludwige6266a22019-03-07 11:24:32 -050033 // U, V, and R coordinates representing local quad. Ignored depending on uvrCount (0, 1, 2).
Michael Ludwigb3461fa2019-04-30 11:50:55 -040034 V4f fU, fV, fR;
Michael Ludwige6266a22019-03-07 11:24:32 -050035 int fUVRCount;
36};
37
38struct QuadMetadata {
39 // Normalized edge vectors of the device space quad, ordered L, B, T, R (i.e. nextCCW(x) - x).
Michael Ludwigb3461fa2019-04-30 11:50:55 -040040 V4f fDX, fDY;
Michael Ludwige6266a22019-03-07 11:24:32 -050041 // 1 / edge length of the device space quad
Michael Ludwigb3461fa2019-04-30 11:50:55 -040042 V4f fInvLengths;
Michael Ludwige6266a22019-03-07 11:24:32 -050043 // Edge mask (set to all 1s if aa flags is kAll), otherwise 1.f if edge was AA, 0.f if non-AA.
Michael Ludwigb3461fa2019-04-30 11:50:55 -040044 V4f fMask;
Michael Ludwige6266a22019-03-07 11:24:32 -050045};
46
47struct Edges {
48 // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
Michael Ludwigb3461fa2019-04-30 11:50:55 -040049 V4f fA, fB, fC;
Michael Ludwige6266a22019-03-07 11:24:32 -050050 // Whether or not the edge normals had to be flipped to preserve positive distance on the inside
51 bool fFlipped;
52};
53
54static constexpr float kTolerance = 1e-2f;
Michael Ludwigb3461fa2019-04-30 11:50:55 -040055// True/false bit masks for initializing an M4f
56static constexpr int32_t kTrue = ~0;
57static constexpr int32_t kFalse = 0;
Michael Ludwige6266a22019-03-07 11:24:32 -050058
Michael Ludwigb3461fa2019-04-30 11:50:55 -040059static AI V4f fma(const V4f& f, const V4f& m, const V4f& a) {
60 return mad(f, m, a);
Michael Ludwigf995c052018-11-26 15:24:29 -050061}
62
63// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
64// order.
Michael Ludwigb3461fa2019-04-30 11:50:55 -040065static AI V4f nextCW(const V4f& v) {
66 return skvx::shuffle<2, 0, 3, 1>(v);
Michael Ludwigf995c052018-11-26 15:24:29 -050067}
68
Michael Ludwigb3461fa2019-04-30 11:50:55 -040069static AI V4f nextCCW(const V4f& v) {
70 return skvx::shuffle<1, 3, 0, 2>(v);
Michael Ludwigf995c052018-11-26 15:24:29 -050071}
72
Michael Ludwige6266a22019-03-07 11:24:32 -050073// Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector.
74// e3 may be null if only 2D edges need to be corrected for.
Michael Ludwigb3461fa2019-04-30 11:50:55 -040075static AI void correct_bad_edges(const M4f& bad, V4f* e1, V4f* e2, V4f* e3) {
76 if (any(bad)) {
Michael Ludwige6266a22019-03-07 11:24:32 -050077 // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding
Michael Ludwigb3461fa2019-04-30 11:50:55 -040078 *e1 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e1), *e1);
79 *e2 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e2), *e2);
Michael Ludwige6266a22019-03-07 11:24:32 -050080 if (e3) {
Michael Ludwigb3461fa2019-04-30 11:50:55 -040081 *e3 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e3), *e3);
Michael Ludwige6266a22019-03-07 11:24:32 -050082 }
83 }
Michael Ludwigf995c052018-11-26 15:24:29 -050084}
85
Michael Ludwige6266a22019-03-07 11:24:32 -050086// Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points.
Michael Ludwigb3461fa2019-04-30 11:50:55 -040087static AI void correct_bad_coords(const M4f& bad, V4f* c1, V4f* c2, V4f* c3) {
88 if (any(bad)) {
89 *c1 = if_then_else(bad, nextCCW(*c1), *c1);
90 *c2 = if_then_else(bad, nextCCW(*c2), *c2);
Michael Ludwige6266a22019-03-07 11:24:32 -050091 if (c3) {
Michael Ludwigb3461fa2019-04-30 11:50:55 -040092 *c3 = if_then_else(bad, nextCCW(*c3), *c3);
Michael Ludwige6266a22019-03-07 11:24:32 -050093 }
94 }
Michael Ludwig4921dc32018-12-03 14:57:29 +000095}
96
Michael Ludwige6266a22019-03-07 11:24:32 -050097static AI QuadMetadata get_metadata(const Vertices& vertices, GrQuadAAFlags aaFlags) {
Michael Ludwigb3461fa2019-04-30 11:50:55 -040098 V4f dx = nextCCW(vertices.fX) - vertices.fX;
99 V4f dy = nextCCW(vertices.fY) - vertices.fY;
100 V4f invLengths = rsqrt(fma(dx, dx, dy * dy));
Michael Ludwige6266a22019-03-07 11:24:32 -0500101
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400102 V4f mask = aaFlags == GrQuadAAFlags::kAll ? V4f(1.f) :
103 V4f{(GrQuadAAFlags::kLeft & aaFlags) ? 1.f : 0.f,
Michael Ludwige6266a22019-03-07 11:24:32 -0500104 (GrQuadAAFlags::kBottom & aaFlags) ? 1.f : 0.f,
105 (GrQuadAAFlags::kTop & aaFlags) ? 1.f : 0.f,
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400106 (GrQuadAAFlags::kRight & aaFlags) ? 1.f : 0.f};
Michael Ludwige6266a22019-03-07 11:24:32 -0500107 return { dx * invLengths, dy * invLengths, invLengths, mask };
108}
109
110static AI Edges get_edge_equations(const QuadMetadata& metadata, const Vertices& vertices) {
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400111 V4f dx = metadata.fDX;
112 V4f dy = metadata.fDY;
Michael Ludwige6266a22019-03-07 11:24:32 -0500113 // Correct for bad edges by copying adjacent edge information into the bad component
114 correct_bad_edges(metadata.fInvLengths >= 1.f / kTolerance, &dx, &dy, nullptr);
115
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400116 V4f c = fma(dx, vertices.fY, -dy * vertices.fX);
Michael Ludwige6266a22019-03-07 11:24:32 -0500117 // Make sure normals point into the shape
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400118 V4f test = fma(dy, nextCW(vertices.fX), fma(-dx, nextCW(vertices.fY), c));
119 if (any(test < -kTolerance)) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500120 return {-dy, dx, -c, true};
121 } else {
122 return {dy, -dx, c, false};
123 }
124}
125
126// Sets 'outset' to the magnitude of outset/inset to adjust each corner of a quad given the
127// edge angles and lengths. If the quad is too small, has empty edges, or too sharp of angles,
128// false is returned and the degenerate slow-path should be used.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400129static bool get_optimized_outset(const QuadMetadata& metadata, bool rectilinear, V4f* outset) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500130 if (rectilinear) {
131 *outset = 0.5f;
132 // Stay in the fast path as long as all edges are at least a pixel long (so 1/len <= 1)
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400133 return all(metadata.fInvLengths <= 1.f);
Michael Ludwige6266a22019-03-07 11:24:32 -0500134 }
135
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400136 if (any(metadata.fInvLengths >= 1.f / kTolerance)) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500137 // Have an empty edge from a degenerate quad, so there's no hope
138 return false;
139 }
140
141 // The distance the point needs to move is 1/2sin(theta), where theta is the angle between the
142 // two edges at that point. cos(theta) is equal to dot(dxy, nextCW(dxy))
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400143 V4f cosTheta = fma(metadata.fDX, nextCW(metadata.fDX), metadata.fDY * nextCW(metadata.fDY));
Michael Ludwige6266a22019-03-07 11:24:32 -0500144 // If the angle is too shallow between edges, go through the degenerate path, otherwise adding
145 // and subtracting very large vectors in almost opposite directions leads to float errors
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400146 if (any(abs(cosTheta) >= 0.9f)) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500147 return false;
148 }
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400149 *outset = 0.5f * rsqrt(1.f - cosTheta * cosTheta); // 1/2sin(theta)
Michael Ludwige6266a22019-03-07 11:24:32 -0500150
151 // When outsetting or insetting, the current edge's AA adds to the length:
152 // cos(pi - theta)/2sin(theta) + cos(pi-ccw(theta))/2sin(ccw(theta))
153 // Moving an adjacent edge updates the length by 1/2sin(theta|ccw(theta))
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400154 V4f halfTanTheta = -cosTheta * (*outset); // cos(pi - theta) = -cos(theta)
155 V4f edgeAdjust = metadata.fMask * (halfTanTheta + nextCCW(halfTanTheta)) +
Michael Ludwige6266a22019-03-07 11:24:32 -0500156 nextCCW(metadata.fMask) * nextCCW(*outset) +
157 nextCW(metadata.fMask) * (*outset);
158 // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make edgeLen negative
159 // then use the slow path
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400160 V4f threshold = 0.1f - (1.f / metadata.fInvLengths);
161 return all(edgeAdjust > threshold) && all(edgeAdjust < -threshold);
Michael Ludwige6266a22019-03-07 11:24:32 -0500162}
163
164// Ignores the quad's fW, use outset_projected_vertices if it's known to need 3D.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400165static AI void outset_vertices(const V4f& outset, const QuadMetadata& metadata, Vertices* quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500166 // The mask is rotated compared to the outsets and edge vectors, since if the edge is "on"
167 // both its points need to be moved along their other edge vectors.
Michael Ludwige6266a22019-03-07 11:24:32 -0500168 auto maskedOutset = -outset * nextCW(metadata.fMask);
169 auto maskedOutsetCW = outset * metadata.fMask;
170 // x = x + outset * mask * nextCW(xdiff) - outset * nextCW(mask) * xdiff
171 quad->fX += fma(maskedOutsetCW, nextCW(metadata.fDX), maskedOutset * metadata.fDX);
172 quad->fY += fma(maskedOutsetCW, nextCW(metadata.fDY), maskedOutset * metadata.fDY);
173 if (quad->fUVRCount > 0) {
Michael Ludwigf995c052018-11-26 15:24:29 -0500174 // We want to extend the texture coords by the same proportion as the positions.
Michael Ludwige6266a22019-03-07 11:24:32 -0500175 maskedOutset *= metadata.fInvLengths;
176 maskedOutsetCW *= nextCW(metadata.fInvLengths);
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400177 V4f du = nextCCW(quad->fU) - quad->fU;
178 V4f dv = nextCCW(quad->fV) - quad->fV;
Michael Ludwige6266a22019-03-07 11:24:32 -0500179 quad->fU += fma(maskedOutsetCW, nextCW(du), maskedOutset * du);
180 quad->fV += fma(maskedOutsetCW, nextCW(dv), maskedOutset * dv);
181 if (quad->fUVRCount == 3) {
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400182 V4f dr = nextCCW(quad->fR) - quad->fR;
Michael Ludwige6266a22019-03-07 11:24:32 -0500183 quad->fR += fma(maskedOutsetCW, nextCW(dr), maskedOutset * dr);
Michael Ludwigf995c052018-11-26 15:24:29 -0500184 }
185 }
186}
187
Michael Ludwige6266a22019-03-07 11:24:32 -0500188// Updates (x,y,w) to be at (x2d,y2d) once projected. Updates (u,v,r) to match if provided.
189// Gracefully handles 2D content if *w holds all 1s.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400190static void outset_projected_vertices(const V4f& x2d, const V4f& y2d,
Michael Ludwige6266a22019-03-07 11:24:32 -0500191 GrQuadAAFlags aaFlags, Vertices* quad) {
192 // Left to right, in device space, for each point
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400193 V4f e1x = skvx::shuffle<2, 3, 2, 3>(quad->fX) - skvx::shuffle<0, 1, 0, 1>(quad->fX);
194 V4f e1y = skvx::shuffle<2, 3, 2, 3>(quad->fY) - skvx::shuffle<0, 1, 0, 1>(quad->fY);
195 V4f e1w = skvx::shuffle<2, 3, 2, 3>(quad->fW) - skvx::shuffle<0, 1, 0, 1>(quad->fW);
Michael Ludwige6266a22019-03-07 11:24:32 -0500196 correct_bad_edges(fma(e1x, e1x, e1y * e1y) < kTolerance * kTolerance, &e1x, &e1y, &e1w);
197
198 // // Top to bottom, in device space, for each point
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400199 V4f e2x = skvx::shuffle<1, 1, 3, 3>(quad->fX) - skvx::shuffle<0, 0, 2, 2>(quad->fX);
200 V4f e2y = skvx::shuffle<1, 1, 3, 3>(quad->fY) - skvx::shuffle<0, 0, 2, 2>(quad->fY);
201 V4f e2w = skvx::shuffle<1, 1, 3, 3>(quad->fW) - skvx::shuffle<0, 0, 2, 2>(quad->fW);
Michael Ludwige6266a22019-03-07 11:24:32 -0500202 correct_bad_edges(fma(e2x, e2x, e2y * e2y) < kTolerance * kTolerance, &e2x, &e2y, &e2w);
203
204 // Can only move along e1 and e2 to reach the new 2D point, so we have
205 // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and
206 // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b
207 // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where
208 // the cNx and cNy coefficients are:
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400209 V4f c1x = e1w * x2d - e1x;
210 V4f c1y = e1w * y2d - e1y;
211 V4f c2x = e2w * x2d - e2x;
212 V4f c2y = e2w * y2d - e2y;
213 V4f c3x = quad->fW * x2d - quad->fX;
214 V4f c3y = quad->fW * y2d - quad->fY;
Michael Ludwige6266a22019-03-07 11:24:32 -0500215
216 // Solve for a and b
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400217 V4f a, b, denom;
Michael Ludwige6266a22019-03-07 11:24:32 -0500218 if (aaFlags == GrQuadAAFlags::kAll) {
219 // When every edge is outset/inset, each corner can use both edge vectors
220 denom = c1x * c2y - c2x * c1y;
221 a = (c2x * c3y - c3x * c2y) / denom;
222 b = (c3x * c1y - c1x * c3y) / denom;
223 } else {
224 // Force a or b to be 0 if that edge cannot be used due to non-AA
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400225 M4f aMask = M4f{(aaFlags & GrQuadAAFlags::kLeft) ? kTrue : kFalse,
226 (aaFlags & GrQuadAAFlags::kLeft) ? kTrue : kFalse,
227 (aaFlags & GrQuadAAFlags::kRight) ? kTrue : kFalse,
228 (aaFlags & GrQuadAAFlags::kRight) ? kTrue : kFalse};
229 M4f bMask = M4f{(aaFlags & GrQuadAAFlags::kTop) ? kTrue : kFalse,
230 (aaFlags & GrQuadAAFlags::kBottom) ? kTrue : kFalse,
231 (aaFlags & GrQuadAAFlags::kTop) ? kTrue : kFalse,
232 (aaFlags & GrQuadAAFlags::kBottom) ? kTrue : kFalse};
Michael Ludwige6266a22019-03-07 11:24:32 -0500233
234 // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case.
235 // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom
236 // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, ""
237 // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400238 M4f useC1x = abs(c1x) > abs(c1y);
239 M4f useC2x = abs(c2x) > abs(c2y);
240
241 denom = if_then_else(aMask,
242 if_then_else(bMask,
243 c1x * c2y - c2x * c1y, /* A & B */
244 if_then_else(useC1x, c1x, c1y)), /* A & !B */
245 if_then_else(bMask,
246 if_then_else(useC2x, c2x, c2y), /* !A & B */
247 V4f(1.f))); /* !A & !B */
248
249 a = if_then_else(aMask,
250 if_then_else(bMask,
251 c2x * c3y - c3x * c2y, /* A & B */
252 if_then_else(useC1x, -c3x, -c3y)), /* A & !B */
253 V4f(0.f)) / denom; /* !A */
254 b = if_then_else(bMask,
255 if_then_else(aMask,
256 c3x * c1y - c1x * c3y, /* A & B */
257 if_then_else(useC2x, -c3x, -c3y)), /* !A & B */
258 V4f(0.f)) / denom; /* !B */
Michael Ludwige6266a22019-03-07 11:24:32 -0500259 }
260
Michael Ludwig784184a2019-04-30 13:28:26 -0400261 V4f newW = quad->fW + a * e1w + b * e2w;
262 // If newW < 0, scale a and b such that the point reaches the infinity plane instead of crossing
263 // This breaks orthogonality of inset/outsets, but GPUs don't handle negative Ws well so this
264 // is far less visually disturbing (likely not noticeable since it's at extreme perspective).
265 // The alternative correction (multiply xyw by -1) has the disadvantage of changing how local
266 // coordinates would be interpolated.
267 static const float kMinW = 1e-6f;
268 if (any(newW < 0.f)) {
269 V4f scale = if_then_else(newW < kMinW, (kMinW - quad->fW) / (newW - quad->fW), V4f(1.f));
270 a *= scale;
271 b *= scale;
272 }
273
Michael Ludwige6266a22019-03-07 11:24:32 -0500274 quad->fX += a * e1x + b * e2x;
275 quad->fY += a * e1y + b * e2y;
276 quad->fW += a * e1w + b * e2w;
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400277 correct_bad_coords(abs(denom) < kTolerance, &quad->fX, &quad->fY, &quad->fW);
Michael Ludwige6266a22019-03-07 11:24:32 -0500278
279 if (quad->fUVRCount > 0) {
280 // Calculate R here so it can be corrected with U and V in case it's needed later
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400281 V4f e1u = skvx::shuffle<2, 3, 2, 3>(quad->fU) - skvx::shuffle<0, 1, 0, 1>(quad->fU);
282 V4f e1v = skvx::shuffle<2, 3, 2, 3>(quad->fV) - skvx::shuffle<0, 1, 0, 1>(quad->fV);
283 V4f e1r = skvx::shuffle<2, 3, 2, 3>(quad->fR) - skvx::shuffle<0, 1, 0, 1>(quad->fR);
Michael Ludwige6266a22019-03-07 11:24:32 -0500284 correct_bad_edges(fma(e1u, e1u, e1v * e1v) < kTolerance * kTolerance, &e1u, &e1v, &e1r);
285
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400286 V4f e2u = skvx::shuffle<1, 1, 3, 3>(quad->fU) - skvx::shuffle<0, 0, 2, 2>(quad->fU);
287 V4f e2v = skvx::shuffle<1, 1, 3, 3>(quad->fV) - skvx::shuffle<0, 0, 2, 2>(quad->fV);
288 V4f e2r = skvx::shuffle<1, 1, 3, 3>(quad->fR) - skvx::shuffle<0, 0, 2, 2>(quad->fR);
Michael Ludwige6266a22019-03-07 11:24:32 -0500289 correct_bad_edges(fma(e2u, e2u, e2v * e2v) < kTolerance * kTolerance, &e2u, &e2v, &e2r);
290
291 quad->fU += a * e1u + b * e2u;
292 quad->fV += a * e1v + b * e2v;
293 if (quad->fUVRCount == 3) {
294 quad->fR += a * e1r + b * e2r;
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400295 correct_bad_coords(abs(denom) < kTolerance, &quad->fU, &quad->fV, &quad->fR);
Michael Ludwige6266a22019-03-07 11:24:32 -0500296 } else {
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400297 correct_bad_coords(abs(denom) < kTolerance, &quad->fU, &quad->fV, nullptr);
Michael Ludwigf995c052018-11-26 15:24:29 -0500298 }
299 }
300}
301
Michael Ludwiga7662062019-09-26 10:54:08 -0400302static V4f degenerate_coverage(const V4f& px, const V4f& py, const Edges& edges) {
303 // Calculate distance of the 4 inset points (px, py) to the 4 edges
304 V4f d0 = fma(edges.fA[0], px, fma(edges.fB[0], py, edges.fC[0]));
305 V4f d1 = fma(edges.fA[1], px, fma(edges.fB[1], py, edges.fC[1]));
306 V4f d2 = fma(edges.fA[2], px, fma(edges.fB[2], py, edges.fC[2]));
307 V4f d3 = fma(edges.fA[3], px, fma(edges.fB[3], py, edges.fC[3]));
Michael Ludwige6266a22019-03-07 11:24:32 -0500308
Michael Ludwiga7662062019-09-26 10:54:08 -0400309 // For each point, pretend that there's a rectangle that touches e0 and e3 on the horizontal
310 // axis, so its width is "approximately" d0 + d3, and it touches e1 and e2 on the vertical axis
311 // so its height is d1 + d2. Pin each of these dimensions to [0, 1] and approximate the coverage
312 // at each point as clamp(d0+d3, 0, 1) x clamp(d1+d2, 0, 1). For rectilinear quads this is an
313 // accurate calculation of its area clipped to an aligned pixel. For arbitrary quads it is not
314 // mathematically accurate but qualitatively provides a stable value proportional to the size of
315 // the shape.
316 V4f w = max(0.f, min(1.f, d0 + d3));
317 V4f h = max(0.f, min(1.f, d1 + d2));
318 return w * h;
Michael Ludwige6266a22019-03-07 11:24:32 -0500319}
320
321// Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
322// parallel, or edges are very short/zero-length. Returns coverage for each vertex.
323// Requires (dx, dy) to already be fixed for empty edges.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400324static V4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const V4f& mask, const Edges& edges,
Michael Ludwiga7662062019-09-26 10:54:08 -0400325 bool outset, Vertices* quad) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500326 // Move the edge 1/2 pixel in or out depending on 'outset'.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400327 V4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
Michael Ludwige6266a22019-03-07 11:24:32 -0500328
329 // There are 6 points that we care about to determine the final shape of the polygon, which
330 // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
331 // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400332 V4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
333 V4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
334 V4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
335 correct_bad_coords(abs(denom) < kTolerance, &px, &py, nullptr);
Michael Ludwige6266a22019-03-07 11:24:32 -0500336
337 // Calculate the signed distances from these 4 corners to the other two edges that did not
338 // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
339 // p(3) to e0,e2
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400340 V4f dists1 = px * skvx::shuffle<3, 3, 0, 0>(edges.fA) +
341 py * skvx::shuffle<3, 3, 0, 0>(edges.fB) +
342 skvx::shuffle<3, 3, 0, 0>(oc);
343 V4f dists2 = px * skvx::shuffle<1, 2, 1, 2>(edges.fA) +
344 py * skvx::shuffle<1, 2, 1, 2>(edges.fB) +
345 skvx::shuffle<1, 2, 1, 2>(oc);
Michael Ludwige6266a22019-03-07 11:24:32 -0500346
347 // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
348 // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
349 // and we need to use a central point to represent it. If all four points are only on the
350 // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
351 // Otherwise, use a triangle that replaces the bad points with the intersections of
352 // (e1, e2) or (e0, e3) as needed.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400353 M4f d1v0 = dists1 < kTolerance;
354 M4f d2v0 = dists2 < kTolerance;
355 M4f d1And2 = d1v0 & d2v0;
356 M4f d1Or2 = d1v0 | d2v0;
Michael Ludwige6266a22019-03-07 11:24:32 -0500357
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400358 V4f coverage;
359 if (!any(d1Or2)) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500360 // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
361 // and use full coverage
362 coverage = 1.f;
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400363 } else if (any(d1And2)) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500364 // A point failed against two edges, so reduce the shape to a single point, which we take as
365 // the center of the original quad to ensure it is contained in the intended geometry. Since
366 // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
367 SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
368 0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
Michael Ludwige6266a22019-03-07 11:24:32 -0500369 px = center.fX;
370 py = center.fY;
Michael Ludwiga7662062019-09-26 10:54:08 -0400371 coverage = degenerate_coverage(px, py, edges);
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400372 } else if (all(d1Or2)) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500373 // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
374 // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
375 if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
376 // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400377 px = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px));
378 py = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py));
Michael Ludwige6266a22019-03-07 11:24:32 -0500379 } else {
380 // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400381 px = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px));
382 py = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py));
Michael Ludwige6266a22019-03-07 11:24:32 -0500383 }
Michael Ludwiga7662062019-09-26 10:54:08 -0400384 coverage = degenerate_coverage(px, py, edges);
Michael Ludwige6266a22019-03-07 11:24:32 -0500385 } else {
386 // This turns into a triangle. Replace corners as needed with the intersections between
387 // (e0,e3) and (e1,e2), which must now be calculated
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400388 using V2f = skvx::Vec<2, float>;
389 V2f eDenom = skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(edges.fB) -
390 skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(edges.fA);
391 V2f ex = (skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(oc) -
392 skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fB)) / eDenom;
393 V2f ey = (skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fA) -
394 skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(oc)) / eDenom;
Michael Ludwige6266a22019-03-07 11:24:32 -0500395
396 if (SkScalarAbs(eDenom[0]) > kTolerance) {
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400397 px = if_then_else(d1v0, V4f(ex[0]), px);
398 py = if_then_else(d1v0, V4f(ey[0]), py);
Michael Ludwige6266a22019-03-07 11:24:32 -0500399 }
400 if (SkScalarAbs(eDenom[1]) > kTolerance) {
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400401 px = if_then_else(d2v0, V4f(ex[1]), px);
402 py = if_then_else(d2v0, V4f(ey[1]), py);
Michael Ludwige6266a22019-03-07 11:24:32 -0500403 }
404
405 coverage = 1.f;
406 }
407
408 outset_projected_vertices(px, py, aaFlags, quad);
409 return coverage;
Michael Ludwigf995c052018-11-26 15:24:29 -0500410}
411
Michael Ludwig93aeba02018-12-21 09:50:31 -0500412// Computes the vertices for the two nested quads used to create AA edges. The original single quad
Michael Ludwige6266a22019-03-07 11:24:32 -0500413// should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
414// stored in-place on return. Returns per-vertex coverage for the inner vertices.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400415static V4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400416 Vertices* inner, Vertices* outer, SkRect* domain) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500417 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
418 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400419
Michael Ludwige6266a22019-03-07 11:24:32 -0500420 QuadMetadata metadata = get_metadata(*inner, aaFlags);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400421
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400422 // Calculate domain first before updating vertices. It's only used when not rectilinear.
423 if (!rectilinear) {
424 SkASSERT(domain);
425 // The domain is the bounding box of the quad, outset by 0.5. Don't worry about edge masks
426 // since the FP only applies the domain on the exterior triangles, which are degenerate for
427 // non-AA edges.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400428 domain->fLeft = min(outer->fX) - 0.5f;
429 domain->fRight = max(outer->fX) + 0.5f;
430 domain->fTop = min(outer->fY) - 0.5f;
431 domain->fBottom = max(outer->fY) + 0.5f;
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400432 }
433
Michael Ludwig93aeba02018-12-21 09:50:31 -0500434 // 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 -0500435 // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
436 // is only computed if there are no empty edges, and it may signal going through the slow path.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400437 V4f outset = 0.5f;
Michael Ludwige6266a22019-03-07 11:24:32 -0500438 if (get_optimized_outset(metadata, rectilinear, &outset)) {
439 // Since it's not subpixel, outsetting and insetting are trivial vector additions.
440 outset_vertices(outset, metadata, outer);
441 outset_vertices(-outset, metadata, inner);
442 return 1.f;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400443 }
Michael Ludwig4921dc32018-12-03 14:57:29 +0000444
Michael Ludwige6266a22019-03-07 11:24:32 -0500445 // Only compute edge equations once since they are the same for inner and outer quads
446 Edges edges = get_edge_equations(metadata, *inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500447
Michael Ludwige6266a22019-03-07 11:24:32 -0500448 // Calculate both outset and inset, returning the coverage reported for the inset, since the
449 // outset will always have 0.0f.
450 compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
451 return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
Michael Ludwigf995c052018-11-26 15:24:29 -0500452}
453
Michael Ludwige6266a22019-03-07 11:24:32 -0500454// Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
455// division of the device coordinates, the original local coordinate value is at the original
456// un-outset device position.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400457static V4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400458 Vertices* outer, SkRect* domain) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500459 SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
460 SkASSERT(outer->fUVRCount == inner->fUVRCount);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500461
Michael Ludwige6266a22019-03-07 11:24:32 -0500462 // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400463 V4f iw = 1.0f / inner->fW;
464 V4f x2d = inner->fX * iw;
465 V4f y2d = inner->fY * iw;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500466
Michael Ludwige6266a22019-03-07 11:24:32 -0500467 Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
468 Vertices outer2D = inner2D;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400469
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400470 V4f coverage = compute_nested_quad_vertices(
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400471 aaFlags, /* rect */ false, &inner2D, &outer2D, domain);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400472
Michael Ludwige6266a22019-03-07 11:24:32 -0500473 // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
474 outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
475 outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500476
Michael Ludwige6266a22019-03-07 11:24:32 -0500477 return coverage;
Eric Boren98cb1592018-11-26 18:38:05 +0000478}
479
Michael Ludwig93aeba02018-12-21 09:50:31 -0500480enum class CoverageMode {
481 kNone,
482 kWithPosition,
483 kWithColor
484};
485
486static CoverageMode get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec& spec) {
487 if (spec.usesCoverageAA()) {
Michael Ludwig3d2753e2019-03-29 14:36:32 -0400488 if (spec.compatibleWithCoverageAsAlpha() && spec.hasVertexColors() &&
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400489 !spec.requiresGeometryDomain()) {
490 // Using a geometric domain acts as a second source of coverage and folding the original
491 // coverage into color makes it impossible to apply the color's alpha to the geometric
492 // domain's coverage when the original shape is clipped.
Michael Ludwig93aeba02018-12-21 09:50:31 -0500493 return CoverageMode::kWithColor;
494 } else {
495 return CoverageMode::kWithPosition;
496 }
Michael Ludwig553e9a92018-11-29 12:38:35 -0500497 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500498 return CoverageMode::kNone;
Michael Ludwig553e9a92018-11-29 12:38:35 -0500499 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500500}
Michael Ludwig553e9a92018-11-29 12:38:35 -0500501
Michael Ludwig93aeba02018-12-21 09:50:31 -0500502// Writes four vertices in triangle strip order, including the additional data for local
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400503// coordinates, geometry + texture domains, color, and coverage as needed to satisfy the vertex spec
Michael Ludwig93aeba02018-12-21 09:50:31 -0500504static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400505 CoverageMode mode, const V4f& coverage, SkPMColor4f color4f,
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400506 const SkRect& geomDomain, const SkRect& texDomain, const Vertices& quad) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500507 static constexpr auto If = GrVertexWriter::If<float>;
508
Michael Ludwig553e9a92018-11-29 12:38:35 -0500509 for (int i = 0; i < 4; ++i) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500510 // save position, this is a float2 or float3 or float4 depending on the combination of
511 // perspective and coverage mode.
Michael Ludwige6266a22019-03-07 11:24:32 -0500512 vb->write(quad.fX[i], quad.fY[i],
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400513 If(spec.deviceQuadType() == GrQuad::Type::kPerspective, quad.fW[i]),
Michael Ludwige6266a22019-03-07 11:24:32 -0500514 If(mode == CoverageMode::kWithPosition, coverage[i]));
Michael Ludwig4921dc32018-12-03 14:57:29 +0000515
Michael Ludwig93aeba02018-12-21 09:50:31 -0500516 // save color
517 if (spec.hasVertexColors()) {
Brian Salomon1d835422019-03-13 16:11:44 -0400518 bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
Michael Ludwige6266a22019-03-07 11:24:32 -0500519 vb->write(GrVertexColor(
Brian Salomon1d835422019-03-13 16:11:44 -0400520 color4f * (mode == CoverageMode::kWithColor ? coverage[i] : 1.f), wide));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500521 }
522
523 // save local position
524 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500525 vb->write(quad.fU[i], quad.fV[i],
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400526 If(spec.localQuadType() == GrQuad::Type::kPerspective, quad.fR[i]));
Michael Ludwig93aeba02018-12-21 09:50:31 -0500527 }
528
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400529 // save the geometry domain
530 if (spec.requiresGeometryDomain()) {
531 vb->write(geomDomain);
532 }
533
534 // save the texture domain
Michael Ludwig93aeba02018-12-21 09:50:31 -0500535 if (spec.hasDomain()) {
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400536 vb->write(texDomain);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500537 }
538 }
539}
540
541GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
542
543static const int kVertsPerAAFillRect = 8;
544static const int kIndicesPerAAFillRect = 30;
545
Brian Salomondbf70722019-02-07 11:31:24 -0500546static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500547 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
548
549 // clang-format off
550 static const uint16_t gFillAARectIdx[] = {
551 0, 1, 2, 1, 3, 2,
552 0, 4, 1, 4, 5, 1,
553 0, 6, 4, 0, 2, 6,
554 2, 3, 6, 3, 7, 6,
555 1, 5, 3, 3, 5, 7,
556 };
557 // clang-format on
558
559 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
560 return resourceProvider->findOrCreatePatternedIndexBuffer(
561 gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
562 kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
Michael Ludwig553e9a92018-11-29 12:38:35 -0500563}
564
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400565} // anonymous namespace
566
Michael Ludwigc182b942018-11-16 10:27:51 -0500567namespace GrQuadPerEdgeAA {
568
Brian Osman8fa7ab42019-03-18 10:22:42 -0400569// This is a more elaborate version of SkPMColor4fNeedsWideColor that allows "no color" for white
570ColorType MinColorType(SkPMColor4f color, GrClampType clampType, const GrCaps& caps) {
Brian Salomon1d835422019-03-13 16:11:44 -0400571 if (color == SK_PMColor4fWHITE) {
572 return ColorType::kNone;
Brian Salomon1d835422019-03-13 16:11:44 -0400573 } else {
Brian Osman8fa7ab42019-03-18 10:22:42 -0400574 return SkPMColor4fNeedsWideColor(color, clampType, caps) ? ColorType::kHalf
575 : ColorType::kByte;
Brian Salomon1d835422019-03-13 16:11:44 -0400576 }
577}
578
Michael Ludwigc182b942018-11-16 10:27:51 -0500579////////////////// Tessellate Implementation
580
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400581void* Tessellate(void* vertices, const VertexSpec& spec, const GrQuad& deviceQuad,
582 const SkPMColor4f& color4f, const GrQuad& localQuad, const SkRect& domain,
Michael Ludwigc182b942018-11-16 10:27:51 -0500583 GrQuadAAFlags aaFlags) {
Michael Ludwig41f395d2019-05-23 13:59:45 -0400584 SkASSERT(deviceQuad.quadType() <= spec.deviceQuadType());
585 SkASSERT(!spec.hasLocalCoords() || localQuad.quadType() <= spec.localQuadType());
586
Michael Ludwig93aeba02018-12-21 09:50:31 -0500587 CoverageMode mode = get_mode_for_spec(spec);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400588
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400589 // Load position data into V4fs (always x, y, and load w to avoid branching down the road)
Michael Ludwige6266a22019-03-07 11:24:32 -0500590 Vertices outer;
591 outer.fX = deviceQuad.x4f();
592 outer.fY = deviceQuad.y4f();
593 outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400594
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400595 // Load local position data into V4fs (either none, just u,v or all three)
Michael Ludwige6266a22019-03-07 11:24:32 -0500596 outer.fUVRCount = spec.localDimensionality();
Michael Ludwigc182b942018-11-16 10:27:51 -0500597 if (spec.hasLocalCoords()) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500598 outer.fU = localQuad.x4f();
599 outer.fV = localQuad.y4f();
600 outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400601 }
602
Michael Ludwigc182b942018-11-16 10:27:51 -0500603 GrVertexWriter vb{vertices};
Michael Ludwig93aeba02018-12-21 09:50:31 -0500604 if (spec.usesCoverageAA()) {
605 SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500606 // Must calculate two new quads, an outset and inset by .5 in projected device space, so
Michael Ludwige6266a22019-03-07 11:24:32 -0500607 // duplicate the original quad for the inner space
608 Vertices inner = outer;
Michael Ludwigc182b942018-11-16 10:27:51 -0500609
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400610 SkRect geomDomain;
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400611 V4f maxCoverage = 1.f;
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400612 if (spec.deviceQuadType() == GrQuad::Type::kPerspective) {
Michael Ludwige6266a22019-03-07 11:24:32 -0500613 // For perspective, send quads with all edges non-AA through the tessellation to ensure
614 // their corners are processed the same as adjacent quads. This approach relies on
615 // solving edge equations to reconstruct corners, which can create seams if an inner
616 // fully non-AA quad is not similarly processed.
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400617 maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer, &geomDomain);
Michael Ludwige6266a22019-03-07 11:24:32 -0500618 } else if (aaFlags != GrQuadAAFlags::kNone) {
619 // In 2D, the simpler corner math does not cause issues with seaming against non-AA
620 // inner quads.
621 maxCoverage = compute_nested_quad_vertices(
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400622 aaFlags, spec.deviceQuadType() <= GrQuad::Type::kRectilinear, &inner, &outer,
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400623 &geomDomain);
Michael Ludwig25071cc2019-04-12 09:23:22 -0400624 } else if (spec.requiresGeometryDomain()) {
625 // The quad itself wouldn't need a geometric domain, but the batch does, so set the
626 // domain to the bounds of the X/Y coords. Since it's non-AA, this won't actually be
627 // evaluated by the shader, but make sure not to upload uninitialized data.
Michael Ludwigb3461fa2019-04-30 11:50:55 -0400628 geomDomain.fLeft = min(outer.fX);
629 geomDomain.fRight = max(outer.fX);
630 geomDomain.fTop = min(outer.fY);
631 geomDomain.fBottom = max(outer.fY);
Michael Ludwige6266a22019-03-07 11:24:32 -0500632 }
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400633
Michael Ludwig93aeba02018-12-21 09:50:31 -0500634 // Write two quads for inner and outer, inner will use the
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400635 write_quad(&vb, spec, mode, maxCoverage, color4f, geomDomain, domain, inner);
636 write_quad(&vb, spec, mode, 0.f, color4f, geomDomain, domain, outer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500637 } else {
638 // No outsetting needed, just write a single quad with full coverage
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400639 SkASSERT(mode == CoverageMode::kNone && !spec.requiresGeometryDomain());
640 write_quad(&vb, spec, mode, 1.f, color4f, SkRect::MakeEmpty(), domain, outer);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400641 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500642
643 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400644}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400645
Michael Ludwig93aeba02018-12-21 09:50:31 -0500646bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
647 int quadCount) {
648 if (spec.usesCoverageAA()) {
649 // AA quads use 8 vertices, basically nested rectangles
Brian Salomondbf70722019-02-07 11:31:24 -0500650 sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
Michael Ludwig93aeba02018-12-21 09:50:31 -0500651 if (!ibuffer) {
652 return false;
653 }
654
655 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500656 mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
657 quadCount, kNumAAQuadsInIndexBuffer);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500658 } else {
659 // Non-AA quads use 4 vertices, and regular triangle strip layout
660 if (quadCount > 1) {
Brian Salomondbf70722019-02-07 11:31:24 -0500661 sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
Michael Ludwig93aeba02018-12-21 09:50:31 -0500662 if (!ibuffer) {
663 return false;
664 }
665
666 mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -0500667 mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500668 GrResourceProvider::QuadCountOfQuadBuffer());
669 } else {
670 mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
671 mesh->setNonIndexedNonInstanced(4);
672 }
673 }
674
675 return true;
676}
677
Michael Ludwigc182b942018-11-16 10:27:51 -0500678////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400679
Michael Ludwigc182b942018-11-16 10:27:51 -0500680int VertexSpec::deviceDimensionality() const {
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400681 return this->deviceQuadType() == GrQuad::Type::kPerspective ? 3 : 2;
Michael Ludwigc182b942018-11-16 10:27:51 -0500682}
683
684int VertexSpec::localDimensionality() const {
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400685 return fHasLocalCoords ? (this->localQuadType() == GrQuad::Type::kPerspective ? 3 : 2) : 0;
Michael Ludwigc182b942018-11-16 10:27:51 -0500686}
687
Michael Ludwig467994d2018-12-03 14:58:31 +0000688////////////////// Geometry Processor Implementation
Michael Ludwigc182b942018-11-16 10:27:51 -0500689
Michael Ludwig467994d2018-12-03 14:58:31 +0000690class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
691public:
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400692 using Saturate = GrTextureOp::Saturate;
Michael Ludwig20e909e2018-10-30 10:43:57 -0400693
Michael Ludwig467994d2018-12-03 14:58:31 +0000694 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
695 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400696 }
697
Michael Ludwig467994d2018-12-03 14:58:31 +0000698 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
Brian Salomon67529b22019-08-13 15:31:04 -0400699 GrTextureType textureType,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500700 const GrSamplerState& samplerState,
Greg Daniel2c3398d2019-06-19 11:58:01 -0400701 const GrSwizzle& swizzle, uint32_t extraSamplerKey,
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400702 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
703 Saturate saturate) {
Michael Ludwig467994d2018-12-03 14:58:31 +0000704 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
Brian Salomon67529b22019-08-13 15:31:04 -0400705 vertexSpec, caps, textureType, samplerState, swizzle, extraSamplerKey,
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400706 std::move(textureColorSpaceXform), saturate));
Michael Ludwig20e909e2018-10-30 10:43:57 -0400707 }
708
Michael Ludwig467994d2018-12-03 14:58:31 +0000709 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
Michael Ludwig024e2622018-11-30 13:25:55 -0500710
Michael Ludwig467994d2018-12-03 14:58:31 +0000711 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400712 // texturing, device-dimensions are single bit flags
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400713 uint32_t x = (fTexDomain.isInitialized() ? 0 : 0x1)
714 | (fSampler.isInitialized() ? 0 : 0x2)
715 | (fNeedsPerspective ? 0 : 0x4)
716 | (fSaturate == Saturate::kNo ? 0 : 0x8);
Michael Ludwig467994d2018-12-03 14:58:31 +0000717 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
718 if (fLocalCoord.isInitialized()) {
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400719 x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 0x10 : 0x20;
Brian Osman78dc72c2018-12-03 13:20:43 +0000720 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000721 // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
Michael Ludwig93aeba02018-12-21 09:50:31 -0500722 if (fColor.isInitialized()) {
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400723 x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 0x40 : 0x80;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500724 }
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400725 // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor, 11 for
726 // position+geomdomain
727 SkASSERT(!fGeomDomain.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500728 if (fCoverageMode != CoverageMode::kNone) {
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400729 x |= fGeomDomain.isInitialized()
730 ? 0x300
731 : (CoverageMode::kWithPosition == fCoverageMode ? 0x100 : 0x200);
Michael Ludwig467994d2018-12-03 14:58:31 +0000732 }
733
734 b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
735 b->add32(x);
Brian Osman78dc72c2018-12-03 13:20:43 +0000736 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000737
738 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
739 class GLSLProcessor : public GrGLSLGeometryProcessor {
740 public:
741 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
742 FPCoordTransformIter&& transformIter) override {
743 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
744 if (gp.fLocalCoord.isInitialized()) {
745 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
746 }
747 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
748 }
749
750 private:
751 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
752 using Interpolation = GrGLSLVaryingHandler::Interpolation;
753
754 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>();
755 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler,
756 gp.fTextureColorSpaceXform.get());
757
758 args.fVaryingHandler->emitAttributes(gp);
759
Michael Ludwig93aeba02018-12-21 09:50:31 -0500760 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
761 // Strip last channel from the vertex attribute to remove coverage and get the
762 // actual position
763 if (gp.fNeedsPerspective) {
764 args.fVertBuilder->codeAppendf("float3 position = %s.xyz;",
765 gp.fPosition.name());
766 } else {
767 args.fVertBuilder->codeAppendf("float2 position = %s.xy;",
768 gp.fPosition.name());
769 }
770 gpArgs->fPositionVar = {"position",
771 gp.fNeedsPerspective ? kFloat3_GrSLType
772 : kFloat2_GrSLType,
773 GrShaderVar::kNone_TypeModifier};
Michael Ludwig467994d2018-12-03 14:58:31 +0000774 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500775 // No coverage to eliminate
776 gpArgs->fPositionVar = gp.fPosition.asShaderVar();
Michael Ludwig467994d2018-12-03 14:58:31 +0000777 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000778
779 // Handle local coordinates if they exist
780 if (gp.fLocalCoord.isInitialized()) {
781 // NOTE: If the only usage of local coordinates is for the inline texture fetch
782 // before FPs, then there are no registered FPCoordTransforms and this ends up
783 // emitting nothing, so there isn't a duplication of local coordinates
784 this->emitTransforms(args.fVertBuilder,
785 args.fVaryingHandler,
786 args.fUniformHandler,
787 gp.fLocalCoord.asShaderVar(),
788 args.fFPCoordTransformHandler);
789 }
790
791 // Solid color before any texturing gets modulated in
792 if (gp.fColor.isInitialized()) {
Michael Ludwig3d2753e2019-03-29 14:36:32 -0400793 SkASSERT(gp.fCoverageMode != CoverageMode::kWithColor || !gp.fNeedsPerspective);
Michael Ludwig93aeba02018-12-21 09:50:31 -0500794 // The color cannot be flat if the varying coverage has been modulated into it
Michael Ludwig467994d2018-12-03 14:58:31 +0000795 args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
Michael Ludwig93aeba02018-12-21 09:50:31 -0500796 gp.fCoverageMode == CoverageMode::kWithColor ?
797 Interpolation::kInterpolated : Interpolation::kCanBeFlat);
798 } else {
799 // Output color must be initialized to something
800 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
Michael Ludwig467994d2018-12-03 14:58:31 +0000801 }
802
803 // If there is a texture, must also handle texture coordinates and reading from
804 // the texture in the fragment shader before continuing to fragment processors.
805 if (gp.fSampler.isInitialized()) {
806 // Texture coordinates clamped by the domain on the fragment shader; if the GP
807 // has a texture, it's guaranteed to have local coordinates
808 args.fFragBuilder->codeAppend("float2 texCoord;");
809 if (gp.fLocalCoord.cpuType() == kFloat3_GrVertexAttribType) {
810 // Can't do a pass through since we need to perform perspective division
811 GrGLSLVarying v(gp.fLocalCoord.gpuType());
812 args.fVaryingHandler->addVarying(gp.fLocalCoord.name(), &v);
813 args.fVertBuilder->codeAppendf("%s = %s;",
814 v.vsOut(), gp.fLocalCoord.name());
815 args.fFragBuilder->codeAppendf("texCoord = %s.xy / %s.z;",
816 v.fsIn(), v.fsIn());
817 } else {
818 args.fVaryingHandler->addPassThroughAttribute(gp.fLocalCoord, "texCoord");
819 }
820
821 // Clamp the now 2D localCoordName variable by the domain if it is provided
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400822 if (gp.fTexDomain.isInitialized()) {
Michael Ludwig467994d2018-12-03 14:58:31 +0000823 args.fFragBuilder->codeAppend("float4 domain;");
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400824 args.fVaryingHandler->addPassThroughAttribute(gp.fTexDomain, "domain",
Michael Ludwig467994d2018-12-03 14:58:31 +0000825 Interpolation::kCanBeFlat);
826 args.fFragBuilder->codeAppend(
827 "texCoord = clamp(texCoord, domain.xy, domain.zw);");
828 }
829
830 // Now modulate the starting output color by the texture lookup
831 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
832 args.fFragBuilder->appendTextureLookupAndModulate(
833 args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
834 &fTextureColorSpaceXformHelper);
835 args.fFragBuilder->codeAppend(";");
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400836 if (gp.fSaturate == Saturate::kYes) {
837 args.fFragBuilder->codeAppendf("%s = saturate(%s);",
838 args.fOutputColor, args.fOutputColor);
839 }
840 } else {
841 // Saturate is only intended for use with a proxy to account for the fact
842 // that GrTextureOp skips SkPaint conversion, which normally handles this.
843 SkASSERT(gp.fSaturate == Saturate::kNo);
Michael Ludwig467994d2018-12-03 14:58:31 +0000844 }
845
846 // And lastly, output the coverage calculation code
Michael Ludwig93aeba02018-12-21 09:50:31 -0500847 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
848 GrGLSLVarying coverage(kFloat_GrSLType);
849 args.fVaryingHandler->addVarying("coverage", &coverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000850 if (gp.fNeedsPerspective) {
Michael Ludwig3d2753e2019-03-29 14:36:32 -0400851 // Multiply by "W" in the vertex shader, then by 1/w (sk_FragCoord.w) in
852 // the fragment shader to get screen-space linear coverage.
853 args.fVertBuilder->codeAppendf("%s = %s.w * %s.z;",
854 coverage.vsOut(), gp.fPosition.name(),
855 gp.fPosition.name());
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400856 args.fFragBuilder->codeAppendf("float coverage = %s * sk_FragCoord.w;",
857 coverage.fsIn());
Michael Ludwig93aeba02018-12-21 09:50:31 -0500858 } else {
Jim Van Verthd5d9c212019-05-02 10:22:10 -0400859 args.fVertBuilder->codeAppendf("%s = %s;",
860 coverage.vsOut(), gp.fCoverage.name());
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400861 args.fFragBuilder->codeAppendf("float coverage = %s;", coverage.fsIn());
Michael Ludwig467994d2018-12-03 14:58:31 +0000862 }
Michael Ludwig93aeba02018-12-21 09:50:31 -0500863
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400864 if (gp.fGeomDomain.isInitialized()) {
865 // Calculate distance from sk_FragCoord to the 4 edges of the domain
866 // and clamp them to (0, 1). Use the minimum of these and the original
867 // coverage. This only has to be done in the exterior triangles, the
868 // interior of the quad geometry can never be clipped by the domain box.
869 args.fFragBuilder->codeAppend("float4 geoDomain;");
870 args.fVaryingHandler->addPassThroughAttribute(gp.fGeomDomain, "geoDomain",
871 Interpolation::kCanBeFlat);
872 args.fFragBuilder->codeAppend(
873 "if (coverage < 0.5) {"
874 " float4 dists4 = clamp(float4(1, 1, -1, -1) * "
875 "(sk_FragCoord.xyxy - geoDomain), 0, 1);"
876 " float2 dists2 = dists4.xy * dists4.zw;"
877 " coverage = min(coverage, dists2.x * dists2.y);"
878 "}");
879 }
880
881 args.fFragBuilder->codeAppendf("%s = half4(half(coverage));",
882 args.fOutputCoverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000883 } else {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500884 // Set coverage to 1, since it's either non-AA or the coverage was already
885 // folded into the output color
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400886 SkASSERT(!gp.fGeomDomain.isInitialized());
Ethan Nicholase1f55022019-02-05 17:17:40 -0500887 args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
Michael Ludwig467994d2018-12-03 14:58:31 +0000888 }
889 }
890 GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
891 };
892 return new GLSLProcessor;
893 }
894
895private:
896 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
897 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
898 , fTextureColorSpaceXform(nullptr) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500899 SkASSERT(!spec.hasDomain());
Michael Ludwig467994d2018-12-03 14:58:31 +0000900 this->initializeAttrs(spec);
901 this->setTextureSamplerCnt(0);
902 }
903
Brian Salomon67529b22019-08-13 15:31:04 -0400904 QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec,
905 const GrShaderCaps& caps,
906 GrTextureType textureType,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500907 const GrSamplerState& samplerState,
Greg Daniel2c3398d2019-06-19 11:58:01 -0400908 const GrSwizzle& swizzle,
Greg Daniel7a82edf2018-12-04 10:54:34 -0500909 uint32_t extraSamplerKey,
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400910 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
911 Saturate saturate)
Michael Ludwig467994d2018-12-03 14:58:31 +0000912 : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400913 , fSaturate(saturate)
Michael Ludwig467994d2018-12-03 14:58:31 +0000914 , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
Brian Salomon67529b22019-08-13 15:31:04 -0400915 , fSampler(textureType, samplerState, swizzle, extraSamplerKey) {
Michael Ludwig93aeba02018-12-21 09:50:31 -0500916 SkASSERT(spec.hasLocalCoords());
Michael Ludwig467994d2018-12-03 14:58:31 +0000917 this->initializeAttrs(spec);
918 this->setTextureSamplerCnt(1);
919 }
920
921 void initializeAttrs(const VertexSpec& spec) {
922 fNeedsPerspective = spec.deviceDimensionality() == 3;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500923 fCoverageMode = get_mode_for_spec(spec);
924
925 if (fCoverageMode == CoverageMode::kWithPosition) {
926 if (fNeedsPerspective) {
927 fPosition = {"positionWithCoverage", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
928 } else {
Jim Van Verthd5d9c212019-05-02 10:22:10 -0400929 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
930 fCoverage = {"coverage", kFloat_GrVertexAttribType, kFloat_GrSLType};
Michael Ludwig93aeba02018-12-21 09:50:31 -0500931 }
932 } else {
933 if (fNeedsPerspective) {
934 fPosition = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
935 } else {
936 fPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
937 }
938 }
Michael Ludwig467994d2018-12-03 14:58:31 +0000939
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400940 // Need a geometry domain when the quads are AA and not rectilinear, since their AA
941 // outsetting can go beyond a half pixel.
942 if (spec.requiresGeometryDomain()) {
943 fGeomDomain = {"geomDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
944 }
945
Michael Ludwig467994d2018-12-03 14:58:31 +0000946 int localDim = spec.localDimensionality();
947 if (localDim == 3) {
948 fLocalCoord = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
949 } else if (localDim == 2) {
950 fLocalCoord = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
951 } // else localDim == 0 and attribute remains uninitialized
952
953 if (ColorType::kByte == spec.colorType()) {
954 fColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
955 } else if (ColorType::kHalf == spec.colorType()) {
956 fColor = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
957 }
958
959 if (spec.hasDomain()) {
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400960 fTexDomain = {"texDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Michael Ludwig467994d2018-12-03 14:58:31 +0000961 }
962
Jim Van Verthd5d9c212019-05-02 10:22:10 -0400963 this->setVertexAttributes(&fPosition, 6);
Michael Ludwig467994d2018-12-03 14:58:31 +0000964 }
965
966 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
967
Michael Ludwig93aeba02018-12-21 09:50:31 -0500968 Attribute fPosition; // May contain coverage as last channel
Jim Van Verthd5d9c212019-05-02 10:22:10 -0400969 Attribute fCoverage; // Used for non-perspective position to avoid Intel Metal issues
Michael Ludwig93aeba02018-12-21 09:50:31 -0500970 Attribute fColor; // May have coverage modulated in if the FPs support it
Michael Ludwig467994d2018-12-03 14:58:31 +0000971 Attribute fLocalCoord;
Michael Ludwigdcfbe322019-04-01 14:55:54 -0400972 Attribute fGeomDomain; // Screen-space bounding box on geometry+aa outset
973 Attribute fTexDomain; // Texture-space bounding box on local coords
Michael Ludwig467994d2018-12-03 14:58:31 +0000974
Michael Ludwig93aeba02018-12-21 09:50:31 -0500975 // The positions attribute may have coverage built into it, so float3 is an ambiguous type
976 // and may mean 2d with coverage, or 3d with no coverage
Michael Ludwig467994d2018-12-03 14:58:31 +0000977 bool fNeedsPerspective;
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400978 // Should saturate() be called on the color? Only relevant when created with a texture.
979 Saturate fSaturate = Saturate::kNo;
Michael Ludwig93aeba02018-12-21 09:50:31 -0500980 CoverageMode fCoverageMode;
Michael Ludwig467994d2018-12-03 14:58:31 +0000981
982 // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
983 // to skip texturing.
984 sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
985 TextureSampler fSampler;
986
987 typedef GrGeometryProcessor INHERITED;
988};
989
990sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
991 return QuadPerEdgeAAGeometryProcessor::Make(spec);
992}
993
994sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
Brian Salomon67529b22019-08-13 15:31:04 -0400995 GrTextureType textureType,
996 const GrSamplerState& samplerState,
997 const GrSwizzle& swizzle, uint32_t extraSamplerKey,
Brian Salomonf19f9ca2019-09-18 15:54:26 -0400998 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
999 Saturate saturate) {
Brian Salomon67529b22019-08-13 15:31:04 -04001000 return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, samplerState, swizzle,
Brian Salomonf19f9ca2019-09-18 15:54:26 -04001001 extraSamplerKey, std::move(textureColorSpaceXform),
1002 saturate);
Michael Ludwig20e909e2018-10-30 10:43:57 -04001003}
Michael Ludwigc182b942018-11-16 10:27:51 -05001004
1005} // namespace GrQuadPerEdgeAA