Make GrTextureOp capable of edge antialiasing.

Bug: skia:
Change-Id: I0088bdbb6a76811611fa4628656bf9513c5bf04a
Reviewed-on: https://skia-review.googlesource.com/91105
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 5ed02cc..6d735f5 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -20,9 +20,13 @@
 #include "GrTextureProxy.h"
 #include "SkGr.h"
 #include "SkMathPriv.h"
+#include "SkPoint.h"
+#include "SkPoint3.h"
 #include "glsl/GrGLSLColorSpaceXformHelper.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
 #include "glsl/GrGLSLGeometryProcessor.h"
 #include "glsl/GrGLSLVarying.h"
+#include "glsl/GrGLSLVertexGeoBuilder.h"
 
 namespace {
 
@@ -38,12 +42,25 @@
         SkPoint fTextureCoords;
         GrColor fColor;
     };
+    struct AAVertex {
+        SkPoint fPosition;
+        SkPoint fTextureCoords;
+        SkPoint3 fEdges[4];
+        GrColor fColor;
+    };
     struct MultiTextureVertex {
         SkPoint fPosition;
         int fTextureIdx;
         SkPoint fTextureCoords;
         GrColor fColor;
     };
+    struct AAMultiTextureVertex {
+        SkPoint fPosition;
+        int fTextureIdx;
+        SkPoint fTextureCoords;
+        SkPoint3 fEdges[4];
+        GrColor fColor;
+    };
 
     // Maximum number of textures supported by this op. Must also be checked against the caps
     // limit. These numbers were based on some limited experiments on a HP Z840 and Pixel XL 2016
@@ -59,7 +76,7 @@
     }
 
     static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
-                                           sk_sp<GrColorSpaceXform> csxf,
+                                           sk_sp<GrColorSpaceXform> csxf, GrAA aa,
                                            const GrSamplerState::Filter filters[],
                                            const GrShaderCaps& caps) {
         // We use placement new to avoid always allocating space for kMaxTextures TextureSampler
@@ -68,7 +85,7 @@
         size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
         void* mem = GrGeometryProcessor::operator new(size);
         return sk_sp<TextureGeometryProcessor>(new (mem) TextureGeometryProcessor(
-                proxies, proxyCnt, samplerCnt, std::move(csxf), filters, caps));
+                proxies, proxyCnt, samplerCnt, std::move(csxf), aa, filters, caps));
     }
 
     ~TextureGeometryProcessor() override {
@@ -82,6 +99,7 @@
 
     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
         b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
+        b->add32(static_cast<uint32_t>(this->aa()));
     }
 
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
@@ -149,13 +167,36 @@
                                                                       &fColorSpaceXformHelper);
                 }
                 args.fFragBuilder->codeAppend(";");
-                args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
+                if (GrAA::kYes == textureGP.aa()) {
+                    GrGLSLVarying aaDistVarying(kFloat4_GrSLType,
+                                                GrGLSLVarying::Scope::kVertToFrag);
+                    args.fVaryingHandler->addVarying("aaDists", &aaDistVarying);
+                    args.fVertBuilder->codeAppendf(
+                            R"(%s = float4(dot(aaEdge0.xy, %s.xy) + aaEdge0.z,
+                                           dot(aaEdge1.xy, %s.xy) + aaEdge1.z,
+                                           dot(aaEdge2.xy, %s.xy) + aaEdge2.z,
+                                           dot(aaEdge3.xy, %s.xy) + aaEdge3.z);)",
+                            aaDistVarying.vsOut(), textureGP.fPositions.fName,
+                            textureGP.fPositions.fName, textureGP.fPositions.fName,
+                            textureGP.fPositions.fName);
+
+                    args.fFragBuilder->codeAppendf(
+                            "float mindist = min(min(%s.x, %s.y), min(%s.z, %s.w));",
+                            aaDistVarying.fsIn(), aaDistVarying.fsIn(), aaDistVarying.fsIn(),
+                            aaDistVarying.fsIn());
+                    args.fFragBuilder->codeAppendf("%s = float4(clamp(mindist, 0, 1));",
+                                                   args.fOutputCoverage);
+                } else {
+                    args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
+                }
             }
             GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
         };
         return new GLSLProcessor;
     }
 
