diff options
author | John Reck <jreck@google.com> | 2014-04-15 09:50:16 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2014-04-29 10:42:11 -0700 |
commit | e45b1fd03b524d2b57cc6c222d89076a31a08bea (patch) | |
tree | 31ad10387f2b59b3ee9d4396be44fce67228ca75 /libs | |
parent | 627aad9c200cb19aa505504dcd232a3710e96a25 (diff) | |
download | frameworks_base-e45b1fd03b524d2b57cc6c222d89076a31a08bea.zip frameworks_base-e45b1fd03b524d2b57cc6c222d89076a31a08bea.tar.gz frameworks_base-e45b1fd03b524d2b57cc6c222d89076a31a08bea.tar.bz2 |
RenderThread animator support
Change-Id: Icf29098edfdaf7ed550bbe9d49e9eaefb4167084
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Android.mk | 4 | ||||
-rw-r--r-- | libs/hwui/Animator.cpp | 240 | ||||
-rw-r--r-- | libs/hwui/Animator.h | 79 | ||||
-rw-r--r-- | libs/hwui/Interpolator.cpp | 32 | ||||
-rw-r--r-- | libs/hwui/Interpolator.h | 45 | ||||
-rw-r--r-- | libs/hwui/RenderNode.cpp | 52 | ||||
-rw-r--r-- | libs/hwui/RenderNode.h | 36 | ||||
-rw-r--r-- | libs/hwui/RenderProperties.cpp | 3 | ||||
-rw-r--r-- | libs/hwui/RenderProperties.h | 29 | ||||
-rw-r--r-- | libs/hwui/TreeInfo.h | 64 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 35 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 17 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 36 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 3 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 4 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.cpp | 123 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.h | 34 |
18 files changed, 780 insertions, 66 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 52be531..d324439 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -11,6 +11,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) font/CacheTexture.cpp \ font/Font.cpp \ AmbientShadow.cpp \ + Animator.cpp \ AssetAtlas.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ @@ -25,6 +26,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) FboCache.cpp \ GradientCache.cpp \ Image.cpp \ + Interpolator.cpp \ Layer.cpp \ LayerCache.cpp \ LayerRenderer.cpp \ @@ -66,6 +68,8 @@ ifeq ($(USE_OPENGL_RENDERER),true) $(LOCAL_PATH)/../../include/utils \ external/skia/src/core + include external/stlport/libstlport.mk + LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp new file mode 100644 index 0000000..ee16586 --- /dev/null +++ b/libs/hwui/Animator.cpp @@ -0,0 +1,240 @@ +/* + * 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. + */ + +#define LOG_TAG "RT-Animator" + +#include "Animator.h" + +#include <set> + +#include "RenderProperties.h" + +namespace android { +namespace uirenderer { + +/************************************************************ + * Private header + ************************************************************/ + +typedef void (RenderProperties::*SetFloatProperty)(float value); +typedef float (RenderProperties::*GetFloatProperty)() const; + +struct PropertyAccessors { + GetFloatProperty getter; + SetFloatProperty setter; +}; + +// Maps RenderProperty enum to accessors +static const PropertyAccessors PROPERTY_ACCESSOR_LUT[] = { + {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, + {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, + {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, + {&RenderProperties::getScaleX, &RenderProperties::setScaleX }, + {&RenderProperties::getScaleY, &RenderProperties::setScaleY }, + {&RenderProperties::getRotation, &RenderProperties::setRotation }, + {&RenderProperties::getRotationX, &RenderProperties::setRotationX }, + {&RenderProperties::getRotationY, &RenderProperties::setRotationY }, + {&RenderProperties::getX, &RenderProperties::setX }, + {&RenderProperties::getY, &RenderProperties::setY }, + {&RenderProperties::getZ, &RenderProperties::setZ }, + {&RenderProperties::getAlpha, &RenderProperties::setAlpha }, +}; + +// Helper class to contain generic animator helpers +class BaseAnimator { +public: + BaseAnimator(); + virtual ~BaseAnimator(); + + void setInterpolator(Interpolator* interpolator); + void setDuration(nsecs_t durationInMs); + + bool isFinished() { return mPlayState == FINISHED; } + +protected: + // This is the main animation entrypoint that subclasses should call + // to generate the onAnimation* lifecycle events + // Returns true if the animation has finished, false otherwise + bool animateFrame(nsecs_t frameTime); + + // Called when PlayState switches from PENDING to RUNNING + virtual void onAnimationStarted() {} + virtual void onAnimationUpdated(float fraction) = 0; + virtual void onAnimationFinished() {} + +private: + enum PlayState { + PENDING, + RUNNING, + FINISHED, + }; + + Interpolator* mInterpolator; + PlayState mPlayState; + long mStartTime; + long mDuration; +}; + +// Hide the base classes & private bits from the exported RenderPropertyAnimator +// in this Impl class so that subclasses of RenderPropertyAnimator don't require +// knowledge of the inner guts but only the public virtual methods. +// Animates a single property +class RenderPropertyAnimatorImpl : public BaseAnimator { +public: + RenderPropertyAnimatorImpl(GetFloatProperty getter, SetFloatProperty setter, + RenderPropertyAnimator::DeltaValueType deltaType, float delta); + ~RenderPropertyAnimatorImpl(); + + bool animate(RenderProperties* target, TreeInfo& info); + +protected: + virtual void onAnimationStarted(); + virtual void onAnimationUpdated(float fraction); + +private: + // mTarget is only valid inside animate() + RenderProperties* mTarget; + GetFloatProperty mGetter; + SetFloatProperty mSetter; + + RenderPropertyAnimator::DeltaValueType mDeltaValueType; + float mDeltaValue; + float mFromValue; +}; + +RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, + DeltaValueType deltaType, float deltaValue) { + PropertyAccessors pa = PROPERTY_ACCESSOR_LUT[property]; + mImpl = new RenderPropertyAnimatorImpl(pa.getter, pa.setter, deltaType, deltaValue); +} + +RenderPropertyAnimator::~RenderPropertyAnimator() { + delete mImpl; + mImpl = NULL; +} + +void RenderPropertyAnimator::setInterpolator(Interpolator* interpolator) { + mImpl->setInterpolator(interpolator); +} + +void RenderPropertyAnimator::setDuration(nsecs_t durationInMs) { + mImpl->setDuration(durationInMs); +} + +bool RenderPropertyAnimator::isFinished() { + return mImpl->isFinished(); +} + +bool RenderPropertyAnimator::animate(RenderProperties* target, TreeInfo& info) { + return mImpl->animate(target, info); +} + + +/************************************************************ + * Base animator + ************************************************************/ + +BaseAnimator::BaseAnimator() + : mInterpolator(0) + , mPlayState(PENDING) + , mStartTime(0) + , mDuration(300) { + +} + +BaseAnimator::~BaseAnimator() { + setInterpolator(NULL); +} + +void BaseAnimator::setInterpolator(Interpolator* interpolator) { + delete mInterpolator; + mInterpolator = interpolator; +} + +void BaseAnimator::setDuration(nsecs_t duration) { + mDuration = duration; +} + +bool BaseAnimator::animateFrame(nsecs_t frameTime) { + if (mPlayState == PENDING) { + mPlayState = RUNNING; + mStartTime = frameTime; + // No interpolator was set, use the default + if (!mInterpolator) { + setInterpolator(Interpolator::createDefaultInterpolator()); + } + onAnimationStarted(); + } + + float fraction = 1.0f; + if (mPlayState == RUNNING) { + fraction = mDuration > 0 ? (float)(frameTime - mStartTime) / mDuration : 1.0f; + if (fraction >= 1.0f) { + fraction = 1.0f; + mPlayState = FINISHED; + } + } + fraction = mInterpolator->interpolate(fraction); + onAnimationUpdated(fraction); + + if (mPlayState == FINISHED) { + onAnimationFinished(); + return true; + } + return false; +} + +/************************************************************ + * RenderPropertyAnimator + ************************************************************/ + +RenderPropertyAnimatorImpl::RenderPropertyAnimatorImpl( + GetFloatProperty getter, SetFloatProperty setter, + RenderPropertyAnimator::DeltaValueType deltaType, float delta) + : mTarget(0) + , mGetter(getter) + , mSetter(setter) + , mDeltaValueType(deltaType) + , mDeltaValue(delta) + , mFromValue(-1) { +} + +RenderPropertyAnimatorImpl::~RenderPropertyAnimatorImpl() { +} + +bool RenderPropertyAnimatorImpl::animate(RenderProperties* target, TreeInfo& info) { + mTarget = target; + bool finished = animateFrame(info.frameTimeMs); + mTarget = NULL; + return finished; +} + +void RenderPropertyAnimatorImpl::onAnimationStarted() { + mFromValue = (mTarget->*mGetter)(); + + if (mDeltaValueType == RenderPropertyAnimator::ABSOLUTE) { + mDeltaValue = (mDeltaValue - mFromValue); + mDeltaValueType = RenderPropertyAnimator::DELTA; + } +} + +void RenderPropertyAnimatorImpl::onAnimationUpdated(float fraction) { + float value = mFromValue + (mDeltaValue * fraction); + (mTarget->*mSetter)(value); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h new file mode 100644 index 0000000..1c8361b --- /dev/null +++ b/libs/hwui/Animator.h @@ -0,0 +1,79 @@ +/* + * 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 ANIMATOR_H +#define ANIMATOR_H + +#include <cutils/compiler.h> + +#include "Interpolator.h" +#include "TreeInfo.h" +#include "utils/VirtualLightRefBase.h" + +namespace android { +namespace uirenderer { + +class RenderProperties; +class RenderPropertyAnimatorImpl; + +class RenderPropertyAnimator : public VirtualLightRefBase { +public: + // Since the UI thread doesn't necessarily know what the current values + // actually are and thus can't do the calculations, this is used to inform + // the animator how to lazy-resolve the input value + enum DeltaValueType { + // The delta value represents an absolute value endpoint + // mDeltaValue needs to be recalculated to be mDelta = (mDelta - fromValue) + // in onAnimationStarted() + ABSOLUTE = 0, + // The final value represents an offset from the current value + // No recalculation is needed + DELTA, + }; + + enum RenderProperty { + TRANSLATION_X = 0, + TRANSLATION_Y, + TRANSLATION_Z, + SCALE_X, + SCALE_Y, + ROTATION, + ROTATION_X, + ROTATION_Y, + X, + Y, + Z, + ALPHA, + }; + + ANDROID_API void setInterpolator(Interpolator* interpolator); + ANDROID_API void setDuration(nsecs_t durationInMs); + ANDROID_API bool isFinished(); + + bool animate(RenderProperties* target, TreeInfo& info); + +protected: + ANDROID_API RenderPropertyAnimator(RenderProperty property, DeltaValueType deltaType, + float deltaValue); + ANDROID_API virtual ~RenderPropertyAnimator(); + +private: + RenderPropertyAnimatorImpl* mImpl; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* ANIMATOR_H */ diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp new file mode 100644 index 0000000..004ddf5 --- /dev/null +++ b/libs/hwui/Interpolator.cpp @@ -0,0 +1,32 @@ +/* + * 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 "Interpolator.h" + +#include <math.h> + +namespace android { +namespace uirenderer { + +Interpolator* Interpolator::createDefaultInterpolator() { + return new AccelerateDecelerateInterpolator(); +} + +float AccelerateDecelerateInterpolator::interpolate(float input) { + return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h new file mode 100644 index 0000000..2cfb60c --- /dev/null +++ b/libs/hwui/Interpolator.h @@ -0,0 +1,45 @@ +/* + * 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 INTERPOLATOR_H +#define INTERPOLATOR_H + +namespace android { +namespace uirenderer { + +class Interpolator { +public: + virtual ~Interpolator() {} + + virtual float interpolate(float input) = 0; + + static Interpolator* createDefaultInterpolator(); + +protected: + Interpolator() {} +}; + +class AccelerateDecelerateInterpolator : public Interpolator { +public: + AccelerateDecelerateInterpolator() {} + virtual ~AccelerateDecelerateInterpolator() {} + + virtual float interpolate(float input); +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* INTERPOLATOR_H */ diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 838e5ac..dcd6bda 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -18,6 +18,8 @@ #include "RenderNode.h" +#include <algorithm> + #include <SkCanvas.h> #include <algorithm> @@ -54,7 +56,8 @@ RenderNode::RenderNode() : mNeedsPropertiesSync(false) , mNeedsDisplayListDataSync(false) , mDisplayListData(0) - , mStagingDisplayListData(0) { + , mStagingDisplayListData(0) + , mNeedsAnimatorsSync(false) { } RenderNode::~RenderNode() { @@ -97,15 +100,32 @@ void RenderNode::prepareTree(TreeInfo& info) { } void RenderNode::prepareTreeImpl(TreeInfo& info) { - pushStagingChanges(info); + if (info.performStagingPush) { + pushStagingChanges(info); + } + if (info.evaluateAnimations) { + evaluateAnimations(info); + } prepareSubTree(info, mDisplayListData); } +static bool is_finished(const sp<RenderPropertyAnimator>& animator) { + return animator->isFinished(); +} + void RenderNode::pushStagingChanges(TreeInfo& info) { if (mNeedsPropertiesSync) { mNeedsPropertiesSync = false; mProperties = mStagingProperties; } + if (mNeedsAnimatorsSync) { + mAnimators.reserve(mStagingAnimators.size()); + std::vector< sp<RenderPropertyAnimator> >::iterator it; + // hint: this means copy_if_not() + it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(), + mAnimators.begin(), is_finished); + mAnimators.resize(std::distance(mAnimators.begin(), it)); + } if (mNeedsDisplayListDataSync) { mNeedsDisplayListDataSync = false; // Do a push pass on the old tree to handle freeing DisplayListData @@ -119,6 +139,34 @@ void RenderNode::pushStagingChanges(TreeInfo& info) { } } +class AnimateFunctor { +public: + AnimateFunctor(RenderProperties* target, TreeInfo& info) + : mTarget(target), mInfo(info) {} + + bool operator() (sp<RenderPropertyAnimator>& animator) { + bool finished = animator->animate(mTarget, mInfo); + if (finished && mInfo.animationListener) { + mInfo.animationListener->onAnimationFinished(animator); + } + return finished; + } +private: + RenderProperties* mTarget; + TreeInfo& mInfo; +}; + +void RenderNode::evaluateAnimations(TreeInfo& info) { + if (!mAnimators.size()) return; + + AnimateFunctor functor(&mProperties, info); + std::vector< sp<RenderPropertyAnimator> >::iterator newEnd; + newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); + mAnimators.erase(newEnd, mAnimators.end()); + mProperties.updateMatrix(); + info.hasAnimations |= mAnimators.size(); +} + void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { if (subtree) { TextureCache& cache = Caches::getInstance().textureCache; diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index b9edbe5..294f436 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -20,6 +20,9 @@ #define LOG_TAG "OpenGLRenderer" #endif +#include <set> +#include <vector> + #include <SkCamera.h> #include <SkMatrix.h> @@ -41,6 +44,7 @@ #include "DeferredDisplayList.h" #include "DisplayList.h" #include "RenderProperties.h" +#include "TreeInfo.h" #include "utils/VirtualLightRefBase.h" class SkBitmap; @@ -65,17 +69,6 @@ class SaveOp; class RestoreToCountOp; class DrawDisplayListOp; -struct TreeInfo { - TreeInfo() - : hasFunctors(false) - , prepareTextures(false) - {} - - bool hasFunctors; - bool prepareTextures; - // TODO: Damage calculations? Flag to skip staging pushes for RT animations? -}; - /** * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. * @@ -91,7 +84,7 @@ struct TreeInfo { class RenderNode : public VirtualLightRefBase { public: ANDROID_API RenderNode(); - ANDROID_API ~RenderNode(); + ANDROID_API virtual ~RenderNode(); // See flags defined in DisplayList.java enum ReplayFlag { @@ -152,7 +145,19 @@ public: return properties().getHeight(); } - ANDROID_API void prepareTree(TreeInfo& info); + ANDROID_API virtual void prepareTree(TreeInfo& info); + + // UI thread only! + ANDROID_API void addAnimator(const sp<RenderPropertyAnimator>& animator) { + mStagingAnimators.insert(animator); + mNeedsAnimatorsSync = true; + } + + // UI thread only! + ANDROID_API void removeAnimator(const sp<RenderPropertyAnimator>& animator) { + mStagingAnimators.erase(animator); + mNeedsAnimatorsSync = true; + } private: typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair; @@ -214,6 +219,7 @@ private: void prepareTreeImpl(TreeInfo& info); void pushStagingChanges(TreeInfo& info); + void evaluateAnimations(TreeInfo& info); void prepareSubTree(TreeInfo& info, DisplayListData* subtree); String8 mName; @@ -226,6 +232,10 @@ private: DisplayListData* mDisplayListData; DisplayListData* mStagingDisplayListData; + bool mNeedsAnimatorsSync; + std::set< sp<RenderPropertyAnimator> > mStagingAnimators; + std::vector< sp<RenderPropertyAnimator> > mAnimators; + /** * Draw time state - these properties are only set and used during rendering */ diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 9ec7297..99de1fc 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -51,7 +51,8 @@ RenderProperties::PrimitiveFields::PrimitiveFields() RenderProperties::ComputedFields::ComputedFields() : mTransformMatrix(NULL) - , mClipPath(NULL) { + , mClipPath(NULL) + , mClipPathOp(SkRegion::kIntersect_Op) { } RenderProperties::ComputedFields::~ComputedFields() { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 8fc2dd0..6fc8bce 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,9 @@ #ifndef RENDERNODEPROPERTIES_H #define RENDERNODEPROPERTIES_H +#include <algorithm> #include <stddef.h> +#include <vector> #include <cutils/compiler.h> #include <androidfw/ResourceTypes.h> @@ -24,6 +26,7 @@ #include <SkMatrix.h> #include <SkRegion.h> +#include "Animator.h" #include "Rect.h" #include "RevealClip.h" #include "Outline.h" @@ -149,6 +152,31 @@ public: return mPrimitiveFields.mTranslationZ; } + // Animation helper + void setX(float value) { + setTranslationX(value - getLeft()); + } + + // Animation helper + float getX() const { + return getLeft() + getTranslationX(); + } + + // Animation helper + void setY(float value) { + setTranslationY(value - getTop()); + } + + // Animation helper + float getY() const { + return getTop() + getTranslationY(); + } + + // Animation helper + void setZ(float value) { + setTranslationZ(value - getElevation()); + } + float getZ() const { return getElevation() + getTranslationZ(); } @@ -457,7 +485,6 @@ private: bool mCaching; } mPrimitiveFields; - // mCameraDistance isn't in mPrimitiveFields as it has a complex setter SkMatrix* mStaticMatrix; SkMatrix* mAnimationMatrix; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h new file mode 100644 index 0000000..8957607 --- /dev/null +++ b/libs/hwui/TreeInfo.h @@ -0,0 +1,64 @@ +/* + * 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 TREEINFO_H +#define TREEINFO_H + +#include <cutils/compiler.h> +#include <utils/Timers.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace uirenderer { + +class RenderPropertyAnimator; + +class AnimationListener { +public: + ANDROID_API virtual void onAnimationFinished(const sp<RenderPropertyAnimator>&) = 0; +protected: + ANDROID_API virtual ~AnimationListener() {} +}; + +struct TreeInfo { + // The defaults here should be safe for everyone but DrawFrameTask to use as-is. + TreeInfo() + : hasFunctors(false) + , prepareTextures(false) + , performStagingPush(true) + , frameTimeMs(0) + , evaluateAnimations(false) + , hasAnimations(false) + , animationListener(0) + {} + + bool hasFunctors; + bool prepareTextures; + bool performStagingPush; + + // Animations + nsecs_t frameTimeMs; + bool evaluateAnimations; + // This is only updated if evaluateAnimations is true + bool hasAnimations; + AnimationListener* animationListener; + + // TODO: Damage calculations +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TREEINFO_H */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5ce7ba6..63f4b95 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -308,18 +308,20 @@ bool GlobalContext::enableDirtyRegions(EGLSurface surface) { return value == EGL_BUFFER_PRESERVED; } -CanvasContext::CanvasContext(bool translucent) +CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode) : mRenderThread(RenderThread::getInstance()) , mEglSurface(EGL_NO_SURFACE) , mDirtyRegionsEnabled(false) , mOpaque(!translucent) , mCanvas(0) - , mHaveNewSurface(false) { + , mHaveNewSurface(false) + , mRootRenderNode(rootRenderNode) { mGlobalContext = GlobalContext::get(); } CanvasContext::~CanvasContext() { destroyCanvasAndSurface(); + mRenderThread.removeFrameCallback(this); } void CanvasContext::destroyCanvasAndSurface() { @@ -403,7 +405,16 @@ void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* lay } } -void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { +void CanvasContext::prepareTree(TreeInfo& info) { + mRootRenderNode->prepareTree(info); + + if (info.hasAnimations && !info.hasFunctors) { + // TODO: Functors + mRenderThread.postFrameCallback(this); + } +} + +void CanvasContext::draw(Rect* dirty) { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawDisplayList called on a context with no canvas or surface!"); @@ -417,7 +428,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { } status_t status; - if (dirty) { + if (dirty && !dirty->isEmpty()) { status = mCanvas->prepareDirty(dirty->left, dirty->top, dirty->right, dirty->bottom, mOpaque); } else { @@ -425,7 +436,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { } Rect outBounds; - status |= mCanvas->drawDisplayList(displayList, outBounds); + status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); // TODO: Draw debug info // TODO: Performance tracking @@ -437,6 +448,20 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) { } } +// Called by choreographer to do an RT-driven animation +void CanvasContext::doFrame(nsecs_t frameTimeNanos) { + ATRACE_CALL(); + + TreeInfo info; + info.evaluateAnimations = true; + info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos); + info.performStagingPush = false; + info.prepareTextures = false; + + prepareTree(info); + draw(NULL); +} + void CanvasContext::invokeFunctor(Functor* functor) { ATRACE_CALL(); DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index a3fe591..0873ad4 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -25,6 +25,7 @@ #include "../RenderNode.h" #include "RenderTask.h" +#include "RenderThread.h" #define FUNCTOR_PROCESS_DELAY 4 @@ -39,15 +40,13 @@ class Layer; namespace renderthread { class GlobalContext; -class CanvasContext; -class RenderThread; // This per-renderer class manages the bridge between the global EGL context // and the render surface. -class CanvasContext { +class CanvasContext : public IFrameCallback { public: - CanvasContext(bool translucent); - ~CanvasContext(); + CanvasContext(bool translucent, RenderNode* rootRenderNode); + virtual ~CanvasContext(); bool initialize(EGLNativeWindowType window); void updateSurface(EGLNativeWindowType window); @@ -55,9 +54,13 @@ public: void setup(int width, int height); void makeCurrent(); void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); - void drawDisplayList(RenderNode* displayList, Rect* dirty); + void prepareTree(TreeInfo& info); + void draw(Rect* dirty); void destroyCanvasAndSurface(); + // IFrameCallback, Chroreographer-driven frame callback entry point + virtual void doFrame(nsecs_t frameTimeNanos); + bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); void invokeFunctor(Functor* functor); @@ -82,6 +85,8 @@ private: bool mOpaque; OpenGLRenderer* mCanvas; bool mHaveNewSurface; + + const sp<RenderNode> mRootRenderNode; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index f542d43..ff4be71 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -30,7 +30,7 @@ namespace android { namespace uirenderer { namespace renderthread { -DrawFrameTask::DrawFrameTask() : mContext(0), mRenderNode(0) { +DrawFrameTask::DrawFrameTask() : mContext(0) { } DrawFrameTask::~DrawFrameTask() { @@ -55,25 +55,17 @@ void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) { } } -void DrawFrameTask::setRenderNode(RenderNode* renderNode) { - LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setRenderNode with!"); - - mRenderNode = renderNode; -} - void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { mDirty.set(left, top, right, bottom); } void DrawFrameTask::drawFrame(RenderThread* renderThread) { - LOG_ALWAYS_FATAL_IF(!mRenderNode.get(), "Cannot drawFrame with no render node!"); LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); postAndWait(renderThread); // Reset the single-frame data mDirty.setEmpty(); - mRenderNode = 0; } void DrawFrameTask::postAndWait(RenderThread* renderThread) { @@ -88,8 +80,7 @@ void DrawFrameTask::run() { bool canUnblockUiThread = syncFrameState(); // Grab a copy of everything we need - Rect dirtyCopy(mDirty); - sp<RenderNode> renderNode = mRenderNode; + Rect dirty(mDirty); CanvasContext* context = mContext; // From this point on anything in "this" is *UNSAFE TO ACCESS* @@ -97,15 +88,20 @@ void DrawFrameTask::run() { unblockUiThread(); } - drawRenderNode(context, renderNode.get(), &dirtyCopy); + context->draw(&dirty); if (!canUnblockUiThread) { unblockUiThread(); } } -static void prepareTreeInfo(TreeInfo& info) { +static void initTreeInfo(TreeInfo& info) { info.prepareTextures = true; + info.performStagingPush = true; + info.evaluateAnimations = true; + // TODO: Get this from Choreographer + nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC); + info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNs); } bool DrawFrameTask::syncFrameState() { @@ -113,9 +109,9 @@ bool DrawFrameTask::syncFrameState() { mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(); TreeInfo info; - prepareTreeInfo(info); + initTreeInfo(info); mContext->processLayerUpdates(&mLayers, info); - mRenderNode->prepareTree(info); + mContext->prepareTree(info); // If prepareTextures is false, we ran out of texture cache space return !info.hasFunctors && info.prepareTextures; } @@ -125,16 +121,6 @@ void DrawFrameTask::unblockUiThread() { mSignal.signal(); } -void DrawFrameTask::drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty) { - ATRACE_CALL(); - - if (dirty->bottom == -1 && dirty->left == -1 - && dirty->top == -1 && dirty->right == -1) { - dirty = 0; - } - context->drawDisplayList(renderNode, dirty); -} - } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 055d4cf..c280868 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -53,7 +53,6 @@ public: void addLayer(DeferredLayerUpdater* layer); void removeLayer(DeferredLayerUpdater* layer); - void setRenderNode(RenderNode* renderNode); void setDirty(int left, int top, int right, int bottom); void drawFrame(RenderThread* renderThread); @@ -63,7 +62,6 @@ private: void postAndWait(RenderThread* renderThread); bool syncFrameState(); void unblockUiThread(); - static void drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty); Mutex mLock; Condition mSignal; @@ -73,7 +71,6 @@ private: /********************************************* * Single frame data *********************************************/ - sp<RenderNode> mRenderNode; Rect mDirty; /********************************************* diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index ce490f1..87886e6 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -51,15 +51,16 @@ namespace renderthread { MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() -CREATE_BRIDGE1(createContext, bool translucent) { - return new CanvasContext(args->translucent); +CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) { + return new CanvasContext(args->translucent, args->rootRenderNode); } -RenderProxy::RenderProxy(bool translucent) +RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) : mRenderThread(RenderThread::getInstance()) , mContext(0) { SETUP_TASK(createContext); args->translucent = translucent; + args->rootRenderNode = rootRenderNode; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(mContext); } @@ -133,9 +134,8 @@ void RenderProxy::setup(int width, int height) { post(task); } -void RenderProxy::drawDisplayList(RenderNode* displayList, +void RenderProxy::syncAndDrawFrame( int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { - mDrawFrameTask.setRenderNode(displayList); mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); mDrawFrameTask.drawFrame(&mRenderThread); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a112493..eab1395 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -56,14 +56,14 @@ class RenderProxyBridge; */ class ANDROID_API RenderProxy { public: - ANDROID_API RenderProxy(bool translucent); + ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode); ANDROID_API virtual ~RenderProxy(); ANDROID_API bool initialize(const sp<ANativeWindow>& window); ANDROID_API void updateSurface(const sp<ANativeWindow>& window); ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height); - ANDROID_API void drawDisplayList(RenderNode* displayList, + ANDROID_API void syncAndDrawFrame( int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); ANDROID_API void destroyCanvasAndSurface(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 212f475..e95707a 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -18,9 +18,11 @@ #include "RenderThread.h" +#include <gui/DisplayEventReceiver.h> +#include <utils/Log.h> + #include "CanvasContext.h" #include "RenderProxy.h" -#include <utils/Log.h> namespace android { using namespace uirenderer::renderthread; @@ -29,6 +31,14 @@ ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread); namespace uirenderer { namespace renderthread { +// Number of events to read at a time from the DisplayEventReceiver pipe. +// The value should be large enough that we can quickly drain the pipe +// using just a few large reads. +static const size_t EVENT_BUFFER_SIZE = 100; + +// Slight delay to give the UI time to push us a new frame before we replay +static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0; + TaskQueue::TaskQueue() : mHead(0), mTail(0) {} RenderTask* TaskQueue::next() { @@ -103,8 +113,25 @@ void TaskQueue::remove(RenderTask* task) { } } +class DispatchFrameCallbacks : public RenderTask { +private: + RenderThread* mRenderThread; +public: + DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {} + + virtual void run() { + mRenderThread->dispatchFrameCallbacks(); + } +}; + RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() - , mNextWakeup(LLONG_MAX) { + , mNextWakeup(LLONG_MAX) + , mDisplayEventReceiver(0) + , mVsyncRequested(false) + , mFrameCallbackTaskPending(false) + , mFrameCallbackTask(0) + , mFrameTime(0) { + mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); } @@ -112,10 +139,86 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() RenderThread::~RenderThread() { } +void RenderThread::initializeDisplayEventReceiver() { + LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?"); + mDisplayEventReceiver = new DisplayEventReceiver(); + status_t status = mDisplayEventReceiver->initCheck(); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver " + "failed with status: %d", status); + + // Register the FD + mLooper->addFd(mDisplayEventReceiver->getFd(), 0, + Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); +} + +int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. " + "events=0x%x", events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. " + "events=0x%x", events); + return 1; // keep the callback + } + + reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue(); + + return 1; // keep the callback +} + +static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) { + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + nsecs_t latest = 0; + ssize_t n; + while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + for (ssize_t i = 0; i < n; i++) { + const DisplayEventReceiver::Event& ev = buf[i]; + switch (ev.header.type) { + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: + latest = ev.header.timestamp; + break; + } + } + } + if (n < 0) { + ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); + } + return latest; +} + +void RenderThread::drainDisplayEventQueue() { + nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver); + if (vsyncEvent > 0) { + mVsyncRequested = false; + mFrameTime = vsyncEvent; + if (!mFrameCallbackTaskPending) { + mFrameCallbackTaskPending = true; + //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY); + queue(mFrameCallbackTask); + } + } +} + +void RenderThread::dispatchFrameCallbacks() { + mFrameCallbackTaskPending = false; + + std::set<IFrameCallback*> callbacks; + mFrameCallbacks.swap(callbacks); + + for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) { + (*it)->doFrame(mFrameTime); + } +} + bool RenderThread::threadLoop() { + initializeDisplayEventReceiver(); + int timeoutMillis = -1; for (;;) { - int result = mLooper->pollAll(timeoutMillis); + int result = mLooper->pollOnce(timeoutMillis); LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, "RenderThread Looper POLL_ERROR!"); @@ -159,6 +262,20 @@ void RenderThread::remove(RenderTask* task) { mQueue.remove(task); } +void RenderThread::postFrameCallback(IFrameCallback* callback) { + mFrameCallbacks.insert(callback); + if (!mVsyncRequested) { + mVsyncRequested = true; + status_t status = mDisplayEventReceiver->requestNextVsync(); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR, + "requestNextVsync failed with status: %d", status); + } +} + +void RenderThread::removeFrameCallback(IFrameCallback* callback) { + mFrameCallbacks.erase(callback); +} + RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) { AutoMutex _lock(mLock); RenderTask* next = mQueue.peek(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index e444aa0..b93dfd6 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -18,6 +18,10 @@ #define RENDERTHREAD_H_ #include "RenderTask.h" + +#include <memory> +#include <set> + #include <cutils/compiler.h> #include <utils/Looper.h> #include <utils/Mutex.h> @@ -25,9 +29,13 @@ #include <utils/Thread.h> namespace android { +class DisplayEventReceiver; + namespace uirenderer { namespace renderthread { +class DispatchFrameCallbacks; + class TaskQueue { public: TaskQueue(); @@ -42,6 +50,15 @@ private: RenderTask* mTail; }; +// Mimics android.view.Choreographer.FrameCallback +class IFrameCallback { +public: + virtual void doFrame(nsecs_t frameTimeNanos) = 0; + +protected: + ~IFrameCallback() {} +}; + class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { public: // RenderThread takes complete ownership of tasks that are queued @@ -50,15 +67,25 @@ public: void queueDelayed(RenderTask* task, int delayMs); void remove(RenderTask* task); + // Mimics android.view.Choreographer + void postFrameCallback(IFrameCallback* callback); + void removeFrameCallback(IFrameCallback* callback); + protected: virtual bool threadLoop(); private: friend class Singleton<RenderThread>; + friend class DispatchFrameCallbacks; RenderThread(); virtual ~RenderThread(); + void initializeDisplayEventReceiver(); + static int displayEventReceiverCallback(int fd, int events, void* data); + void drainDisplayEventQueue(); + void dispatchFrameCallbacks(); + // Returns the next task to be run. If this returns NULL nextWakeup is set // to the time to requery for the nextTask to run. mNextWakeup is also // set to this time @@ -69,6 +96,13 @@ private: nsecs_t mNextWakeup; TaskQueue mQueue; + + DisplayEventReceiver* mDisplayEventReceiver; + bool mVsyncRequested; + std::set<IFrameCallback*> mFrameCallbacks; + bool mFrameCallbackTaskPending; + DispatchFrameCallbacks* mFrameCallbackTask; + nsecs_t mFrameTime; }; } /* namespace renderthread */ |