Fix orthographic shadows projection, simplify shadow reordering

Separate matrix passed to shadow system into two parts, one for
transforming the polygon XY points (using the actual draw matrix) and
a separate one which respects correct 4x4 3d rotations and
translations for determining Z values.

Change-Id: I7e30a84774a8709df6b2241e8f51fc5583648fe8
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 5c5a0425..b954c1f 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -304,8 +304,11 @@
 
 /**
  * Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
  */
-void DisplayList::applyViewPropertyTransforms(mat4& matrix) {
+void DisplayList::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
     if (mLeft != 0 || mTop != 0) {
         matrix.translate(mLeft, mTop);
     }
@@ -319,15 +322,31 @@
     if (mMatrixFlags != 0) {
         updateMatrix();
         if (mMatrixFlags == TRANSLATION) {
-            matrix.translate(mTranslationX, mTranslationY, mTranslationZ);
+            matrix.translate(mTranslationX, mTranslationY,
+                    true3dTransform ? mTranslationZ : 0.0f);
         } else {
-            matrix.multiply(*mTransformMatrix);
+            if (!true3dTransform) {
+                matrix.multiply(*mTransformMatrix);
+            } else {
+                mat4 true3dMat;
+                true3dMat.loadTranslate(
+                        mPivotX + mTranslationX,
+                        mPivotY + mTranslationY,
+                        mTranslationZ);
+                true3dMat.rotate(mRotationX, 1, 0, 0);
+                true3dMat.rotate(mRotationY, 0, 1, 0);
+                true3dMat.rotate(mRotation, 0, 0, 1);
+                true3dMat.scale(mScaleX, mScaleY, 1);
+                true3dMat.translate(-mPivotX, -mPivotY);
+
+                matrix.multiply(true3dMat);
+            }
         }
     }
 }
 
 /**
- * Organizes the DisplayList hierarchy to prepare for Z-based draw order.
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
  *
  * This should be called before a call to defer() or drawDisplayList()
  *
@@ -336,7 +355,6 @@
  */
 void DisplayList::computeOrdering() {
     ATRACE_CALL();
-    m3dNodes.clear();
     mProjectedNodes.clear();
 
     // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
@@ -345,40 +363,23 @@
     for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
         DrawDisplayListOp* childOp = mDisplayListData->children[i];
         childOp->mDisplayList->computeOrderingImpl(childOp,
-                &m3dNodes, &mat4::identity(),
                 &mProjectedNodes, &mat4::identity());
     }
 }
 
 void DisplayList::computeOrderingImpl(
         DrawDisplayListOp* opState,
-        Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot,
-        const mat4* transformFrom3dRoot,
         Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
         const mat4* transformFromProjectionSurface) {
-    m3dNodes.clear();
     mProjectedNodes.clear();
     if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
 
     // TODO: should avoid this calculation in most cases
     // TODO: just calculate single matrix, down to all leaf composited elements
-    Matrix4 localTransformFrom3dRoot(*transformFrom3dRoot);
-    localTransformFrom3dRoot.multiply(opState->mTransformFromParent);
     Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
     localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
 
-    if (mTranslationZ != 0.0f) { // TODO: other signals for 3d compositing, such as custom matrix4
-        // composited 3d layer, flag for out of order draw and save matrix...
-        opState->mSkipInOrderDraw = true;
-        opState->mTransformFromCompositingAncestor.load(localTransformFrom3dRoot);
-
-        // ... and insert into current 3d root, keyed with pivot z for later sorting
-        Vector3 pivot(mPivotX, mPivotY, 0.0f);
-        mat4 totalTransform(localTransformFrom3dRoot);
-        applyViewPropertyTransforms(totalTransform);
-        totalTransform.mapPoint3d(pivot);
-        compositedChildrenOf3dRoot->add(ZDrawDisplayListOpPair(pivot.z, opState));
-    } else if (mProjectBackwards) {
+    if (mProjectBackwards) {
         // composited projectee, flag for out of order draw, save matrix, and store in proj surface
         opState->mSkipInOrderDraw = true;
         opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
@@ -389,15 +390,6 @@
     }
 
     if (mDisplayListData->children.size() > 0) {
-        if (mIsolatedZVolume) {
-            // create a new 3d space for descendents by collecting them
-            compositedChildrenOf3dRoot = &m3dNodes;
-            transformFrom3dRoot = &mat4::identity();
-        } else {
-            applyViewPropertyTransforms(localTransformFrom3dRoot);
-            transformFrom3dRoot = &localTransformFrom3dRoot;
-        }
-
         const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
         bool haveAppliedPropertiesToProjection = false;
         for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
@@ -422,9 +414,7 @@
                 projectionChildren = compositedChildrenOfProjectionSurface;
                 projectionTransform = &localTransformFromProjectionSurface;
             }
-            child->computeOrderingImpl(childOp,
-                    compositedChildrenOf3dRoot, transformFrom3dRoot,
-                    projectionChildren, projectionTransform);
+            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
         }
     }
 
