blob: d27a5039c8c6803cff62447ab0c220b01828300f [file] [log] [blame]
Michael Ludwig460eb5e2018-10-29 11:09:29 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrQuadPerEdgeAA.h"
9#include "GrQuad.h"
Michael Ludwigc182b942018-11-16 10:27:51 -050010#include "GrVertexWriter.h"
Michael Ludwig20e909e2018-10-30 10:43:57 -040011#include "glsl/GrGLSLColorSpaceXformHelper.h"
12#include "glsl/GrGLSLPrimitiveProcessor.h"
13#include "glsl/GrGLSLFragmentShaderBuilder.h"
14#include "glsl/GrGLSLVarying.h"
15#include "glsl/GrGLSLVertexGeoBuilder.h"
Michael Ludwig460eb5e2018-10-29 11:09:29 -040016#include "SkNx.h"
17
18namespace {
19
20// This computes the four edge equations for a quad, then outsets them and optionally computes a new
21// quad as the intersection points of the outset edges. 'x' and 'y' contain the original points as
22// input and the outset points as output. 'a', 'b', and 'c' are the edge equation coefficients on
23// output. The values in x, y, u, v, and r are possibly updated if outsetting is needed.
24// r is the local position's w component if it exists.
25static void compute_quad_edges_and_outset_vertices(GrQuadAAFlags aaFlags, Sk4f* x, Sk4f* y, Sk4f* a,
26 Sk4f* b, Sk4f* c, Sk4f* u, Sk4f* v, Sk4f* r,
27 int uvrChannelCount, bool outsetCorners) {
28 SkASSERT(uvrChannelCount == 0 || uvrChannelCount == 2 || uvrChannelCount == 3);
29
30 static constexpr auto fma = SkNx_fma<4, float>;
31 // These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
32 // order.
33 auto nextCW = [](const Sk4f& v) { return SkNx_shuffle<2, 0, 3, 1>(v); };
34 auto nextCCW = [](const Sk4f& v) { return SkNx_shuffle<1, 3, 0, 2>(v); };
35
36 // Compute edge equations for the quad.
37 auto xnext = nextCCW(*x);
38 auto ynext = nextCCW(*y);
39 // xdiff and ydiff will comprise the normalized vectors pointing along each quad edge.
40 auto xdiff = xnext - *x;
41 auto ydiff = ynext - *y;
42 auto invLengths = fma(xdiff, xdiff, ydiff * ydiff).rsqrt();
43 xdiff *= invLengths;
44 ydiff *= invLengths;
45
46 // Use above vectors to compute edge equations.
47 *c = fma(xnext, *y, -ynext * *x) * invLengths;
48 // Make sure the edge equations have their normals facing into the quad in device space.
49 auto test = fma(ydiff, nextCW(*x), fma(-xdiff, nextCW(*y), *c));
50 if ((test < Sk4f(0)).anyTrue()) {
51 *a = -ydiff;
52 *b = xdiff;
53 *c = -*c;
54 } else {
55 *a = ydiff;
56 *b = -xdiff;
57 }
58 // Outset the edge equations so aa coverage evaluates to zero half a pixel away from the
59 // original quad edge.
60 *c += 0.5f;
61
62 if (aaFlags != GrQuadAAFlags::kAll) {
63 // This order is the same order the edges appear in xdiff/ydiff and therefore as the
64 // edges in a/b/c.
65 auto mask = Sk4f(GrQuadAAFlags::kLeft & aaFlags ? 1.f : 0.f,
66 GrQuadAAFlags::kBottom & aaFlags ? 1.f : 0.f,
67 GrQuadAAFlags::kTop & aaFlags ? 1.f : 0.f,
68 GrQuadAAFlags::kRight & aaFlags ? 1.f : 0.f);
69 // Outset edge equations for masked out edges another pixel so that they always evaluate
70 // >= 1.
71 *c += (1.f - mask);
72 if (outsetCorners) {
73 // Do the vertex outset.
74 mask *= 0.5f;
75 auto maskCW = nextCW(mask);
76 *x += maskCW * -xdiff + mask * nextCW(xdiff);
77 *y += maskCW * -ydiff + mask * nextCW(ydiff);
78 if (uvrChannelCount > 0) {
79 // We want to extend the texture coords by the same proportion as the positions.
80 maskCW *= invLengths;
81 mask *= nextCW(invLengths);
82 Sk4f udiff = nextCCW(*u) - *u;
83 Sk4f vdiff = nextCCW(*v) - *v;
84 *u += maskCW * -udiff + mask * nextCW(udiff);
85 *v += maskCW * -vdiff + mask * nextCW(vdiff);
86 if (uvrChannelCount == 3) {
87 Sk4f rdiff = nextCCW(*r) - *r;
88 *r += maskCW * -rdiff + mask * nextCW(rdiff);
89 }
90 }
91 }
92 } else if (outsetCorners) {
93 *x += 0.5f * (-xdiff + nextCW(xdiff));
94 *y += 0.5f * (-ydiff + nextCW(ydiff));
95 if (uvrChannelCount > 0) {
96 Sk4f t = 0.5f * invLengths;
97 Sk4f udiff = nextCCW(*u) - *u;
98 Sk4f vdiff = nextCCW(*v) - *v;
99 *u += t * -udiff + nextCW(t) * nextCW(udiff);
100 *v += t * -vdiff + nextCW(t) * nextCW(vdiff);
101 if (uvrChannelCount == 3) {
102 Sk4f rdiff = nextCCW(*r) - *r;
103 *r += t * -rdiff + nextCW(t) * nextCW(rdiff);
104 }
105 }
106 }
107}
108
109// Generalizes the above function to extrapolate local coords such that after perspective division
110// of the device coordinate, the original local coordinate value is at the original un-outset
111// device position. r is the local coordinate's w component.
112static void compute_quad_edges_and_outset_persp_vertices(GrQuadAAFlags aaFlags, Sk4f* x, Sk4f* y,
113 Sk4f* w, Sk4f* a, Sk4f* b, Sk4f* c,
114 Sk4f* u, Sk4f* v, Sk4f* r,
115 int uvrChannelCount) {
116 SkASSERT(uvrChannelCount == 0 || uvrChannelCount == 2 || uvrChannelCount == 3);
117
118 auto iw = (*w).invert();
119 auto x2d = (*x) * iw;
120 auto y2d = (*y) * iw;
121 // Don't compute outset corners in the normalized space, which means u, v, and r don't need
122 // to be provided here (outset separately below).
123 compute_quad_edges_and_outset_vertices(aaFlags, &x2d, &y2d, a, b, c, nullptr, nullptr, nullptr,
124 /* uvr ct */ 0, /* outsetCorners */ false);
125
126 static const float kOutset = 0.5f;
127 if ((GrQuadAAFlags::kLeft | GrQuadAAFlags::kRight) & aaFlags) {
128 // For each entry in x the equivalent entry in opX is the left/right opposite and so on.
129 Sk4f opX = SkNx_shuffle<2, 3, 0, 1>(*x);
130 Sk4f opW = SkNx_shuffle<2, 3, 0, 1>(*w);
131 Sk4f opY = SkNx_shuffle<2, 3, 0, 1>(*y);
132 // vx/vy holds the device space left-to-right vectors along top and bottom of the quad.
133 Sk2f vx = SkNx_shuffle<2, 3>(x2d) - SkNx_shuffle<0, 1>(x2d);
134 Sk2f vy = SkNx_shuffle<2, 3>(y2d) - SkNx_shuffle<0, 1>(y2d);
135 Sk2f len = SkNx_fma(vx, vx, vy * vy).sqrt();
136 // For each device space corner, devP, label its left/right opposite device space point
137 // opDevPt. The new device space point is opDevPt + s (devPt - opDevPt) where s is
138 // (length(devPt - opDevPt) + 0.5) / length(devPt - opDevPt);
139 Sk4f s = SkNx_shuffle<0, 1, 0, 1>((len + kOutset) / len);
140 // Compute t in homogeneous space from s using similar triangles so that we can produce
141 // homogeneous outset vertices for perspective-correct interpolation.
142 Sk4f sOpW = s * opW;
143 Sk4f t = sOpW / (sOpW + (1.f - s) * (*w));
144 // mask is used to make the t values be 1 when the left/right side is not antialiased.
145 Sk4f mask(GrQuadAAFlags::kLeft & aaFlags ? 1.f : 0.f,
146 GrQuadAAFlags::kLeft & aaFlags ? 1.f : 0.f,
147 GrQuadAAFlags::kRight & aaFlags ? 1.f : 0.f,
148 GrQuadAAFlags::kRight & aaFlags ? 1.f : 0.f);
149 t = t * mask + (1.f - mask);
150 *x = opX + t * (*x - opX);
151 *y = opY + t * (*y - opY);
152 *w = opW + t * (*w - opW);
153
154 if (uvrChannelCount > 0) {
155 Sk4f opU = SkNx_shuffle<2, 3, 0, 1>(*u);
156 Sk4f opV = SkNx_shuffle<2, 3, 0, 1>(*v);
157 *u = opU + t * (*u - opU);
158 *v = opV + t * (*v - opV);
159 if (uvrChannelCount == 3) {
160 Sk4f opR = SkNx_shuffle<2, 3, 0, 1>(*r);
161 *r = opR + t * (*r - opR);
162 }
163 }
164
165 if ((GrQuadAAFlags::kTop | GrQuadAAFlags::kBottom) & aaFlags) {
166 // Update the 2D points for the top/bottom calculation.
167 iw = (*w).invert();
168 x2d = (*x) * iw;
169 y2d = (*y) * iw;
170 }
171 }
172
173 if ((GrQuadAAFlags::kTop | GrQuadAAFlags::kBottom) & aaFlags) {
174 // This operates the same as above but for top/bottom rather than left/right.
175 Sk4f opX = SkNx_shuffle<1, 0, 3, 2>(*x);
176 Sk4f opW = SkNx_shuffle<1, 0, 3, 2>(*w);
177 Sk4f opY = SkNx_shuffle<1, 0, 3, 2>(*y);
178
179 Sk2f vx = SkNx_shuffle<1, 3>(x2d) - SkNx_shuffle<0, 2>(x2d);
180 Sk2f vy = SkNx_shuffle<1, 3>(y2d) - SkNx_shuffle<0, 2>(y2d);
181 Sk2f len = SkNx_fma(vx, vx, vy * vy).sqrt();
182
183 Sk4f s = SkNx_shuffle<0, 0, 1, 1>((len + kOutset) / len);
184
185 Sk4f sOpW = s * opW;
186 Sk4f t = sOpW / (sOpW + (1.f - s) * (*w));
187
188 Sk4f mask(GrQuadAAFlags::kTop & aaFlags ? 1.f : 0.f,
189 GrQuadAAFlags::kBottom & aaFlags ? 1.f : 0.f,
190 GrQuadAAFlags::kTop & aaFlags ? 1.f : 0.f,
191 GrQuadAAFlags::kBottom & aaFlags ? 1.f : 0.f);
192 t = t * mask + (1.f - mask);
193 *x = opX + t * (*x - opX);
194 *y = opY + t * (*y - opY);
195 *w = opW + t * (*w - opW);
196
197 if (uvrChannelCount > 0) {
198 Sk4f opU = SkNx_shuffle<1, 0, 3, 2>(*u);
199 Sk4f opV = SkNx_shuffle<1, 0, 3, 2>(*v);
200 *u = opU + t * (*u - opU);
201 *v = opV + t * (*v - opV);
202 if (uvrChannelCount == 3) {
203 Sk4f opR = SkNx_shuffle<1, 0, 3, 2>(*r);
204 *r = opR + t * (*r - opR);
205 }
206 }
207 }
208}
209
210// Fast path for non-AA quads batched into an AA op. Since they are part of the AA op, the vertices
211// need to have valid edge equations that ensure coverage is set to 1. To get perspective
212// interpolation of the edge distance, the vertex shader outputs d*w and then multiplies by 1/w in
213// the fragment shader. For non-AA edges, the edge equation can be simplified to 0*x/w + y/w + c >=
214// 1, so the vertex shader outputs c*w. The quad is sent as two triangles, so a fragment is the
215// interpolation between 3 of the 4 vertices. If iX are the weights for the 3 involved quad
216// vertices, then the fragment shader's state is:
217// f_cw = c * (iA*wA + iB*wB + iC*wC) and f_1/w = iA/wA + iB/wB + iC/wC
218// (where A,B,C are chosen from {1,2,3, 4})
219// When there's no perspective, then f_cw*f_1/w = c and setting c = 1 guarantees a proper non-AA
220// edge. Unfortunately when there is perspective, f_cw*f_1/w != c unless the fragment is at a
221// vertex. We must pick a c such that f_cw*f_1/w >= 1 across the whole primitive.
222// Let n = min(w1,w2,w3,w4) and m = max(w1,w2,w3,w4) and rewrite
223// f_1/w=(iA*wB*wC + iB*wA*wC + iC*wA*wB) / (wA*wB*wC)
224// Since the iXs are weights for the interior of the primitive, then we have:
225// n <= (iA*wA + iB*wB + iC*wC) <= m and
226// n^2 <= (iA*wB*wC + iB*wA*wC + iC*wA*wB) <= m^2 and
227// n^3 <= wA*wB*wC <= m^3 regardless of the choice of A,B, and C
228// Thus if we set c = m^3/n^3, it guarantees f_cw*f_1/w >= 1 for any perspective.
229static SkPoint3 compute_non_aa_persp_edge_coeffs(const Sk4f& w) {
230 float n = w.min();
231 float m = w.max();
232 return {0.f, 0.f, (m * m * m) / (n * n * n)};
233}
234
235// When there's guaranteed no perspective, the edge coefficients for non-AA quads is constant
236static constexpr SkPoint3 kNonAANoPerspEdgeCoeffs = {0, 0, 1};
237
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400238} // anonymous namespace
239
Michael Ludwigc182b942018-11-16 10:27:51 -0500240namespace GrQuadPerEdgeAA {
241
242////////////////// Tessellate Implementation
243
244void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
245 const GrColor& color, const GrPerspQuad& localQuad, const SkRect& domain,
246 GrQuadAAFlags aaFlags) {
247 bool deviceHasPerspective = spec.deviceQuadType() == GrQuadType::kPerspective;
248 bool localHasPerspective = spec.localQuadType() == GrQuadType::kPerspective;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400249
250 // Load position data into Sk4fs (always x, y and maybe w)
251 Sk4f x = deviceQuad.x4f();
252 Sk4f y = deviceQuad.y4f();
253 Sk4f w;
Michael Ludwigc182b942018-11-16 10:27:51 -0500254 if (deviceHasPerspective) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400255 w = deviceQuad.w4f();
256 }
257
258 // Load local position data into Sk4fs (either none, just u,v or all three)
259 Sk4f u, v, r;
Michael Ludwigc182b942018-11-16 10:27:51 -0500260 if (spec.hasLocalCoords()) {
261 u = localQuad.x4f();
262 v = localQuad.y4f();
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400263
Michael Ludwigc182b942018-11-16 10:27:51 -0500264 if (localHasPerspective) {
265 r = localQuad.w4f();
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400266 }
267 }
268
269 Sk4f a, b, c;
Michael Ludwigc182b942018-11-16 10:27:51 -0500270 if (spec.usesCoverageAA()) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400271 // Must calculate edges and possibly outside the positions
272 if (aaFlags == GrQuadAAFlags::kNone) {
273 // A non-AA quad that got batched into an AA group, so its edges will be the same for
274 // all four vertices and it does not need to be outset
275 SkPoint3 edgeCoeffs;
Michael Ludwigc182b942018-11-16 10:27:51 -0500276 if (deviceHasPerspective) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400277 edgeCoeffs = compute_non_aa_persp_edge_coeffs(w);
278 } else {
279 edgeCoeffs = kNonAANoPerspEdgeCoeffs;
280 }
281
282 // Copy the coefficients into all four equations
283 a = edgeCoeffs.fX;
284 b = edgeCoeffs.fY;
285 c = edgeCoeffs.fZ;
Michael Ludwigc182b942018-11-16 10:27:51 -0500286 } else if (deviceHasPerspective) {
287 // For simplicity, pointers to u, v, and r are always provided, but the local dim param
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400288 // ensures that only loaded Sk4fs are modified in the compute functions.
Michael Ludwigc182b942018-11-16 10:27:51 -0500289 compute_quad_edges_and_outset_persp_vertices(aaFlags, &x, &y, &w, &a, &b, &c,
290 &u, &v, &r, spec.localDimensionality());
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400291 } else {
Michael Ludwigc182b942018-11-16 10:27:51 -0500292 compute_quad_edges_and_outset_vertices(aaFlags, &x, &y, &a, &b, &c, &u, &v, &r,
293 spec.localDimensionality(), /* outset */ true);
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400294 }
295 }
296
Michael Ludwigc182b942018-11-16 10:27:51 -0500297 // Now rearrange the Sk4fs into the interleaved vertex layout:
298 // i.e. x1x2x3x4 y1y2y3y4 -> x1y1 x2y2 x3y3 x4y
299 GrVertexWriter vb{vertices};
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400300 for (int i = 0; i < 4; ++i) {
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400301 // save position
Michael Ludwigc182b942018-11-16 10:27:51 -0500302 if (deviceHasPerspective) {
303 vb.write<SkPoint3>({x[i], y[i], w[i]});
304 } else {
305 vb.write<SkPoint>({x[i], y[i]});
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400306 }
307
Michael Ludwigc182b942018-11-16 10:27:51 -0500308 // save color
309 if (spec.hasVertexColors()) {
310 vb.write<GrColor>(color);
311 }
312
313 // save local position
314 if (spec.hasLocalCoords()) {
315 if (localHasPerspective) {
316 vb.write<SkPoint3>({u[i], v[i], r[i]});
317 } else {
318 vb.write<SkPoint>({u[i], v[i]});
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400319 }
320 }
321
Michael Ludwigc182b942018-11-16 10:27:51 -0500322 // save the domain
323 if (spec.hasDomain()) {
324 vb.write<SkRect>(domain);
325 }
326
327 // save the edges
328 if (spec.usesCoverageAA()) {
329 for (int j = 0; j < 4; j++) {
330 vb.write<SkPoint3>({a[j], b[j], c[j]});
331 }
332 }
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400333 }
Michael Ludwigc182b942018-11-16 10:27:51 -0500334
335 return vb.fPtr;
Michael Ludwig460eb5e2018-10-29 11:09:29 -0400336}
Michael Ludwig20e909e2018-10-30 10:43:57 -0400337
Michael Ludwigc182b942018-11-16 10:27:51 -0500338////////////////// VertexSpec Implementation
Michael Ludwig20e909e2018-10-30 10:43:57 -0400339
Michael Ludwigc182b942018-11-16 10:27:51 -0500340int VertexSpec::deviceDimensionality() const {
341 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2;
342}
343
344int VertexSpec::localDimensionality() const {
345 return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0;
346}
347
348////////////////// GPAttributes Implementation
349
350GPAttributes::GPAttributes(const VertexSpec& spec) {
351 if (spec.deviceDimensionality() == 3) {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400352 fPositions = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
353 } else {
354 fPositions = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
355 }
356
Michael Ludwigc182b942018-11-16 10:27:51 -0500357 int localDim = spec.localDimensionality();
Michael Ludwig20e909e2018-10-30 10:43:57 -0400358 if (localDim == 3) {
359 fLocalCoords = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
360 } else if (localDim == 2) {
361 fLocalCoords = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
362 } // else localDim == 0 and attribute remains uninitialized
363
Michael Ludwigc182b942018-11-16 10:27:51 -0500364 if (spec.hasVertexColors()) {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400365 fColors = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
366 }
367
Michael Ludwigc182b942018-11-16 10:27:51 -0500368 if (spec.hasDomain()) {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400369 fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
370 }
371
Michael Ludwigc182b942018-11-16 10:27:51 -0500372 if (spec.usesCoverageAA()) {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400373 fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
374 fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
375 fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
376 fAAEdges[3] = {"aaEdge3", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
377 }
378}
379
Michael Ludwigc182b942018-11-16 10:27:51 -0500380bool GPAttributes::needsPerspectiveInterpolation() const {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400381 // This only needs to check the position; local position having perspective does not change
382 // how the varyings are interpolated
383 return fPositions.cpuType() == kFloat3_GrVertexAttribType;
384}
385
Michael Ludwigc182b942018-11-16 10:27:51 -0500386uint32_t GPAttributes::getKey() const {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400387 // aa, color, domain are single bit flags
388 uint32_t x = this->usesCoverageAA() ? 0 : 1;
389 x |= this->hasVertexColors() ? 0 : 2;
390 x |= this->hasDomain() ? 0 : 4;
391 // regular position has two options as well
392 x |= kFloat3_GrVertexAttribType == fPositions.cpuType() ? 0 : 8;
393 // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
394 if (this->hasLocalCoords()) {
395 x |= kFloat3_GrVertexAttribType == fLocalCoords.cpuType() ? 16 : 32;
396 }
397 return x;
398}
399
Michael Ludwigc182b942018-11-16 10:27:51 -0500400void GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args,
401 GrGLSLColorSpaceXformHelper* csXformHelper,
402 const char* colorVarName) const {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400403 using Interpolation = GrGLSLVaryingHandler::Interpolation;
404
405 if (!fColors.isInitialized()) {
406 return;
407 }
408
409 if (csXformHelper == nullptr || csXformHelper->isNoop()) {
410 args.fVaryingHandler->addPassThroughAttribute(
411 fColors, args.fOutputColor, Interpolation::kCanBeFlat);
412 } else {
413 GrGLSLVarying varying(kHalf4_GrSLType);
414 args.fVaryingHandler->addVarying(colorVarName, &varying);
415 args.fVertBuilder->codeAppendf("half4 %s = ", colorVarName);
416 args.fVertBuilder->appendColorGamutXform(fColors.name(), csXformHelper);
417 args.fVertBuilder->codeAppend(";");
418 args.fVertBuilder->codeAppendf("%s = half4(%s.rgb * %s.a, %s.a);",
419 varying.vsOut(), colorVarName, colorVarName, colorVarName);
420 args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
421 }
422}
423
Michael Ludwigc182b942018-11-16 10:27:51 -0500424void GPAttributes::emitExplicitLocalCoords(
Michael Ludwig20e909e2018-10-30 10:43:57 -0400425 GrGLSLPrimitiveProcessor::EmitArgs& args, const char* localCoordName,
426 const char* domainName) const {
427 using Interpolation = GrGLSLVaryingHandler::Interpolation;
428 if (!this->hasLocalCoords()) {
429 return;
430 }
431
432 args.fFragBuilder->codeAppendf("float2 %s;", localCoordName);
433 bool localHasPerspective = fLocalCoords.cpuType() == kFloat3_GrVertexAttribType;
434 if (localHasPerspective) {
435 // Can't do a pass through since we need to perform perspective division
436 GrGLSLVarying v(fLocalCoords.gpuType());
437 args.fVaryingHandler->addVarying(fLocalCoords.name(), &v);
438 args.fVertBuilder->codeAppendf("%s = %s;", v.vsOut(), fLocalCoords.name());
439 args.fFragBuilder->codeAppendf("%s = %s.xy / %s.z;", localCoordName, v.fsIn(), v.fsIn());
440 } else {
441 args.fVaryingHandler->addPassThroughAttribute(fLocalCoords, localCoordName);
442 }
443
444 // Clamp the now 2D localCoordName variable by the domain if it is provided
445 if (this->hasDomain()) {
446 args.fFragBuilder->codeAppendf("float4 %s;", domainName);
447 args.fVaryingHandler->addPassThroughAttribute(fDomain, domainName,
448 Interpolation::kCanBeFlat);
449 args.fFragBuilder->codeAppendf("%s = clamp(%s, %s.xy, %s.zw);",
450 localCoordName, localCoordName, domainName, domainName);
451 }
452}
453
Michael Ludwigc182b942018-11-16 10:27:51 -0500454void GPAttributes::emitCoverage(GrGLSLPrimitiveProcessor::EmitArgs& args,
455 const char* edgeDistName) const {
Michael Ludwig20e909e2018-10-30 10:43:57 -0400456 if (this->usesCoverageAA()) {
457 bool mulByFragCoordW = false;
458 GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
459 GrGLSLVarying::Scope::kVertToFrag);
460 args.fVaryingHandler->addVarying(edgeDistName, &aaDistVarying);
461
462 if (kFloat3_GrVertexAttribType == fPositions.cpuType()) {
463 // The distance from edge equation e to homogeneous point p=sk_Position
464 // is e.x*p.x/p.w + e.y*p.y/p.w + e.z. However, we want screen space
465 // interpolation of this distance. We can do this by multiplying the
466 // varying in the VS by p.w and then multiplying by sk_FragCoord.w in
467 // the FS. So we output e.x*p.x + e.y*p.y + e.z * p.w
468 args.fVertBuilder->codeAppendf(
469 R"(%s = float4(dot(%s, %s), dot(%s, %s),
470 dot(%s, %s), dot(%s, %s));)",
471 aaDistVarying.vsOut(), fAAEdges[0].name(), fPositions.name(),
472 fAAEdges[1].name(), fPositions.name(), fAAEdges[2].name(), fPositions.name(),
473 fAAEdges[3].name(), fPositions.name());
474 mulByFragCoordW = true;
475 } else {
476 args.fVertBuilder->codeAppendf(
477 R"(%s = float4(dot(%s.xy, %s.xy) + %s.z,
478 dot(%s.xy, %s.xy) + %s.z,
479 dot(%s.xy, %s.xy) + %s.z,
480 dot(%s.xy, %s.xy) + %s.z);)",
481 aaDistVarying.vsOut(),
482 fAAEdges[0].name(), fPositions.name(), fAAEdges[0].name(),
483 fAAEdges[1].name(), fPositions.name(), fAAEdges[1].name(),
484 fAAEdges[2].name(), fPositions.name(), fAAEdges[2].name(),
485 fAAEdges[3].name(), fPositions.name(), fAAEdges[3].name());
486 }
487
488 args.fFragBuilder->codeAppendf(
489 "float min%s = min(min(%s.x, %s.y), min(%s.z, %s.w));",
490 edgeDistName, aaDistVarying.fsIn(), aaDistVarying.fsIn(), aaDistVarying.fsIn(),
491 aaDistVarying.fsIn());
492 if (mulByFragCoordW) {
493 args.fFragBuilder->codeAppendf("min%s *= sk_FragCoord.w;", edgeDistName);
494 }
495 args.fFragBuilder->codeAppendf("%s = float4(saturate(min%s));",
496 args.fOutputCoverage, edgeDistName);
497 } else {
498 // Set coverage to 1
499 args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
500 }
501}
Michael Ludwigc182b942018-11-16 10:27:51 -0500502
503} // namespace GrQuadPerEdgeAA