Varying-based AA rect drawing

Instead of calculating opacity from relative position in the shader, use a
shader varying to do this computation for us.

bug:5045101

Also adds a test to HwAccelerationTest to show incorrect antialiasing in
scaled drawAARect / boundarySize calculation.

Change-Id: Icdc41acb01dc10ce354834f8389a5aed2f439162
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index f4f56d6..23d9018 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -67,6 +67,7 @@
 static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
 static const GLsizei gAAVertexStride = sizeof(AAVertex);
 static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
+static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
 static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
 static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
 static const GLsizei gMeshCount = 4;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8da9f66..9865ec4 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1145,6 +1145,10 @@
     mDescription.isAA = true;
 }
 
+void OpenGLRenderer::setupDrawAARect() {
+    mDescription.isAARect = true;
+}
+
 void OpenGLRenderer::setupDrawPoint(float pointSize) {
     mDescription.isPoint = true;
     mDescription.pointSize = pointSize;
@@ -1745,9 +1749,9 @@
 
 /**
  * This function uses a similar approach to that of AA lines in the drawLines() function.
- * We expand the rectangle by a half pixel in screen space on all sides, and use a fragment
- * shader to compute the translucency of the color, determined by whether a given pixel is
- * within that boundary region and how far into the region it is.
+ * We expand the rectangle by a half pixel in screen space on all sides. However, instead of using
+ * a fragment shader to compute the translucency of the color from its position, we simply use a
+ * varying parameter to define how far a given pixel is into the region.
  */
 void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom,
         int color, SkXfermode::Mode mode) {
@@ -1759,10 +1763,8 @@
         Matrix4 *mat = mSnapshot->transform;
         float m00 = mat->data[Matrix4::kScaleX];
         float m01 = mat->data[Matrix4::kSkewY];
-        float m02 = mat->data[2];
         float m10 = mat->data[Matrix4::kSkewX];
         float m11 = mat->data[Matrix4::kScaleX];
-        float m12 = mat->data[6];
         float scaleX = sqrt(m00 * m00 + m01 * m01);
         float scaleY = sqrt(m10 * m10 + m11 * m11);
         inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
@@ -1772,6 +1774,11 @@
     float boundarySizeX = .5 * inverseScaleX;
     float boundarySizeY = .5 * inverseScaleY;
 
+    float innerLeft = left + boundarySizeX;
+    float innerRight = right - boundarySizeX;
+    float innerTop = top + boundarySizeY;
+    float innerBottom = bottom - boundarySizeY;
+
     // Adjust the rect by the AA boundary padding
     left -= boundarySizeX;
     right += boundarySizeX;
@@ -1781,7 +1788,7 @@
     if (!quickReject(left, top, right, bottom)) {
         setupDraw();
         setupDrawNoTexture();
-        setupDrawAALine();
+        setupDrawAARect();
         setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
         setupDrawColorFilter();
         setupDrawShader();
@@ -1792,34 +1799,52 @@
         setupDrawColorFilterUniforms();
         setupDrawShaderIdentityUniforms();
 
-        AAVertex rects[4];
-        AAVertex* aaVertices = &rects[0];
-        void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
-        void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
+        AlphaVertex rects[14];
+        AlphaVertex* aVertices = &rects[0];
+        void* alphaCoords = ((GLbyte*) aVertices) + gVertexAlphaOffset;
 
-        int widthSlot;
-        int lengthSlot;
+        bool force = mCaches.unbindMeshBuffer();
+        mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
+                aVertices, gAlphaVertexStride);
+        mCaches.resetTexCoordsVertexPointer();
+        mCaches.unbindIndicesBuffer();
 
-        float width = right - left;
-        float height = bottom - top;
+        int alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
+        glEnableVertexAttribArray(alphaSlot);
+        glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
 
-        float boundaryWidthProportion = .5 - ((width != 0) ? (2 * boundarySizeX) / width : 0);
-        float boundaryHeightProportion = .5 - ((height != 0) ? (2 * boundarySizeY) / height : 0);
-        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
-                boundaryWidthProportion, widthSlot, lengthSlot);
+        // draw left
+        AlphaVertex::set(aVertices++, left, bottom, 0);
+        AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
+        AlphaVertex::set(aVertices++, left, top, 0);
+        AlphaVertex::set(aVertices++, innerLeft, innerTop, 1);
 
