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);
+ }
};
///////////////////////////////////////////////////////////////////////////////