From 67ce99b66ebc816ae8bbc222db8f3695fb15495b Mon Sep 17 00:00:00 2001 From: Doris Liu Date: Tue, 17 May 2016 16:50:31 -0700 Subject: [PATCH] Handle hidden RT VectorDrawable animators This CL changes the target of VD specific animators to VectorDrawable, instead of RenderNode. The benefit of doing so is that animators can now detect whether the animation is meaningful by checking whether their VD target is in the display list. If not, that means the VD is not drawing for the current frame, in which case we can be smarter and more power efficient by removing the animator from the list and posting a delayed onFinished listener callback. By setting VD as the animation target, when an ImageView decides to update its drawable from one AVD to something else, we'll be able to detect that the previous AVD is no longer in the display list, and stop providing animation pulse to the stale AVD, which is something we couldn't do previously. This change also handles the case where one AVD instance could be drawn in two different views. Bug: 27441375 Change-Id: Id4b3b37f28274c917cb9beb9dcd3d1e6991b5c5d --- core/java/android/view/RenderNode.java | 6 +- core/java/android/view/ThreadedRenderer.java | 8 ++ core/java/android/view/ViewRootImpl.java | 8 ++ ...aphics_drawable_AnimatedVectorDrawable.cpp | 7 + core/jni/android_view_ThreadedRenderer.cpp | 127 ++++++++++++++++++ .../drawable/AnimatedVectorDrawable.java | 43 +++--- .../graphics/drawable/VectorDrawable.java | 7 + libs/hwui/AnimationContext.h | 2 + libs/hwui/Animator.cpp | 4 + libs/hwui/Animator.h | 12 +- libs/hwui/DisplayList.cpp | 2 +- libs/hwui/DisplayList.h | 21 ++- libs/hwui/DisplayListCanvas.cpp | 2 +- libs/hwui/PropertyValuesAnimatorSet.cpp | 17 +++ libs/hwui/PropertyValuesAnimatorSet.h | 14 ++ libs/hwui/RecordingCanvas.cpp | 2 +- libs/hwui/RenderNode.cpp | 12 +- libs/hwui/VectorDrawable.h | 19 +-- libs/hwui/renderthread/CanvasContext.cpp | 1 + 19 files changed, 262 insertions(+), 52 deletions(-) diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index ab4cbcf21bed8..0164fcd4f0167 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -793,12 +793,12 @@ public class RenderNode { return mOwningView != null && mOwningView.mAttachInfo != null; } - public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) { + public void registerVectorDrawableAnimator( + AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) { if (mOwningView == null || mOwningView.mAttachInfo == null) { throw new IllegalStateException("Cannot start this animator on a detached view!"); } - nAddAnimator(mNativeRenderNode, animatorSet.getAnimatorNativePtr()); - mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this); + mOwningView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animatorSet); } public void endAllAnimators() { diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index e650d956915ac..fcca7395fe8c5 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -23,6 +23,7 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.drawable.AnimatedVectorDrawable; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -881,6 +882,12 @@ public final class ThreadedRenderer { nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); } + void registerVectorDrawableAnimator( + AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) { + nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode, + animator.getAnimatorNativePtr()); + } + public void serializeDisplayListTree() { nSerializeDisplayListTree(mNativeProxy); } @@ -992,6 +999,7 @@ public final class ThreadedRenderer { private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); private static native void nDestroy(long nativeProxy, long rootRenderNode); private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode); + private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator); private static native void nInvokeFunctor(long functor, boolean waitForCompletion); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 130b440dfbbd1..fa5449e1c17ca 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -42,6 +42,7 @@ import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; @@ -825,6 +826,13 @@ public final class ViewRootImpl implements ViewParent, } } + public void registerVectorDrawableAnimator( + AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) { + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.registerVectorDrawableAnimator(animator); + } + } + private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp index 4b2a72d8e28e3..0ba88e6b066b0 100644 --- a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp +++ b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp @@ -95,6 +95,12 @@ static jlong createAnimatorSet(JNIEnv*, jobject) { return reinterpret_cast(animatorSet); } +static void setVectorDrawableTarget(JNIEnv*, jobject,jlong animatorPtr, jlong vectorDrawablePtr) { + VectorDrawable::Tree* tree = reinterpret_cast(vectorDrawablePtr); + PropertyValuesAnimatorSet* set = reinterpret_cast(animatorPtr); + set->setVectorDrawable(tree); +} + static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId, jfloat startValue, jfloat endValue) { VectorDrawable::Group* group = reinterpret_cast(nativePtr); @@ -168,6 +174,7 @@ static void reset(JNIEnv*, jobject, jlong animatorSetPtr) { static const JNINativeMethod gMethods[] = { {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet}, + {"nSetVectorDrawableTarget", "(JJ)V", (void*)setVectorDrawableTarget}, {"nAddAnimator", "(JJJJJI)V", (void*)addAnimator}, {"nCreateGroupPropertyHolder", "!(JIFF)J", (void*)createGroupPropertyHolder}, {"nCreatePathDataPropertyHolder", "!(JJJ)J", (void*)createPathDataPropertyHolder}, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 1cf2611927a01..eabcae908c716 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +124,31 @@ private: std::vector mOnFinishedEvents; }; +class FinishAndInvokeListener : public MessageHandler { +public: + explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) + : mAnimator(anim) { + mListener = anim->getOneShotListener(); + mRequestId = anim->getRequestId(); + } + + virtual void handleMessage(const Message& message) { + if (mAnimator->getRequestId() == mRequestId) { + // Request Id has not changed, meaning there's no animation lifecyle change since the + // message is posted, so go ahead and call finish to make sure the PlayState is properly + // updated. This is needed because before the next frame comes in from UI thread to + // trigger an animation update, there could be reverse/cancel etc. So we need to update + // the playstate in time to ensure all the subsequent events get chained properly. + mAnimator->end(); + } + mListener->onAnimationFinished(nullptr); + } +private: + sp mAnimator; + sp mListener; + uint32_t mRequestId; +}; + class RenderingException : public MessageHandler { public: RenderingException(JavaVM* vm, const std::string& message) @@ -160,6 +187,15 @@ public: virtual void prepareTree(TreeInfo& info) override { info.errorHandler = this; + + for (auto& anim : mVectorDrawableAnimators) { + // Assume that the property change in VD from the animators will not be consumed. Mark + // otherwise if the VDs are found in the display list tree. For VDs that are not in + // the display list tree, we stop providing animation pulses by 1) removing them from + // the animation list, 2) post a delayed message to end them at end time so their + // listeners can receive the corresponding callbacks. + anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); + } // TODO: This is hacky info.windowInsetLeft = -stagingProperties().getLeft(); info.windowInsetTop = -stagingProperties().getTop(); @@ -169,16 +205,46 @@ public: info.windowInsetLeft = 0; info.windowInsetTop = 0; info.errorHandler = nullptr; + + for (auto it = mVectorDrawableAnimators.begin(); it != mVectorDrawableAnimators.end();) { + if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { + // Vector Drawable is not in the display list, we should remove this animator from + // the list and post a delayed message to end the animator. + detachVectorDrawableAnimator(it->get()); + it = mVectorDrawableAnimators.erase(it); + } else { + ++it; + } + } + info.out.hasAnimations |= !mVectorDrawableAnimators.empty(); } void sendMessage(const sp& handler) { mLooper->sendMessage(handler, 0); } + void sendMessageDelayed(const sp& handler, nsecs_t delayInMs) { + mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0); + } + void attachAnimatingNode(RenderNode* animatingNode) { mPendingAnimatingRenderNodes.push_back(animatingNode); } + void attachPendingVectorDrawableAnimators() { + mVectorDrawableAnimators.insert(mPendingVectorDrawableAnimators.begin(), + mPendingVectorDrawableAnimators.end()); + mPendingVectorDrawableAnimators.clear(); + } + + void detachAnimators() { + // Remove animators from the list and post a delayed message in future to end the animator + for (auto& anim : mVectorDrawableAnimators) { + detachVectorDrawableAnimator(anim.get()); + } + mVectorDrawableAnimators.clear(); + } + void doAttachAnimatingNodes(AnimationContext* context) { for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { RenderNode* node = mPendingAnimatingRenderNodes[i].get(); @@ -187,17 +253,57 @@ public: mPendingAnimatingRenderNodes.clear(); } + void runVectorDrawableAnimators(AnimationContext* context) { + for (auto it = mVectorDrawableAnimators.begin(); it != mVectorDrawableAnimators.end();) { + (*it)->pushStaging(*context); + if ((*it)->animate(*context)) { + it = mVectorDrawableAnimators.erase(it); + } else { + ++it; + } + } + } + void destroy() { for (auto& renderNode : mPendingAnimatingRenderNodes) { renderNode->animators().endAllStagingAnimators(); } mPendingAnimatingRenderNodes.clear(); + mPendingVectorDrawableAnimators.clear(); + } + + void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + mPendingVectorDrawableAnimators.insert(anim); } private: sp mLooper; JavaVM* mVm; std::vector< sp > mPendingAnimatingRenderNodes; + std::set< sp > mPendingVectorDrawableAnimators; + std::set< sp > mVectorDrawableAnimators; + void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { + if (anim->isInfinite() || !anim->isRunning()) { + // Do not need to post anything if the animation is infinite (i.e. no meaningful + // end listener action), or if the animation has already ended. + return; + } + nsecs_t remainingTimeInMs = anim->getRemainingPlayTime(); + // Post a delayed onFinished event that is scheduled to be handled when the animator ends. + if (anim->getOneShotListener()) { + // VectorDrawable's oneshot listener is updated when there are user triggered animation + // lifecycle changes, such as start(), end(), etc. By using checking and clearing + // one shot listener, we ensure the same end listener event gets posted only once. + // Therefore no duplicates. Another benefit of using one shot listener is that no + // removal is necessary: the end time of animation will not change unless triggered by + // user events, in which case the already posted listener's id will become stale, and + // the onFinished callback will then be ignored. + sp message + = new FinishAndInvokeListener(anim); + sendMessageDelayed(message, remainingTimeInMs); + anim->clearOneShotListener(); + } + } }; class AnimationContextBridge : public AnimationContext { @@ -213,8 +319,16 @@ public: virtual void startFrame(TreeInfo::TraversalMode mode) { if (mode == TreeInfo::MODE_FULL) { mRootNode->doAttachAnimatingNodes(this); + mRootNode->attachPendingVectorDrawableAnimators(); } AnimationContext::startFrame(mode); + // Run VectorDrawable animators in the beginning of the frame instead of during prepareTree, + // because one VD can be in multiple render nodes' display list. So it's more simple to + // run them all at once before prepareTree than running them or checking whether they have + // already ran in each RenderNode. Note that these animators don't damage the RenderNodes. + // The damaging is done in prepareTree as needed after checking whether a VD has been + // modified. + mRootNode->runVectorDrawableAnimators(this); } // Runs any animations still left in mCurrentFrameAnimations @@ -223,6 +337,10 @@ public: postOnFinishedEvents(); } + virtual void detachAnimators() override { + mRootNode->detachAnimators(); + } + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { OnFinishedEvent event(animator, listener); mOnFinishedEvents.push_back(event); @@ -230,6 +348,7 @@ public: virtual void destroy() { AnimationContext::destroy(); + detachAnimators(); postOnFinishedEvents(); } @@ -527,6 +646,13 @@ static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* en rootRenderNode->attachAnimatingNode(animatingNode); } +static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz, + jlong rootNodePtr, jlong animatorPtr) { + RootRenderNode* rootRenderNode = reinterpret_cast(rootNodePtr); + PropertyValuesAnimatorSet* animator = reinterpret_cast(animatorPtr); + rootRenderNode->addVectorDrawableAnimator(animator); +} + static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz, jlong functorPtr, jboolean waitForCompletion) { Functor* functor = reinterpret_cast(functorPtr); @@ -738,6 +864,7 @@ static const JNINativeMethod gMethods[] = { { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy }, { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, + { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator }, { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 7fa3947d80f95..44d95d22f8b73 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -158,7 +158,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false; /** Local, mutable animator set. */ - private VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimatorUI(this); + private VectorDrawableAnimator mAnimatorSet; /** * The resources against which this drawable was created. Used to attempt @@ -183,6 +183,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) { mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res); + mAnimatorSet = new VectorDrawableAnimatorRT(this); mRes = res; } @@ -1033,7 +1034,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { private WeakReference mLastSeenTarget = null; private int mLastListenerId = 0; private final IntArray mPendingAnimationActions = new IntArray(); - private final Drawable mDrawable; + private final AnimatedVectorDrawable mDrawable; VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) { mDrawable = drawable; @@ -1052,6 +1053,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation(); parseAnimatorSet(set, 0); + long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable + .getNativeTree(); + nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr); mInitialized = true; mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE; @@ -1288,16 +1292,19 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { * to the last seen RenderNode target and start right away. */ protected void recordLastSeenTarget(DisplayListCanvas canvas) { - mLastSeenTarget = new WeakReference( - RenderNodeAnimatorSetHelper.getTarget(canvas)); - if (mPendingAnimationActions.size() > 0 && useLastSeenTarget()) { - if (DBG_ANIMATION_VECTOR_DRAWABLE) { - Log.d(LOGTAG, "Target is set in the next frame"); + final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas); + mLastSeenTarget = new WeakReference(node); + // Add the animator to the list of animators on every draw + if (mInitialized || mPendingAnimationActions.size() > 0) { + if (useTarget(node)) { + if (DBG_ANIMATION_VECTOR_DRAWABLE) { + Log.d(LOGTAG, "Target is set in the next frame"); + } + for (int i = 0; i < mPendingAnimationActions.size(); i++) { + handlePendingAction(mPendingAnimationActions.get(i)); + } + mPendingAnimationActions.clear(); } - for (int i = 0; i < mPendingAnimationActions.size(); i++) { - handlePendingAction(mPendingAnimationActions.get(i)); - } - mPendingAnimationActions.clear(); } } @@ -1319,10 +1326,15 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { private boolean useLastSeenTarget() { if (mLastSeenTarget != null) { final RenderNode target = mLastSeenTarget.get(); - if (target != null && target.isAttached()) { - target.addAnimator(this); - return true; - } + return useTarget(target); + } + return false; + } + + private boolean useTarget(RenderNode target) { + if (target != null && target.isAttached()) { + target.registerVectorDrawableAnimator(this); + return true; } return false; } @@ -1517,6 +1529,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } private static native long nCreateAnimatorSet(); + private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr); private static native void nAddAnimator(long setPtr, long propertyValuesHolder, long nativeInterpolator, long startDelay, long duration, int repeatCount); diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index a1874a859a9ff..47bafe323a974 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -761,6 +761,13 @@ public class VectorDrawable extends Drawable { return mVectorState.mAutoMirrored; } + /** + * @hide + */ + public long getNativeTree() { + return mVectorState.getNativeRenderer(); + } + static class VectorDrawableState extends ConstantState { // Variables below need to be copied (deep copy if applicable) for mutation. int[] mThemeAttrs; diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h index 909ed36a21276..801fd8719a142 100644 --- a/libs/hwui/AnimationContext.h +++ b/libs/hwui/AnimationContext.h @@ -100,6 +100,8 @@ public: ANDROID_API virtual void destroy(); + ANDROID_API virtual void detachAnimators() {} + private: friend class AnimationHandle; void addAnimationHandle(AnimationHandle* handle); diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 4d65782f684be..dc180188100b1 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -274,6 +274,10 @@ bool BaseRenderNodeAnimator::updatePlayTime(nsecs_t playTime) { return playTime >= mDuration; } +nsecs_t BaseRenderNodeAnimator::getRemainingPlayTime() { + return mPlayState == PlayState::Reversing ? mPlayTime : mDuration - mPlayTime; +} + void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) { if (mPlayState < PlayState::Finished) { mPlayState = PlayState::Finished; diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index fdae0f32d4e64..9476750afd52e 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -62,19 +62,23 @@ public: } bool mayRunAsync() { return mMayRunAsync; } ANDROID_API void start(); - ANDROID_API void reset(); + ANDROID_API virtual void reset(); ANDROID_API void reverse(); // Terminates the animation at its current progress. ANDROID_API void cancel(); // Terminates the animation and skip to the end of the animation. - ANDROID_API void end(); + ANDROID_API virtual void end(); void attach(RenderNode* target); virtual void onAttached() {} void detach() { mTarget = nullptr; } - void pushStaging(AnimationContext& context); - bool animate(AnimationContext& context); + ANDROID_API void pushStaging(AnimationContext& context); + ANDROID_API bool animate(AnimationContext& context); + + // Returns the remaining time in ms for the animation. Note this should only be called during + // an animation on RenderThread. + ANDROID_API nsecs_t getRemainingPlayTime(); bool isRunning() { return mPlayState == PlayState::Running || mPlayState == PlayState::Reversing; } diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index b572bdaccb860..28be05c52cfcb 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -45,7 +45,7 @@ DisplayList::DisplayList() , regions(stdAllocator) , referenceHolders(stdAllocator) , functors(stdAllocator) - , pushStagingFunctors(stdAllocator) + , vectorDrawables(stdAllocator) , hasDrawOps(false) { } diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 5b3227b7db97d..ccf71c6d360b7 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -69,6 +69,11 @@ typedef DisplayListOp BaseOpType; typedef DrawRenderNodeOp NodeOpType; #endif +namespace VectorDrawable { +class Tree; +}; +typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; + /** * Holds data used in the playback a tree of DisplayLists. */ @@ -110,16 +115,6 @@ struct ReplayStateStruct : public PlaybackStateStruct { LinearAllocator mReplayAllocator; }; -/** - * Functor that can be used for objects with data in both UI thread and RT to keep the data - * in sync. This functor, when added to DisplayList, will be call during DisplayList sync. - */ -struct PushStagingFunctor { - PushStagingFunctor() {} - virtual ~PushStagingFunctor() {} - virtual void operator ()() {} -}; - struct FunctorContainer { Functor* functor; GlFunctorLifecycleListener* listener; @@ -161,7 +156,7 @@ public: const LsaVector& getBitmapResources() const { return bitmapResources; } const LsaVector& getFunctors() const { return functors; } - const LsaVector& getPushStagingFunctors() { return pushStagingFunctors; } + const LsaVector& getVectorDrawables() { return vectorDrawables; } size_t addChild(NodeOpType* childOp); @@ -203,10 +198,10 @@ private: // List of functors LsaVector functors; - // List of functors that need to be notified of pushStaging. Note that this list gets nothing + // List of VectorDrawables that need to be notified of pushStaging. Note that this list gets nothing // but a callback during sync DisplayList, unlike the list of functors defined above, which // gets special treatment exclusive for webview. - LsaVector pushStagingFunctors; + LsaVector vectorDrawables; bool hasDrawOps; // only used if !HWUI_NEW_OPS diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index ca968cef91b2a..bec662959f915 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -417,7 +417,7 @@ void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { mDisplayList->ref(tree); - mDisplayList->pushStagingFunctors.push_back(tree->getFunctor()); + mDisplayList->vectorDrawables.push_back(tree); addDrawOp(new (alloc()) DrawVectorDrawableOp(tree, tree->stagingProperties()->getBounds())); } diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp index b29f91ff34aad..e416e0c0061d5 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.cpp +++ b/libs/hwui/PropertyValuesAnimatorSet.cpp @@ -30,6 +30,11 @@ void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* proper interpolator, startDelay, duration, repeatCount); mAnimators.emplace_back(animator); setListener(new PropertyAnimatorSetListener(this)); + + // Check whether any child animator is infinite after adding it them to the set. + if (repeatCount == -1) { + mIsInfinite = true; + } } PropertyValuesAnimatorSet::PropertyValuesAnimatorSet() @@ -78,15 +83,27 @@ void PropertyValuesAnimatorSet::onPlayTimeChanged(nsecs_t playTime) { void PropertyValuesAnimatorSet::start(AnimationListener* listener) { init(); mOneShotListener = listener; + mRequestId++; BaseRenderNodeAnimator::start(); } void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) { init(); mOneShotListener = listener; + mRequestId++; BaseRenderNodeAnimator::reverse(); } +void PropertyValuesAnimatorSet::reset() { + mRequestId++; + BaseRenderNodeAnimator::reset(); +} + +void PropertyValuesAnimatorSet::end() { + mRequestId++; + BaseRenderNodeAnimator::end(); +} + void PropertyValuesAnimatorSet::init() { if (mInitialized) { return; diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h index c7ae7c0e8ce1c..49021bc828251 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.h +++ b/libs/hwui/PropertyValuesAnimatorSet.h @@ -43,6 +43,7 @@ private: float mLatestFraction = 0.0f; }; +// TODO: This class should really be named VectorDrawableAnimator class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator { public: friend class PropertyAnimatorSetListener; @@ -50,11 +51,19 @@ public: void start(AnimationListener* listener); void reverse(AnimationListener* listener); + virtual void reset() override; + virtual void end() override; void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder, Interpolator* interpolators, int64_t startDelays, nsecs_t durations, int repeatCount); virtual uint32_t dirtyMask(); + bool isInfinite() { return mIsInfinite; } + void setVectorDrawable(VectorDrawableRoot* vd) { mVectorDrawable = vd; } + VectorDrawableRoot* getVectorDrawable() const { return mVectorDrawable; } + AnimationListener* getOneShotListener() { return mOneShotListener.get(); } + void clearOneShotListener() { mOneShotListener = nullptr; } + uint32_t getRequestId() const { return mRequestId; } protected: virtual float getValue(RenderNode* target) const override; @@ -69,6 +78,11 @@ private: std::vector< std::unique_ptr > mAnimators; float mLastFraction = 0.0f; bool mInitialized = false; + VectorDrawableRoot* mVectorDrawable = nullptr; + bool mIsInfinite = false; + // This request id gets incremented (on UI thread only) when a new request to modfiy the + // lifecycle of an animation happens, namely when start/end/reset/reverse is called. + uint32_t mRequestId = 0; }; class PropertyAnimatorSetListener : public AnimationListener { diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index b49f9b5299894..b35c92612a428 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -440,8 +440,8 @@ void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { } void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { - mDisplayList->pushStagingFunctors.push_back(tree->getFunctor()); mDisplayList->ref(tree); + mDisplayList->vectorDrawables.push_back(tree); addOp(alloc().create_trivial( tree, Rect(tree->stagingProperties()->getBounds()), diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 6e848fddf48f6..0f6c43e9a2c6d 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -419,6 +419,14 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) { prepareSubTree(info, childFunctorsNeedLayer, mDisplayList); pushLayerUpdate(info); + for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { + // If any vector drawable in the display list needs update, damage the node. + if (vectorDrawable->isDirty()) { + damageSelf(info); + } + vectorDrawable->setPropertyChangeWillBeConsumed(true); + } + info.damageAccumulator->popTransform(); } @@ -477,8 +485,8 @@ void RenderNode::syncDisplayList(TreeObserver* observer) { for (auto& iter : mDisplayList->getFunctors()) { (*iter.functor)(DrawGlInfo::kModeSync, nullptr); } - for (size_t i = 0; i < mDisplayList->getPushStagingFunctors().size(); i++) { - (*mDisplayList->getPushStagingFunctors()[i])(); + for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) { + vectorDrawable->syncProperties(); } } } diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index 1e9eecdb65218..1a1f49f04cb92 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -674,21 +674,16 @@ public: void onPropertyChanged(TreeProperties* prop); TreeProperties* mutateStagingProperties() { return &mStagingProperties; } const TreeProperties* stagingProperties() { return &mStagingProperties; } - PushStagingFunctor* getFunctor() { return &mFunctor;} // This should only be called from animations on RT TreeProperties* mutateProperties() { return &mProperties; } + // This should always be called from RT. + bool isDirty() const { return mCache.dirty; } + bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; } + void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; } + private: - class VectorDrawableFunctor : public PushStagingFunctor { - public: - VectorDrawableFunctor(Tree* tree) : mTree(tree) {} - virtual void operator ()() { - mTree->syncProperties(); - } - private: - Tree* mTree; - }; SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop); bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height); @@ -705,8 +700,6 @@ private: TreeProperties mProperties = TreeProperties(this); TreeProperties mStagingProperties = TreeProperties(this); - VectorDrawableFunctor mFunctor = VectorDrawableFunctor(this); - SkPaint mPaint; struct Cache { SkBitmap bitmap; @@ -718,6 +711,8 @@ private: PropertyChangedListener mPropertyChangedListener = PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty); + + mutable bool mWillBeConsumed = false; }; } // namespace VectorDrawable diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 18cecc2231a8b..ac2e08c338507 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -278,6 +278,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, void CanvasContext::stopDrawing() { mRenderThread.removeFrameCallback(this); + mAnimationContext->detachAnimators(); } void CanvasContext::notifyFramePending() {