@@ -477,14 +467,36 @@
             replayStruct.mDrawGlStatus);
 }
 
+void DisplayList::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+    if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
+
+    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+        DrawDisplayListOp* childOp = mDisplayListData->children[i];
+        DisplayList* child = childOp->mDisplayList;
+        float childZ = child->mTranslationZ;
+
+        if (childZ != 0.0f) {
+            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+            childOp->mSkipInOrderDraw = true;
+        } else if (!child->mProjectBackwards) {
+            // regular, in order drawing DisplayList
+            childOp->mSkipInOrderDraw = false;
+        }
+    }
+
+    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
 #define SHADOW_DELTA 0.1f
 
 template <class T>
-void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
-        T& handler, const int level) {
-    if (m3dNodes.size() == 0 ||
-            (mode == kNegativeZChildren && m3dNodes[0].key > 0.0f) ||
-            (mode == kPositiveZChildren && m3dNodes[m3dNodes.size() - 1].key < 0.0f)) {
+void DisplayList::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+    const int size = zTranslatedNodes.size();
+    if (size == 0
+            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
         // no 3d children to draw
         return;
     }
@@ -502,7 +514,7 @@
      * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
      * underneath both, and neither's shadow is drawn on top of the other.
      */
-    const size_t nonNegativeIndex = findNonNegativeIndex(m3dNodes);
+    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
     size_t drawIndex, shadowIndex, endIndex;
     if (mode == kNegativeZChildren) {
         drawIndex = 0;
@@ -510,24 +522,29 @@
         shadowIndex = endIndex; // draw no shadows
     } else {
         drawIndex = nonNegativeIndex;
-        endIndex = m3dNodes.size();
+        endIndex = size;
         shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
     }
     float lastCasterZ = 0.0f;
     while (shadowIndex < endIndex || drawIndex < endIndex) {
         if (shadowIndex < endIndex) {
-            DrawDisplayListOp* casterOp = m3dNodes[shadowIndex].value;
+            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
             DisplayList* caster = casterOp->mDisplayList;
-            const float casterZ = m3dNodes[shadowIndex].key;
+            const float casterZ = zTranslatedNodes[shadowIndex].key;
             // attempt to render the shadow if the caster about to be drawn is its caster,
             // OR if its caster's Z value is similar to the previous potential caster
             if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
 
                 if (caster->mCastsShadow && caster->mAlpha > 0.0f) {
-                    mat4 shadowMatrix(casterOp->mTransformFromCompositingAncestor);
-                    caster->applyViewPropertyTransforms(shadowMatrix);
+                    mat4 shadowMatrixXY(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixXY);
 
-                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(shadowMatrix,
+                    // Z matrix needs actual 3d transformation, so mapped z values will be correct
+                    mat4 shadowMatrixZ(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+
+                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(
+                            shadowMatrixXY, shadowMatrixZ,
                             caster->mAlpha, &(caster->mOutline), caster->mWidth, caster->mHeight);
                     handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds);
                 }
@@ -542,10 +559,10 @@
         // since it modifies the renderer's matrix
         int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
 
-        DrawDisplayListOp* childOp = m3dNodes[drawIndex].value;
+        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
         DisplayList* child = childOp->mDisplayList;
 
-        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+        renderer.concatMatrix(childOp->mTransformFromParent);
         childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
         handler(childOp, renderer.getSaveCount() - 1, mClipToBounds);
         childOp->mSkipInOrderDraw = true;
@@ -617,11 +634,11 @@
 
     bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight);
     if (!quickRejected) {
-        // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
-        std::stable_sort(m3dNodes.begin(), m3dNodes.end());
+        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+        buildZSortedChildList(zTranslatedNodes);
 
         // for 3d root, draw children with negative z values
-        iterate3dChildren(kNegativeZChildren, renderer, handler, level);
+        iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
 
         DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
         const int saveCountOffset = renderer.getSaveCount() - 1;
@@ -642,7 +659,7 @@
         }
 
         // for 3d root, draw children with positive z values
-        iterate3dChildren(kPositiveZChildren, renderer, handler, level);
+        iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
     }
 
     DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index a3577d4..189b544a 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -573,20 +573,20 @@
 
     void outputViewProperties(const int level);
 
-    void applyViewPropertyTransforms(mat4& matrix);
+    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
 
     void computeOrderingImpl(DrawDisplayListOp* opState,
-            Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot,
-            const mat4* transformFrom3dRoot,
             Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
             const mat4* transformFromProjectionSurface);
 
     template <class T>
     inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
 
+    void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+
     template <class T>
-    inline void iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
-        T& handler, const int level);
+    inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+            ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
 
     template <class T>
     inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
@@ -657,9 +657,6 @@
      * Draw time state - these properties are only set and used during rendering
      */
 
-    // for 3d roots, contains a z sorted list of all children items
-    Vector<ZDrawDisplayListOpPair> m3dNodes;
-
     // for projection surfaces, contains a list of all children items
     Vector<DrawDisplayListOp*> mProjectedNodes;
 }; // class DisplayList
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 65eda29..6ce8317 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1534,10 +1534,10 @@
     const mat4 mTransformFromParent;
 
     /**
-     * Holds the transformation between the 3d root OR projection surface ViewGroup and this
-     * DisplayList drawing instance. Represents any translations / transformations done within the
-     * drawing of the compositing ancestor ViewGroup's draw, before the draw of the View represented
-     * by this DisplayList draw instance.
+     * Holds the transformation between the projection surface ViewGroup and this DisplayList
+     * drawing instance. Represents any translations / transformations done within the drawing of
+     * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
+     * DisplayList draw instance.
      *
      * Note: doesn't include any transformation recorded within the DisplayList and its properties.
      */
@@ -1550,19 +1550,20 @@
  */
 class DrawShadowOp : public DrawOp {
 public:
-    DrawShadowOp(const mat4& transform, float alpha, const SkPath* outline,
+    DrawShadowOp(const mat4& transformXY, const mat4& transformZ, float alpha, const SkPath* outline,
             float fallbackWidth, float fallbackHeight)
-            : DrawOp(NULL), mTransform(transform), mAlpha(alpha), mOutline(outline),
+            : DrawOp(NULL), mTransformXY(transformXY), mTransformZ(transformZ),
+            mAlpha(alpha), mOutline(outline),
             mFallbackWidth(fallbackWidth), mFallbackHeight(fallbackHeight) {}
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
-        if (!mOutline->isEmpty()) {
-            return renderer.drawShadow(mTransform, mAlpha, mOutline);
+        if (mOutline->isEmpty()) {
+            SkPath fakeOutline;
+            fakeOutline.addRect(0, 0, mFallbackWidth, mFallbackHeight);
+            return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, &fakeOutline);
         }
 
-        SkPath fakeOutline;
-        fakeOutline.addRect(0, 0, mFallbackWidth, mFallbackHeight);
-        return renderer.drawShadow(mTransform, mAlpha, &fakeOutline);
+        return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, mOutline);
     }
 
     virtual void output(int level, uint32_t logFlags) const {
@@ -1572,7 +1573,8 @@
     virtual const char* name() { return "DrawShadow"; }
 
 private:
-    const mat4 mTransform;
+    const mat4 mTransformXY;
+    const mat4 mTransformZ;
     const float mAlpha;
     const SkPath* mOutline;
     const float mFallbackWidth;
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 3d6188a..f06106b 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -385,9 +385,14 @@
     mType = kTypeTranslate | kTypeScale | kTypeRectToRect;
 }
 
+float Matrix4::mapZ(const Vector3& orig) const {
+    // duplicates logic for mapPoint3d's z coordinate
+    return orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
+}
+
 void Matrix4::mapPoint3d(Vector3& vec) const {
     //TODO: optimize simple case
-    Vector3 orig(vec);
+    const Vector3 orig(vec);
     vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX];
     vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY];
     vec.z = orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 8b586f0..26cb05f 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -199,6 +199,7 @@
     void copyTo(float* v) const;
     void copyTo(SkMatrix& v) const;
 
