1 /*
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"
10 #include "GrVertexWriter.h"
11 #include "glsl/GrGLSLColorSpaceXformHelper.h"
12 #include "glsl/GrGLSLGeometryProcessor.h"
13 #include "glsl/GrGLSLPrimitiveProcessor.h"
14 #include "glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "glsl/GrGLSLVarying.h"
16 #include "glsl/GrGLSLVertexGeoBuilder.h"
17 #include "SkNx.h"
18 
19 #define AI SK_ALWAYS_INLINE
20 
21 namespace {
22 
fma(const Sk4f & f,const Sk4f & m,const Sk4f & a)23 static 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.
nextCW(const Sk4f & v)29 static AI Sk4f nextCW(const Sk4f& v) {
30     return SkNx_shuffle<2, 0, 3, 1>(v);
31 }
32 
nextCCW(const Sk4f & v)33 static 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.
compute_edge_mask(GrQuadAAFlags aaFlags)39 static 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 
46 // Outputs normalized edge vectors in xdiff and ydiff, as well as the reciprocal of the original
47 // edge lengths in invLengths
compute_edge_vectors(const Sk4f & x,const Sk4f & y,const Sk4f & xnext,const Sk4f & ynext,Sk4f * xdiff,Sk4f * ydiff,Sk4f * invLengths)48 static 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 
57 // 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.
outset_masked_vertices(const Sk4f & outset,const Sk4f & outsetCW,const Sk4f & xdiff,const Sk4f & ydiff,const Sk4f & invLengths,const Sk4f & mask,Sk4f * x,Sk4f * y,Sk4f * u,Sk4f * v,Sk4f * r,int uvrCount)60 static 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);
70     if (uvrCount > 0) {
71         // We want to extend the texture coords by the same proportion as the positions.
72         maskedOutset *= invLengths;
73         maskedOutsetCW *= nextCW(invLengths);
74         Sk4f udiff = nextCCW(*u) - *u;
75         Sk4f vdiff = nextCCW(*v) - *v;
76         *u += fma(maskedOutsetCW, nextCW(udiff), maskedOutset * udiff);
77         *v += fma(maskedOutsetCW, nextCW(vdiff), maskedOutset * vdiff);
78         if (uvrCount == 3) {
79             Sk4f rdiff = nextCCW(*r) - *r;
80             *r += fma(maskedOutsetCW, nextCW(rdiff), maskedOutset * rdiff);
81         }
82     }
83 }
84 
outset_vertices(const Sk4f & outset,const Sk4f & outsetCW,const Sk4f & xdiff,const Sk4f & ydiff,const Sk4f & invLengths,Sk4f * x,Sk4f * y,Sk4f * u,Sk4f * v,Sk4f * r,int uvrCount)85 static AI void outset_vertices(const Sk4f& outset, const Sk4f& outsetCW, const Sk4f& xdiff,
86                                const Sk4f& ydiff, const Sk4f& invLengths,
87                                Sk4f* x, Sk4f* y, Sk4f* u, Sk4f* v, Sk4f* r, int uvrCount) {
88     // 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);
91     if (uvrCount > 0) {
92         Sk4f t = -outset * invLengths; // Bake minus sign in here
93         Sk4f tCW = outsetCW * nextCW(invLengths);
94         Sk4f udiff = nextCCW(*u) - *u;
95         Sk4f vdiff = nextCCW(*v) - *v;
96         *u += fma(tCW, nextCW(udiff), t * udiff);
97         *v += fma(tCW, nextCW(vdiff), t * vdiff);
98         if (uvrCount == 3) {
99             Sk4f rdiff = nextCCW(*r) - *r;
100             *r += fma(tCW, nextCW(rdiff), t * rdiff);
101         }
102     }
103 }
104 
105 // 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).
adjust_non_rectilinear_outset(const Sk4f & xdiff,const Sk4f & ydiff,Sk4f * outset)107 static 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));
114 }
115 
116 // 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.
compute_nested_quad_vertices(GrQuadAAFlags aaFlags,Sk4f * x1,Sk4f * y1,Sk4f * u1,Sk4f * v1,Sk4f * r1,Sk4f * x2,Sk4f * y2,Sk4f * u2,Sk4f * v2,Sk4f * r2,int uvrCount,bool rectilinear)121 static 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);
125 
126     // Compute edge vectors for the quad.
127     auto xnext = nextCCW(*x1);
128     auto ynext = nextCCW(*y1);
129     // xdiff and ydiff will comprise the normalized vectors pointing along each quad edge.
130     Sk4f xdiff, ydiff, invLengths;
131     compute_edge_vectors(*x1, *y1, xnext, ynext, &xdiff, &ydiff, &invLengths);
132 
133     // 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);
138     }
139 
140     // 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.
143     Sk4f lengths = invLengths.invert();
144     Sk2f sides(SkMinScalar(lengths[0], lengths[3]), SkMinScalar(lengths[1], lengths[2]));
145     Sk4f edgeLimits = 0.5f * SkNx_shuffle<0, 1, 1, 0>(sides);
146 
147     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));
152 
153         // 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;
159 
160         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);
169         }
170     } else {
171         // 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         }
184     }
185 
186     // An approximation of the pixel area covered by the quad
187     sides = Sk2f::Min(1.f, sides);
188     return sides[0] * sides[1];
189 }
190 
191 // 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.
get_projected_interpolant(const Sk4f & len,const Sk4f & outsets,float * maxCoverage)195 static 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
212 // after perspective division of the device coordinate, the original local coordinate value is at
213 // 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.
compute_quad_persp_vertices(GrQuadAAFlags aaFlags,Sk4f * x,Sk4f * y,Sk4f * w,Sk4f * u,Sk4f * v,Sk4f * r,int uvrCount,bool inset)216 static 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);
219 
220     auto iw = (*w).invert();
221     auto x2d = (*x) * iw;
222     auto y2d = (*y) * iw;
223 
224     // 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 
232     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);
240         Sk4f len = SkNx_shuffle<0, 1, 0, 1>(SkNx_fma(vx, vx, vy * vy).sqrt());
241 
242         // Compute t in homogeneous space from s using similar triangles so that we can produce
243         // homogeneous outset vertices for perspective-correct interpolation.
244         Sk4f s = get_projected_interpolant(len, outset, &maxProjectedCoverage);
245         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 
257         if (uvrCount > 0) {
258             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);
262             if (uvrCount == 3) {
263                 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);
284         Sk4f len = SkNx_shuffle<0, 0, 1, 1>(SkNx_fma(vx, vx, vy * vy).sqrt());
285 
286         Sk4f s = get_projected_interpolant(len, outset, &maxProjectedCoverage);
287         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 
299         if (uvrCount > 0) {
300             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);
304             if (uvrCount == 3) {
305                 Sk4f opR = SkNx_shuffle<1, 0, 3, 2>(*r);
306                 *r = opR + t * (*r - opR);
307             }
308         }
309     }
310 
311     return maxProjectedCoverage;
312 }
313 
314 enum class CoverageMode {
315     kNone,
316     kWithPosition,
317     kWithColor
318 };
319 
get_mode_for_spec(const GrQuadPerEdgeAA::VertexSpec & spec)320 static 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         }
327     } else {
328         return CoverageMode::kNone;
329     }
330 }
331 
332 // 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.
write_quad(GrVertexWriter * vb,const GrQuadPerEdgeAA::VertexSpec & spec,CoverageMode mode,float coverage,SkPMColor4f color4f,bool wideColor,const SkRect & domain,const Sk4f & x,const Sk4f & y,const Sk4f & w,const Sk4f & u,const Sk4f & v,const Sk4f & r)334 static 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 
349     for (int i = 0; i < 4; ++i) {
350         // 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));
354 
355         // 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 
372 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
373 
374 static const int kVertsPerAAFillRect = 8;
375 static const int kIndicesPerAAFillRect = 30;
376 
get_index_buffer(GrResourceProvider * resourceProvider)377 static sk_sp<const GrBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
378     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);
394 }
395 
396 } // anonymous namespace
397 
398 namespace GrQuadPerEdgeAA {
399 
400 ////////////////// Tessellate Implementation
401 
Tessellate(void * vertices,const VertexSpec & spec,const GrPerspQuad & deviceQuad,const SkPMColor4f & color4f,const GrPerspQuad & localQuad,const SkRect & domain,GrQuadAAFlags aaFlags)402 void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
403                  const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
404                  GrQuadAAFlags aaFlags) {
405     bool wideColor = GrQuadPerEdgeAA::ColorType::kHalf == spec.colorType();
406     CoverageMode mode = get_mode_for_spec(spec);
407 
408     // Load position data into Sk4fs (always x, y, and load w to avoid branching down the road)
409     Sk4f oX = deviceQuad.x4f();
410     Sk4f oY = deviceQuad.y4f();
411     Sk4f oW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
412 
413     // Load local position data into Sk4fs (either none, just u,v or all three)
414     Sk4f oU, oV, oR;
415     if (spec.hasLocalCoords()) {
416         oU = localQuad.x4f();
417         oV = localQuad.y4f();
418         oR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
419     }
420 
421     GrVertexWriter vb{vertices};
422     if (spec.usesCoverageAA()) {
423         SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
424 
425         // 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;
429 
430         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);
440             } else {
441                 // 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);
446             }
447             // 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
452 
453         // 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);
461     }
462 
463     return vb.fPtr;
464 }
465 
ConfigureMeshIndices(GrMeshDrawOp::Target * target,GrMesh * mesh,const VertexSpec & spec,int quadCount)466 bool 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
470         sk_sp<const GrBuffer> ibuffer = get_index_buffer(target->resourceProvider());
471         if (!ibuffer) {
472             return false;
473         }
474 
475         mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
476         mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
477                                   quadCount, kNumAAQuadsInIndexBuffer);
478     } else {
479         // Non-AA quads use 4 vertices, and regular triangle strip layout
480         if (quadCount > 1) {
481             sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
482             if (!ibuffer) {
483                 return false;
484             }
485 
486             mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
487             mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
488                                       GrResourceProvider::QuadCountOfQuadBuffer());
489         } else {
490             mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
491             mesh->setNonIndexedNonInstanced(4);
492         }
493     }
494 
495     return true;
496 }
497 
498 ////////////////// VertexSpec Implementation
499 
deviceDimensionality() const500 int VertexSpec::deviceDimensionality() const {
501     return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
502 }
503 
localDimensionality() const504 int VertexSpec::localDimensionality() const {
505     return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
506 }
507 
508 ////////////////// Geometry Processor Implementation
509 
510 class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
511 public:
512 
Make(const VertexSpec & spec)513     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
514         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
515     }
516 
Make(const VertexSpec & vertexSpec,const GrShaderCaps & caps,GrTextureType textureType,GrPixelConfig textureConfig,const GrSamplerState & samplerState,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)517     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
518                                            GrTextureType textureType, GrPixelConfig textureConfig,
519                                            const GrSamplerState& samplerState,
520                                            uint32_t extraSamplerKey,
521                                            sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
522         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
523                 vertexSpec, caps, textureType, textureConfig, samplerState, extraSamplerKey,
524                 std::move(textureColorSpaceXform)));
525     }
526 
name() const527     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
528 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const529     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
530         // 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;
534         // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
535         if (fLocalCoord.isInitialized()) {
536             x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
537         }
538         // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
539         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;
545         }
546 
547         b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
548         b->add32(x);
549     }
550 
createGLSLInstance(const GrShaderCaps & caps) const551     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 
573                 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};
587                 } else {
588                     // No coverage to eliminate
589                     gpArgs->fPositionVar = gp.fPosition.asShaderVar();
590                 }
591 
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()) {
606                     // The color cannot be flat if the varying coverage has been modulated into it
607                     args.fVaryingHandler->addPassThroughAttribute(gp.fColor, args.fOutputColor,
608                             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);
613                 }
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
651                 if (gp.fCoverageMode == CoverageMode::kWithPosition) {
652                     GrGLSLVarying coverage(kFloat_GrSLType);
653                     args.fVaryingHandler->addVarying("coverage", &coverage);
654                     if (gp.fNeedsPerspective) {
655                         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());
660                     }
661 
662                     args.fFragBuilder->codeAppendf("%s = float4(%s);",
663                                                    args.fOutputCoverage, coverage.fsIn());
664                 } else {
665                     // Set coverage to 1, since it's either non-AA or the coverage was already
666                     // folded into the output color
667                     args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
668                 }
669             }
670             GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
671         };
672         return new GLSLProcessor;
673     }
674 
675 private:
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec)676     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
677             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
678             , fTextureColorSpaceXform(nullptr) {
679         SkASSERT(!spec.hasDomain());
680         this->initializeAttrs(spec);
681         this->setTextureSamplerCnt(0);
682     }
683 
QuadPerEdgeAAGeometryProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,GrPixelConfig textureConfig,const GrSamplerState & samplerState,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)684     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
685                                    GrTextureType textureType, GrPixelConfig textureConfig,
686                                    const GrSamplerState& samplerState,
687                                    uint32_t extraSamplerKey,
688                                    sk_sp<GrColorSpaceXform> textureColorSpaceXform)
689             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
690             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
691             , fSampler(textureType, textureConfig, samplerState, extraSamplerKey) {
692         SkASSERT(spec.hasLocalCoords());
693         this->initializeAttrs(spec);
694         this->setTextureSamplerCnt(1);
695     }
696 
initializeAttrs(const VertexSpec & spec)697     void initializeAttrs(const VertexSpec& spec) {
698         fNeedsPerspective = spec.deviceDimensionality() == 3;
699         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         }
714 
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 
732         this->setVertexAttributes(&fPosition, 4);
733     }
734 
onTextureSampler(int) const735     const TextureSampler& onTextureSampler(int) const override { return fSampler; }
736 
737     Attribute fPosition; // May contain coverage as last channel
738     Attribute fColor; // May have coverage modulated in if the FPs support it
739     Attribute fLocalCoord;
740     Attribute fDomain;
741 
742     // 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
744     bool fNeedsPerspective;
745     CoverageMode fCoverageMode;
746 
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 
MakeProcessor(const VertexSpec & spec)755 sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
756     return QuadPerEdgeAAGeometryProcessor::Make(spec);
757 }
758 
MakeTexturedProcessor(const VertexSpec & spec,const GrShaderCaps & caps,GrTextureType textureType,GrPixelConfig textureConfig,const GrSamplerState & samplerState,uint32_t extraSamplerKey,sk_sp<GrColorSpaceXform> textureColorSpaceXform)759 sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
760         GrTextureType textureType, GrPixelConfig textureConfig,
761         const GrSamplerState& samplerState, uint32_t extraSamplerKey,
762         sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
763     return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, textureConfig,
764                                                 samplerState, extraSamplerKey,
765                                                 std::move(textureColorSpaceXform));
766 }
767 
768 } // namespace GrQuadPerEdgeAA
769