VectorDrawable native rendering - Step 5 of MANY
This is reverting the revert of what reverts the revert of the original
implementation. Fourth revert is a charm!
This reverts commit df7fdb1e0bdb5c289bbc08047e5c710185503309.
Change-Id: I6fc3a5accfd8b79c3da31bbc101ad9e9b4d6e7dd
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 7bd2b24..372bcb3 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -42,7 +42,8 @@
, mStartTime(0)
, mDuration(300)
, mStartDelay(0)
- , mMayRunAsync(true) {
+ , mMayRunAsync(true)
+ , mPlayTime(0) {
}
BaseRenderNodeAnimator::~BaseRenderNodeAnimator() {
@@ -85,20 +86,113 @@
onAttached();
}
+void BaseRenderNodeAnimator::start() {
+ mStagingPlayState = PlayState::Running;
+ mStagingRequests.push_back(Request::Start);
+ onStagingPlayStateChanged();
+}
+
+void BaseRenderNodeAnimator::cancel() {
+ mStagingPlayState = PlayState::Finished;
+ mStagingRequests.push_back(Request::Cancel);
+ onStagingPlayStateChanged();
+}
+
+void BaseRenderNodeAnimator::reset() {
+ mStagingPlayState = PlayState::Finished;
+ mStagingRequests.push_back(Request::Reset);
+ onStagingPlayStateChanged();
+}
+
+void BaseRenderNodeAnimator::reverse() {
+ mStagingPlayState = PlayState::Reversing;
+ mStagingRequests.push_back(Request::Reverse);
+ onStagingPlayStateChanged();
+}
+
+void BaseRenderNodeAnimator::end() {
+ mStagingPlayState = PlayState::Finished;
+ mStagingRequests.push_back(Request::End);
+ onStagingPlayStateChanged();
+}
+
+void BaseRenderNodeAnimator::resolveStagingRequest(Request request) {
+ switch (request) {
+ case Request::Start:
+ mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) ?
+ mPlayTime : 0;
+ mPlayState = PlayState::Running;
+ break;
+ case Request::Reverse:
+ mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) ?
+ mPlayTime : mDuration;
+ mPlayState = PlayState::Reversing;
+ break;
+ case Request::Reset:
+ mPlayTime = 0;
+ mPlayState = PlayState::Finished;
+ break;
+ case Request::Cancel:
+ mPlayState = PlayState::Finished;
+ break;
+ case Request::End:
+ mPlayTime = mPlayState == PlayState::Reversing ? 0 : mDuration;
+ mPlayState = PlayState::Finished;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid staging request: %d", static_cast<int>(request));
+ };
+}
+
void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) {
if (!mHasStartValue) {
doSetStartValue(getValue(mTarget));
}
- if (mStagingPlayState > mPlayState) {
- if (mStagingPlayState == PlayState::Restarted) {
- mStagingPlayState = PlayState::Running;
+
+ if (!mStagingRequests.empty()) {
+ // Keep track of the play state and play time before they are changed when
+ // staging requests are resolved.
+ nsecs_t currentPlayTime = mPlayTime;
+ PlayState prevFramePlayState = mPlayState;
+
+ // Resolve staging requests one by one.
+ for (Request request : mStagingRequests) {
+ resolveStagingRequest(request);
}
- mPlayState = mStagingPlayState;
- // Oh boy, we're starting! Man the battle stations!
- if (mPlayState == PlayState::Running) {
- transitionToRunning(context);
- } else if (mPlayState == PlayState::Finished) {
+ mStagingRequests.clear();
+
+ if (mStagingPlayState == PlayState::Finished) {
+ // Set the staging play time and end the animation
+ updatePlayTime(mPlayTime);
callOnFinishedListener(context);
+ } else if (mStagingPlayState == PlayState::Running
+ || mStagingPlayState == PlayState::Reversing) {
+ bool changed = currentPlayTime != mPlayTime || prevFramePlayState != mStagingPlayState;
+ if (prevFramePlayState != mStagingPlayState) {
+ transitionToRunning(context);
+ }
+ if (changed) {
+ // Now we need to seek to the stagingPlayTime (i.e. the animation progress that was
+ // requested from UI thread). It is achieved by modifying mStartTime, such that
+ // current time - mStartTime = stagingPlayTime (or mDuration -stagingPlayTime in the
+ // case of reversing)
+ nsecs_t currentFrameTime = context.frameTimeMs();
+ if (mPlayState == PlayState::Reversing) {
+ // Reverse is not supported for animations with a start delay, so here we
+ // assume no start delay.
+ mStartTime = currentFrameTime - (mDuration - mPlayTime);
+ } else {
+ // Animation should play forward
+ if (mPlayTime == 0) {
+ // If the request is to start from the beginning, include start delay.
+ mStartTime = currentFrameTime + mStartDelay;
+ } else {
+ // If the request is to seek to a non-zero play time, then we skip start
+ // delay.
+ mStartTime = currentFrameTime - mPlayTime;
+ }
+ }
+ }
}
}
}
@@ -136,37 +230,37 @@
// This should be set before setValue() so animators can query this time when setValue
// is called.
- nsecs_t currentFrameTime = context.frameTimeMs();
- onPlayTimeChanged(currentFrameTime - mStartTime);
+ nsecs_t currentPlayTime = context.frameTimeMs() - mStartTime;
+ bool finished = updatePlayTime(currentPlayTime);
+ if (finished && mPlayState != PlayState::Finished) {
+ mPlayState = PlayState::Finished;
+ callOnFinishedListener(context);
+ }
+ return finished;
+}
+bool BaseRenderNodeAnimator::updatePlayTime(nsecs_t playTime) {
+ mPlayTime = mPlayState == PlayState::Reversing ? mDuration - playTime : playTime;
+ onPlayTimeChanged(mPlayTime);
// If BaseRenderNodeAnimator is handling the delay (not typical), then
// 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 > context.frameTimeMs()) {
+ if (playTime < 0) {
setValue(mTarget, mFromValue);
return false;
}
float fraction = 1.0f;
-
- if (mPlayState == PlayState::Running && mDuration > 0) {
- fraction = (float)(currentFrameTime - mStartTime) / mDuration;
+ if ((mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) && mDuration > 0) {
+ fraction = mPlayTime / (float) mDuration;
}
- if (fraction >= 1.0f) {
- fraction = 1.0f;
- mPlayState = PlayState::Finished;
- }
+ fraction = MathUtils::clamp(fraction, 0.0f, 1.0f);
fraction = mInterpolator->interpolate(fraction);
setValue(mTarget, mFromValue + (mDeltaValue * fraction));
- if (mPlayState == PlayState::Finished) {
- callOnFinishedListener(context);
- return true;
- }
-
- return false;
+ return playTime >= mDuration;
}
void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) {
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 2c9c9c3..fcbc11b 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -24,6 +24,8 @@
#include "utils/Macros.h"
+#include <vector>
+
namespace android {
namespace uirenderer {
@@ -59,14 +61,14 @@
mMayRunAsync = mayRunAsync;
}
bool mayRunAsync() { return mMayRunAsync; }
- ANDROID_API void start() {
- if (mStagingPlayState == PlayState::NotStarted) {
- mStagingPlayState = PlayState::Running;
- } else {
- mStagingPlayState = PlayState::Restarted;
- }
- onStagingPlayStateChanged(); }
- ANDROID_API void end() { mStagingPlayState = PlayState::Finished; onStagingPlayStateChanged(); }
+ ANDROID_API void start();
+ ANDROID_API 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();
void attach(RenderNode* target);
virtual void onAttached() {}
@@ -74,36 +76,41 @@
void pushStaging(AnimationContext& context);
bool animate(AnimationContext& context);
- bool isRunning() { return mPlayState == PlayState::Running; }
+ bool isRunning() { return mPlayState == PlayState::Running
+ || mPlayState == PlayState::Reversing; }
bool isFinished() { return mPlayState == PlayState::Finished; }
float finalValue() { return mFinalValue; }
ANDROID_API virtual uint32_t dirtyMask() = 0;
void forceEndNow(AnimationContext& context);
+ RenderNode* target() { return mTarget; }
protected:
// PlayState is used by mStagingPlayState and mPlayState to track the state initiated from UI
// thread and Render Thread animation state, respectively.
// From the UI thread, mStagingPlayState transition looks like
- // NotStarted -> Running -> Finished
- // ^ |
- // | |
- // Restarted <------
+ // NotStarted -> Running/Reversing -> Finished
+ // ^ |
+ // | |
+ // ----------------------
// Note: For mStagingState, the Finished state (optional) is only set when the animation is
// terminated by user.
//
// On Render Thread, mPlayState transition:
- // NotStart -> Running -> Finished
- // ^ |
- // | |
- // -------------
+ // NotStart -> Running/Reversing-> Finished
+ // ^ |
+ // | |
+ // ------------------
+ // Note that if the animation is in Running/Reversing state, calling start or reverse again
+ // would do nothing if the animation has the same play direction as the request; otherwise,
+ // the animation would start from where it is and change direction (i.e. Reversing <-> Running)
enum class PlayState {
NotStarted,
Running,
+ Reversing,
Finished,
- Restarted,
};
BaseRenderNodeAnimator(float finalValue);
@@ -111,7 +118,6 @@
virtual float getValue(RenderNode* target) const = 0;
virtual void setValue(RenderNode* target, float value) = 0;
- RenderNode* target() { return mTarget; }
void callOnFinishedListener(AnimationContext& context);
@@ -132,13 +138,28 @@
nsecs_t mDuration;
nsecs_t mStartDelay;
bool mMayRunAsync;
+ // Play Time tracks the progress of animation, it should always be [0, mDuration], 0 being
+ // the beginning of the animation, will reach mDuration at the end of an animation.
+ nsecs_t mPlayTime;
sp<AnimationListener> mListener;
private:
+ enum class Request {
+ Start,
+ Reverse,
+ Reset,
+ Cancel,
+ End
+ };
inline void checkMutable();
virtual void transitionToRunning(AnimationContext& context);
void doSetStartValue(float value);
+ bool updatePlayTime(nsecs_t playTime);
+ void resolveStagingRequest(Request request);
+
+ std::vector<Request> mStagingRequests;
+
};
class RenderPropertyAnimator : public BaseRenderNodeAnimator {
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index cd30b18..2b49b47 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -27,9 +27,8 @@
using namespace std;
-static void unref(BaseRenderNodeAnimator* animator) {
+static void detach(sp<BaseRenderNodeAnimator>& animator) {
animator->detach();
- animator->decStrong(nullptr);
}
AnimatorManager::AnimatorManager(RenderNode& parent)
@@ -38,14 +37,12 @@
}
AnimatorManager::~AnimatorManager() {
- for_each(mNewAnimators.begin(), mNewAnimators.end(), unref);
- for_each(mAnimators.begin(), mAnimators.end(), unref);
+ for_each(mNewAnimators.begin(), mNewAnimators.end(), detach);
+ for_each(mAnimators.begin(), mAnimators.end(), detach);
}
void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
- animator->incStrong(nullptr);
- animator->attach(&mParent);
- mNewAnimators.push_back(animator.get());
+ mNewAnimators.emplace_back(animator.get());
}
void AnimatorManager::setAnimationHandle(AnimationHandle* handle) {
@@ -56,25 +53,31 @@
&mParent, mParent.getName());
}
-template<typename T>
-static void move_all(T& source, T& dest) {
- dest.reserve(source.size() + dest.size());
- for (typename T::iterator it = source.begin(); it != source.end(); it++) {
- dest.push_back(*it);
- }
- source.clear();
-}
-
void AnimatorManager::pushStaging() {
if (mNewAnimators.size()) {
LOG_ALWAYS_FATAL_IF(!mAnimationHandle,
"Trying to start new animators on %p (%s) without an animation handle!",
&mParent, mParent.getName());
- // Since this is a straight move, we don't need to inc/dec the ref count
- move_all(mNewAnimators, mAnimators);
+ // Only add animators that are not already in the on-going animator list.
+ for (auto& animator : mNewAnimators) {
+ RenderNode* targetRenderNode = animator->target();
+ if (targetRenderNode == &mParent) {
+ // Animator already in the animator list: skip adding again
+ continue;
+ }
+
+ if (targetRenderNode){
+ // If the animator is already in another RenderNode's animator list, remove animator from
+ // that list and add animator to current RenderNode's list.
+ targetRenderNode->animators().removeActiveAnimator(animator);
+ }
+ animator->attach(&mParent);
+ mAnimators.push_back(std::move(animator));
+ }
+ mNewAnimators.clear();
}
- for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
- (*it)->pushStaging(mAnimationHandle->context());
+ for (auto& animator : mAnimators) {
+ animator->pushStaging(mAnimationHandle->context());
}
}
@@ -83,11 +86,11 @@
AnimateFunctor(TreeInfo& info, AnimationContext& context)
: dirtyMask(0), mInfo(info), mContext(context) {}
- bool operator() (BaseRenderNodeAnimator* animator) {
+ bool operator() (sp<BaseRenderNodeAnimator>& animator) {
dirtyMask |= animator->dirtyMask();
bool remove = animator->animate(mContext);
if (remove) {
- animator->decStrong(nullptr);
+ animator->detach();
} else {
if (animator->isRunning()) {
mInfo.out.hasAnimations = true;
@@ -129,20 +132,18 @@
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);
+ auto newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
mAnimators.erase(newEnd, mAnimators.end());
mAnimationHandle->notifyAnimationsRan();
mParent.mProperties.updateMatrix();
return functor.dirtyMask;
}
-static void endStagingAnimator(BaseRenderNodeAnimator* animator) {
- animator->end();
+static void endStagingAnimator(sp<BaseRenderNodeAnimator>& animator) {
+ animator->cancel();
if (animator->listener()) {
- animator->listener()->onAnimationFinished(animator);
+ animator->listener()->onAnimationFinished(animator.get());
}
- animator->decStrong(nullptr);
}
void AnimatorManager::endAllStagingAnimators() {
@@ -153,13 +154,16 @@
mNewAnimators.clear();
}
+void AnimatorManager::removeActiveAnimator(const sp<BaseRenderNodeAnimator>& animator) {
+ std::remove(mAnimators.begin(), mAnimators.end(), animator);
+}
+
class EndActiveAnimatorsFunctor {
public:
EndActiveAnimatorsFunctor(AnimationContext& context) : mContext(context) {}
- void operator() (BaseRenderNodeAnimator* animator) {
+ void operator() (sp<BaseRenderNodeAnimator>& animator) {
animator->forceEndNow(mContext);
- animator->decStrong(nullptr);
}
private:
diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h
index fb75eb8..c24ef47 100644
--- a/libs/hwui/AnimatorManager.h
+++ b/libs/hwui/AnimatorManager.h
@@ -62,13 +62,17 @@
private:
uint32_t animateCommon(TreeInfo& info);
+ // This would remove the animator from mAnimators list. It should only be called during
+ // push staging.
+ void removeActiveAnimator(const sp<BaseRenderNodeAnimator>& animator);
+
RenderNode& mParent;
AnimationHandle* mAnimationHandle;
// To improve the efficiency of resizing & removing from the vector
// use manual ref counting instead of sp<>.
- std::vector<BaseRenderNodeAnimator*> mNewAnimators;
- std::vector<BaseRenderNodeAnimator*> mAnimators;
+ std::vector< sp<BaseRenderNodeAnimator> > mNewAnimators;
+ std::vector< sp<BaseRenderNodeAnimator> > mAnimators;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp
index eca1afcc..b29f91f 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.cpp
+++ b/libs/hwui/PropertyValuesAnimatorSet.cpp
@@ -17,6 +17,8 @@
#include "PropertyValuesAnimatorSet.h"
#include "RenderNode.h"
+#include <algorithm>
+
namespace android {
namespace uirenderer {
@@ -53,16 +55,26 @@
}
void PropertyValuesAnimatorSet::onPlayTimeChanged(nsecs_t playTime) {
- for (size_t i = 0; i < mAnimators.size(); i++) {
- mAnimators[i]->setCurrentPlayTime(playTime);
+ if (playTime == 0 && mDuration > 0) {
+ // Reset all the animators
+ for (auto it = mAnimators.rbegin(); it != mAnimators.rend(); it++) {
+ // Note that this set may containing animators modifying the same property, so when we
+ // reset the animators, we need to make sure the animators that end the first will
+ // have the final say on what the property value should be.
+ (*it)->setFraction(0);
+ }
+ } else if (playTime >= mDuration) {
+ // Skip all the animators to end
+ for (auto& anim : mAnimators) {
+ anim->setFraction(1);
+ }
+ } else {
+ for (auto& anim : mAnimators) {
+ anim->setCurrentPlayTime(playTime);
+ }
}
}
-void PropertyValuesAnimatorSet::reset() {
- // TODO: implement reset through adding a play state because we need to support reset() even
- // during an animation run.
-}
-
void PropertyValuesAnimatorSet::start(AnimationListener* listener) {
init();
mOneShotListener = listener;
@@ -70,20 +82,23 @@
}
void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) {
-// TODO: implement reverse
+ init();
+ mOneShotListener = listener;
+ BaseRenderNodeAnimator::reverse();
}
void PropertyValuesAnimatorSet::init() {
if (mInitialized) {
return;
}
- nsecs_t maxDuration = 0;
- for (size_t i = 0; i < mAnimators.size(); i++) {
- if (maxDuration < mAnimators[i]->getTotalDuration()) {
- maxDuration = mAnimators[i]->getTotalDuration();
- }
- }
- mDuration = maxDuration;
+
+ // Sort the animators by their total duration. Note that all the animators in the set start at
+ // the same time, so the ones with longer total duration (which includes start delay) will
+ // be the ones that end later.
+ std::sort(mAnimators.begin(), mAnimators.end(), [](auto& a, auto&b) {
+ return a->getTotalDuration() < b->getTotalDuration();
+ });
+ mDuration = mAnimators[mAnimators.size() - 1]->getTotalDuration();
mInitialized = true;
}
@@ -106,18 +121,19 @@
void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) {
if (playTime >= mStartDelay && playTime < mTotalDuration) {
nsecs_t currentIterationPlayTime = (playTime - mStartDelay) % mDuration;
- mLatestFraction = currentIterationPlayTime / (float) mDuration;
+ float fraction = currentIterationPlayTime / (float) mDuration;
+ setFraction(fraction);
} else if (mLatestFraction < 1.0f && playTime >= mTotalDuration) {
- mLatestFraction = 1.0f;
- } else {
- return;
+ // This makes sure we only set the fraction = 1 once. It is needed because there might
+ // be another animator modifying the same property after this animator finishes, we need
+ // to make sure we don't set conflicting values on the same property within one frame.
+ setFraction(1.0f);
}
-
- setFraction(mLatestFraction);
}
void PropertyAnimator::setFraction(float fraction) {
- float interpolatedFraction = mInterpolator->interpolate(mLatestFraction);
+ mLatestFraction = fraction;
+ float interpolatedFraction = mInterpolator->interpolate(fraction);
mPropertyValuesHolder->setFraction(interpolatedFraction);
}
diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h
index 4c7ce52..c7ae7c0 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.h
+++ b/libs/hwui/PropertyValuesAnimatorSet.h
@@ -50,7 +50,6 @@
void start(AnimationListener* listener);
void reverse(AnimationListener* listener);
- void reset();
void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
Interpolator* interpolators, int64_t startDelays,