diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/hwui/Android.mk | 4 | ||||
| -rw-r--r-- | libs/hwui/Animator.cpp | 189 | ||||
| -rw-r--r-- | libs/hwui/Animator.h | 65 | ||||
| -rw-r--r-- | libs/hwui/AnimatorManager.cpp | 70 | ||||
| -rw-r--r-- | libs/hwui/AnimatorManager.h | 7 | ||||
| -rw-r--r-- | libs/hwui/BakedOpDispatcher.cpp | 24 | ||||
| -rw-r--r-- | libs/hwui/LayerBuilder.cpp | 2 | ||||
| -rw-r--r-- | libs/hwui/Matrix.h | 27 | ||||
| -rw-r--r-- | libs/hwui/OpDumper.cpp | 44 | ||||
| -rw-r--r-- | libs/hwui/OpDumper.h | 32 | ||||
| -rw-r--r-- | libs/hwui/PropertyValuesAnimatorSet.cpp | 60 | ||||
| -rw-r--r-- | libs/hwui/PropertyValuesAnimatorSet.h | 1 | ||||
| -rw-r--r-- | libs/hwui/RecordedOp.h | 3 | ||||
| -rw-r--r-- | libs/hwui/RecordingCanvas.cpp | 18 | ||||
| -rw-r--r-- | libs/hwui/Rect.h | 29 | ||||
| -rw-r--r-- | libs/hwui/RenderNode.cpp | 45 | ||||
| -rw-r--r-- | libs/hwui/RenderNode.h | 10 | ||||
| -rw-r--r-- | libs/hwui/RenderProperties.cpp | 20 | ||||
| -rw-r--r-- | libs/hwui/Snapshot.h | 4 | ||||
| -rw-r--r-- | libs/hwui/tests/common/TestUtils.cpp | 1 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/OpDumperTests.cpp | 43 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/RecordingCanvasTests.cpp | 16 | ||||
| -rw-r--r-- | libs/hwui/utils/LinearAllocator.h | 7 | ||||
| -rw-r--r-- | libs/hwui/utils/StringUtils.h | 2 |
24 files changed, 563 insertions, 160 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 625712292234..da7b7fb55330 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -7,7 +7,7 @@ HWUI_NEW_OPS := true # Enables fine-grained GLES error checking # If set to true, every GLES call is wrapped & error checked # Has moderate overhead -HWUI_ENABLE_OPENGL_VALIDATION := false +HWUI_ENABLE_OPENGL_VALIDATION := true hwui_src_files := \ font/CacheTexture.cpp \ @@ -119,6 +119,7 @@ ifeq (true, $(HWUI_NEW_OPS)) BakedOpState.cpp \ FrameBuilder.cpp \ LayerBuilder.cpp \ + OpDumper.cpp \ RecordingCanvas.cpp hwui_cflags += -DHWUI_NEW_OPS @@ -253,6 +254,7 @@ ifeq (true, $(HWUI_NEW_OPS)) tests/unit/BakedOpStateTests.cpp \ tests/unit/FrameBuilderTests.cpp \ tests/unit/LeakCheckTests.cpp \ + tests/unit/OpDumperTests.cpp \ tests/unit/RecordingCanvasTests.cpp endif diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index 7bd2b24bf56b..bd71e0d579f8 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -33,6 +33,7 @@ namespace uirenderer { BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) : mTarget(nullptr) + , mStagingTarget(nullptr) , mFinalValue(finalValue) , mDeltaValue(0) , mFromValue(0) @@ -42,7 +43,8 @@ BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) , mStartTime(0) , mDuration(300) , mStartDelay(0) - , mMayRunAsync(true) { + , mMayRunAsync(true) + , mPlayTime(0) { } BaseRenderNodeAnimator::~BaseRenderNodeAnimator() { @@ -81,26 +83,133 @@ void BaseRenderNodeAnimator::setStartDelay(nsecs_t startDelay) { } void BaseRenderNodeAnimator::attach(RenderNode* target) { - mTarget = target; + mStagingTarget = target; 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 (mStagingTarget) { + RenderNode* oldTarget = mTarget; + mTarget = mStagingTarget; + mStagingTarget = nullptr; + if (oldTarget && oldTarget != mTarget) { + oldTarget->onAnimatorTargetChanged(this); + } + } + if (!mHasStartValue) { doSetStartValue(getValue(mTarget)); } - if (mStagingPlayState > mPlayState) { - if (mStagingPlayState == PlayState::Restarted) { - mStagingPlayState = PlayState::Running; + + if (!mStagingRequests.empty()) { + // No interpolator was set, use the default + if (mPlayState == PlayState::NotStarted && !mInterpolator) { + mInterpolator.reset(Interpolator::createDefaultInterpolator()); } - mPlayState = mStagingPlayState; - // Oh boy, we're starting! Man the battle stations! - if (mPlayState == PlayState::Running) { - transitionToRunning(context); - } else if (mPlayState == PlayState::Finished) { + // 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); + } + 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; + } + } + } } } + onPushStaging(); } void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) { @@ -117,10 +226,6 @@ void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) { // Set to 0 so that the animate() basically instantly finishes mStartTime = 0; } - // No interpolator was set, use the default - if (!mInterpolator) { - mInterpolator.reset(Interpolator::createDefaultInterpolator()); - } if (mDuration < 0 || mDuration > 50000) { ALOGW("Your duration is strange and confusing: %" PRId64, mDuration); } @@ -136,37 +241,37 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) { // 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 (fraction >= 1.0f) { - fraction = 1.0f; - mPlayState = PlayState::Finished; + if ((mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) && mDuration > 0) { + fraction = mPlayTime / (float) mDuration; } + 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) { @@ -215,18 +320,36 @@ RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float fi void RenderPropertyAnimator::onAttached() { if (!mHasStartValue - && mTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { - setStartValue((mTarget->stagingProperties().*mPropertyAccess->getter)()); + && mStagingTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { + setStartValue((mStagingTarget->stagingProperties().*mPropertyAccess->getter)()); } } void RenderPropertyAnimator::onStagingPlayStateChanged() { if (mStagingPlayState == PlayState::Running) { - (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); + if (mStagingTarget) { + (mStagingTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); + } else { + // In the case of start delay where stagingTarget has been sync'ed over and null'ed + // we delay the properties update to push staging. + mShouldUpdateStagingProperties = true; + } } else if (mStagingPlayState == PlayState::Finished) { // We're being canceled, so make sure that whatever values the UI thread // is observing for us is pushed over + mShouldSyncPropertyFields = true; + } +} + +void RenderPropertyAnimator::onPushStaging() { + if (mShouldUpdateStagingProperties) { + (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); + mShouldUpdateStagingProperties = false; + } + + if (mShouldSyncPropertyFields) { mTarget->setPropertyFieldsDirty(dirtyMask()); + mShouldSyncPropertyFields = false; } } diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 2c9c9c3fe0f9..fdae0f32d4e6 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 @@ public: 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,42 @@ public: 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; } + RenderNode* stagingTarget() { return mStagingTarget; } 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,14 +119,15 @@ protected: virtual float getValue(RenderNode* target) const = 0; virtual void setValue(RenderNode* target, float value) = 0; - RenderNode* target() { return mTarget; } void callOnFinishedListener(AnimationContext& context); virtual void onStagingPlayStateChanged() {} virtual void onPlayTimeChanged(nsecs_t playTime) {} + virtual void onPushStaging() {} RenderNode* mTarget; + RenderNode* mStagingTarget; float mFinalValue; float mDeltaValue; @@ -132,13 +141,28 @@ protected: 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 { @@ -167,6 +191,7 @@ protected: virtual void setValue(RenderNode* target, float value) override; virtual void onAttached() override; virtual void onStagingPlayStateChanged() override; + virtual void onPushStaging() override; private: typedef bool (RenderProperties::*SetFloatProperty)(float value); @@ -176,6 +201,8 @@ private: const PropertyAccessors* mPropertyAccess; static const PropertyAccessors PROPERTY_ACCESSOR_LUT[]; + bool mShouldSyncPropertyFields = false; + bool mShouldUpdateStagingProperties = false; }; class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator { diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp index cd30b1859384..2198fcc95fe5 100644 --- a/libs/hwui/AnimatorManager.cpp +++ b/libs/hwui/AnimatorManager.cpp @@ -27,9 +27,8 @@ namespace uirenderer { 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,28 @@ AnimatorManager::AnimatorManager(RenderNode& parent) } 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); + RenderNode* stagingTarget = animator->stagingTarget(); + if (stagingTarget == &mParent) { + return; + } + mNewAnimators.emplace_back(animator.get()); + // If the animator is already attached to other RenderNode, remove it from that RenderNode's + // new animator list. This ensures one animator only ends up in one newAnimatorList during one + // frame, even when it's added multiple times to multiple targets. + if (stagingTarget) { + stagingTarget->removeAnimator(animator); + } animator->attach(&mParent); - mNewAnimators.push_back(animator.get()); +} + +void AnimatorManager::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) { + mNewAnimators.erase(std::remove(mNewAnimators.begin(), mNewAnimators.end(), animator), + mNewAnimators.end()); } void AnimatorManager::setAnimationHandle(AnimationHandle* handle) { @@ -56,38 +69,40 @@ void AnimatorManager::setAnimationHandle(AnimationHandle* handle) { &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 new animators that are not already in the mAnimators list + for (auto& anim : mNewAnimators) { + if (anim->target() != &mParent) { + mAnimators.push_back(std::move(anim)); + } + } + 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()); } } +void AnimatorManager::onAnimatorTargetChanged(BaseRenderNodeAnimator* animator) { + LOG_ALWAYS_FATAL_IF(animator->target() == &mParent, "Target has not been changed"); + mAnimators.erase(std::remove(mAnimators.begin(), mAnimators.end(), animator), mAnimators.end()); +} + class AnimateFunctor { public: 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 +144,18 @@ void AnimatorManager::animateNoDamage(TreeInfo& info) { uint32_t AnimatorManager::animateCommon(TreeInfo& info) { AnimateFunctor functor(info, mAnimationHandle->context()); - std::vector< BaseRenderNodeAnimator* >::iterator newEnd; - newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); + 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() { @@ -157,9 +170,8 @@ 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 fb75eb8599b4..61f6179d217c 100644 --- a/libs/hwui/AnimatorManager.h +++ b/libs/hwui/AnimatorManager.h @@ -39,11 +39,13 @@ public: ~AnimatorManager(); void addAnimator(const sp<BaseRenderNodeAnimator>& animator); + void removeAnimator(const sp<BaseRenderNodeAnimator>& animator); void setAnimationHandle(AnimationHandle* handle); bool hasAnimationHandle() { return mAnimationHandle; } void pushStaging(); + void onAnimatorTargetChanged(BaseRenderNodeAnimator* animator); // Returns the combined dirty mask of all animators run uint32_t animate(TreeInfo& info); @@ -66,9 +68,8 @@ private: 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/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index f83e1faf9c8a..78764b5f4448 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -334,15 +334,15 @@ static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& stat } static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, - PathTexture& texture, const RecordedOp& op) { + float xOffset, float yOffset, PathTexture& texture, const SkPaint& paint) { Rect dest(texture.width(), texture.height()); - dest.translate(texture.left - texture.offset, - texture.top - texture.offset); + dest.translate(xOffset + texture.left - texture.offset, + yOffset + texture.top - texture.offset); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedUnitQuad(nullptr) - .setFillPathTexturePaint(texture, *(op.paint), state.alpha) + .setFillPathTexturePaint(texture, paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewMapUnitToRect(dest) .build(); @@ -368,7 +368,8 @@ void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, cons op.startAngle, op.sweepAngle, op.useCenter, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.right, + *texture, *(op.paint)); } } else { SkRect rect = getBoundsOfFill(op); @@ -519,7 +520,8 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.right, + *texture, *(op.paint)); } } else { SkPath path; @@ -562,7 +564,9 @@ void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, co PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't + // have any translate built in, other than what's in the SkPath itself + renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint)); } } @@ -588,7 +592,8 @@ void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, co op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, + *texture, *(op.paint)); } } else { SkPath path; @@ -622,7 +627,8 @@ void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRect op.rx, op.ry, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, + *texture, *(op.paint)); } } else { const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect( diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp index 1ba3bf26c0d4..c5d7191f1b62 100644 --- a/libs/hwui/LayerBuilder.cpp +++ b/libs/hwui/LayerBuilder.cpp @@ -239,7 +239,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { // put the verts in the frame allocator, since // 1) SimpleRectsOps needs verts, not rects // 2) even if mClearRects stored verts, std::vectors will move their contents - Vertex* const verts = (Vertex*) allocator.alloc<Vertex>(vertCount * sizeof(Vertex)); + Vertex* const verts = (Vertex*) allocator.create_trivial_array<Vertex>(vertCount); Vertex* currentVert = verts; Rect bounds = mClearRects[0]; diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index 1c25f26c0c25..36007cd5dc0e 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_MATRIX_H -#define ANDROID_HWUI_MATRIX_H +#pragma once -#include <SkMatrix.h> +#include "Rect.h" #include <cutils/compiler.h> - -#include "Rect.h" +#include <iomanip> +#include <ostream> +#include <SkMatrix.h> namespace android { namespace uirenderer { @@ -218,6 +218,22 @@ public: void dump(const char* label = nullptr) const; + friend std::ostream& operator<<(std::ostream& os, const Matrix4& matrix) { + if (matrix.isSimple()) { + os << "offset " << matrix.getTranslateX() << "x" << matrix.getTranslateY(); + if (!matrix.isPureTranslate()) { + os << ", scale " << matrix[kScaleX] << "x" << matrix[kScaleY]; + } + } else { + os << "[" << matrix[0]; + for (int i = 1; i < 16; i++) { + os << ", " << matrix[i]; + } + os << "]"; + } + return os; + } + static const Matrix4& identity(); private: @@ -244,4 +260,3 @@ typedef Matrix4 mat4; }; // namespace uirenderer }; // namespace android -#endif // ANDROID_HWUI_MATRIX_H diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp new file mode 100644 index 000000000000..c34cfbe1b259 --- /dev/null +++ b/libs/hwui/OpDumper.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 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 "OpDumper.h" + +#include "RecordedOp.h" + +namespace android { +namespace uirenderer { + +#define STRINGIFY(n) #n, +static const char* sOpNameLut[] = BUILD_FULL_OP_LUT(STRINGIFY); + +void OpDumper::dump(const RecordedOp& op, std::ostream& output, int level) { + for (int i = 0; i < level; i++) { + output << " "; + } + + Rect localBounds(op.unmappedBounds); + op.localMatrix.mapRect(localBounds); + output << sOpNameLut[op.opId] << " " << localBounds; + + if (op.localClip && !op.localClip->rect.contains(localBounds)) { + output << std::fixed << std::setprecision(0) + << " clip=" << op.localClip->rect + << " mode=" << (int)op.localClip->mode; + } +} + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/OpDumper.h b/libs/hwui/OpDumper.h new file mode 100644 index 000000000000..c99b51796b5c --- /dev/null +++ b/libs/hwui/OpDumper.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#include <ostream> + +namespace android { +namespace uirenderer { + +struct RecordedOp; + +class OpDumper { +public: + static void dump(const RecordedOp& op, std::ostream& output, int level = 0); +}; + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp index eca1afcc54dc..b29f91ff34aa 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::setValue(RenderNode* target, float value) { } 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::start(AnimationListener* listener) { } 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 @@ PropertyAnimator::PropertyAnimator(PropertyValuesHolder* holder, Interpolator* i 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 4c7ce528bb20..c7ae7c0e8ce1 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.h +++ b/libs/hwui/PropertyValuesAnimatorSet.h @@ -50,7 +50,6 @@ public: void start(AnimationListener* listener); void reverse(AnimationListener* listener); - void reset(); void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder, Interpolator* interpolators, int64_t startDelays, diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index bb26e2ec67a8..c37458d31029 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -119,6 +119,9 @@ class Tree; #define BUILD_RENDERABLE_OP_LUT(OP_FN) \ { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, OP_FN, OP_FN, OP_FN) } +#define BUILD_FULL_OP_LUT(OP_FN) \ + { MAP_OPS_BASED_ON_TYPE(OP_FN, OP_FN, OP_FN, OP_FN) } + /** * Op mapping functions, which skip unsupported ops. * diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 269e590892d3..78a0b13aaf90 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -290,7 +290,7 @@ void RecordingCanvas::drawRect(float left, float top, float right, float bottom, void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) { if (rects == nullptr) return; - Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc<Vertex>(vertexCount * sizeof(Vertex)); + Vertex* rectData = (Vertex*) mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount); Vertex* vertex = rectData; float left = FLT_MAX; @@ -406,12 +406,16 @@ void RecordingCanvas::drawOval(float left, float top, float right, float bottom, void RecordingCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { - addOp(alloc().create_trivial<ArcOp>( - Rect(left, top, right, bottom), - *(mState.currentSnapshot()->transform), - getRecordedClip(), - refPaint(&paint), - startAngle, sweepAngle, useCenter)); + if (fabs(sweepAngle) >= 360.0f) { + drawOval(left, top, right, bottom, paint); + } else { + addOp(alloc().create_trivial<ArcOp>( + Rect(left, top, right, bottom), + *(mState.currentSnapshot()->transform), + getRecordedClip(), + refPaint(&paint), + startAngle, sweepAngle, useCenter)); + } } void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 30c925c8775b..d9fce9b2c824 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -14,16 +14,17 @@ * limitations under the License. */ -#ifndef ANDROID_HWUI_RECT_H -#define ANDROID_HWUI_RECT_H +#pragma once -#include <cmath> -#include <algorithm> -#include <SkRect.h> +#include "Vertex.h" #include <utils/Log.h> -#include "Vertex.h" +#include <algorithm> +#include <cmath> +#include <iomanip> +#include <ostream> +#include <SkRect.h> namespace android { namespace uirenderer { @@ -282,9 +283,23 @@ public: void dump(const char* label = nullptr) const { ALOGD("%s[l=%.2f t=%.2f r=%.2f b=%.2f]", label ? label : "Rect", left, top, right, bottom); } + + friend std::ostream& operator<<(std::ostream& os, const Rect& rect) { + if (rect.isEmpty()) { + return os << "empty"; + } + + if (rect.left == 0 && rect.top == 0) { + return os << "[" << rect.right << " x " << rect.bottom << "]"; + } + + return os << "[" << rect.left + << " " << rect.top + << " " << rect.right + << " " << rect.bottom << "]"; + } }; // class Rect }; // namespace uirenderer }; // namespace android -#endif // ANDROID_HWUI_RECT_H diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index bade216b3b21..61441ce9b16e 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -19,8 +19,9 @@ #include "DamageAccumulator.h" #include "Debug.h" #if HWUI_NEW_OPS -#include "RecordedOp.h" #include "BakedOpRenderer.h" +#include "RecordedOp.h" +#include "OpDumper.h" #endif #include "DisplayListOp.h" #include "LayerRenderer.h" @@ -95,6 +96,34 @@ void RenderNode::setStagingDisplayList(DisplayList* displayList) { * This function is a simplified version of replay(), where we simply retrieve and log the * display list. This function should remain in sync with the replay() function. */ +#if HWUI_NEW_OPS +void RenderNode::output(uint32_t level, const char* label) { + ALOGD("%s (%s %p%s%s%s%s%s)", + label, + getName(), + this, + (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""), + (properties().hasShadow() ? ", casting shadow" : ""), + (isRenderable() ? "" : ", empty"), + (properties().getProjectBackwards() ? ", projected" : ""), + (mLayer != nullptr ? ", on HW Layer" : "")); + properties().debugOutputProperties(level + 1); + + if (mDisplayList) { + for (auto&& op : mDisplayList->getOps()) { + std::stringstream strout; + OpDumper::dump(*op, strout, level + 1); + if (op->opId == RecordedOpId::RenderNodeOp) { + auto rnOp = reinterpret_cast<const RenderNodeOp*>(op); + rnOp->renderNode->output(level + 1, strout.str().c_str()); + } else { + ALOGD("%s", strout.str().c_str()); + } + } + } + ALOGD("%*s/RenderNode(%s %p)", level * 2, "", getName(), this); +} +#else void RenderNode::output(uint32_t level) { ALOGD("%*sStart display list (%p, %s%s%s%s%s%s)", (level - 1) * 2, "", this, getName(), @@ -104,22 +133,16 @@ void RenderNode::output(uint32_t level) { (properties().getProjectBackwards() ? ", projected" : ""), (mLayer != nullptr ? ", on HW Layer" : "")); ALOGD("%*s%s %d", level * 2, "", "Save", SaveFlags::MatrixClip); - properties().debugOutputProperties(level); - if (mDisplayList) { -#if HWUI_NEW_OPS - LOG_ALWAYS_FATAL("op dumping unsupported"); -#else // TODO: consider printing the chunk boundaries here for (auto&& op : mDisplayList->getOps()) { op->output(level, DisplayListOp::kOpLogFlag_Recurse); } -#endif } - ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName()); -} + } +#endif void RenderNode::copyTo(proto::RenderNode *pnode) { pnode->set_id(static_cast<uint64_t>( @@ -218,6 +241,10 @@ void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) { mAnimatorManager.addAnimator(animator); } +void RenderNode::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) { + mAnimatorManager.removeAnimator(animator); +} + void RenderNode::damageSelf(TreeInfo& info) { if (isRenderable()) { if (properties().getClipDamageToBounds()) { diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index f248de54acba..838192552127 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -123,7 +123,11 @@ public: void defer(DeferStateStruct& deferStruct, const int level); void replay(ReplayStateStruct& replayStruct, const int level); +#if HWUI_NEW_OPS + ANDROID_API void output(uint32_t level = 0, const char* label = "Root"); +#else ANDROID_API void output(uint32_t level = 1); +#endif ANDROID_API int getDebugSize(); void copyTo(proto::RenderNode* node); @@ -187,6 +191,12 @@ public: // UI thread only! ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator); + void removeAnimator(const sp<BaseRenderNodeAnimator>& animator); + + // This can only happen during pushStaging() + void onAnimatorTargetChanged(BaseRenderNodeAnimator* animator) { + mAnimatorManager.onAnimatorTargetChanged(animator); + } AnimatorManager& animators() { return mAnimatorManager; } diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index b848af4e7b1e..0b0f0fa4f304 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -102,22 +102,23 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) { void RenderProperties::debugOutputProperties(const int level) const { if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) { - ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mPrimitiveFields.mLeft, mPrimitiveFields.mTop); + ALOGD("%*s(Translate (left, top) %d, %d)", level * 2, "", + mPrimitiveFields.mLeft, mPrimitiveFields.mTop); } if (mStaticMatrix) { - ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING, + ALOGD("%*s(ConcatMatrix (static) %p: " SK_MATRIX_STRING ")", level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix)); } if (mAnimationMatrix) { - ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING, + ALOGD("%*s(ConcatMatrix (animation) %p: " SK_MATRIX_STRING ")", level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix)); } if (hasTransformMatrix()) { if (isTransformTranslateOnly()) { - ALOGD("%*sTranslate %.2f, %.2f, %.2f", + ALOGD("%*s(Translate %.2f, %.2f, %.2f)", level * 2, "", getTranslationX(), getTranslationY(), getZ()); } else { - ALOGD("%*sConcatMatrix %p: " SK_MATRIX_STRING, + ALOGD("%*s(ConcatMatrix %p: " SK_MATRIX_STRING ")", level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix)); } } @@ -132,7 +133,7 @@ void RenderProperties::debugOutputProperties(const int level) const { if (CC_LIKELY(isLayer || !getHasOverlappingRendering())) { // simply scale rendering content's alpha - ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha); + ALOGD("%*s(ScaleAlpha %.2f)", level * 2, "", mPrimitiveFields.mAlpha); } else { // savelayeralpha to create an offscreen buffer to apply alpha Rect layerBounds(0, 0, getWidth(), getHeight()); @@ -140,19 +141,18 @@ void RenderProperties::debugOutputProperties(const int level) const { getClippingRectForFlags(clipFlags, &layerBounds); clipFlags = 0; // all clipping done by savelayer } - ALOGD("%*sSaveLayerAlpha %d, %d, %d, %d, %d, 0x%x", level * 2, "", + ALOGD("%*s(SaveLayerAlpha %d, %d, %d, %d, %d, 0x%x)", level * 2, "", (int)layerBounds.left, (int)layerBounds.top, (int)layerBounds.right, (int)layerBounds.bottom, (int)(mPrimitiveFields.mAlpha * 255), SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer); } - - } + if (clipFlags) { Rect clipRect; getClippingRectForFlags(clipFlags, &clipRect); - ALOGD("%*sClipRect %d, %d, %d, %d", level * 2, "", + ALOGD("%*s(ClipRect %d, %d, %d, %d)", level * 2, "", (int)clipRect.left, (int)clipRect.top, (int)clipRect.right, (int)clipRect.bottom); } } diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 0ac2f1411140..b03643f06f1c 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -44,7 +44,7 @@ namespace uirenderer { */ class RoundRectClipState { public: - /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/ + static void* operator new(size_t size) = delete; static void* operator new(size_t size, LinearAllocator& allocator) { return allocator.alloc<RoundRectClipState>(size); } @@ -65,7 +65,7 @@ public: class ProjectionPathMask { public: - /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/ + static void* operator new(size_t size) = delete; static void* operator new(size_t size, LinearAllocator& allocator) { return allocator.alloc<ProjectionPathMask>(size); } diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 3440d03b4fc5..9595a85e99dd 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -162,6 +162,7 @@ void TestUtils::TestTask::run() { renderState.onGLContextCreated(); rtCallback(renderthread::RenderThread::getInstance()); + renderState.flush(Caches::FlushMode::Full); renderState.onGLContextDestroyed(); // Restore the previous signal handler diff --git a/libs/hwui/tests/unit/OpDumperTests.cpp b/libs/hwui/tests/unit/OpDumperTests.cpp new file mode 100644 index 000000000000..01840d72b766 --- /dev/null +++ b/libs/hwui/tests/unit/OpDumperTests.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 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 <gtest/gtest.h> + +#include "tests/common/TestUtils.h" +#include "OpDumper.h" + +using namespace android; +using namespace android::uirenderer; + +TEST(OpDumper, dump) { + SkPaint paint; + RectOp op(uirenderer::Rect(100, 100), Matrix4::identity(), nullptr, &paint); + + std::stringstream stream; + OpDumper::dump(op, stream); + EXPECT_STREQ("RectOp [100 x 100]", stream.str().c_str()); + + stream.str(""); + OpDumper::dump(op, stream, 2); + EXPECT_STREQ(" RectOp [100 x 100]", stream.str().c_str()); + + ClipRect clipRect(uirenderer::Rect(50, 50)); + op.localClip = &clipRect; + + stream.str(""); + OpDumper::dump(op, stream, 2); + EXPECT_STREQ(" RectOp [100 x 100] clip=[50 x 50] mode=0", stream.str().c_str()); +} diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index 20d2f1f18c58..cd9ffc5590f8 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -57,6 +57,22 @@ TEST(RecordingCanvas, clipRect) { << "Clip should be serialized once"; } +TEST(RecordingCanvas, drawArc) { + auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { + canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint()); + canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint()); + }); + + auto&& ops = dl->getOps(); + ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops"; + EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId); + EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds); + + EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId) + << "Circular arcs should be converted to ovals"; + EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds); +} + TEST(RecordingCanvas, drawLines) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { SkPaint paint; diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h index 0a0e1858cd91..34c8c6beea81 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -84,6 +84,13 @@ public: return new (allocImpl(sizeof(T))) T(std::forward<Params>(params)...); } + template<class T> + T* create_trivial_array(int count) { + static_assert(std::is_trivially_destructible<T>::value, + "Error, called create_trivial_array on a non-trivial type"); + return reinterpret_cast<T*>(allocImpl(sizeof(T) * count)); + } + /** * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its * state if possible. diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h index 05a3d5931e5d..5add95711f2d 100644 --- a/libs/hwui/utils/StringUtils.h +++ b/libs/hwui/utils/StringUtils.h @@ -42,7 +42,7 @@ struct SizePrinter { static const char* SUFFIXES[] = {"B", "KiB", "MiB"}; size_t suffix = 0; double temp = d.bytes; - while (temp > 1000 && suffix < 2) { + while (temp > 1024 && suffix < 2) { temp /= 1024.0; suffix++; } |