Fedor Kudasov | 34a2576 | 2019-06-28 21:53:56 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "RootRenderNode.h" |
| 18 | |
Fedor Kudasov | 09cfce0 | 2019-07-04 09:41:13 +0100 | [diff] [blame] | 19 | #ifdef __ANDROID__ // Layoutlib does not support Looper (windows) |
Fedor Kudasov | bc409cf | 2019-07-03 13:56:56 +0100 | [diff] [blame] | 20 | #include <utils/Looper.h> |
Fedor Kudasov | 09cfce0 | 2019-07-04 09:41:13 +0100 | [diff] [blame] | 21 | #endif |
Fedor Kudasov | bc409cf | 2019-07-03 13:56:56 +0100 | [diff] [blame] | 22 | |
Fedor Kudasov | 34a2576 | 2019-06-28 21:53:56 +0100 | [diff] [blame] | 23 | namespace android::uirenderer { |
| 24 | |
Fedor Kudasov | 09cfce0 | 2019-07-04 09:41:13 +0100 | [diff] [blame] | 25 | #ifdef __ANDROID__ // Layoutlib does not support Looper |
Fedor Kudasov | 34a2576 | 2019-06-28 21:53:56 +0100 | [diff] [blame] | 26 | class FinishAndInvokeListener : public MessageHandler { |
| 27 | public: |
| 28 | explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim) : mAnimator(anim) { |
| 29 | mListener = anim->getOneShotListener(); |
| 30 | mRequestId = anim->getRequestId(); |
| 31 | } |
| 32 | |
| 33 | virtual void handleMessage(const Message& message) { |
| 34 | if (mAnimator->getRequestId() == mRequestId) { |
| 35 | // Request Id has not changed, meaning there's no animation lifecyle change since the |
| 36 | // message is posted, so go ahead and call finish to make sure the PlayState is properly |
| 37 | // updated. This is needed because before the next frame comes in from UI thread to |
| 38 | // trigger an animation update, there could be reverse/cancel etc. So we need to update |
| 39 | // the playstate in time to ensure all the subsequent events get chained properly. |
| 40 | mAnimator->end(); |
| 41 | } |
| 42 | mListener->onAnimationFinished(nullptr); |
| 43 | } |
| 44 | |
| 45 | private: |
| 46 | sp<PropertyValuesAnimatorSet> mAnimator; |
| 47 | sp<AnimationListener> mListener; |
| 48 | uint32_t mRequestId; |
| 49 | }; |
| 50 | |
| 51 | void RootRenderNode::prepareTree(TreeInfo& info) { |
| 52 | info.errorHandler = mErrorHandler.get(); |
| 53 | |
| 54 | for (auto& anim : mRunningVDAnimators) { |
| 55 | // Assume that the property change in VD from the animators will not be consumed. Mark |
| 56 | // otherwise if the VDs are found in the display list tree. For VDs that are not in |
| 57 | // the display list tree, we stop providing animation pulses by 1) removing them from |
| 58 | // the animation list, 2) post a delayed message to end them at end time so their |
| 59 | // listeners can receive the corresponding callbacks. |
| 60 | anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); |
| 61 | // Mark the VD dirty so it will damage itself during prepareTree. |
| 62 | anim->getVectorDrawable()->markDirty(); |
| 63 | } |
| 64 | if (info.mode == TreeInfo::MODE_FULL) { |
| 65 | for (auto& anim : mPausedVDAnimators) { |
| 66 | anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false); |
| 67 | anim->getVectorDrawable()->markDirty(); |
| 68 | } |
| 69 | } |
| 70 | // TODO: This is hacky |
| 71 | info.updateWindowPositions = true; |
| 72 | RenderNode::prepareTree(info); |
| 73 | info.updateWindowPositions = false; |
| 74 | info.errorHandler = nullptr; |
| 75 | } |
| 76 | |
| 77 | void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { |
| 78 | mPendingAnimatingRenderNodes.push_back(animatingNode); |
| 79 | } |
| 80 | |
| 81 | void RootRenderNode::attachPendingVectorDrawableAnimators() { |
| 82 | mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(), |
| 83 | mPendingVectorDrawableAnimators.end()); |
| 84 | mPendingVectorDrawableAnimators.clear(); |
| 85 | } |
| 86 | |
| 87 | void RootRenderNode::detachAnimators() { |
| 88 | // Remove animators from the list and post a delayed message in future to end the animator |
| 89 | // For infinite animators, remove the listener so we no longer hold a global ref to the AVD |
| 90 | // java object, and therefore the AVD objects in both native and Java can be properly |
| 91 | // released. |
| 92 | for (auto& anim : mRunningVDAnimators) { |
| 93 | detachVectorDrawableAnimator(anim.get()); |
| 94 | anim->clearOneShotListener(); |
| 95 | } |
| 96 | for (auto& anim : mPausedVDAnimators) { |
| 97 | anim->clearOneShotListener(); |
| 98 | } |
| 99 | mRunningVDAnimators.clear(); |
| 100 | mPausedVDAnimators.clear(); |
| 101 | } |
| 102 | |
| 103 | // Move all the animators to the paused list, and send a delayed message to notify the finished |
| 104 | // listener. |
| 105 | void RootRenderNode::pauseAnimators() { |
| 106 | mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end()); |
| 107 | for (auto& anim : mRunningVDAnimators) { |
| 108 | detachVectorDrawableAnimator(anim.get()); |
| 109 | } |
| 110 | mRunningVDAnimators.clear(); |
| 111 | } |
| 112 | |
| 113 | void RootRenderNode::doAttachAnimatingNodes(AnimationContext* context) { |
| 114 | for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) { |
| 115 | RenderNode* node = mPendingAnimatingRenderNodes[i].get(); |
| 116 | context->addAnimatingRenderNode(*node); |
| 117 | } |
| 118 | mPendingAnimatingRenderNodes.clear(); |
| 119 | } |
| 120 | |
| 121 | // Run VectorDrawable animators after prepareTree. |
| 122 | void RootRenderNode::runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info) { |
| 123 | // Push staging. |
| 124 | if (info.mode == TreeInfo::MODE_FULL) { |
| 125 | pushStagingVectorDrawableAnimators(context); |
| 126 | } |
| 127 | |
| 128 | // Run the animators in the running list. |
| 129 | for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { |
| 130 | if ((*it)->animate(*context)) { |
| 131 | it = mRunningVDAnimators.erase(it); |
| 132 | } else { |
| 133 | it++; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | // Run the animators in paused list during full sync. |
| 138 | if (info.mode == TreeInfo::MODE_FULL) { |
| 139 | // During full sync we also need to pulse paused animators, in case their targets |
| 140 | // have been added back to the display list. All the animators that passed the |
| 141 | // scheduled finish time will be removed from the paused list. |
| 142 | for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { |
| 143 | if ((*it)->animate(*context)) { |
| 144 | // Animator has finished, remove from the list. |
| 145 | it = mPausedVDAnimators.erase(it); |
| 146 | } else { |
| 147 | it++; |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | // Move the animators with a target not in DisplayList to paused list. |
| 153 | for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) { |
| 154 | if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { |
| 155 | // Vector Drawable is not in the display list, we should remove this animator from |
| 156 | // the list, put it in the paused list, and post a delayed message to end the |
| 157 | // animator. |
| 158 | detachVectorDrawableAnimator(it->get()); |
| 159 | mPausedVDAnimators.insert(*it); |
| 160 | it = mRunningVDAnimators.erase(it); |
| 161 | } else { |
| 162 | it++; |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | // Move the animators with a target in DisplayList from paused list to running list, and |
| 167 | // trim paused list. |
| 168 | if (info.mode == TreeInfo::MODE_FULL) { |
| 169 | // Check whether any paused animator's target is back in Display List. If so, put the |
| 170 | // animator back in the running list. |
| 171 | for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { |
| 172 | if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) { |
| 173 | mRunningVDAnimators.insert(*it); |
| 174 | it = mPausedVDAnimators.erase(it); |
| 175 | } else { |
| 176 | it++; |
| 177 | } |
| 178 | } |
| 179 | // Trim paused VD animators at full sync, so that when Java loses reference to an |
| 180 | // animator, we know we won't be requested to animate it any more, then we remove such |
| 181 | // animators from the paused list so they can be properly freed. We also remove the |
| 182 | // animators from paused list when the time elapsed since start has exceeded duration. |
| 183 | trimPausedVDAnimators(context); |
| 184 | } |
| 185 | |
| 186 | info.out.hasAnimations |= !mRunningVDAnimators.empty(); |
| 187 | } |
| 188 | |
| 189 | void RootRenderNode::trimPausedVDAnimators(AnimationContext* context) { |
| 190 | // Trim paused vector drawable animator list. |
| 191 | for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) { |
| 192 | // Remove paused VD animator if no one else is referencing it. Note that animators that |
| 193 | // have passed scheduled finish time are removed from list when they are being pulsed |
| 194 | // before prepare tree. |
| 195 | // TODO: this is a bit hacky, need to figure out a better way to track when the paused |
| 196 | // animators should be freed. |
| 197 | if ((*it)->getStrongCount() == 1) { |
| 198 | it = mPausedVDAnimators.erase(it); |
| 199 | } else { |
| 200 | it++; |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | void RootRenderNode::pushStagingVectorDrawableAnimators(AnimationContext* context) { |
| 206 | for (auto& anim : mRunningVDAnimators) { |
| 207 | anim->pushStaging(*context); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | void RootRenderNode::destroy() { |
| 212 | for (auto& renderNode : mPendingAnimatingRenderNodes) { |
| 213 | renderNode->animators().endAllStagingAnimators(); |
| 214 | } |
| 215 | mPendingAnimatingRenderNodes.clear(); |
| 216 | mPendingVectorDrawableAnimators.clear(); |
| 217 | } |
| 218 | |
| 219 | void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { |
| 220 | mPendingVectorDrawableAnimators.insert(anim); |
| 221 | } |
| 222 | |
| 223 | void RootRenderNode::detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { |
| 224 | if (anim->isInfinite() || !anim->isRunning()) { |
| 225 | // Do not need to post anything if the animation is infinite (i.e. no meaningful |
| 226 | // end listener action), or if the animation has already ended. |
| 227 | return; |
| 228 | } |
| 229 | nsecs_t remainingTimeInMs = anim->getRemainingPlayTime(); |
| 230 | // Post a delayed onFinished event that is scheduled to be handled when the animator ends. |
| 231 | if (anim->getOneShotListener()) { |
| 232 | // VectorDrawable's oneshot listener is updated when there are user triggered animation |
| 233 | // lifecycle changes, such as start(), end(), etc. By using checking and clearing |
| 234 | // one shot listener, we ensure the same end listener event gets posted only once. |
| 235 | // Therefore no duplicates. Another benefit of using one shot listener is that no |
| 236 | // removal is necessary: the end time of animation will not change unless triggered by |
| 237 | // user events, in which case the already posted listener's id will become stale, and |
| 238 | // the onFinished callback will then be ignored. |
| 239 | sp<FinishAndInvokeListener> message = new FinishAndInvokeListener(anim); |
| 240 | auto looper = Looper::getForThread(); |
| 241 | LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?"); |
| 242 | looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0); |
| 243 | anim->clearOneShotListener(); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | class AnimationContextBridge : public AnimationContext { |
| 248 | public: |
| 249 | AnimationContextBridge(renderthread::TimeLord& clock, RootRenderNode* rootNode) |
| 250 | : AnimationContext(clock), mRootNode(rootNode) {} |
| 251 | |
| 252 | virtual ~AnimationContextBridge() {} |
| 253 | |
| 254 | // Marks the start of a frame, which will update the frame time and move all |
| 255 | // next frame animations into the current frame |
| 256 | virtual void startFrame(TreeInfo::TraversalMode mode) { |
| 257 | if (mode == TreeInfo::MODE_FULL) { |
| 258 | mRootNode->doAttachAnimatingNodes(this); |
| 259 | mRootNode->attachPendingVectorDrawableAnimators(); |
| 260 | } |
| 261 | AnimationContext::startFrame(mode); |
| 262 | } |
| 263 | |
| 264 | // Runs any animations still left in mCurrentFrameAnimations |
| 265 | virtual void runRemainingAnimations(TreeInfo& info) { |
| 266 | AnimationContext::runRemainingAnimations(info); |
| 267 | mRootNode->runVectorDrawableAnimators(this, info); |
| 268 | } |
| 269 | |
| 270 | virtual void pauseAnimators() override { mRootNode->pauseAnimators(); } |
| 271 | |
| 272 | virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { |
| 273 | listener->onAnimationFinished(animator); |
| 274 | } |
| 275 | |
| 276 | virtual void destroy() { |
| 277 | AnimationContext::destroy(); |
| 278 | mRootNode->detachAnimators(); |
| 279 | } |
| 280 | |
| 281 | private: |
| 282 | sp<RootRenderNode> mRootNode; |
| 283 | }; |
| 284 | |
| 285 | AnimationContext* ContextFactoryImpl::createAnimationContext(renderthread::TimeLord& clock) { |
| 286 | return new AnimationContextBridge(clock, mRootNode); |
| 287 | } |
Fedor Kudasov | 09cfce0 | 2019-07-04 09:41:13 +0100 | [diff] [blame] | 288 | #else |
| 289 | |
| 290 | void RootRenderNode::prepareTree(TreeInfo& info) { |
| 291 | info.errorHandler = mErrorHandler.get(); |
| 292 | info.updateWindowPositions = true; |
| 293 | RenderNode::prepareTree(info); |
| 294 | info.updateWindowPositions = false; |
| 295 | info.errorHandler = nullptr; |
| 296 | } |
| 297 | |
| 298 | void RootRenderNode::attachAnimatingNode(RenderNode* animatingNode) { } |
| 299 | |
| 300 | void RootRenderNode::destroy() { } |
| 301 | |
| 302 | void RootRenderNode::addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) { } |
| 303 | |
| 304 | #endif |
Fedor Kudasov | 34a2576 | 2019-06-28 21:53:56 +0100 | [diff] [blame] | 305 | |
| 306 | } // namespace android::uirenderer |