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 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. 29 static AI Sk4f nextCW(const Sk4f& v) { 30 return SkNx_shuffle<2, 0, 3, 1>(v); 31 } 32 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. 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 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. 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 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). 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. 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. 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. 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 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. 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 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 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 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 500 int VertexSpec::deviceDimensionality() const { 501 return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2; 502 } 503 504 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 513 static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) { 514 return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec)); 515 } 516 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 527 const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; } 528 529 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 551 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override { 552 class GLSLProcessor : public GrGLSLGeometryProcessor { 553 public: 554 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, 555 FPCoordTransformIter&& transformIter) override { 556 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>(); 557 if (gp.fLocalCoord.isInitialized()) { 558 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 559 } 560 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get()); 561 } 562 563 private: 564 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 565 using Interpolation = GrGLSLVaryingHandler::Interpolation; 566 567 const auto& gp = args.fGP.cast<QuadPerEdgeAAGeometryProcessor>(); 568 fTextureColorSpaceXformHelper.emitCode(args.fUniformHandler, 569 gp.fTextureColorSpaceXform.get()); 570 571 args.fVaryingHandler->emitAttributes(gp); 572 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: 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 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 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 735 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 755 sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) { 756 return QuadPerEdgeAAGeometryProcessor::Make(spec); 757 } 758 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