-        int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
-        glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
+        // draw top
+        AlphaVertex::set(aVertices++, right, top, 0);
+        AlphaVertex::set(aVertices++, innerRight, innerTop, 1);
 
-        AAVertex::set(aaVertices++, left, bottom, 1, 1);
-        AAVertex::set(aaVertices++, left, top, 1, 0);
-        AAVertex::set(aaVertices++, right, bottom, 0, 1);
-        AAVertex::set(aaVertices++, right, top, 0, 0);
+        // draw right
+        AlphaVertex::set(aVertices++, right, bottom, 0);
+        AlphaVertex::set(aVertices++, innerRight, innerBottom, 1);
+
+        // draw bottom
+        AlphaVertex::set(aVertices++, left, bottom, 0);
+        AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
+
+        // draw inner rect (repeating last vertex to create degenerate bridge triangles)
+        // TODO: also consider drawing the inner rect without the blending-forced shader, if
+        // blending is expensive. Note: can't use drawColorRect() since it doesn't use vertex
+        // buffers like below, resulting in slightly different transformed coordinates.
+        AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
+        AlphaVertex::set(aVertices++, innerLeft, innerTop, 1);
+        AlphaVertex::set(aVertices++, innerRight, innerBottom, 1);
+        AlphaVertex::set(aVertices++, innerRight, innerTop, 1);
+
         dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
 
-        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 14);
 
-        finishDrawAALine(widthSlot, lengthSlot);
+        glDisableVertexAttribArray(alphaSlot);
     }
 }
 
@@ -1874,10 +1899,8 @@
             Matrix4 *mat = mSnapshot->transform;
             float m00 = mat->data[Matrix4::kScaleX];
             float m01 = mat->data[Matrix4::kSkewY];
-            float m02 = mat->data[2];
             float m10 = mat->data[Matrix4::kSkewX];
             float m11 = mat->data[Matrix4::kScaleX];
-            float m12 = mat->data[6];
 
             float scaleX = sqrtf(m00 * m00 + m01 * m01);
             float scaleY = sqrtf(m10 * m10 + m11 * m11);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 2369f47..b7e57a6 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -440,7 +440,6 @@
      * @param color The rectangle's ARGB color, defined as a packed 32 bits word
      * @param mode The Skia xfermode to use
      * @param ignoreTransform True if the current transform should be ignored
-     * @param ignoreBlending True if the blending is set by the caller
      */
     void drawColorRect(float left, float top, float right, float bottom,
             int color, SkXfermode::Mode mode, bool ignoreTransform = false);
@@ -655,6 +654,7 @@
     void setupDrawWithExternalTexture();
     void setupDrawNoTexture();
     void setupDrawAALine();
+    void setupDrawAARect();
     void setupDrawPoint(float pointSize);
     void setupDrawColor(int color);
     void setupDrawColor(int color, int alpha);
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 1818f82..a3bfaa4 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -81,6 +81,8 @@
 
 #define PROGRAM_IS_SIMPLE_GRADIENT 41
 
+#define PROGRAM_IS_AA_RECT_SHIFT 42
+
 ///////////////////////////////////////////////////////////////////////////////
 // Types
 ///////////////////////////////////////////////////////////////////////////////
@@ -128,6 +130,7 @@
     bool isBitmapNpot;
 
     bool isAA;
+    bool isAARect;
 
     bool hasGradient;
     Gradient gradientType;
@@ -165,6 +168,7 @@
         hasTextureTransform = false;
 
         isAA = false;
+        isAARect = false;
 
         modulate = false;
 