+    GrAA aa() const { return GrAA(fAAEdges[0].isInitialized()); }
+
 private:
     // This exists to reduce the number of shaders generated. It does some rounding of sampler
     // counts.
@@ -173,10 +214,9 @@
     }
 
     TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
-                             sk_sp<GrColorSpaceXform> csxf, const GrSamplerState::Filter filters[],
-                             const GrShaderCaps& caps)
-            : INHERITED(kTextureGeometryProcessor_ClassID)
-            , fColorSpaceXform(std::move(csxf)) {
+                             sk_sp<GrColorSpaceXform> csxf, GrAA aa,
+                             const GrSamplerState::Filter filters[], const GrShaderCaps& caps)
+            : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
         SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
         fPositions = this->addVertexAttrib("position", kFloat2_GrVertexAttribType);
         fSamplers[0].reset(std::move(proxies[0]), filters[0]);
@@ -200,6 +240,12 @@
         }
 
         fTextureCoords = this->addVertexAttrib("textureCoords", kFloat2_GrVertexAttribType);
+        if (GrAA::kYes == aa) {
+            fAAEdges[0] = this->addVertexAttrib("aaEdge0", kFloat3_GrVertexAttribType);
+            fAAEdges[1] = this->addVertexAttrib("aaEdge1", kFloat3_GrVertexAttribType);
+            fAAEdges[2] = this->addVertexAttrib("aaEdge2", kFloat3_GrVertexAttribType);
+            fAAEdges[3] = this->addVertexAttrib("aaEdge3", kFloat3_GrVertexAttribType);
+        }
         fColors = this->addVertexAttrib("color", kUByte4_norm_GrVertexAttribType);
     }
 
@@ -207,12 +253,148 @@
     Attribute fTextureIdx;
     Attribute fTextureCoords;
     Attribute fColors;
+    Attribute fAAEdges[4];
     sk_sp<GrColorSpaceXform> fColorSpaceXform;
     TextureSampler fSamplers[1];
 
     typedef GrGeometryProcessor INHERITED;
 };
 
