diff options
28 files changed, 611 insertions, 138 deletions
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index de90899..edb3798 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -485,4 +485,6 @@ public abstract class HardwareRenderer { * Called by {@link ViewRootImpl} when a new performTraverals is scheduled. */ abstract void notifyFramePending(); + + abstract void registerAnimatingRenderNode(RenderNode animator); } diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 099f153..9dc9766 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -167,10 +167,13 @@ public class RenderNode { public static final int STATUS_DREW = 0x4; private boolean mValid; - private final long mNativeRenderNode; + // Do not access directly unless you are ThreadedRenderer + final long mNativeRenderNode; + private final View mOwningView; - private RenderNode(String name) { + private RenderNode(String name, View owningView) { mNativeRenderNode = nCreate(name); + mOwningView = owningView; } /** @@ -178,6 +181,7 @@ public class RenderNode { */ private RenderNode(long nativePtr) { mNativeRenderNode = nativePtr; + mOwningView = null; } /** @@ -188,8 +192,8 @@ public class RenderNode { * * @return A new RenderNode. */ - public static RenderNode create(String name) { - return new RenderNode(name); + public static RenderNode create(String name, @Nullable View owningView) { + return new RenderNode(name, owningView); } /** @@ -805,7 +809,15 @@ public class RenderNode { /////////////////////////////////////////////////////////////////////////// public void addAnimator(RenderNodeAnimator animator) { + if (mOwningView == null || mOwningView.mAttachInfo == null) { + throw new IllegalStateException("Cannot start this animator on a detached view!"); + } nAddAnimator(mNativeRenderNode, animator.getNativeAnimator()); + mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this); + } + + public void endAllAnimators() { + nEndAllAnimators(mNativeRenderNode); } /////////////////////////////////////////////////////////////////////////// @@ -891,6 +903,7 @@ public class RenderNode { /////////////////////////////////////////////////////////////////////////// private static native void nAddAnimator(long renderNode, long animatorPtr); + private static native void nEndAllAnimators(long renderNode); /////////////////////////////////////////////////////////////////////////// // Finalization diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 9433237..413ea04 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -19,7 +19,6 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; -import android.animation.Animator.AnimatorListener; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; @@ -30,7 +29,6 @@ import com.android.internal.view.animation.FallbackLUTInterpolator; import com.android.internal.view.animation.HasNativeInterpolator; import com.android.internal.view.animation.NativeInterpolatorFactory; -import java.lang.ref.WeakReference; import java.util.ArrayList; /** @@ -111,13 +109,11 @@ public class RenderNodeAnimator extends Animator { mRenderProperty = property; mFinalValue = finalValue; mUiThreadHandlesDelay = true; - init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), - property, finalValue)); + init(nCreateAnimator(property, finalValue)); } public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) { init(nCreateCanvasPropertyFloatAnimator( - new WeakReference<RenderNodeAnimator>(this), property.getNativeContainer(), finalValue)); mUiThreadHandlesDelay = false; } @@ -132,14 +128,12 @@ public class RenderNodeAnimator extends Animator { */ public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) { init(nCreateCanvasPropertyPaintAnimator( - new WeakReference<RenderNodeAnimator>(this), property.getNativeContainer(), paintField, finalValue)); mUiThreadHandlesDelay = false; } public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) { - init(nCreateRevealAnimator(new WeakReference<RenderNodeAnimator>(this), - x, y, startRadius, endRadius)); + init(nCreateRevealAnimator(x, y, startRadius, endRadius)); mUiThreadHandlesDelay = true; } @@ -192,7 +186,7 @@ public class RenderNodeAnimator extends Animator { } private void doStart() { - nStart(mNativePtr.get()); + nStart(mNativePtr.get(), this); // Alpha is a special snowflake that has the canonical value stored // in mTransformationInfo instead of in RenderNode, so we need to update @@ -248,24 +242,21 @@ public class RenderNodeAnimator extends Animator { public void setTarget(View view) { mViewTarget = view; - mTarget = view.mRenderNode; - mTarget.addAnimator(this); + setTarget(mViewTarget.mRenderNode); } public void setTarget(Canvas canvas) { if (!(canvas instanceof GLES20RecordingCanvas)) { throw new IllegalArgumentException("Not a GLES20RecordingCanvas"); } - final GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas; setTarget(recordingCanvas.mNode); } - public void setTarget(RenderNode node) { + private void setTarget(RenderNode node) { if (mTarget != null) { throw new IllegalStateException("Target already set!"); } - mViewTarget = null; mTarget = node; mTarget.addAnimator(this); } @@ -335,6 +326,12 @@ public class RenderNodeAnimator extends Animator { for (int i = 0; i < numListeners; i++) { listeners.get(i).onAnimationEnd(this); } + + // Release the native object, as it has a global reference to us. This + // breaks the cyclic reference chain, and allows this object to be + // GC'd + mNativePtr.release(); + mNativePtr = null; } @SuppressWarnings("unchecked") @@ -427,11 +424,8 @@ public class RenderNodeAnimator extends Animator { } // Called by native - private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) { - RenderNodeAnimator animator = weakThis.get(); - if (animator != null) { - animator.onFinished(); - } + private static void callOnFinished(RenderNodeAnimator animator) { + animator.onFinished(); } @Override @@ -439,22 +433,20 @@ public class RenderNodeAnimator extends Animator { throw new IllegalStateException("Cannot clone this animator"); } - private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis, - int property, float finalValue); - private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis, + private static native long nCreateAnimator(int property, float finalValue); + private static native long nCreateCanvasPropertyFloatAnimator( long canvasProperty, float finalValue); - private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, + private static native long nCreateCanvasPropertyPaintAnimator( long canvasProperty, int paintField, float finalValue); - private static native long nCreateRevealAnimator(WeakReference<RenderNodeAnimator> weakThis, + private static native long nCreateRevealAnimator( int x, int y, float startRadius, float endRadius); private static native void nSetStartValue(long nativePtr, float startValue); private static native void nSetDuration(long nativePtr, long duration); private static native long nGetDuration(long nativePtr); private static native void nSetStartDelay(long nativePtr, long startDelay); - private static native long nGetStartDelay(long nativePtr); private static native void nSetInterpolator(long animPtr, long interpolatorPtr); - private static native void nStart(long animPtr); + private static native void nStart(long animPtr, RenderNodeAnimator finishListener); private static native void nEnd(long animPtr); } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 50341fc..faade96 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -314,6 +314,20 @@ public class ThreadedRenderer extends HardwareRenderer { attachInfo.mIgnoreDirtyState = false; + // register animating rendernodes which started animating prior to renderer + // creation, which is typical for animators started prior to first draw + if (attachInfo.mPendingAnimatingRenderNodes != null) { + final int count = attachInfo.mPendingAnimatingRenderNodes.size(); + for (int i = 0; i < count; i++) { + registerAnimatingRenderNode( + attachInfo.mPendingAnimatingRenderNodes.get(i)); + } + attachInfo.mPendingAnimatingRenderNodes.clear(); + // We don't need this anymore as subsequent calls to + // ViewRootImpl#attachRenderNodeAnimator will go directly to us. + attachInfo.mPendingAnimatingRenderNodes = null; + } + int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, recordDuration, view.getResources().getDisplayMetrics().density); if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { @@ -372,6 +386,11 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override + void registerAnimatingRenderNode(RenderNode animator) { + nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); + } + + @Override protected void finalize() throws Throwable { try { nDeleteProxy(mNativeProxy); @@ -468,6 +487,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos, long recordDuration, float density); private static native void nDestroyCanvasAndSurface(long nativeProxy); + private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); private static native void nInvokeFunctor(long functor, boolean waitForCompletion); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6f58582..9e1dbe0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3565,7 +3565,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; - mRenderNode = RenderNode.create(getClass().getName()); + mRenderNode = RenderNode.create(getClass().getName(), this); if (!sCompatibilityDone && context != null) { final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; @@ -4161,7 +4161,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ View() { mResources = null; - mRenderNode = RenderNode.create(getClass().getName()); + mRenderNode = RenderNode.create(getClass().getName(), this); } private static SparseArray<String> getAttributeMap() { @@ -15180,9 +15180,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param renderNode Existing RenderNode, or {@code null} * @return A valid display list for the specified drawable */ - private static RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) { + private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) { if (renderNode == null) { - renderNode = RenderNode.create(drawable.getClass().getName()); + renderNode = RenderNode.create(drawable.getClass().getName(), this); } final Rect bounds = drawable.getBounds(); @@ -19908,6 +19908,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mHardwareAccelerated; boolean mHardwareAccelerationRequested; HardwareRenderer mHardwareRenderer; + List<RenderNode> mPendingAnimatingRenderNodes; /** * The state of the display to which the window is attached, as reported diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index bb469a3..80dbbf5 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -671,6 +671,17 @@ public final class ViewRootImpl implements ViewParent, ThreadedRenderer.invokeFunctor(functor, waitForCompletion); } + public void registerAnimatingRenderNode(RenderNode animator) { + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.registerAnimatingRenderNode(animator); + } else { + if (mAttachInfo.mPendingAnimatingRenderNodes == null) { + mAttachInfo.mPendingAnimatingRenderNodes = new ArrayList<RenderNode>(); + } + mAttachInfo.mPendingAnimatingRenderNodes.add(animator); + } + } + private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; @@ -2329,6 +2340,16 @@ public final class ViewRootImpl implements ViewParent, Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + // For whatever reason we didn't create a HardwareRenderer, end any + // hardware animations that are now dangling + if (mAttachInfo.mPendingAnimatingRenderNodes != null) { + final int count = mAttachInfo.mPendingAnimatingRenderNodes.size(); + for (int i = 0; i < count; i++) { + mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators(); + } + mAttachInfo.mPendingAnimatingRenderNodes.clear(); + } + if (mReportNextDraw) { mReportNextDraw = false; if (mAttachInfo.mHardwareRenderer != null) { diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 29c8298..46b225d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -147,7 +147,7 @@ public class Editor { boolean isDirty; public TextDisplayList(String name) { isDirty = true; - displayList = RenderNode.create(name); + displayList = RenderNode.create(name, null); } boolean needsRecord() { return isDirty || !displayList.isValid(); } } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 1e9d722..1296831 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -455,6 +455,12 @@ static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz, renderNode->addAnimator(animator); } +static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz, + jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + renderNode->animators().endAllAnimators(); +} + #endif // USE_OPENGL_RENDERER // ---------------------------------------------------------------------------- @@ -534,6 +540,7 @@ static JNINativeMethod gMethods[] = { { "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY }, { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator }, + { "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators }, #endif }; diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp index 767534f..85c2a09 100644 --- a/core/jni/android_view_RenderNodeAnimator.cpp +++ b/core/jni/android_view_RenderNodeAnimator.cpp @@ -51,28 +51,36 @@ public: // cyclic-references-of-doom. If you think "I know, just use NewWeakGlobalRef!" // then you end up with basically a PhantomReference, which is totally not // what we want. - AnimationListenerBridge(JNIEnv* env, jobject weakThis) { - mWeakThis = env->NewGlobalRef(weakThis); + AnimationListenerBridge(JNIEnv* env, jobject finishListener) { + mFinishListener = env->NewGlobalRef(finishListener); env->GetJavaVM(&mJvm); } virtual ~AnimationListenerBridge() { - JNIEnv* env = getEnv(mJvm); - env->DeleteGlobalRef(mWeakThis); - mWeakThis = NULL; + if (mFinishListener) { + onAnimationFinished(NULL); + } } virtual void onAnimationFinished(BaseRenderNodeAnimator*) { + LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?"); JNIEnv* env = getEnv(mJvm); env->CallStaticVoidMethod( gRenderNodeAnimatorClassInfo.clazz, gRenderNodeAnimatorClassInfo.callOnFinished, - mWeakThis); + mFinishListener); + releaseJavaObject(); } private: + void releaseJavaObject() { + JNIEnv* env = getEnv(mJvm); + env->DeleteGlobalRef(mFinishListener); + mFinishListener = NULL; + } + JavaVM* mJvm; - jobject mWeakThis; + jobject mFinishListener; }; static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint property) { @@ -88,38 +96,33 @@ static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) { return static_cast<CanvasPropertyPaintAnimator::PaintField>(field); } -static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis, +static jlong createAnimator(JNIEnv* env, jobject clazz, jint propertyRaw, jfloat finalValue) { RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw); - BaseRenderNodeAnimator* animator = new RenderPropertyAnimator(property, finalValue); - animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz, - jobject weakThis, jlong canvasPropertyPtr, jfloat finalValue) { + jlong canvasPropertyPtr, jfloat finalValue) { CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr); BaseRenderNodeAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, finalValue); - animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz, - jobject weakThis, jlong canvasPropertyPtr, jint paintFieldRaw, + jlong canvasPropertyPtr, jint paintFieldRaw, jfloat finalValue) { CanvasPropertyPaint* canvasProperty = reinterpret_cast<CanvasPropertyPaint*>(canvasPropertyPtr); CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw); BaseRenderNodeAnimator* animator = new CanvasPropertyPaintAnimator( canvasProperty, paintField, finalValue); - animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } -static jlong createRevealAnimator(JNIEnv* env, jobject clazz, jobject weakThis, +static jlong createRevealAnimator(JNIEnv* env, jobject clazz, jint centerX, jint centerY, jfloat startRadius, jfloat endRadius) { BaseRenderNodeAnimator* animator = new RevealAnimator(centerX, centerY, startRadius, endRadius); - animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } @@ -156,8 +159,11 @@ static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong animator->setInterpolator(interpolator); } -static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) { +static void start(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) { BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + if (finishListener) { + animator->setListener(new AnimationListenerBridge(env, finishListener)); + } animator->start(); } @@ -176,17 +182,16 @@ const char* const kClassPathName = "android/view/RenderNodeAnimator"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER - { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IF)J", (void*) createAnimator }, - { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JF)J", (void*) createCanvasPropertyFloatAnimator }, - { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyPaintAnimator }, - { "nCreateRevealAnimator", "(Ljava/lang/ref/WeakReference;IIFF)J", (void*) createRevealAnimator }, + { "nCreateAnimator", "(IF)J", (void*) createAnimator }, + { "nCreateCanvasPropertyFloatAnimator", "(JF)J", (void*) createCanvasPropertyFloatAnimator }, + { "nCreateCanvasPropertyPaintAnimator", "(JIF)J", (void*) createCanvasPropertyPaintAnimator }, + { "nCreateRevealAnimator", "(IIFF)J", (void*) createRevealAnimator }, { "nSetStartValue", "(JF)V", (void*) setStartValue }, { "nSetDuration", "(JJ)V", (void*) setDuration }, { "nGetDuration", "(J)J", (void*) getDuration }, { "nSetStartDelay", "(JJ)V", (void*) setStartDelay }, - { "nGetStartDelay", "(J)J", (void*) getStartDelay }, { "nSetInterpolator", "(JJ)V", (void*) setInterpolator }, - { "nStart", "(J)V", (void*) start }, + { "nStart", "(JLandroid/view/RenderNodeAnimator;)V", (void*) start }, { "nEnd", "(J)V", (void*) end }, #endif }; @@ -204,7 +209,7 @@ int register_android_view_RenderNodeAnimator(JNIEnv* env) { gRenderNodeAnimatorClassInfo.clazz = jclass(env->NewGlobalRef(gRenderNodeAnimatorClassInfo.clazz)); GET_STATIC_METHOD_ID(gRenderNodeAnimatorClassInfo.callOnFinished, gRenderNodeAnimatorClassInfo.clazz, - "callOnFinished", "(Ljava/lang/ref/WeakReference;)V"); + "callOnFinished", "(Landroid/view/RenderNodeAnimator;)V"); return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 4e3419a..99babac 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -33,6 +33,8 @@ #include "android_view_GraphicBuffer.h" #include <Animator.h> +#include <AnimationContext.h> +#include <IContextFactory.h> #include <RenderNode.h> #include <renderthread/CanvasContext.h> #include <renderthread/RenderProxy.h> @@ -103,7 +105,7 @@ private: std::string mMessage; }; -class RootRenderNode : public RenderNode, AnimationHook, ErrorHandler { +class RootRenderNode : public RenderNode, ErrorHandler { public: RootRenderNode(JNIEnv* env) : RenderNode() { mLooper = Looper::getForThread(); @@ -114,34 +116,84 @@ public: virtual ~RootRenderNode() {} - virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { - OnFinishedEvent event(animator, listener); - mOnFinishedEvents.push_back(event); - } - virtual void onError(const std::string& message) { mLooper->sendMessage(new RenderingException(mVm, message), 0); } virtual void prepareTree(TreeInfo& info) { - info.animationHook = this; info.errorHandler = this; RenderNode::prepareTree(info); - info.animationHook = NULL; info.errorHandler = NULL; + } + + void sendMessage(const sp<MessageHandler>& handler) { + mLooper->sendMessage(handler, 0); + } + + void attachAnimatingNode(RenderNode* animatingNode) { + mPendingAnimatingRenderNodes.push_back(animatingNode); + } + + void doAttachAnimatingNodes(AnimationContext* context) { + for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { + RenderNode* node = mPendingAnimatingRenderNodes[i].get(); + context->addAnimatingRenderNode(*node); + } + mPendingAnimatingRenderNodes.clear(); + } + +private: + sp<Looper> mLooper; + JavaVM* mVm; + std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes; +}; + +class AnimationContextBridge : public AnimationContext { +public: + AnimationContextBridge(renderthread::TimeLord& clock, RootRenderNode* rootNode) + : AnimationContext(clock), mRootNode(rootNode) { + } + + virtual ~AnimationContextBridge() {} + // Marks the start of a frame, which will update the frame time and move all + // next frame animations into the current frame + virtual void startFrame() { + mRootNode->doAttachAnimatingNodes(this); + AnimationContext::startFrame(); + } + + // Runs any animations still left in mCurrentFrameAnimations + virtual void runRemainingAnimations(TreeInfo& info) { + AnimationContext::runRemainingAnimations(info); // post all the finished stuff if (mOnFinishedEvents.size()) { sp<InvokeAnimationListeners> message = new InvokeAnimationListeners(mOnFinishedEvents); - mLooper->sendMessage(message, 0); + mRootNode->sendMessage(message); } } + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { + OnFinishedEvent event(animator, listener); + mOnFinishedEvents.push_back(event); + } + private: - sp<Looper> mLooper; + sp<RootRenderNode> mRootNode; std::vector<OnFinishedEvent> mOnFinishedEvents; - JavaVM* mVm; +}; + +class ContextFactoryImpl : public IContextFactory { +public: + ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} + + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { + return new AnimationContextBridge(clock, mRootNode); + } + +private: + RootRenderNode* mRootNode; }; static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz, @@ -168,8 +220,9 @@ static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, job static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, jboolean translucent, jlong rootRenderNodePtr) { - RenderNode* rootRenderNode = reinterpret_cast<RenderNode*>(rootRenderNodePtr); - return (jlong) new RenderProxy(translucent, rootRenderNode); + RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr); + ContextFactoryImpl factory(rootRenderNode); + return (jlong) new RenderProxy(translucent, rootRenderNode, &factory); } static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, @@ -244,6 +297,13 @@ static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, j proxy->destroyCanvasAndSurface(); } +static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz, + jlong rootNodePtr, jlong animatingNodePtr) { + RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr); + RenderNode* animatingNode = reinterpret_cast<RenderNode*>(animatingNodePtr); + rootRenderNode->attachAnimatingNode(animatingNode); +} + static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz, jlong functorPtr, jboolean waitForCompletion) { Functor* functor = reinterpret_cast<Functor*>(functorPtr); @@ -371,6 +431,7 @@ static JNINativeMethod gMethods[] = { { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, { "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface }, + { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nCreateDisplayListLayer", "(JII)J", (void*) android_view_ThreadedRenderer_createDisplayListLayer }, { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index d9f7941..49560ff 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -12,6 +12,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) font/CacheTexture.cpp \ font/Font.cpp \ AmbientShadow.cpp \ + AnimationContext.cpp \ Animator.cpp \ AnimatorManager.cpp \ AssetAtlas.cpp \ diff --git a/libs/hwui/AnimationContext.cpp b/libs/hwui/AnimationContext.cpp new file mode 100644 index 0000000..ec44de3 --- /dev/null +++ b/libs/hwui/AnimationContext.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "AnimationContext.h" + +#include "Animator.h" +#include "RenderNode.h" +#include "TreeInfo.h" +#include "renderthread/TimeLord.h" + +namespace android { +namespace uirenderer { + +AnimationContext::AnimationContext(renderthread::TimeLord& clock) + : mClock(clock) + , mCurrentFrameAnimations(*this) + , mNextFrameAnimations(*this) + , mFrameTimeMs(0) { +} + +AnimationContext::~AnimationContext() { +} + +void AnimationContext::addAnimatingRenderNode(RenderNode& node) { + if (!node.animators().hasAnimationHandle()) { + AnimationHandle* handle = new AnimationHandle(node, *this); + addAnimationHandle(handle); + } +} + +void AnimationContext::addAnimationHandle(AnimationHandle* handle) { + handle->insertAfter(&mNextFrameAnimations); +} + +void AnimationContext::startFrame() { + LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle, + "Missed running animations last frame!"); + AnimationHandle* head = mNextFrameAnimations.mNextHandle; + if (head) { + mNextFrameAnimations.mNextHandle = NULL; + mCurrentFrameAnimations.mNextHandle = head; + head->mPreviousHandle = &mCurrentFrameAnimations; + } + mFrameTimeMs = mClock.computeFrameTimeMs(); +} + +void AnimationContext::runRemainingAnimations(TreeInfo& info) { + while (mCurrentFrameAnimations.mNextHandle) { + AnimationHandle* current = mCurrentFrameAnimations.mNextHandle; + AnimatorManager& animators = current->mRenderNode->animators(); + animators.pushStaging(); + animators.animateNoDamage(info); + LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle == current, + "Animate failed to remove from current frame list!"); + } +} + +void AnimationContext::callOnFinished(BaseRenderNodeAnimator* animator, + AnimationListener* listener) { + listener->onAnimationFinished(animator); +} + +AnimationHandle::AnimationHandle(AnimationContext& context) + : mContext(context) + , mPreviousHandle(NULL) + , mNextHandle(NULL) { +} + +AnimationHandle::AnimationHandle(RenderNode& animatingNode, AnimationContext& context) + : mRenderNode(&animatingNode) + , mContext(context) + , mPreviousHandle(NULL) + , mNextHandle(NULL) { + mRenderNode->animators().setAnimationHandle(this); +} + +AnimationHandle::~AnimationHandle() { + LOG_ALWAYS_FATAL_IF(mPreviousHandle || mNextHandle, + "AnimationHandle destroyed while still animating!"); +} + +void AnimationHandle::notifyAnimationsRan() { + removeFromList(); + if (mRenderNode->animators().hasAnimators()) { + mContext.addAnimationHandle(this); + } else { + mRenderNode->animators().setAnimationHandle(NULL); + delete this; + } +} + +void AnimationHandle::insertAfter(AnimationHandle* prev) { + removeFromList(); + mNextHandle = prev->mNextHandle; + if (mNextHandle) { + mNextHandle->mPreviousHandle = this; + } + prev->mNextHandle = this; + mPreviousHandle = prev; +} + +void AnimationHandle::removeFromList() { + if (mPreviousHandle) { + mPreviousHandle->mNextHandle = mNextHandle; + } + if (mNextHandle) { + mNextHandle->mPreviousHandle = mPreviousHandle; + } + mPreviousHandle = NULL; + mNextHandle = NULL; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h new file mode 100644 index 0000000..e32c33d --- /dev/null +++ b/libs/hwui/AnimationContext.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TREEANIMATIONTRACKER_H_ +#define TREEANIMATIONTRACKER_H_ + +#include <cutils/compiler.h> +#include <utils/RefBase.h> +#include <utils/StrongPointer.h> + +#include "renderthread/TimeLord.h" +#include "utils/Macros.h" + +namespace android { +namespace uirenderer { + +class AnimationContext; +class AnimationListener; +class BaseRenderNodeAnimator; +class RenderNode; +class TreeInfo; + +/* + * AnimationHandle is several classes merged into one. + * 1: It maintains the reference to the AnimationContext required to run animators. + * 2: It keeps a strong reference to RenderNodes with animators so that + * we don't lose them if they are no longer in the display tree. This is + * required so that we can keep animating them, and properly notify listeners + * of onAnimationFinished. + * 3: It forms a doubly linked list so that we can cheaply move between states. + */ +class AnimationHandle { + PREVENT_COPY_AND_ASSIGN(AnimationHandle); +public: + AnimationContext& context() { return mContext; } + + void notifyAnimationsRan(); + +private: + friend class AnimationContext; + AnimationHandle(AnimationContext& context); + AnimationHandle(RenderNode& animatingNode, AnimationContext& context); + ~AnimationHandle(); + + void insertAfter(AnimationHandle* prev); + void removeFromList(); + + sp<RenderNode> mRenderNode; + + AnimationContext& mContext; + + AnimationHandle* mPreviousHandle; + AnimationHandle* mNextHandle; +}; + +class AnimationContext { + PREVENT_COPY_AND_ASSIGN(AnimationContext); +public: + ANDROID_API AnimationContext(renderthread::TimeLord& clock); + ANDROID_API virtual ~AnimationContext(); + + nsecs_t frameTimeMs() { return mFrameTimeMs; } + bool hasAnimations() { + return mCurrentFrameAnimations.mNextHandle + || mNextFrameAnimations.mNextHandle; + } + + // Will always add to the next frame list, which is swapped when + // startFrame() is called + ANDROID_API void addAnimatingRenderNode(RenderNode& node); + + // Marks the start of a frame, which will update the frame time and move all + // next frame animations into the current frame + ANDROID_API virtual void startFrame(); + + // Runs any animations still left in mCurrentFrameAnimations that were not run + // as part of the standard RenderNode:prepareTree pass. + ANDROID_API virtual void runRemainingAnimations(TreeInfo& info); + + ANDROID_API virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener); + +private: + friend class AnimationHandle; + void addAnimationHandle(AnimationHandle* handle); + + renderthread::TimeLord& mClock; + + // Animations left to run this frame, at the end of the frame this should + // be null + AnimationHandle mCurrentFrameAnimations; + // Animations queued for next frame + AnimationHandle mNextFrameAnimations; + + nsecs_t mFrameTimeMs; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TREEANIMATIONTRACKER_H_ */ diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 78d569d..1c697d5 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -19,6 +19,7 @@ #include <inttypes.h> #include <set> +#include "AnimationContext.h" #include "RenderNode.h" #include "RenderProperties.h" @@ -85,7 +86,7 @@ void BaseRenderNodeAnimator::attach(RenderNode* target) { onAttached(); } -void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) { +void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) { if (!mHasStartValue) { doSetStartValue(getValue(mTarget)); } @@ -93,21 +94,22 @@ void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) { mPlayState = mStagingPlayState; // Oh boy, we're starting! Man the battle stations! if (mPlayState == RUNNING) { - transitionToRunning(info); + transitionToRunning(context); } } } -void BaseRenderNodeAnimator::transitionToRunning(TreeInfo& info) { - LOG_ALWAYS_FATAL_IF(info.frameTimeMs <= 0, "%" PRId64 " isn't a real frame time!", info.frameTimeMs); +void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) { + nsecs_t frameTimeMs = context.frameTimeMs(); + LOG_ALWAYS_FATAL_IF(frameTimeMs <= 0, "%" PRId64 " isn't a real frame time!", frameTimeMs); if (mStartDelay < 0 || mStartDelay > 50000) { ALOGW("Your start delay is strange and confusing: %" PRId64, mStartDelay); } - mStartTime = info.frameTimeMs + mStartDelay; + mStartTime = frameTimeMs + mStartDelay; if (mStartTime < 0) { ALOGW("Ended up with a really weird start time of %" PRId64 " with frame time %" PRId64 " and start delay %" PRId64, - mStartTime, info.frameTimeMs, mStartDelay); + mStartTime, frameTimeMs, mStartDelay); // Set to 0 so that the animate() basically instantly finishes mStartTime = 0; } @@ -120,7 +122,7 @@ void BaseRenderNodeAnimator::transitionToRunning(TreeInfo& info) { } } -bool BaseRenderNodeAnimator::animate(TreeInfo& info) { +bool BaseRenderNodeAnimator::animate(AnimationContext& context) { if (mPlayState < RUNNING) { return false; } @@ -132,15 +134,14 @@ bool BaseRenderNodeAnimator::animate(TreeInfo& info) { // because the staging properties reflect the final value, we always need // to call setValue even if the animation isn't yet running or is still // being delayed as we need to override the staging value - if (mStartTime > info.frameTimeMs) { - info.out.hasAnimations |= true; + if (mStartTime > context.frameTimeMs()) { setValue(mTarget, mFromValue); return false; } float fraction = 1.0f; if (mPlayState == RUNNING && mDuration > 0) { - fraction = (float)(info.frameTimeMs - mStartTime) / mDuration; + fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration; } if (fraction >= 1.0f) { fraction = 1.0f; @@ -151,21 +152,16 @@ bool BaseRenderNodeAnimator::animate(TreeInfo& info) { setValue(mTarget, mFromValue + (mDeltaValue * fraction)); if (mPlayState == FINISHED) { - callOnFinishedListener(info); + callOnFinishedListener(context); return true; } - info.out.hasAnimations |= true; return false; } -void BaseRenderNodeAnimator::callOnFinishedListener(TreeInfo& info) { +void BaseRenderNodeAnimator::callOnFinishedListener(AnimationContext& context) { if (mListener.get()) { - if (!info.animationHook) { - mListener->onAnimationFinished(this); - } else { - info.animationHook->callOnFinished(this, mListener.get()); - } + context.callOnFinished(this, mListener.get()); } } diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 6dfe7b4..c52a93f 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -28,6 +28,8 @@ namespace android { namespace uirenderer { +class AnimationContext; +class BaseRenderNodeAnimator; class RenderNode; class RenderProperties; @@ -50,15 +52,17 @@ public: ANDROID_API void setListener(AnimationListener* listener) { mListener = listener; } + AnimationListener* listener() { return mListener.get(); } ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); } ANDROID_API void end() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); } void attach(RenderNode* target); virtual void onAttached() {} void detach() { mTarget = 0; } - void pushStaging(TreeInfo& info); - bool animate(TreeInfo& info); + void pushStaging(AnimationContext& context); + bool animate(AnimationContext& context); + bool isRunning() { return mPlayState == RUNNING; } bool isFinished() { return mPlayState == FINISHED; } float finalValue() { return mFinalValue; } @@ -72,7 +76,7 @@ protected: virtual void setValue(RenderNode* target, float value) = 0; RenderNode* target() { return mTarget; } - void callOnFinishedListener(TreeInfo& info); + void callOnFinishedListener(AnimationContext& context); virtual void onStagingPlayStateChanged() {} @@ -100,7 +104,7 @@ protected: private: inline void checkMutable(); - virtual void transitionToRunning(TreeInfo& info); + virtual void transitionToRunning(AnimationContext& context); void doSetStartValue(float value); }; diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp index 7221295a4..3832d42 100644 --- a/libs/hwui/AnimatorManager.cpp +++ b/libs/hwui/AnimatorManager.cpp @@ -17,6 +17,7 @@ #include <algorithm> +#include "AnimationContext.h" #include "RenderNode.h" namespace android { @@ -30,7 +31,8 @@ static void unref(BaseRenderNodeAnimator* animator) { } AnimatorManager::AnimatorManager(RenderNode& parent) - : mParent(parent) { + : mParent(parent) + , mAnimationHandle(NULL) { } AnimatorManager::~AnimatorManager() { @@ -44,6 +46,11 @@ void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) { mNewAnimators.push_back(animator.get()); } +void AnimatorManager::setAnimationHandle(AnimationHandle* handle) { + LOG_ALWAYS_FATAL_IF(mAnimationHandle && handle, "Already have an AnimationHandle!"); + mAnimationHandle = handle; +} + template<typename T> static void move_all(T& source, T& dest) { dest.reserve(source.size() + dest.size()); @@ -53,26 +60,30 @@ static void move_all(T& source, T& dest) { source.clear(); } -void AnimatorManager::pushStaging(TreeInfo& info) { +void AnimatorManager::pushStaging() { if (mNewAnimators.size()) { // Since this is a straight move, we don't need to inc/dec the ref count move_all(mNewAnimators, mAnimators); } for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) { - (*it)->pushStaging(info); + (*it)->pushStaging(mAnimationHandle->context()); } } class AnimateFunctor { public: - AnimateFunctor(RenderNode& target, TreeInfo& info) - : dirtyMask(0), mTarget(target), mInfo(info) {} + AnimateFunctor(TreeInfo& info, AnimationContext& context) + : dirtyMask(0), mInfo(info), mContext(context) {} bool operator() (BaseRenderNodeAnimator* animator) { dirtyMask |= animator->dirtyMask(); - bool remove = animator->animate(mInfo); + bool remove = animator->animate(mContext); if (remove) { animator->decStrong(0); + } else { + if (animator->isRunning()) { + mInfo.out.hasAnimations = true; + } } return remove; } @@ -80,8 +91,8 @@ public: uint32_t dirtyMask; private: - RenderNode& mTarget; TreeInfo& mInfo; + AnimationContext& mContext; }; uint32_t AnimatorManager::animate(TreeInfo& info) { @@ -93,17 +104,70 @@ uint32_t AnimatorManager::animate(TreeInfo& info) { mParent.damageSelf(info); info.damageAccumulator->popTransform(); - AnimateFunctor functor(mParent, info); - std::vector< BaseRenderNodeAnimator* >::iterator newEnd; - newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); - mAnimators.erase(newEnd, mAnimators.end()); + uint32_t dirty = animateCommon(info); mParent.mProperties.updateMatrix(); info.damageAccumulator->pushTransform(&mParent); mParent.damageSelf(info); + return dirty; +} + +void AnimatorManager::animateNoDamage(TreeInfo& info) { + if (!mAnimators.size()) return; + + animateCommon(info); +} + +uint32_t AnimatorManager::animateCommon(TreeInfo& info) { + AnimateFunctor functor(info, mAnimationHandle->context()); + std::vector< BaseRenderNodeAnimator* >::iterator newEnd; + newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); + mAnimators.erase(newEnd, mAnimators.end()); + mAnimationHandle->notifyAnimationsRan(); return functor.dirtyMask; } +class EndAnimatorsFunctor { +public: + EndAnimatorsFunctor(AnimationContext& context) : mContext(context) {} + + void operator() (BaseRenderNodeAnimator* animator) { + animator->end(); + animator->pushStaging(mContext); + animator->animate(mContext); + animator->decStrong(0); + } + +private: + AnimationContext& mContext; +}; + +static void endAnimatorsHard(BaseRenderNodeAnimator* animator) { + animator->end(); + if (animator->listener()) { + animator->listener()->onAnimationFinished(animator); + } + animator->decStrong(0); +} + +void AnimatorManager::endAllAnimators() { + if (mNewAnimators.size()) { + // Since this is a straight move, we don't need to inc/dec the ref count + move_all(mNewAnimators, mAnimators); + } + // First try gracefully ending them + if (mAnimationHandle) { + EndAnimatorsFunctor functor(mAnimationHandle->context()); + for_each(mAnimators.begin(), mAnimators.end(), functor); + } else { + // We have no context, so bust out the sledgehammer + // This works because this state can only happen on the UI thread, + // which means we're already on the right thread to invoke listeners + for_each(mAnimators.begin(), mAnimators.end(), endAnimatorsHard); + } + mAnimators.clear(); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h index 0d177c5..d5f56ed 100644 --- a/libs/hwui/AnimatorManager.h +++ b/libs/hwui/AnimatorManager.h @@ -27,6 +27,7 @@ namespace android { namespace uirenderer { +class AnimationHandle; class BaseRenderNodeAnimator; class RenderNode; @@ -39,12 +40,26 @@ public: void addAnimator(const sp<BaseRenderNodeAnimator>& animator); - void pushStaging(TreeInfo& info); + void setAnimationHandle(AnimationHandle* handle); + bool hasAnimationHandle() { return mAnimationHandle; } + + void pushStaging(); + // Returns the combined dirty mask of all animators run uint32_t animate(TreeInfo& info); + void animateNoDamage(TreeInfo& info); + + // Hard-ends all animators. Used for cleanup if the root is being destroyed. + ANDROID_API void endAllAnimators(); + + bool hasAnimators() { return mAnimators.size(); } + private: + uint32_t animateCommon(TreeInfo& info); + RenderNode& mParent; + AnimationHandle* mAnimationHandle; // To improve the efficiency of resizing & removing from the vector // use manual ref counting instead of sp<>. diff --git a/libs/hwui/IContextFactory.h b/libs/hwui/IContextFactory.h new file mode 100644 index 0000000..463b55e --- /dev/null +++ b/libs/hwui/IContextFactory.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONTEXTFACTORY_H_ +#define CONTEXTFACTORY_H_ + +namespace android { +namespace uirenderer { + +namespace renderthread { +class TimeLord; +} + +class AnimationContext; + +class IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) = 0; + +protected: + virtual ~IContextFactory() {} +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* CONTEXTFACTORY_H_ */ diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 658265d..a79875e 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -235,7 +235,7 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { // before properties() is trampled by stagingProperties(), as they are // required by some animators. if (CC_LIKELY(info.runAnimations)) { - mAnimatorManager.pushStaging(info); + mAnimatorManager.pushStaging(); } if (mDirtyPropertyFields) { mDirtyPropertyFields = 0; diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 18402b2..27b05e2 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -174,6 +174,8 @@ public: // UI thread only! ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator); + AnimatorManager& animators() { return mAnimatorManager; } + void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const; private: diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 74d52a3..e78d8bd 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -26,18 +26,9 @@ namespace android { namespace uirenderer { -class BaseRenderNodeAnimator; -class AnimationListener; class OpenGLRenderer; class RenderState; -class AnimationHook { -public: - virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) = 0; -protected: - ~AnimationHook() {} -}; - class ErrorHandler { public: virtual void onError(const std::string& message) = 0; @@ -62,8 +53,6 @@ public: explicit TreeInfo(TraversalMode mode, RenderState& renderState) : mode(mode) - , frameTimeMs(0) - , animationHook(NULL) , prepareTextures(mode == MODE_FULL) , runAnimations(true) , damageAccumulator(NULL) @@ -74,8 +63,6 @@ public: explicit TreeInfo(TraversalMode mode, const TreeInfo& clone) : mode(mode) - , frameTimeMs(clone.frameTimeMs) - , animationHook(clone.animationHook) , prepareTextures(mode == MODE_FULL) , runAnimations(clone.runAnimations) , damageAccumulator(clone.damageAccumulator) @@ -85,8 +72,6 @@ public: {} const TraversalMode mode; - nsecs_t frameTimeMs; - AnimationHook* animationHook; // TODO: Remove this? Currently this is used to signal to stop preparing // textures if we run out of cache space. bool prepareTextures; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 4bf5a8a..d9fa0bc 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -21,6 +21,7 @@ #include "EglManager.h" #include "RenderThread.h" +#include "../AnimationContext.h" #include "../Caches.h" #include "../DeferredLayerUpdater.h" #include "../RenderState.h" @@ -35,7 +36,8 @@ namespace android { namespace uirenderer { namespace renderthread { -CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode) +CanvasContext::CanvasContext(RenderThread& thread, bool translucent, + RenderNode* rootRenderNode, IContextFactory* contextFactory) : mRenderThread(thread) , mEglManager(thread.eglManager()) , mEglSurface(EGL_NO_SURFACE) @@ -44,11 +46,13 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mCanvas(NULL) , mHaveNewSurface(false) , mRootRenderNode(rootRenderNode) { + mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord()); } CanvasContext::~CanvasContext() { destroyCanvasAndSurface(); mRenderThread.removeFrameCallback(this); + delete mAnimationContext; } void CanvasContext::destroyCanvasAndSurface() { @@ -136,10 +140,11 @@ void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) { void CanvasContext::prepareTree(TreeInfo& info) { mRenderThread.removeFrameCallback(this); - info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); info.damageAccumulator = &mDamageAccumulator; info.renderer = mCanvas; + mAnimationContext->startFrame(); mRootRenderNode->prepareTree(info); + mAnimationContext->runRemainingAnimations(info); int runningBehind = 0; // TODO: This query is moderately expensive, investigate adding some sort @@ -254,7 +259,6 @@ void CanvasContext::buildLayer(RenderNode* node) { stopDrawing(); TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); - info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); info.damageAccumulator = &mDamageAccumulator; info.renderer = mCanvas; info.runAnimations = false; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0cbed6f..749da1b 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -25,6 +25,7 @@ #include "../DamageAccumulator.h" #include "../DrawProfiler.h" +#include "../IContextFactory.h" #include "../RenderNode.h" #include "RenderTask.h" #include "RenderThread.h" @@ -34,6 +35,7 @@ namespace android { namespace uirenderer { +class AnimationContext; class DeferredLayerUpdater; class OpenGLRenderer; class Rect; @@ -45,9 +47,11 @@ class EglManager; // This per-renderer class manages the bridge between the global EGL context // and the render surface. +// TODO: Rename to Renderer or some other per-window, top-level manager class CanvasContext : public IFrameCallback { public: - CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode); + CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, + IContextFactory* contextFactory); virtual ~CanvasContext(); bool initialize(ANativeWindow* window); @@ -105,6 +109,7 @@ private: OpenGLRenderer* mCanvas; bool mHaveNewSurface; DamageAccumulator mDamageAccumulator; + AnimationContext* mAnimationContext; const sp<RenderNode> mRootRenderNode; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 405ce24..3d04316 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -52,17 +52,20 @@ namespace renderthread { MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() -CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) { - return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode); +CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, + RenderNode* rootRenderNode, IContextFactory* contextFactory) { + return new CanvasContext(*args->thread, args->translucent, + args->rootRenderNode, args->contextFactory); } -RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) +RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) : mRenderThread(RenderThread::getInstance()) , mContext(0) { SETUP_TASK(createContext); args->translucent = translucent; args->rootRenderNode = rootRenderNode; args->thread = &mRenderThread; + args->contextFactory = contextFactory; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(&mRenderThread, mContext); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index eea3674..9e6bcf5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -30,6 +30,7 @@ #include <utils/Vector.h> #include "../Caches.h" +#include "../IContextFactory.h" #include "DrawFrameTask.h" namespace android { @@ -58,7 +59,7 @@ class RenderProxyBridge; */ class ANDROID_API RenderProxy { public: - ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode); + ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode, IContextFactory* contextFactory); ANDROID_API virtual ~RenderProxy(); ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos); diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index 758d96e..cf3d039 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -30,7 +30,7 @@ void TimeLord::vsyncReceived(nsecs_t vsync) { } } -nsecs_t TimeLord::frameTimeMs() { +nsecs_t TimeLord::computeFrameTimeMs() { // Logic copied from Choreographer.java nsecs_t now = systemTime(CLOCK_MONOTONIC); nsecs_t jitterNanos = now - mFrameTimeNanos; diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h index 52c6d9e..8b0372c 100644 --- a/libs/hwui/renderthread/TimeLord.h +++ b/libs/hwui/renderthread/TimeLord.h @@ -30,7 +30,7 @@ class TimeLord { public: void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; } void vsyncReceived(nsecs_t vsync); - nsecs_t frameTimeMs(); + nsecs_t computeFrameTimeMs(); private: friend class RenderThread; diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java index a81e063..afd6a8d 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java @@ -16,7 +16,6 @@ package com.android.test.hwui; -import android.animation.TimeInterpolator; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; @@ -28,11 +27,6 @@ import android.os.Trace; import android.view.HardwareCanvas; import android.view.RenderNodeAnimator; import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.OvershootInterpolator; -import android.webkit.WebChromeClient; -import android.webkit.WebView; -import android.webkit.WebViewClient; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.ProgressBar; |