+    float mapZ(const Vector3& orig) const;
     void mapPoint3d(Vector3& vec) const;
     void mapPoint(float& x, float& y) const; // 2d only
     void mapRect(Rect& r) const; // 2d only
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7002e26..83de7721 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -3190,8 +3190,16 @@
     return drawColorRects(rects, count, paint, false, true, true);
 }
 
-status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha,
-        const SkPath* casterOutline) {
+static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& transformZ) {
+    // map z coordinate with true 3d matrix
+    point.z = transformZ.mapZ(point);
+
+    // map x,y coordinates with draw/Skia matrix
+    transformXY.mapPoint(point.x, point.y);
+}
+
+status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
+        float casterAlpha, const SkPath* casterOutline) {
     if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
 
     // TODO: use quickRejectWithScissor. For now, always force enable scissor.
@@ -3217,7 +3225,7 @@
     for (int i = 0; i < casterVertexCount; i++) {
         const Vertex& point2d = casterVertices2d[i];
         casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
-        casterTransform.mapPoint3d(casterPolygon[i]);
+        mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
     }
 
     // map the centroid of the caster into 3d
@@ -3225,7 +3233,7 @@
             reinterpret_cast<const Vector2*>(casterVertices2d.array()),
             casterVertexCount);
     Vector3 centroid3d(centroid.x, centroid.y, 0);