+namespace {
+// This is a class soley so it can be partially specialized (functions cannot be).
+template<GrAA, typename Vertex> class VertexAAHandler;
+
+template<typename Vertex> class VertexAAHandler<GrAA::kNo, Vertex> {
+public:
+    static void AssignPositionsAndTexCoords(Vertex* vertices, const GrQuad& quad,
+                                            const SkRect& texRect) {
+        vertices[0].fPosition = quad.point(0);
+        vertices[0].fTextureCoords = {texRect.fLeft, texRect.fTop};
+        vertices[1].fPosition = quad.point(1);
+        vertices[1].fTextureCoords = {texRect.fLeft, texRect.fBottom};
+        vertices[2].fPosition = quad.point(2);
+        vertices[2].fTextureCoords = {texRect.fRight, texRect.fTop};
+        vertices[3].fPosition = quad.point(3);
+        vertices[3].fTextureCoords = {texRect.fRight, texRect.fBottom};
+    }
+};
+
+template<typename Vertex> class VertexAAHandler<GrAA::kYes, Vertex> {
+public:
+    static void AssignPositionsAndTexCoords(Vertex* vertices, const GrQuad& quad,
+                                            const SkRect& texRect) {
+        // We compute the four edge equations for quad, then outset them and compute a new quad
+        // as the intersection points of the outset edges.
+
+        // GrQuad is in tristip order but we want the points to be in a fan order, so swap 2 and 3.
+        Sk4f xs(quad.point(0).fX, quad.point(1).fX, quad.point(3).fX, quad.point(2).fX);
+        Sk4f ys(quad.point(0).fY, quad.point(1).fY, quad.point(3).fY, quad.point(2).fY);
+        Sk4f xsrot = SkNx_shuffle<1, 2, 3, 0>(xs);
+        Sk4f ysrot = SkNx_shuffle<1, 2, 3, 0>(ys);
+        Sk4f normXs = ysrot - ys;
+        Sk4f normYs = xs - xsrot;
+        Sk4f ds = xsrot * ys - ysrot * xs;
+        Sk4f invNormLengths = (normXs * normXs + normYs * normYs).rsqrt();
+        float test = normXs[0] * xs[2] + normYs[0] * ys[2] + ds[0];
+        // Make sure the edge equations have their normals facing into the quad in device space
+        if (test < 0) {
+            invNormLengths = -invNormLengths;
+        }
+        normXs *= invNormLengths;
+        normYs *= invNormLengths;
+        ds *= invNormLengths;
+
+        // Here is the bloat. This makes our edge equations compute coverage without requiring a
+        // half pixel offset and is also used to compute the bloated quad that will cover all
+        // pixels.
+        ds += Sk4f(0.5f);
+
+        for (int i = 0; i < 4; ++i) {
+            for (int j = 0; j < 4; ++j) {
+                vertices[j].fEdges[i].fX = normXs[i];
+                vertices[j].fEdges[i].fY = normYs[i];
+                vertices[j].fEdges[i].fZ = ds[i];
+            }
+        }
+
+        // Reverse the process to compute the points of the bloated quad from the edge equations.
+        // This time the inputs don't have 1s as their third coord and we want to homogenize rather
+        // than normalize the output since we need a GrQuad with 2D points.
+        xsrot = SkNx_shuffle<3, 0, 1, 2>(normXs);
+        ysrot = SkNx_shuffle<3, 0, 1, 2>(normYs);
+        Sk4f dsrot = SkNx_shuffle<3, 0, 1, 2>(ds);
+        xs = ysrot * ds - normYs * dsrot;
+        ys = normXs * dsrot - xsrot * ds;
+        ds = xsrot * normYs - ysrot * normXs;
+        ds = ds.invert();
+        xs *= ds;
+        ys *= ds;
+
+        // Go back to tri strip order when writing out the bloated quad to vertex positions.
+        vertices[0].fPosition = {xs[0], ys[0]};
+        vertices[1].fPosition = {xs[1], ys[1]};
+        vertices[3].fPosition = {xs[2], ys[2]};
+        vertices[2].fPosition = {xs[3], ys[3]};
+
+        AssignTexCoords(vertices, quad, texRect);
+    }
+
+private:
+    static void AssignTexCoords(Vertex* vertices, const GrQuad& quad, const SkRect& tex) {
+        SkMatrix q = SkMatrix::MakeAll(quad.point(0).fX, quad.point(1).fX, quad.point(2).fX,
+                                       quad.point(0).fY, quad.point(1).fY, quad.point(2).fY,
+                                                    1.f,              1.f,              1.f);
+        SkMatrix qinv;
+        if (!q.invert(&qinv)) {
+            return;
+        }
+        SkMatrix t = SkMatrix::MakeAll(tex.fLeft,    tex.fLeft, tex.fRight,
+                                        tex.fTop,  tex.fBottom,   tex.fTop,
+                                             1.f,          1.f,        1.f);
+        SkMatrix map;
+        map.setConcat(t, qinv);
+        SkMatrixPriv::MapPointsWithStride(map, &vertices[0].fTextureCoords, sizeof(Vertex),
+                                          &vertices[0].fPosition, sizeof(Vertex), 4);
+    }
+};
+
+template <typename Vertex, bool IsMultiTex> struct TexIdAssigner;
+
+template <typename Vertex> struct TexIdAssigner<Vertex, true> {
+    static void Assign(Vertex* vertices, int textureIdx) {
+        vertices[0].fTextureIdx = textureIdx;
+        vertices[1].fTextureIdx = textureIdx;
+        vertices[2].fTextureIdx = textureIdx;
+        vertices[3].fTextureIdx = textureIdx;
+    }
+};
+
+template <typename Vertex> struct TexIdAssigner<Vertex, false> {
+    static void Assign(Vertex* vertices, int textureIdx) {}
+};
+}  // anonymous namespace
+
+template <typename Vertex, bool IsMultiTex, GrAA AA>
+static void tessellate_quad(const GrQuad& devQuad, const SkRect& srcRect, GrColor color,
+                            GrSurfaceOrigin origin, Vertex* vertices, SkScalar iw, SkScalar ih,
+                            int textureIdx) {
+    SkRect texRect = {
+            iw * srcRect.fLeft,
+            ih * srcRect.fTop,
+            iw * srcRect.fRight,
+            ih * srcRect.fBottom
+    };
+    if (origin == kBottomLeft_GrSurfaceOrigin) {
+        texRect.fTop = 1.f - texRect.fTop;
+        texRect.fBottom = 1.f - texRect.fBottom;
+    }
+    VertexAAHandler<AA, Vertex>::AssignPositionsAndTexCoords(vertices, devQuad, texRect);
+    vertices[0].fColor = color;
+    vertices[1].fColor = color;
+    vertices[2].fColor = color;
+    vertices[3].fColor = color;
+    TexIdAssigner<Vertex, IsMultiTex>::Assign(vertices, textureIdx);
+}
 /**
  * Op that implements GrTextureOp::Make. It draws textured quads. Each quad can modulate against a
  * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
@@ -221,11 +403,11 @@
 public:
     static std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy,
                                           GrSamplerState::Filter filter, GrColor color,
-                                          const SkRect srcRect, const SkRect dstRect,
+                                          const SkRect& srcRect, const SkRect& dstRect, GrAA aa,
                                           const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
                                           bool allowSRBInputs) {
         return std::unique_ptr<GrDrawOp>(new TextureOp(std::move(proxy), filter, color, srcRect,
-                                                       dstRect, viewMatrix, std::move(csxf),
+                                                       dstRect, aa, viewMatrix, std::move(csxf),
                                                        allowSRBInputs));
     }
 
@@ -298,22 +480,23 @@
 #if defined(__clang__) && (__clang_major__ * 1000 + __clang_minor__) >= 3007
 __attribute__((no_sanitize("float-cast-overflow")))
 #endif
-    size_t RectSizeAsSizeT(const SkRect &rect) {;
+    size_t RectSizeAsSizeT(const SkRect& rect) {;
         return static_cast<size_t>(SkTMax(rect.width(), 1.f) * SkTMax(rect.height(), 1.f));
     }
 
     static constexpr int kMaxTextures = TextureGeometryProcessor::kMaxTextures;
 
     TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
-              const SkRect& srcRect, const SkRect& dstRect, const SkMatrix& viewMatrix,
+              const SkRect& srcRect, const SkRect& dstRect, GrAA aa, const SkMatrix& viewMatrix,
               sk_sp<GrColorSpaceXform> csxf, bool allowSRGBInputs)
             : INHERITED(ClassID())
             , fColorSpaceXform(std::move(csxf))
             , fProxy0(proxy.release())
             , fFilter0(filter)
             , fProxyCnt(1)
-            , fFinalized(false)
-            , fAllowSRGBInputs(allowSRGBInputs) {
+            , fAA(aa == GrAA::kYes ? 1 : 0)
+            , fFinalized(0)
+            , fAllowSRGBInputs(allowSRGBInputs ? 1 : 0) {
         Draw& draw = fDraws.push_back();
         draw.fSrcRect = srcRect;
         draw.fTextureIdx = 0;
@@ -339,7 +522,7 @@
 
         sk_sp<GrGeometryProcessor> gp =
                 TextureGeometryProcessor::Make(proxiesSPs, fProxyCnt, std::move(fColorSpaceXform),
-                                               filters, *target->caps().shaderCaps());
+                                               this->aa(), filters, *target->caps().shaderCaps());
         GrPipeline::InitArgs args;
         args.fProxy = target->proxy();
         args.fCaps = &target->caps();
@@ -356,35 +539,28 @@
             return;
         }
         if (1 == fProxyCnt) {
-            SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
-            for (int i = 0; i < fDraws.count(); ++i) {
-                auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
-                GrTexture* texture = proxies[0]->priv().peekTexture();
-                float iw = 1.f / texture->width();
-                float ih = 1.f / texture->height();
-                float tl = iw * fDraws[i].fSrcRect.fLeft;
-                float tr = iw * fDraws[i].fSrcRect.fRight;
-                float tt = ih * fDraws[i].fSrcRect.fTop;
-                float tb = ih * fDraws[i].fSrcRect.fBottom;
-                if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
-                    tt = 1.f - tt;
-                    tb = 1.f - tb;
+            GrSurfaceOrigin origin = proxies[0]->origin();
+            GrTexture* texture = proxies[0]->priv().peekTexture();
+            float iw = 1.f / texture->width();
+            float ih = 1.f / texture->height();
+            if (GrAA::kYes == this->aa()) {
+                SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::AAVertex));
+                auto vertices = static_cast<TextureGeometryProcessor::AAVertex*>(vdata);
+                for (int i = 0; i < fDraws.count(); ++i) {
+                    tessellate_quad<TextureGeometryProcessor::AAVertex, false, GrAA::kYes>(
+                            fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin,
+                            vertices + 4 * i, iw, ih, 0);
                 }
-                vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
-                vertices[0 + 4 * i].fTextureCoords = {tl, tt};
-                vertices[0 + 4 * i].fColor = fDraws[i].fColor;
-                vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
-                vertices[1 + 4 * i].fTextureCoords = {tl, tb};
-                vertices[1 + 4 * i].fColor = fDraws[i].fColor;
-                vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
-                vertices[2 + 4 * i].fTextureCoords = {tr, tt};
-                vertices[2 + 4 * i].fColor = fDraws[i].fColor;
-                vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
-                vertices[3 + 4 * i].fTextureCoords = {tr, tb};
-                vertices[3 + 4 * i].fColor = fDraws[i].fColor;
+            } else {
+                SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
+                auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
+                for (int i = 0; i < fDraws.count(); ++i) {
+                    tessellate_quad<TextureGeometryProcessor::Vertex, false, GrAA::kNo>(
+                            fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin,
+                            vertices + 4 * i, iw, ih, 0);
+                }
             }
         } else {
-            SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::MultiTextureVertex));
             GrTexture* textures[kMaxTextures];
             float iw[kMaxTextures];
             float ih[kMaxTextures];
@@ -393,33 +569,29 @@
                 iw[t] = 1.f / textures[t]->width();
                 ih[t] = 1.f / textures[t]->height();
             }
-            for (int i = 0; i < fDraws.count(); ++i) {
-                int t = fDraws[i].fTextureIdx;
-                auto vertices = static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
-                float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
-                float tr = iw[t] * fDraws[i].fSrcRect.fRight;
-                float tt = ih[t] * fDraws[i].fSrcRect.fTop;
-                float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
-                if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
-                    tt = 1.f - tt;
-                    tb = 1.f - tb;
+            if (GrAA::kYes == this->aa()) {
+                SkASSERT(gp->getVertexStride() ==
+                         sizeof(TextureGeometryProcessor::AAMultiTextureVertex));
+                auto vertices = static_cast<TextureGeometryProcessor::AAMultiTextureVertex*>(vdata);
+                for (int i = 0; i < fDraws.count(); ++i) {
+                    auto tidx = fDraws[i].fTextureIdx;
+                    GrSurfaceOrigin origin = proxies[tidx]->origin();
+                    tessellate_quad<TextureGeometryProcessor::AAMultiTextureVertex, true,
+                                    GrAA::kYes>(fDraws[i].fQuad, fDraws[i].fSrcRect,
+                                                fDraws[i].fColor, origin, vertices + 4 * i,
+                                                iw[tidx], ih[tidx], tidx);
                 }
-                vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
-                vertices[0 + 4 * i].fTextureIdx = t;
-                vertices[0 + 4 * i].fTextureCoords = {tl, tt};
-                vertices[0 + 4 * i].fColor = fDraws[i].fColor;
-                vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
-                vertices[1 + 4 * i].fTextureIdx = t;
-                vertices[1 + 4 * i].fTextureCoords = {tl, tb};
-                vertices[1 + 4 * i].fColor = fDraws[i].fColor;
-                vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
-                vertices[2 + 4 * i].fTextureIdx = t;
-                vertices[2 + 4 * i].fTextureCoords = {tr, tt};
-                vertices[2 + 4 * i].fColor = fDraws[i].fColor;
-                vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
-                vertices[3 + 4 * i].fTextureIdx = t;
-                vertices[3 + 4 * i].fTextureCoords = {tr, tb};
-                vertices[3 + 4 * i].fColor = fDraws[i].fColor;
+            } else {
+                SkASSERT(gp->getVertexStride() ==
+                         sizeof(TextureGeometryProcessor::MultiTextureVertex));
+                auto vertices = static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
+                for (int i = 0; i < fDraws.count(); ++i) {
+                    auto tidx = fDraws[i].fTextureIdx;
+                    GrSurfaceOrigin origin = proxies[tidx]->origin();
+                    tessellate_quad<TextureGeometryProcessor::MultiTextureVertex, true, GrAA::kNo>(
+                            fDraws[i].fQuad, fDraws[i].fSrcRect, fDraws[i].fColor, origin,
+                            vertices + 4 * i, iw[tidx], ih[tidx], tidx);
+                }
             }
         }
         GrPrimitiveType primitiveType =
@@ -446,6 +618,9 @@
         if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
             return false;
         }
+        if (this->fAA != that->fAA) {
+            return false;
+        }
         // Because of an issue where GrColorSpaceXform adds the same function every time it is used
         // in a texture lookup, we only allow multiple textures when there is no transform.
         if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && !fColorSpaceXform &&
@@ -560,6 +735,8 @@
         return newProxyCnt;
     }
 
+    GrAA aa() const { return static_cast<GrAA>(fAA); }
+
     GrTextureProxy* const* proxies() const { return fProxyCnt > 1 ? fProxyArray : &fProxy0; }
 
     const GrSamplerState::Filter* filters() const {
@@ -585,12 +762,12 @@
         GrTextureProxy** fProxyArray;
     };
     size_t fMaxApproxDstPixelArea;
-    // The next four members should pack.
     GrSamplerState::Filter fFilter0;
     uint8_t fProxyCnt;
+    unsigned fAA : 1;
     // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
-    uint8_t fFinalized;
-    uint8_t fAllowSRGBInputs;
+    unsigned fFinalized : 1;
+    unsigned fAllowSRGBInputs : 1;
 
     typedef GrMeshDrawOp INHERITED;
 };
@@ -603,11 +780,11 @@
 namespace GrTextureOp {
 
 std::unique_ptr<GrDrawOp> Make(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter,
-                               GrColor color, const SkRect& srcRect, const SkRect& dstRect,
+                               GrColor color, const SkRect& srcRect, const SkRect& dstRect, GrAA aa,
                                const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> csxf,
                                bool allowSRGBInputs) {
     SkASSERT(!viewMatrix.hasPerspective());
-    return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, viewMatrix,
+    return TextureOp::Make(std::move(proxy), filter, color, srcRect, dstRect, aa, viewMatrix,
                            std::move(csxf), allowSRGBInputs);
 }
 
@@ -639,7 +816,8 @@
             static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
     auto csxf = GrTest::TestColorXform(random);
     bool allowSRGBInputs = random->nextBool();
-    return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, viewMatrix,
+    GrAA aa = GrAA(random->nextBool());
+    return GrTextureOp::Make(std::move(proxy), filter, color, srcRect, rect, aa, viewMatrix,
                              std::move(csxf), allowSRGBInputs);
 }