Merge "Use LUT for computing final shadow alpha" into nyc-dev
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 20ecda2..3982fa0 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -43,9 +43,7 @@
 /**
  * Other constants:
  */
-// For the edge of the penumbra, the opacity is 0. After transform (1 - alpha),
-// it is 1.
-#define TRANSFORMED_OUTER_OPACITY (1.0f)
+#define OUTER_ALPHA (0.0f)
 
 // Once the alpha difference is greater than this threshold, we will allocate extra
 // edge vertices.
@@ -81,17 +79,6 @@
     return 1.0 / (1 + std::max(factoredZ, 0.0f));
 }
 
-// The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform
-// the alpha value to (1 - alpha)
-inline float getTransformedAlphaFromAlpha(float alpha) {
-    return 1.0f - alpha;
-}
-
-// The output is ranged from 0 to 1.
-inline float getTransformedAlphaFromFactoredZ(float factoredZ) {
-    return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ));
-}
-
 inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike,
         const Vector3& secondVertex, const Vector3& centroid) {
     Vector2 secondSpike  = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
@@ -225,9 +212,9 @@
         if (!isCasterOpaque) {
             umbraVertices[umbraIndex++] = vertexBufferIndex;
         }
-        AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
-                casterVertices[i].y,
-                getTransformedAlphaFromAlpha(currentAlpha));
+        AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
+                casterVertices[i].x, casterVertices[i].y,
+                currentAlpha);
 
         const Vector3& innerStart = casterVertices[i];
 
@@ -249,7 +236,7 @@
             indexBuffer[indexBufferIndex++] = vertexBufferIndex;
             indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
             AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x,
-                    outerVertex.y, TRANSFORMED_OUTER_OPACITY);
+                    outerVertex.y, OUTER_ALPHA);
 
             if (j == 0) {
                 outerStart = outerVertex;
@@ -285,7 +272,7 @@
                     (outerLast * startWeight + outerNext * k) / extraVerticesNumber;
                 indexBuffer[indexBufferIndex++] = vertexBufferIndex;
                 AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
-                        currentOuter.y, TRANSFORMED_OUTER_OPACITY);
+                        currentOuter.y, OUTER_ALPHA);
 
                 if (!isCasterOpaque) {
                     umbraVertices[umbraIndex++] = vertexBufferIndex;
@@ -295,7 +282,7 @@
                 indexBuffer[indexBufferIndex++] = vertexBufferIndex;
                 AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x,
                         currentInner.y,
-                        getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor));
+                        getAlphaFromFactoredZ(currentInner.z * heightFactor));
             }
         }
         currentAlpha = nextAlpha;
@@ -307,7 +294,7 @@
     if (!isCasterOpaque) {
         // Add the centroid as the last one in the vertex buffer.
         float centroidOpacity =
-            getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor);
+            getAlphaFromFactoredZ(centroid3d.z * heightFactor);
         int centroidIndex = vertexBufferIndex;
         AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x,
                 centroid3d.y, centroidOpacity);
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 0f670a8..831473c 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -346,11 +346,12 @@
         bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
         const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset
                 ? TransformFlags::OffsetByFudgeFactor : 0;
+
         Glop glop;
         GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
                 .setRoundRectClipState(state.roundRectClipState)
-                .setMeshVertexBuffer(vertexBuffer, shadowInterp)
-                .setFillPaint(paint, state.alpha)
+                .setMeshVertexBuffer(vertexBuffer)
+                .setFillPaint(paint, state.alpha, shadowInterp)
                 .setTransform(state.computedState.transform, transformFlags)
                 .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
                 .build();
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index eaa1c33..949ad45 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -74,6 +74,7 @@
 
     mPixelBufferState = new PixelBufferState();
     mTextureState = new TextureState();
+    mTextureState->constructTexture(*this);
 
     return true;
 }
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 6a96634..6433c86 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -44,9 +44,14 @@
 
 namespace VertexAttribFlags {
     enum {
+        // Mesh is pure x,y vertex pairs
         None = 0,
+        // Mesh has texture coordinates embedded. Note that texture can exist without this flag
+        // being set, if coordinates passed to sampler are determined another way.
         TextureCoord = 1 << 0,
+        // Mesh has color embedded (to export to varying)
         Color = 1 << 1,
+        // Mesh has alpha embedded (to export to varying)
         Alpha = 1 << 2,
     };
 };
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 0a8e3f3..e502725 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -193,7 +193,7 @@
     return *this;
 }
 
-GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) {
+GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer) {
     TRIGGER_STAGE(kMeshStage);
 
     const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags();
@@ -210,8 +210,6 @@
             alphaVertex ? kAlphaVertexStride : kVertexStride };
     mOutGlop->mesh.elementCount = indices
                 ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
-
-    mDescription.useShadowAlphaInterp = shadowInterp;
     return *this;
 }
 
@@ -368,15 +366,23 @@
     return *this;
 }
 
-GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale) {
+GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp) {
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
 
-    mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+    if (CC_LIKELY(!shadowInterp)) {
+        mOutGlop->fill.texture = {
+                nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+    } else {
+        mOutGlop->fill.texture = {
+                mCaches.textureState().getShadowLutTexture(), GL_TEXTURE_2D,
+                GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+    }
 
     setFill(paint.getColor(), alphaScale,
             PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
             paint.getShader(), paint.getColorFilter());
+    mDescription.useShadowAlphaInterp = shadowInterp;
     mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
     return *this;
 }
@@ -593,8 +599,11 @@
 void verify(const ProgramDescription& description, const Glop& glop) {
     if (glop.fill.texture.texture != nullptr) {
         LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture)
-                        || (!description.hasTexture && !description.hasExternalTexture)
-                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0)),
+                        || (!description.hasTexture
+                                && !description.hasExternalTexture
+                                && !description.useShadowAlphaInterp)
+                        || ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0
+                                && !description.useShadowAlphaInterp)),
                 "Texture %p, hT%d, hET %d, attribFlags %x",
                 glop.fill.texture.texture,
                 description.hasTexture, description.hasExternalTexture,
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index a9dd56f..1c520c2 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -51,14 +51,14 @@
     GlopBuilder& setMeshUnitQuad();
     GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
     GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
-    GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp);
+    GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer);
     GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount);
     GlopBuilder& setMeshTexturedMesh(TextureVertex* vertexData, int elementCount); // TODO: delete
     GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads
     GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount
     GlopBuilder& setMeshPatchQuads(const Patch& patch);
 
-    GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale);
+    GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp = false); // TODO: avoid boolean with default
     GlopBuilder& setFillTexturePaint(Texture& texture, const int textureFillFlags,
             const SkPaint* paint, float alphaScale);
     GlopBuilder& setFillPathTexturePaint(PathTexture& texture,
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 53ea7fa..b68240a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1697,8 +1697,8 @@
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
-            .setMeshVertexBuffer(vertexBuffer, shadowInterp)
-            .setFillPaint(*paint, currentSnapshot()->alpha)
+            .setMeshVertexBuffer(vertexBuffer)
+            .setFillPaint(*paint, currentSnapshot()->alpha, shadowInterp)
             .setTransform(*currentSnapshot(), transformFlags)
             .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
             .build();
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 05be488..59225e1 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -231,9 +231,8 @@
 const char* gFS_Main_ApplyVertexAlphaLinearInterp =
         "    fragColor *= alpha;\n";
 const char* gFS_Main_ApplyVertexAlphaShadowInterp =
-        // Use a gaussian function for the shadow fall off. Note that alpha here
-        // is actually (1.0 - alpha) for saving computation.
-        "    fragColor *= exp(- alpha * alpha * 4.0) - 0.018;\n";
+        // map alpha through shadow alpha sampler
+        "    fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
 const char* gFS_Main_FetchTexture[2] = {
         // Don't modulate
         "    fragColor = texture2D(baseSampler, outTexCoords);\n",
@@ -565,7 +564,7 @@
         shader.append(gFS_Uniforms_Color);
         if (!singleColor) modulateOp = MODULATE_OP_MODULATE;
     }
-    if (description.hasTexture) {
+    if (description.hasTexture || description.useShadowAlphaInterp) {
         shader.append(gFS_Uniforms_TextureSampler);
     } else if (description.hasExternalTexture) {
         shader.append(gFS_Uniforms_ExternalTextureSampler);
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 759e39b..760d814 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -42,9 +42,8 @@
 // For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
 #define SPOT_CORNER_RADIANS_DIVISOR (M_PI / SPOT_EXTRA_CORNER_VERTEX_PER_PI)
 
-// For performance, we use (1 - alpha) value for the shader input.
-#define TRANSFORMED_PENUMBRA_ALPHA 1.0f
-#define TRANSFORMED_UMBRA_ALPHA 0.0f
+#define PENUMBRA_ALPHA 0.0f
+#define UMBRA_ALPHA 1.0f
 
 #include "SpotShadow.h"
 
@@ -941,11 +940,11 @@
     // Fill the IB and VB for the penumbra area.
     for (int i = 0; i < newPenumbraLength; i++) {
         AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x,
-                newPenumbra[i].y, TRANSFORMED_PENUMBRA_ALPHA);
+                newPenumbra[i].y, PENUMBRA_ALPHA);
     }
     for (int i = 0; i < umbraLength; i++) {
         AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y,
-                TRANSFORMED_UMBRA_ALPHA);
+                UMBRA_ALPHA);
     }
 
     for (int i = 0; i < verticesPairIndex; i++) {
@@ -985,14 +984,14 @@
             indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
             indexBuffer[indexBufferIndex++] = vertexBufferIndex;
             AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
-                    closerVertex.x, closerVertex.y, TRANSFORMED_UMBRA_ALPHA);
+                    closerVertex.x, closerVertex.y, UMBRA_ALPHA);
         }
     } else {
         // If there is no occluded umbra at all, then draw the triangle fan
         // starting from the centroid to all umbra vertices.
         int lastCentroidIndex = vertexBufferIndex;
         AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x,
-                centroid.y, TRANSFORMED_UMBRA_ALPHA);
+                centroid.y, UMBRA_ALPHA);
         for (int i = 0; i < umbraLength; i++) {
             indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
             indexBuffer[indexBufferIndex++] = lastCentroidIndex;
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index ea4391b..e78cd72 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -298,7 +298,8 @@
     // indices
     meshState().bindIndicesBuffer(indices.bufferObject);
 
-    if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
+    // texture
+    if (fill.texture.texture != nullptr) {
         const Glop::Fill::TextureData& texture = fill.texture;
         // texture always takes slot 0, shader samplers increment from there
         mCaches->textureState().activateTexture(0);
@@ -311,13 +312,16 @@
             texture.texture->setFilter(texture.filter, false, false, texture.target);
         }
 
-        meshState().enableTexCoordsVertexArray();
-        meshState().bindTexCoordsVertexPointer(vertices.texCoord, vertices.stride);
-
         if (texture.textureTransform) {
             glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1,
                     GL_FALSE, &texture.textureTransform->data[0]);
         }