@@ -260,6 +264,7 @@
         if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
         if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
         if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
+        if (isAARect) key |= programid(0x1) << PROGRAM_IS_AA_RECT_SHIFT;
         return key;
     }
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 8a9a2ac..0ed8008 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -43,6 +43,8 @@
 const char* gVS_Header_Attributes_AAParameters =
         "attribute float vtxWidth;\n"
         "attribute float vtxLength;\n";
+const char* gVS_Header_Attributes_AARectParameters =
+        "attribute float vtxAlpha;\n";
 const char* gVS_Header_Uniforms_TextureTransform =
         "uniform mat4 mainTextureTransform;\n";
 const char* gVS_Header_Uniforms =
@@ -65,6 +67,8 @@
 const char* gVS_Header_Varyings_IsAA =
         "varying float widthProportion;\n"
         "varying float lengthProportion;\n";
+const char* gVS_Header_Varyings_IsAARect =
+        "varying float alpha;\n";
 const char* gVS_Header_Varyings_HasBitmap =
         "varying highp vec2 outBitmapTexCoords;\n";
 const char* gVS_Header_Varyings_PointHasBitmap =
@@ -112,6 +116,8 @@
 const char* gVS_Main_AA =
         "    widthProportion = vtxWidth;\n"
         "    lengthProportion = vtxLength;\n";
+const char* gVS_Main_AARect =
+        "    alpha = vtxAlpha;\n";
 const char* gVS_Footer =
         "}\n\n";
 
@@ -242,6 +248,8 @@
 const char* gFS_Main_AccountForAA =
         "    fragColor *= (1.0 - smoothstep(boundaryWidth, 0.5, abs(0.5 - widthProportion)))\n"
         "               * (1.0 - smoothstep(boundaryLength, 0.5, abs(0.5 - lengthProportion)));\n";
+const char* gFS_Main_AccountForAARect =
+        "    fragColor *= alpha;\n";
 
 const char* gFS_Main_FetchTexture[2] = {
         // Don't modulate
@@ -439,7 +447,9 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Attributes_TexCoords);
     }
-    if (description.isAA) {
+    if (description.isAARect) {
+        shader.append(gVS_Header_Attributes_AARectParameters);
+    } else if (description.isAA) {
         shader.append(gVS_Header_Attributes_AAParameters);
     }
     // Uniforms
@@ -460,7 +470,9 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
-    if (description.isAA) {
+    if (description.isAARect) {
+        shader.append(gVS_Header_Varyings_IsAARect);
+    } else if (description.isAA) {
         shader.append(gVS_Header_Varyings_IsAA);
     }
     if (description.hasGradient) {
@@ -479,7 +491,9 @@
         } else if (description.hasTexture || description.hasExternalTexture) {
             shader.append(gVS_Main_OutTexCoords);
         }
-        if (description.isAA) {
+        if (description.isAARect) {
+            shader.append(gVS_Main_AARect);
+        } else if (description.isAA) {
             shader.append(gVS_Main_AA);
         }
         if (description.hasGradient) {
@@ -521,7 +535,9 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
-    if (description.isAA) {
+    if (description.isAARect) {
+        shader.append(gVS_Header_Varyings_IsAARect);
+    } else if (description.isAA) {
         shader.append(gVS_Header_Varyings_IsAA);
     }
     if (description.hasGradient) {
@@ -562,7 +578,8 @@
 
     // Optimization for common cases
     if (!description.isAA && !blendFramebuffer &&
-            description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
+            description.colorOp == ProgramDescription::kColorNone &&
+            !description.isPoint && !description.isAARect) {
         bool fast = false;
 
         const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -654,7 +671,9 @@
                 shader.append(gFS_Main_FetchColor);
             }
         }
-        if (description.isAA) {
+        if (description.isAARect) {
+            shader.append(gFS_Main_AccountForAARect);
+        } else if (description.isAA) {
             shader.append(gFS_Main_AccountForAA);
         }
         if (description.hasGradient) {