From f57776b2d195f0937906eb88b777bb55ccc36967 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Fri, 25 Oct 2013 18:30:17 -0700 Subject: 3d view system! True 3d transformations are now supported by DisplayLists and the renderer, initially with the translationZ property on view. Renderer operations used directly by DisplayList (formerly, clip/save/restore/saveLayer) are now more simply managed by allocating them temporarily on the handler's allocator, which exists for a single frame. This is much simpler than continuing to expand the pool of pre-allocated DisplayListOps now that more operations are called directly by DisplayList, especially with z ordered drawing. Still TODO: -APIs for camera positioning, shadows -Make Z apis public, and expose through XML -Make invalidation / input 3d aware Change-Id: I95fe6fa03f9b6ddd34a7e0c6ec8dd9fe47c6c6eb --- core/java/android/view/DisplayList.java | 18 +- core/java/android/view/GLES20DisplayList.java | 27 ++- core/java/android/view/View.java | 33 +++ core/java/android/view/ViewPropertyAnimator.java | 27 ++- core/jni/android_view_GLES20DisplayList.cpp | 13 +- libs/hwui/Debug.h | 3 + libs/hwui/DeferredDisplayList.h | 4 +- libs/hwui/DisplayList.cpp | 264 ++++++++++++++++++----- libs/hwui/DisplayList.h | 131 +++++++---- libs/hwui/DisplayListOp.h | 66 ++++-- libs/hwui/DisplayListRenderer.cpp | 9 +- libs/hwui/DisplayListRenderer.h | 1 + libs/hwui/Layer.cpp | 9 +- libs/hwui/Matrix.cpp | 95 +++++++- libs/hwui/Matrix.h | 14 +- libs/hwui/OpenGLRenderer.cpp | 67 +++++- libs/hwui/OpenGLRenderer.h | 13 +- libs/hwui/Vector.h | 15 ++ 18 files changed, 667 insertions(+), 142 deletions(-) diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index 623472a..daa7537 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -438,6 +438,21 @@ public abstract class DisplayList { public abstract float getTranslationY(); /** + * Sets the translation value for the display list on the Z axis + * + * @see View#setTranslationZ(float) + * @see #getTranslationZ() + */ + public abstract void setTranslationZ(float translationZ); + + /** + * Returns the translation value for this display list on the Z axis. + * + * @see #setTranslationZ(float) + */ + public abstract float getTranslationZ(); + + /** * Sets the rotation value for the display list around the Z axis * * @param rotation The rotation value of the display list, in degrees @@ -536,7 +551,8 @@ public abstract class DisplayList { * * @hide */ - public abstract void setTransformationInfo(float alpha, float translationX, float translationY, + public abstract void setTransformationInfo(float alpha, + float translationX, float translationY, float translationZ, float rotation, float rotationX, float rotationY, float scaleX, float scaleY); /** diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index c652bac..911fc34 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -231,6 +231,21 @@ class GLES20DisplayList extends DisplayList { } @Override + public void setTranslationZ(float translationZ) { + if (hasNativeDisplayList()) { + nSetTranslationZ(mFinalizer.mNativeDisplayList, translationZ); + } + } + + @Override + public float getTranslationZ() { + if (hasNativeDisplayList()) { + return nGetTranslationZ(mFinalizer.mNativeDisplayList); + } + return 0.0f; + } + + @Override public void setRotation(float rotation) { if (hasNativeDisplayList()) { nSetRotation(mFinalizer.mNativeDisplayList, rotation); @@ -306,10 +321,12 @@ class GLES20DisplayList extends DisplayList { } @Override - public void setTransformationInfo(float alpha, float translationX, float translationY, + public void setTransformationInfo(float alpha, + float translationX, float translationY, float translationZ, float rotation, float rotationX, float rotationY, float scaleX, float scaleY) { if (hasNativeDisplayList()) { - nSetTransformationInfo(mFinalizer.mNativeDisplayList, alpha, translationX, translationY, + nSetTransformationInfo(mFinalizer.mNativeDisplayList, alpha, + translationX, translationY, translationZ, rotation, rotationX, rotationY, scaleX, scaleY); } } @@ -459,14 +476,15 @@ class GLES20DisplayList extends DisplayList { boolean hasOverlappingRendering); private static native void nSetTranslationX(int displayList, float translationX); private static native void nSetTranslationY(int displayList, float translationY); + private static native void nSetTranslationZ(int displayList, float translationZ); private static native void nSetRotation(int displayList, float rotation); private static native void nSetRotationX(int displayList, float rotationX); private static native void nSetRotationY(int displayList, float rotationY); private static native void nSetScaleX(int displayList, float scaleX); private static native void nSetScaleY(int displayList, float scaleY); private static native void nSetTransformationInfo(int displayList, float alpha, - float translationX, float translationY, float rotation, float rotationX, - float rotationY, float scaleX, float scaleY); + float translationX, float translationY, float translationZ, + float rotation, float rotationX, float rotationY, float scaleX, float scaleY); private static native void nSetStaticMatrix(int displayList, int nativeMatrix); private static native void nSetAnimationMatrix(int displayList, int animationMatrix); @@ -482,6 +500,7 @@ class GLES20DisplayList extends DisplayList { private static native float nGetScaleY(int displayList); private static native float nGetTranslationX(int displayList); private static native float nGetTranslationY(int displayList); + private static native float nGetTranslationZ(int displayList); private static native float nGetRotation(int displayList); private static native float nGetRotationX(int displayList); private static native float nGetRotationY(int displayList); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5d264b6..4b97c40 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2981,6 +2981,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.ExportedProperty float mTranslationY = 0f; + @ViewDebug.ExportedProperty + float mTranslationZ = 0f; + /** * The amount of scale in the x direction around the pivot point. A * value of 1 means no scaling is applied. @@ -10484,6 +10487,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + @ViewDebug.ExportedProperty(category = "drawing") + public float getTranslationZ() { + return mTransformationInfo != null ? mTransformationInfo.mTranslationZ : 0; + } + + /** + * @hide + */ + public void setTranslationZ(float translationZ) { + ensureTransformationInfo(); + final TransformationInfo info = mTransformationInfo; + if (info.mTranslationZ != translationZ) { + invalidateViewProperty(true, false); + info.mTranslationZ = translationZ; + info.mMatrixDirty = true; + invalidateViewProperty(false, true); + if (mDisplayList != null) { + mDisplayList.setTranslationZ(translationZ); + } + if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) { + // View was rejected last time it was drawn by its parent; this may have changed + invalidateParentIfNeeded(); + } + } + } + + /** * Hit rectangle in parent's coordinates * * @param outRect The hit rectangle of the view. @@ -14142,6 +14174,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } displayList.setTransformationInfo(alpha, mTransformationInfo.mTranslationX, mTransformationInfo.mTranslationY, + mTransformationInfo.mTranslationZ, mTransformationInfo.mRotation, mTransformationInfo.mRotationX, mTransformationInfo.mRotationY, mTransformationInfo.mScaleX, mTransformationInfo.mScaleY); diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 67a94be..391b345 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -144,9 +144,10 @@ public class ViewPropertyAnimator { private static final int X = 0x0080; private static final int Y = 0x0100; private static final int ALPHA = 0x0200; + private static final int TRANSLATION_Z = 0x0400; - private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y | - ROTATION | ROTATION_X | ROTATION_Y | X | Y; + private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | TRANSLATION_Z | + SCALE_X | SCALE_Y | ROTATION | ROTATION_X | ROTATION_Y | X | Y; /** * The mechanism by which the user can request several properties that are then animated @@ -573,6 +574,22 @@ public class ViewPropertyAnimator { } /** + * @hide + */ + public ViewPropertyAnimator translationZ(float value) { + animateProperty(TRANSLATION_Z, value); + return this; + } + + /** + * @hide + */ + public ViewPropertyAnimator translationZBy(float value) { + animatePropertyBy(TRANSLATION_Z, value); + return this; + } + + /** * This method will cause the View's translationY property to be animated to the * specified value. Animations already running on the property will be canceled. * @@ -909,6 +926,10 @@ public class ViewPropertyAnimator { info.mTranslationY = value; if (displayList != null) displayList.setTranslationY(value); break; + case TRANSLATION_Z: + info.mTranslationZ = value; + if (displayList != null) displayList.setTranslationZ(value); + break; case ROTATION: info.mRotation = value; if (displayList != null) displayList.setRotation(value); @@ -957,6 +978,8 @@ public class ViewPropertyAnimator { return info.mTranslationX; case TRANSLATION_Y: return info.mTranslationY; + case TRANSLATION_Z: + return info.mTranslationZ; case ROTATION: return info.mRotation; case ROTATION_X: diff --git a/core/jni/android_view_GLES20DisplayList.cpp b/core/jni/android_view_GLES20DisplayList.cpp index 4ce2e24..24b411a7 100644 --- a/core/jni/android_view_GLES20DisplayList.cpp +++ b/core/jni/android_view_GLES20DisplayList.cpp @@ -109,6 +109,11 @@ static void android_view_GLES20DisplayList_setTranslationY(JNIEnv* env, displayList->setTranslationY(ty); } +static void android_view_GLES20DisplayList_setTranslationZ(JNIEnv* env, + jobject clazz, DisplayList* displayList, float tz) { + displayList->setTranslationZ(tz); +} + static void android_view_GLES20DisplayList_setRotation(JNIEnv* env, jobject clazz, DisplayList* displayList, float rotation) { displayList->setRotation(rotation); @@ -136,11 +141,12 @@ static void android_view_GLES20DisplayList_setScaleY(JNIEnv* env, static void android_view_GLES20DisplayList_setTransformationInfo(JNIEnv* env, jobject clazz, DisplayList* displayList, float alpha, - float translationX, float translationY, float rotation, float rotationX, float rotationY, - float scaleX, float scaleY) { + float translationX, float translationY, float translationZ, + float rotation, float rotationX, float rotationY, float scaleX, float scaleY) { displayList->setAlpha(alpha); displayList->setTranslationX(translationX); displayList->setTranslationY(translationY); + displayList->setTranslationZ(translationZ); displayList->setRotation(rotation); displayList->setRotationX(rotationX); displayList->setRotationY(rotationY); @@ -314,12 +320,13 @@ static JNINativeMethod gMethods[] = { (void*) android_view_GLES20DisplayList_setHasOverlappingRendering }, { "nSetTranslationX", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationX }, { "nSetTranslationY", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationY }, + { "nSetTranslationZ", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationZ }, { "nSetRotation", "(IF)V", (void*) android_view_GLES20DisplayList_setRotation }, { "nSetRotationX", "(IF)V", (void*) android_view_GLES20DisplayList_setRotationX }, { "nSetRotationY", "(IF)V", (void*) android_view_GLES20DisplayList_setRotationY }, { "nSetScaleX", "(IF)V", (void*) android_view_GLES20DisplayList_setScaleX }, { "nSetScaleY", "(IF)V", (void*) android_view_GLES20DisplayList_setScaleY }, - { "nSetTransformationInfo","(IFFFFFFFF)V", + { "nSetTransformationInfo","(IFFFFFFFFF)V", (void*) android_view_GLES20DisplayList_setTransformationInfo }, { "nSetPivotX", "(IF)V", (void*) android_view_GLES20DisplayList_setPivotX }, { "nSetPivotY", "(IF)V", (void*) android_view_GLES20DisplayList_setPivotY }, diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 786f12a..79afad1 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -85,6 +85,9 @@ // Turn on to highlight drawing batches and merged batches with different colors #define DEBUG_MERGE_BEHAVIOR 0 +// Turn on to enable 3D support in the renderer (off by default until API for control exists) +#define DEBUG_ENABLE_3D 0 + #if DEBUG_INIT #define INIT_LOGD(...) ALOGD(__VA_ARGS__) #else diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 3dcbd0b..fca3588 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -79,13 +79,13 @@ public: }; class DeferredDisplayList { + friend class DeferStateStruct; // used to give access to allocator public: DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : mBounds(bounds), mAvoidOverdraw(avoidOverdraw) { clear(); } ~DeferredDisplayList() { clear(); } - void reset(const Rect& bounds) { mBounds.set(bounds); } enum OpBatchId { kOpBatch_None = 0, // Don't batch @@ -120,6 +120,8 @@ public: void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); private: + DeferredDisplayList(const DeferredDisplayList& other); // disallow copy + DeferredDisplayState* createState() { return new (mAllocator) DeferredDisplayState(); } diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index a3e4bb4..c616cd8 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -14,8 +14,12 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_VIEW + #include +#include + #include "Debug.h" #include "DisplayList.h" #include "DisplayListOp.h" @@ -65,11 +69,6 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) { void DisplayList::clearResources() { mDisplayListData = NULL; - mClipRectOp = NULL; - mSaveLayerOp = NULL; - mSaveOp = NULL; - mRestoreToCountOp = NULL; - delete mTransformMatrix; delete mTransformCamera; delete mTransformMatrix3D; @@ -168,17 +167,6 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde return; } - // allocate reusable ops for state-deferral - LinearAllocator& alloc = mDisplayListData->allocator; - mClipRectOp = new (alloc) ClipRectOp(); - mSaveLayerOp = new (alloc) SaveLayerOp(); - mSaveOp = new (alloc) SaveOp(); - mRestoreToCountOp = new (alloc) RestoreToCountOp(); - if (CC_UNLIKELY(!mSaveOp)) { // temporary debug logging - ALOGW("Error: %s's SaveOp not allocated, size %d", getName(), mSize); - CRASH(); - } - mFunctorCount = recorder.getFunctorCount(); Caches& caches = Caches::getInstance(); @@ -253,6 +241,7 @@ void DisplayList::init() { mHasOverlappingRendering = true; mTranslationX = 0; mTranslationY = 0; + mTranslationZ = 0; mRotation = 0; mRotationX = 0; mRotationY= 0; @@ -269,6 +258,7 @@ void DisplayList::init() { mHeight = 0; mPivotExplicitlySet = false; mCaching = false; + mIs3dRoot = true; // TODO: setter, java side impl } size_t DisplayList::getSize() { @@ -320,27 +310,38 @@ void DisplayList::updateMatrix() { mPivotY = mPrevHeight / 2.0f; } } - if ((mMatrixFlags & ROTATION_3D) == 0) { + if (!DEBUG_ENABLE_3D && (mMatrixFlags & ROTATION_3D) == 0) { mTransformMatrix->setTranslate(mTranslationX, mTranslationY); mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY); mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); } else { - if (!mTransformCamera) { - mTransformCamera = new Sk3DView(); - mTransformMatrix3D = new SkMatrix(); + if (DEBUG_ENABLE_3D) { + mTransform.loadTranslate(mPivotX + mTranslationX, mPivotY + mTranslationY, + mTranslationZ); + mTransform.rotate(mRotationX, 1, 0, 0); + mTransform.rotate(mRotationY, 0, 1, 0); + mTransform.rotate(mRotation, 0, 0, 1); + mTransform.scale(mScaleX, mScaleY, 1); + mTransform.translate(-mPivotX, -mPivotY); + } else { + /* TODO: support this old transform approach, based on API level */ + if (!mTransformCamera) { + mTransformCamera = new Sk3DView(); + mTransformMatrix3D = new SkMatrix(); + } + mTransformMatrix->reset(); + mTransformCamera->save(); + mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); + mTransformCamera->rotateX(mRotationX); + mTransformCamera->rotateY(mRotationY); + mTransformCamera->rotateZ(-mRotation); + mTransformCamera->getMatrix(mTransformMatrix3D); + mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY); + mTransformMatrix3D->postTranslate(mPivotX + mTranslationX, + mPivotY + mTranslationY); + mTransformMatrix->postConcat(*mTransformMatrix3D); + mTransformCamera->restore(); } - mTransformMatrix->reset(); - mTransformCamera->save(); - mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); - mTransformCamera->rotateX(mRotationX); - mTransformCamera->rotateY(mRotationY); - mTransformCamera->rotateZ(-mRotation); - mTransformCamera->getMatrix(mTransformMatrix3D); - mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY); - mTransformMatrix3D->postTranslate(mPivotX + mTranslationX, - mPivotY + mTranslationY); - mTransformMatrix->postConcat(*mTransformMatrix3D); - mTransformCamera->restore(); } } mMatrixDirty = false; @@ -417,8 +418,13 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, if (mMatrixFlags != 0) { if (mMatrixFlags == TRANSLATION) { renderer.translate(mTranslationX, mTranslationY); + renderer.translateZ(mTranslationZ); } else { +#if DEBUG_ENABLE_3D + renderer.concatMatrix(mTransform); +#else renderer.concatMatrix(mTransformMatrix); +#endif } } bool clipToBoundsNeeded = mCaching ? false : mClipToBounds; @@ -436,14 +442,107 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, saveFlags |= SkCanvas::kClipToLayer_SaveFlag; clipToBoundsNeeded = false; // clipping done by saveLayer } - handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, - mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT, - mClipToBounds); + + SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( + 0, 0, mRight - mLeft, mBottom - mTop, + mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags); + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); } } if (clipToBoundsNeeded) { - handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op), - PROPERTY_SAVECOUNT, mClipToBounds); + ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0, + mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op); + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); + } +} + +/** + * Apply property-based transformations to input matrix + */ +void DisplayList::applyViewPropertyTransforms(mat4& matrix) { + if (mLeft != 0 || mTop != 0) { + matrix.translate(mLeft, mTop); + } + if (mStaticMatrix) { + mat4 stat(*mStaticMatrix); + matrix.multiply(stat); + } else if (mAnimationMatrix) { + mat4 anim(*mAnimationMatrix); + matrix.multiply(anim); + } + if (mMatrixFlags != 0) { + if (mMatrixFlags == TRANSLATION) { + matrix.translate(mTranslationX, mTranslationY, mTranslationZ); + } else { +#if DEBUG_ENABLE_3D + matrix.multiply(mTransform); +#else + mat4 temp(*mTransformMatrix); + matrix.multiply(temp); +#endif + } + } +} + +/** + * Organizes the DisplayList hierarchy to prepare for Z-based draw order. + * + * This should be called before a call to defer() or drawDisplayList() + * + * Each DisplayList that serves as a 3d root builds its list of composited children, + * which are flagged to not draw in the standard draw loop. + */ +void DisplayList::computeOrdering() { + ATRACE_CALL(); + mat4::identity(); + for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children[i]; + childOp->mDisplayList->computeOrderingImpl(childOp, &m3dNodes, &mat4::identity()); + } +} + +void DisplayList::computeOrderingImpl( + DrawDisplayListOp* opState, + KeyedVector >* compositedChildrenOf3dRoot, + const mat4* transformFrom3dRoot) { + // TODO: should avoid this calculation in most cases + opState->mTransformFrom3dRoot.load(*transformFrom3dRoot); + opState->mTransformFrom3dRoot.multiply(opState->mTransformFromParent); + + if (mTranslationZ != 0.0f) { // TODO: other signals, such as custom 4x4 matrix + // composited layer, insert into current 3d root and flag for out of order draw + opState->mSkipInOrderDraw = true; + + Vector3 pivot(mPivotX, mPivotY, 0.0f); + mat4 totalTransform(opState->mTransformFrom3dRoot); + applyViewPropertyTransforms(totalTransform); + totalTransform.mapPoint3d(pivot); + const float key = pivot.z; + + if (compositedChildrenOf3dRoot->indexOfKey(key) < 0) { + compositedChildrenOf3dRoot->add(key, Vector()); + } + compositedChildrenOf3dRoot->editValueFor(key).push(opState); + } else { + // standard in order draw + opState->mSkipInOrderDraw = false; + } + + m3dNodes.clear(); + if (mIs3dRoot) { + // create a new 3d space for children by separating their ordering + compositedChildrenOf3dRoot = &m3dNodes; + transformFrom3dRoot = &mat4::identity(); + } else { + transformFrom3dRoot = &(opState->mTransformFrom3dRoot); + } + + if (mDisplayListData->children.size() > 0) { + for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children[i]; + childOp->mDisplayList->computeOrderingImpl(childOp, + compositedChildrenOf3dRoot, transformFrom3dRoot); + } } } @@ -454,6 +553,8 @@ public: inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); } + inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } + private: DeferStateStruct& mDeferStruct; const int mLevel; @@ -474,6 +575,8 @@ public: #endif operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); } + inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } + private: ReplayStateStruct& mReplayStruct; const int mLevel; @@ -490,11 +593,60 @@ void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) { replayStruct.mDrawGlStatus); } +template +void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer, + T& handler, const int level) { + if (m3dNodes.size() == 0 || + (mode == kNegativeZChildren && m3dNodes.keyAt(0) > 0.0f) || + (mode == kPositiveZChildren && m3dNodes.keyAt(m3dNodes.size() - 1) < 0.0f)) { + // nothing to draw + return; + } + + LinearAllocator& alloc = handler.allocator(); + ClipRectOp* op = new (alloc) ClipRectOp(0, 0, mWidth, mHeight, + SkRegion::kIntersect_Op); // clip to 3d root bounds for now + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); + int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + + for (int i = 0; i < m3dNodes.size(); i++) { + const float zValue = m3dNodes.keyAt(i); + + if (mode == kPositiveZChildren && zValue < 0.0f) continue; + if (mode == kNegativeZChildren && zValue > 0.0f) break; + + const Vector& nodesAtZ = m3dNodes[i]; + for (int j = 0; j < nodesAtZ.size(); j++) { + DrawDisplayListOp* op = nodesAtZ[j]; + if (mode == kPositiveZChildren) { + /* draw shadow on renderer with parent matrix applied, passing in the child's total matrix + * + * TODO: + * -determine and pass background shape (and possibly drawable alpha) + * -view must opt-in to shadows + * -consider shadows for other content + */ + mat4 shadowMatrix(op->mTransformFrom3dRoot); + op->mDisplayList->applyViewPropertyTransforms(shadowMatrix); + DisplayListOp* shadowOp = new (alloc) DrawShadowOp(shadowMatrix, op->mDisplayList->mAlpha, + op->mDisplayList->getWidth(), op->mDisplayList->getHeight()); + handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds); + } + + renderer.concatMatrix(op->mTransformFrom3dRoot); + op->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + handler(op, renderer.getSaveCount() - 1, mClipToBounds); + op->mSkipInOrderDraw = true; + } + } + handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds); +} + /** * This function serves both defer and replay modes, and will organize the displayList's component * operations for a single frame: * - * Every 'simple' operation that affects just the matrix and alpha (or other factors of + * Every 'simple' state operation that affects just the matrix and alpha (or other factors of * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom * defer logic) and operations in displayListOps are issued through the 'handler' which handles the * defer vs replay logic, per operation @@ -517,8 +669,9 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) clipRect->right, clipRect->bottom); #endif + LinearAllocator& alloc = handler.allocator(); int restoreTo = renderer.getSaveCount(); - handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), PROPERTY_SAVECOUNT, mClipToBounds); DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", @@ -526,30 +679,31 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) setViewProperties(renderer, handler, level + 1); - if (mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight)) { - DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); - renderer.setOverrideLayerAlpha(1.0f); - return; - } + bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight); + if (!quickRejected) { + // for 3d root, draw children with negative z values + iterate3dChildren(kNegativeZChildren, renderer, handler, level); - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - int saveCount = renderer.getSaveCount() - 1; - for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { - DisplayListOp *op = mDisplayListData->displayListOps[i]; + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + const int saveCountOffset = renderer.getSaveCount() - 1; + for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { + DisplayListOp *op = mDisplayListData->displayListOps[i]; #if DEBUG_DISPLAY_LIST - op->output(level + 1); + op->output(level + 1); #endif - logBuffer.writeCommand(level, op->name()); - handler(op, saveCount, mClipToBounds); + logBuffer.writeCommand(level, op->name()); + handler(op, saveCountOffset, mClipToBounds); + } + + // for 3d root, draw children with positive z values + iterate3dChildren(kPositiveZChildren, renderer, handler, level); } DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); + handler(new (alloc) RestoreToCountOp(restoreTo), + PROPERTY_SAVECOUNT, mClipToBounds); renderer.setOverrideLayerAlpha(1.0f); } diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 1cd5f1c..983cc02 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -26,6 +26,7 @@ #include +#include #include #include #include @@ -37,6 +38,8 @@ #include #include "Debug.h" +#include "Matrix.h" +#include "DeferredDisplayList.h" #define TRANSLATION 0x0001 #define ROTATION 0x0002 @@ -65,36 +68,70 @@ class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; +class DrawDisplayListOp; -struct DeferStateStruct { - DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) - : mDeferredList(deferredList), mRenderer(renderer), mReplayFlags(replayFlags) {} - DeferredDisplayList& mDeferredList; +/** + * Holds data used in the playback a tree of DisplayLists. + */ +class PlaybackStateStruct { +protected: + PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator) + : mRenderer(renderer), mReplayFlags(replayFlags), mAllocator(allocator){} + +public: OpenGLRenderer& mRenderer; const int mReplayFlags; + + // Allocator with the lifetime of a single frame. + // replay uses an Allocator owned by the struct, while defer shares the DeferredDisplayList's Allocator + LinearAllocator * const mAllocator; }; -struct ReplayStateStruct { +class DeferStateStruct : public PlaybackStateStruct { +public: + DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) + : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)), mDeferredList(deferredList) {} + + DeferredDisplayList& mDeferredList; +}; + +class ReplayStateStruct : public PlaybackStateStruct { +public: ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags) - : mRenderer(renderer), mDirty(dirty), mReplayFlags(replayFlags), - mDrawGlStatus(DrawGlInfo::kStatusDone) {} - OpenGLRenderer& mRenderer; + : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator), + mDirty(dirty), mDrawGlStatus(DrawGlInfo::kStatusDone) {} + Rect& mDirty; - const int mReplayFlags; status_t mDrawGlStatus; + LinearAllocator mReplayAllocator; }; /** - * Refcounted structure that holds data used in display list stream + * Refcounted structure that holds the list of commands used in display list stream. */ class DisplayListData : public LightRefBase { public: + // allocator into which all ops were allocated LinearAllocator allocator; + + // pointers to all ops within display list, pointing into allocator data Vector displayListOps; + + // list of children display lists for quick, non-drawing traversal + Vector children; }; /** - * Replays recorded drawing commands. + * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. + * + * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording + * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData + * (which holds the actual data), and DisplayList (which holds properties and performs playback onto + * a renderer). + * + * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's + * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay + * attached. */ class DisplayList { public: @@ -113,6 +150,7 @@ public: void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); + void computeOrdering(); void defer(DeferStateStruct& deferStruct, const int level); void replay(ReplayStateStruct& replayStruct, const int level); @@ -188,12 +226,7 @@ public: void setTranslationX(float translationX) { if (translationX != mTranslationX) { mTranslationX = translationX; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } + onTranslationUpdate(); } } @@ -204,12 +237,7 @@ public: void setTranslationY(float translationY) { if (translationY != mTranslationY) { mTranslationY = translationY; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } + onTranslationUpdate(); } } @@ -217,6 +245,17 @@ public: return mTranslationY; } + void setTranslationZ(float translationZ) { + if (translationZ != mTranslationZ) { + mTranslationZ = translationZ; + onTranslationUpdate(); + } + } + + float getTranslationZ() const { + return mTranslationZ; + } + void setRotation(float rotation) { if (rotation != mRotation) { mRotation = rotation; @@ -454,12 +493,36 @@ public: } private: + enum ChildrenSelectMode { + kNegativeZChildren, + kPositiveZChildren + }; + + void onTranslationUpdate() { + mMatrixDirty = true; + if (mTranslationX == 0.0f && mTranslationY == 0.0f && mTranslationZ == 0.0f) { + mMatrixFlags &= ~TRANSLATION; + } else { + mMatrixFlags |= TRANSLATION; + } + } + void outputViewProperties(const int level); + void applyViewPropertyTransforms(mat4& matrix); + + void computeOrderingImpl(DrawDisplayListOp* opState, + KeyedVector >* compositedChildrenOf3dRoot, + const mat4* transformFromRoot); + template inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level); template + inline void iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer, + T& handler, const int level); + + template inline void iterate(OpenGLRenderer& renderer, T& handler, const int level); void init(); @@ -509,7 +572,7 @@ private: bool mClipToBounds; float mAlpha; bool mHasOverlappingRendering; - float mTranslationX, mTranslationY; + float mTranslationX, mTranslationY, mTranslationZ; float mRotation, mRotationX, mRotationY; float mScaleX, mScaleY; float mPivotX, mPivotY; @@ -526,23 +589,17 @@ private: SkMatrix* mTransformMatrix3D; SkMatrix* mStaticMatrix; SkMatrix* mAnimationMatrix; + Matrix4 mTransform; bool mCaching; + bool mIs3dRoot; + /** - * State operations - needed to defer displayList property operations (for example, when setting - * an alpha causes a SaveLayerAlpha to occur). These operations point into mDisplayListData's - * allocation, or null if uninitialized. - * - * These are initialized (via friend re-constructors) when a displayList is issued in either - * replay or deferred mode. If replaying, the ops are not used until the next frame. If - * deferring, the ops may be stored in the DeferredDisplayList to be played back a second time. - * - * They should be used at most once per frame (one call to 'iterate') to avoid overwriting data + * Draw time state - these properties are only set and used during rendering */ - ClipRectOp* mClipRectOp; - SaveLayerOp* mSaveLayerOp; - SaveOp* mSaveOp; - RestoreToCountOp* mRestoreToCountOp; + + // for 3d roots, contains a z sorted list of all children items + KeyedVector > m3dNodes; // TODO: good data structure }; // class DisplayList }; // namespace uirenderer diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 88077d4..1980b03 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -262,7 +262,6 @@ protected: /////////////////////////////////////////////////////////////////////////////// class SaveOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: SaveOp(int flags) : mFlags(flags) {} @@ -295,7 +294,6 @@ private: }; class RestoreToCountOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: RestoreToCountOp(int count) : mCount(count) {} @@ -328,7 +326,6 @@ private: }; class SaveLayerOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: SaveLayerOp(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags) @@ -524,7 +521,6 @@ protected: }; class ClipRectOp : public ClipOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op) : ClipOp(op), mArea(left, top, right, bottom) {} @@ -1100,7 +1096,7 @@ private: class DrawColorOp : public DrawOp { public: DrawColorOp(int color, SkXfermode::Mode mode) - : DrawOp(0), mColor(color), mMode(mode) {}; + : DrawOp(NULL), mColor(color), mMode(mode) {}; virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawColor(mColor, mMode); @@ -1505,7 +1501,7 @@ private: class DrawFunctorOp : public DrawOp { public: DrawFunctorOp(Functor* functor) - : DrawOp(0), mFunctor(functor) {} + : DrawOp(NULL), mFunctor(functor) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { renderer.startMark("GL functor"); @@ -1525,20 +1521,21 @@ private: }; class DrawDisplayListOp : public DrawBoundedOp { + friend class DisplayList; // grant DisplayList access to info of child public: - DrawDisplayListOp(DisplayList* displayList, int flags) + DrawDisplayListOp(DisplayList* displayList, int flags, const mat4& transformFromParent) : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0), - mDisplayList(displayList), mFlags(flags) {} + mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { mDisplayList->defer(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { mDisplayList->replay(replayStruct, level + 1); } } @@ -1559,13 +1556,58 @@ public: private: DisplayList* mDisplayList; - int mFlags; + const int mFlags; + + /////////////////////////// + // Properties below are used by DisplayList::computeOrderingImpl() and iterate() + /////////////////////////// + /** + * Records transform vs parent, used for computing total transform without rerunning DL contents + */ + const mat4 mTransformFromParent; + + /** + * Holds the transformation between the 3d root ViewGroup and this DisplayList drawing + * instance. Represents any translations / transformations done within the drawing of the 3d + * root 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. + */ + mat4 mTransformFrom3dRoot; + bool mSkipInOrderDraw; +}; + +/** + * Not a canvas operation, used only by 3d / z ordering logic in DisplayList::iterate() + */ +class DrawShadowOp : public DrawOp { +public: + DrawShadowOp(const mat4& casterTransform, float casterAlpha, float width, float height) + : DrawOp(NULL), mCasterTransform(casterTransform), mCasterAlpha(casterAlpha), + mWidth(width), mHeight(height) {} + + virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + return renderer.drawShadow(mCasterTransform, mCasterAlpha, mWidth, mHeight); + } + + virtual void output(int level, uint32_t logFlags) const { + OP_LOG("DrawShadow of width %.2f, height %.2f", mWidth, mHeight); + } + + virtual const char* name() { return "DrawShadow"; } + +private: + const mat4 mCasterTransform; + const float mCasterAlpha; + const float mWidth; + const float mHeight; }; class DrawLayerOp : public DrawOp { public: DrawLayerOp(Layer* layer, float x, float y) - : DrawOp(0), mLayer(layer), mX(x), mY(y) {} + : DrawOp(NULL), mLayer(layer), mX(x), mY(y) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawLayer(mLayer, mX, mY); diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index d024923..19a027d 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -111,6 +111,7 @@ DisplayList* DisplayListRenderer::getDisplayList(DisplayList* displayList) { } else { displayList->initFromDisplayListRenderer(*this, true); } + // TODO: should just avoid setting the DisplayList's DisplayListData displayList->setRenderable(mHasDrawOps); return displayList; } @@ -120,7 +121,8 @@ bool DisplayListRenderer::isDeferred() { } void DisplayListRenderer::setViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used + mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); mWidth = width; mHeight = height; @@ -248,7 +250,10 @@ status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList, // resources cache, but we rely on the caller (UI toolkit) to // do the right thing for now - addDrawOp(new (alloc()) DrawDisplayListOp(displayList, flags)); + DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList, flags, currentTransform()); + addDrawOp(op); + mDisplayListData->children.push(op); + return DrawGlInfo::kStatusDone; } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index d233150..7269378 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -328,6 +328,7 @@ private: return patch; } + // TODO: move these to DisplayListData Vector mBitmapResources; Vector mOwnedBitmapResources; Vector mFilterResources; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index bd371a3..742ffd4 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -194,11 +194,9 @@ void Layer::defer() { dirtyRect.set(0, 0, width, height); } - if (deferredList) { - deferredList->reset(dirtyRect); - } else { - deferredList = new DeferredDisplayList(dirtyRect); - } + delete deferredList; + deferredList = new DeferredDisplayList(dirtyRect); + DeferStateStruct deferredState(*deferredList, *renderer, DisplayList::kReplayFlag_ClipChildren); @@ -206,6 +204,7 @@ void Layer::defer() { renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); + displayList->computeOrdering(); displayList->defer(deferredState, 0); deferredUpdateScheduled = false; diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index ba22071..4f5cd26 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -89,8 +89,9 @@ uint8_t Matrix4::getType() const { float m01 = data[kSkewX]; float m10 = data[kSkewY]; float m11 = data[kScaleY]; + float m32 = data[kTranslateZ]; - if (m01 != 0.0f || m10 != 0.0f) { + if (m01 != 0.0f || m10 != 0.0f || m32 != 0.0f) { mType |= kTypeAffine; } @@ -131,11 +132,13 @@ bool Matrix4::changesBounds() const { } bool Matrix4::isPureTranslate() const { - return getGeometryType() <= kTypeTranslate; + // NOTE: temporary hack to workaround ignoreTransform behavior with Z values + // TODO: separate this into isPure2dTranslate vs isPure3dTranslate + return getGeometryType() <= kTypeTranslate && (data[kTranslateZ] == 0.0f); } bool Matrix4::isSimple() const { - return getGeometryType() <= (kTypeScale | kTypeTranslate); + return getGeometryType() <= (kTypeScale | kTypeTranslate) && (data[kTranslateZ] == 0.0f); } bool Matrix4::isIdentity() const { @@ -369,6 +372,84 @@ void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { mType = kTypeUnknown; } +// translated from android.opengl.Matrix#frustumM() +void Matrix4::loadFrustum(float left, float top, float right, float bottom, float near, float far) { + float r_width = 1.0f / (right - left); + float r_height = 1.0f / (top - bottom); + float r_depth = 1.0f / (near - far); + float x = 2.0f * (near * r_width); + float y = 2.0f * (near * r_height); + float A = (right + left) * r_width; + float B = (top + bottom) * r_height; + float C = (far + near) * r_depth; + float D = 2.0f * (far * near * r_depth); + + memset(&data, 0, sizeof(data)); + mType = kTypeUnknown; + + data[kScaleX] = x; + data[kScaleY] = y; + data[8] = A; + data[9] = B; + data[kScaleZ] = C; + data[kTranslateZ] = D; + data[11] = -1.0f; +} + +// translated from android.opengl.Matrix#setLookAtM() +void Matrix4::loadLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + float fx = centerX - eyeX; + float fy = centerY - eyeY; + float fz = centerZ - eyeZ; + + // Normalize f + float rlf = 1.0f / sqrt(fx*fx + fy*fy + fz*fz); + fx *= rlf; + fy *= rlf; + fz *= rlf; + + // compute s = f x up (x means "cross product") + float sx = fy * upZ - fz * upY; + float sy = fz * upX - fx * upZ; + float sz = fx * upY - fy * upX; + + // and normalize s + float rls = 1.0f / sqrt(sx*sx + sy*sy + sz*sz); + sx *= rls; + sy *= rls; + sz *= rls; + + // compute u = s x f + float ux = sy * fz - sz * fy; + float uy = sz * fx - sx * fz; + float uz = sx * fy - sy * fx; + + mType = kTypeUnknown; + data[0] = sx; + data[1] = ux; + data[2] = -fx; + data[3] = 0.0f; + + data[4] = sy; + data[5] = uy; + data[6] = -fy; + data[7] = 0.0f; + + data[8] = sz; + data[9] = uz; + data[10] = -fz; + data[11] = 0.0f; + + data[12] = 0.0f; + data[13] = 0.0f; + data[14] = 0.0f; + data[15] = 1.0f; + + translate(-eyeX, -eyeY, -eyeZ); +} + void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) { loadIdentity(); @@ -382,6 +463,14 @@ void Matrix4::loadOrtho(float left, float right, float bottom, float top, float mType = kTypeTranslate | kTypeScale | kTypeRectToRect; } +void Matrix4::mapPoint3d(Vector3& vec) const { + //TODO: optimize simple case + 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]; +} + #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c) void Matrix4::mapPoint(float& x, float& y) const { diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index b861ba4..00ca050 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -121,6 +121,10 @@ public: void loadRotate(float angle); void loadRotate(float angle, float x, float y, float z); void loadMultiply(const Matrix4& u, const Matrix4& v); + void loadFrustum(float left, float top, float right, float bottom, float near, float far); + void loadLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ); void loadOrtho(float left, float right, float bottom, float top, float near, float far); @@ -134,17 +138,18 @@ public: void multiply(float v); - void translate(float x, float y) { + void translate(float x, float y, float z = 0) { if ((getType() & sGeometryMask) <= kTypeTranslate) { data[kTranslateX] += x; data[kTranslateY] += y; + data[kTranslateZ] += z; } else { // Doing a translation will only affect the translate bit of the type // Save the type uint8_t type = mType; Matrix4 u; - u.loadTranslate(x, y, 0.0f); + u.loadTranslate(x, y, z); multiply(u); // Restore the type and fix the translate bit @@ -190,8 +195,9 @@ public: void copyTo(float* v) const; void copyTo(SkMatrix& v) const; - void mapRect(Rect& r) const; - void mapPoint(float& x, float& y) const; + void mapPoint3d(Vector3& vec) const; + void mapPoint(float& x, float& y) const; // 2d only + void mapRect(Rect& r) const; // 2d only float getTranslateX() const; float getTranslateY() const; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index e256ec2..6599b20 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -180,7 +180,21 @@ void OpenGLRenderer::setViewport(int width, int height) { } void OpenGLRenderer::initViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + float dist = std::max(width, height) * 1.5; + + if (DEBUG_ENABLE_3D) { + // TODO: make view proj app configurable + Matrix4 projection; + projection.loadFrustum(-width / 2, -height / 2, width / 2, height / 2, dist, 0); + Matrix4 view; + view.loadLookAt(0, 0, dist, + 0, 0, 0, + 0, 1, 0); + mViewProjMatrix.loadMultiply(projection, view); + mViewProjMatrix.translate(-width/2, -height/2); + } else { + mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); + } mWidth = width; mHeight = height; @@ -753,7 +767,7 @@ bool OpenGLRenderer::restoreSnapshot() { if (restoreOrtho) { Rect& r = previous->viewport; glViewport(r.left, r.top, r.right, r.bottom); - mOrthoMatrix.load(current->orthoMatrix); + mViewProjMatrix.load(current->orthoMatrix); } mSaveCount--; @@ -984,7 +998,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); mSnapshot->height = bounds.getHeight(); - mSnapshot->orthoMatrix.load(mOrthoMatrix); + mSnapshot->orthoMatrix.load(mViewProjMatrix); endTiling(); debugOverdraw(false, false); @@ -1013,7 +1027,9 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + + // TODO: determine best way to support 3d drawing within HW layers + mViewProjMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); return true; } @@ -1539,10 +1555,8 @@ void OpenGLRenderer::getMatrix(SkMatrix* matrix) { } void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { - SkMatrix transform; - currentTransform().copyTo(transform); - transform.preConcat(*matrix); - currentTransform().load(transform); + mat4 transform(*matrix); + currentTransform().multiply(transform); } /////////////////////////////////////////////////////////////////////////////// @@ -1927,10 +1941,10 @@ void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, bool dirty = right - left > 0.0f && bottom - top > 0.0f; if (!ignoreTransform) { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform(), offset); + mCaches.currentProgram->set(mViewProjMatrix, mModelView, currentTransform(), offset); if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform()); } else { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity(), offset); + mCaches.currentProgram->set(mViewProjMatrix, mModelView, mat4::identity(), offset); if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); } } @@ -2064,9 +2078,12 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags) { status_t status; + // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself if (displayList && displayList->isRenderable()) { + // compute 3d ordering + displayList->computeOrdering(); if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { status = startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); @@ -2082,7 +2099,7 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, flushLayers(); status = startFrame(); - return status | deferredList.flush(*this, dirty); + return deferredList.flush(*this, dirty) | status; } return DrawGlInfo::kStatusDone; @@ -3372,6 +3389,34 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint return drawColorRects(rects, count, color, mode); } +status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha, + float width, float height) { + if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; + + // For now, always and scissor + // TODO: use quickReject + mCaches.enableScissor(); + + SkPaint paint; + paint.setColor(0x3f000000); + paint.setAntiAlias(true); + VertexBuffer vertexBuffer; + { + //TODO: populate vertex buffer with better shadow geometry. + Vector3 pivot(width/2, height/2, 0.0f); + casterTransform.mapPoint3d(pivot); + + float zScaleFactor = 0.5 + 0.0005f * pivot.z; + + SkPath path; + path.addRect(pivot.x - width * zScaleFactor, pivot.y - height * zScaleFactor, + pivot.x + width * zScaleFactor, pivot.y + height * zScaleFactor); + PathTessellator::tessellatePath(path, &paint, mSnapshot->transform, vertexBuffer); + } + + return drawVertexBuffer(vertexBuffer, &paint); +} + status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color, SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) { if (count == 0) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index cfc5931..185e247 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -265,6 +265,12 @@ public: ANDROID_API void getMatrix(SkMatrix* matrix); virtual void setMatrix(SkMatrix* matrix); virtual void concatMatrix(SkMatrix* matrix); + virtual void concatMatrix(Matrix4& matrix) { + currentTransform().multiply(matrix); + } + void translateZ(float z) { + currentTransform().translate(0,0,z); + } ANDROID_API const Rect& getClipBounds(); @@ -315,6 +321,9 @@ public: DrawOpMode drawOpMode = kDrawOpMode_Immediate); virtual status_t drawRects(const float* rects, int count, SkPaint* paint); + status_t drawShadow(const mat4& casterTransform, float casterAlpha, + float width, float height); + virtual void resetShader(); virtual void setupShader(SkiaShader* shader); @@ -1070,8 +1079,8 @@ private: // Dimensions of the drawing surface int mWidth, mHeight; - // Matrix used for ortho projection in shaders - mat4 mOrthoMatrix; + // Matrix used for view/projection in shaders + mat4 mViewProjMatrix; /** * Model-view matrix used to position/size objects diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index 497924e..5110272 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -107,6 +107,21 @@ struct Vector2 { } }; // class Vector2 +class Vector3 { +public: + float x; + float y; + float z; + + Vector3() : + x(0.0f), y(0.0f), z(0.0f) { + } + + Vector3(float px, float py, float pz) : + x(px), y(py), z(pz) { + } +}; + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// -- cgit v1.1