+    }
+
+    // vertex attributes (tex coord, color, alpha)
+    if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
+        meshState().enableTexCoordsVertexArray();
+        meshState().bindTexCoordsVertexPointer(vertices.texCoord, vertices.stride);
     } else {
         meshState().disableTexCoordsVertexArray();
     }
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
index 78b8eda..f9a1f8c 100644
--- a/libs/hwui/renderstate/TextureState.cpp
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -26,6 +26,9 @@
 namespace android {
 namespace uirenderer {
 
+// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is
+static const int SHADOW_LUT_SIZE = 128;
+
 // Must define as many texture units as specified by kTextureUnitsCount
 const GLenum kTextureUnits[] = {
     GL_TEXTURE0,
@@ -46,6 +49,41 @@
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 }
 
+TextureState::~TextureState() {
+    if (mShadowLutTexture != nullptr) {
+        mShadowLutTexture->deleteTexture();
+    }
+}
+
+/**
+ * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
+ * darkness at that spot. Input values of 0->1 should be mapped within the same
+ * range, but can affect the curve for a different visual falloff.
+ *
+ * This is used to populate the shadow LUT texture for quick lookup in the
+ * shadow shader.
+ */
+static float computeShadowOpacity(float ratio) {
+    // exponential falloff function provided by UX
+    float val = 1 - ratio;
+    return exp(-val * val * 4.0) - 0.018;
+}
+
+void TextureState::constructTexture(Caches& caches) {
+    if (mShadowLutTexture == nullptr) {
+        mShadowLutTexture.reset(new Texture(caches));
+
+        unsigned char bytes[SHADOW_LUT_SIZE];
+        for (int i = 0; i < SHADOW_LUT_SIZE; i++) {
+            float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f);
+            bytes[i] = computeShadowOpacity(inputRatio) * 255;
+        }
+        mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes);
+        mShadowLutTexture->setFilter(GL_LINEAR);
+        mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE);
+    }
+}
+
 void TextureState::activateTexture(GLuint textureUnit) {
     LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount,
             "Tried to use texture unit index %d, only %d exist",
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
index ec94d7e..7296fd3 100644
--- a/libs/hwui/renderstate/TextureState.h
+++ b/libs/hwui/renderstate/TextureState.h
@@ -17,14 +17,12 @@
 #define RENDERSTATE_TEXTURESTATE_H
 
 #include "Vertex.h"
+#include "Texture.h"
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
-#include <SkXfermode.h>
 #include <memory>
 
-class SkBitmap;
-
 namespace android {
 namespace uirenderer {
 
@@ -33,6 +31,9 @@
 class TextureState {
     friend class Caches; // TODO: move to RenderState
 public:
+
+    void constructTexture(Caches& caches);
+
     /**
      * Activate the specified texture unit. The texture unit must
      * be specified using an integer number (0 for GL_TEXTURE0 etc.)
@@ -76,15 +77,20 @@
      */
     void unbindTexture(GLuint texture);
 
+    Texture* getShadowLutTexture() { return mShadowLutTexture.get(); }
+
 private:
     // total number of texture units available for use
     static const int kTextureUnitsCount = 4;
 
     TextureState();
+    ~TextureState();
     GLuint mTextureUnit;
 
     // Caches texture bindings for the GL_TEXTURE_2D target
     GLuint mBoundTextures[kTextureUnitsCount];
+
+    std::unique_ptr<Texture> mShadowLutTexture;
 };
 
 } /* namespace uirenderer */