summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/hwui/Android.mk4
-rw-r--r--libs/hwui/Animator.cpp189
-rw-r--r--libs/hwui/Animator.h65
-rw-r--r--libs/hwui/AnimatorManager.cpp70
-rw-r--r--libs/hwui/AnimatorManager.h7
-rw-r--r--libs/hwui/BakedOpDispatcher.cpp24
-rw-r--r--libs/hwui/LayerBuilder.cpp2
-rw-r--r--libs/hwui/Matrix.h27
-rw-r--r--libs/hwui/OpDumper.cpp44
-rw-r--r--libs/hwui/OpDumper.h32
-rw-r--r--libs/hwui/PropertyValuesAnimatorSet.cpp60
-rw-r--r--libs/hwui/PropertyValuesAnimatorSet.h1
-rw-r--r--libs/hwui/RecordedOp.h3
-rw-r--r--libs/hwui/RecordingCanvas.cpp18
-rw-r--r--libs/hwui/Rect.h29
-rw-r--r--libs/hwui/RenderNode.cpp45
-rw-r--r--libs/hwui/RenderNode.h10
-rw-r--r--libs/hwui/RenderProperties.cpp20
-rw-r--r--libs/hwui/Snapshot.h4
-rw-r--r--libs/hwui/tests/common/TestUtils.cpp1
-rw-r--r--libs/hwui/tests/unit/OpDumperTests.cpp43
-rw-r--r--libs/hwui/tests/unit/RecordingCanvasTests.cpp16
-rw-r--r--libs/hwui/utils/LinearAllocator.h7
-rw-r--r--libs/hwui/utils/StringUtils.h2
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++;
}