-    casterTransform.mapPoint3d(centroid3d);
+    mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
 
     // draw caster's shadows
     if (mCaches.propertyAmbientShadowStrength > 0) {
@@ -3244,7 +3252,6 @@
         ShadowTessellator::tessellateSpotShadow(casterPolygon, casterVertexCount,
                 lightPosScale, *currentTransform(), getWidth(), getHeight(),
                 spotShadowVertexBuffer);
-
         drawVertexBuffer(kVertexBufferMode_Shadow, spotShadowVertexBuffer, &paint);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 03beae3..76dd014 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -208,8 +208,8 @@
             DrawOpMode drawOpMode = kDrawOpMode_Immediate);
     virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
 
-    status_t drawShadow(const mat4& casterTransform, float casterAlpha,
-            const SkPath* casterOutline);
+    status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
+            float casterAlpha, const SkPath* casterOutline);
 
     virtual void resetShader();
     virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 54039c01..4e52555 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -35,7 +35,7 @@
  * Calculate the angle between and x and a y coordinate.
  * The atan2 range from -PI to PI.
  */
-float angle(const Vector2& point, const Vector2& center) {
+static float angle(const Vector2& point, const Vector2& center) {
     return atan2(point.y - center.y, point.x - center.x);
 }
 
@@ -51,7 +51,7 @@
  * @param p2 The second point defining the line segment
  * @return The distance along the ray if it intersects with the line segment, negative if otherwise
  */
-float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy,
+static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy,
         const Vector2& p1, const Vector2& p2) {
     // The math below is derived from solving this formula, basically the
     // intersection point should stay on both the ray and the edge of (p1, p2).
@@ -550,12 +550,12 @@
     for (int i = 0; i < polyLength; i++) {
         if (poly[i].z <= 0.00001) {
             inputPolyPositionValid = false;
-            ALOGE("polygon below the surface");
+            ALOGW("polygon below the surface");
             break;
         }
         if (poly[i].z >= lightPoly[0].z) {
             inputPolyPositionValid = false;
-            ALOGE("polygon above the light");
+            ALOGW("polygon above the light");
             break;
         }
     }
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 15b9d6b..c61cb61 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -124,6 +124,10 @@
     Vector3(float px, float py, float pz) :
         x(px), y(py), z(pz) {
     }
+
+    void dump() {
+        ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
+    }
 };
 
 ///////////////////////////////////////////////////////////////////////////////