diff options
author | John Reck <jreck@google.com> | 2014-06-11 18:39:44 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2014-06-12 09:13:45 -0700 |
commit | a447d29c65fb811cd184775a3476101a1cede929 (patch) | |
tree | 0be082c0fdf19035551bf3671208fd606b430ab2 /libs/hwui | |
parent | f8333cc38126c7efb1b95958ca7d7c825253bc58 (diff) | |
download | frameworks_base-a447d29c65fb811cd184775a3476101a1cede929.zip frameworks_base-a447d29c65fb811cd184775a3476101a1cede929.tar.gz frameworks_base-a447d29c65fb811cd184775a3476101a1cede929.tar.bz2 |
Fix DA bugs
* Now aware of transform of DrawDisplayListOp
* Supports projection
Bug: 15539677
Bug: 15506680
Change-Id: Ic16f482cd48c3add12e49eca529281be12b93491
Diffstat (limited to 'libs/hwui')
-rw-r--r-- | libs/hwui/DamageAccumulator.cpp | 149 | ||||
-rw-r--r-- | libs/hwui/DamageAccumulator.h | 48 | ||||
-rw-r--r-- | libs/hwui/Matrix.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/Matrix.h | 1 | ||||
-rw-r--r-- | libs/hwui/RenderNode.cpp | 40 | ||||
-rw-r--r-- | libs/hwui/RenderNode.h | 5 | ||||
-rw-r--r-- | libs/hwui/RenderProperties.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/RenderProperties.h | 17 | ||||
-rw-r--r-- | libs/hwui/TreeInfo.h | 7 |
9 files changed, 194 insertions, 76 deletions
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 8aa8c92..898e81a 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -26,8 +26,23 @@ namespace android { namespace uirenderer { +NullDamageAccumulator NullDamageAccumulator::sInstance; + +NullDamageAccumulator* NullDamageAccumulator::instance() { + return &sInstance; +} + +enum TransformType { + TransformRenderNode, + TransformMatrix4, +}; + struct DirtyStack { - const RenderNode* node; + TransformType type; + union { + const RenderNode* renderNode; + const Matrix4* matrix4; + }; // When this frame is pop'd, this rect is mapped through the above transform // and applied to the previous (aka parent) frame SkRect pendingDirty; @@ -42,7 +57,7 @@ DamageAccumulator::DamageAccumulator() { mHead->prev = mHead; } -void DamageAccumulator::pushNode(const RenderNode* node) { +void DamageAccumulator::pushCommon() { if (!mHead->next) { DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); nextFrame->next = 0; @@ -50,42 +65,120 @@ void DamageAccumulator::pushNode(const RenderNode* node) { mHead->next = nextFrame; } mHead = mHead->next; - mHead->node = node; mHead->pendingDirty.setEmpty(); } -void DamageAccumulator::popNode() { +void DamageAccumulator::pushTransform(const RenderNode* transform) { + pushCommon(); + mHead->type = TransformRenderNode; + mHead->renderNode = transform; +} + +void DamageAccumulator::pushTransform(const Matrix4* transform) { + pushCommon(); + mHead->type = TransformMatrix4; + mHead->matrix4 = transform; +} + +void DamageAccumulator::popTransform() { LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!"); DirtyStack* dirtyFrame = mHead; mHead = mHead->prev; - if (!dirtyFrame->pendingDirty.isEmpty()) { - SkRect mappedDirty; - const RenderProperties& props = dirtyFrame->node->properties(); - const SkMatrix* transform = props.getTransformMatrix(); - if (transform && !transform->isIdentity()) { - transform->mapRect(&mappedDirty, dirtyFrame->pendingDirty); - } else { - mappedDirty = dirtyFrame->pendingDirty; + if (dirtyFrame->type == TransformRenderNode) { + applyRenderNodeTransform(dirtyFrame); + } else { + applyMatrix4Transform(dirtyFrame); + } +} + +static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + Rect temp(in); + matrix->mapRect(temp); + out->join(RECT_ARGS(temp)); +} + +void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { + mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty); +} + +static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + const SkMatrix* transform = props.getTransformMatrix(); + SkRect temp(in); + if (transform && !transform->isIdentity()) { + transform->mapRect(&temp); + } + temp.offset(props.getLeft(), props.getTop()); + out->join(temp); +} + +static DirtyStack* findParentRenderNode(DirtyStack* frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode) { + return frame; } - if (CC_LIKELY(mHead->node)) { - const RenderProperties& parentProps = mHead->node->properties(); - mappedDirty.offset(props.getLeft() - parentProps.getScrollX(), - props.getTop() - parentProps.getScrollY()); - if (props.getClipToBounds()) { - if (!mappedDirty.intersect(0, 0, parentProps.getWidth(), parentProps.getHeight())) { - mappedDirty.setEmpty(); - } - } - if (CC_UNLIKELY(!MathUtils::isZero(props.getTranslationZ()))) { - // TODO: Can we better bound the shadow damage area? For now - // match the old damageShadowReceiver() path and just dirty - // the entire parent bounds - mappedDirty.join(0, 0, parentProps.getWidth(), parentProps.getHeight()); + } + return NULL; +} + +static DirtyStack* findProjectionReceiver(DirtyStack* frame) { + if (frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode + && frame->renderNode->hasProjectionReceiver()) { + return frame; } + } + } + return NULL; +} + +static void applyTransforms(DirtyStack* frame, DirtyStack* end) { + SkRect* rect = &frame->pendingDirty; + while (frame != end) { + if (frame->type == TransformRenderNode) { + mapRect(frame->renderNode->properties(), *rect, rect); + } else { + mapRect(frame->matrix4, *rect, rect); + } + frame = frame->prev; + } +} + +void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { + if (frame->pendingDirty.isEmpty()) { + return; + } + + const RenderProperties& props = frame->renderNode->properties(); + + // Perform clipping + if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) { + if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) { + frame->pendingDirty.setEmpty(); + } + } + + // apply all transforms + mapRect(props, frame->pendingDirty, &mHead->pendingDirty); + + // project backwards if necessary + if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) { + // First, find our parent RenderNode: + DirtyStack* parentNode = findParentRenderNode(frame); + // Find our parent's projection receiver, which is what we project onto + DirtyStack* projectionReceiver = findProjectionReceiver(parentNode); + if (projectionReceiver) { + applyTransforms(frame, projectionReceiver); + projectionReceiver->pendingDirty.join(frame->pendingDirty); } else { - mappedDirty.offset(props.getLeft(), props.getTop()); + ALOGW("Failed to find projection receiver? Dropping on the floor..."); } - dirty(mappedDirty.fLeft, mappedDirty.fTop, mappedDirty.fRight, mappedDirty.fBottom); + + frame->pendingDirty.setEmpty(); } } diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index c62a351..2ca30d4 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -16,6 +16,7 @@ #ifndef DAMAGEACCUMULATOR_H #define DAMAGEACCUMULATOR_H +#include <cutils/compiler.h> #include <utils/LinearAllocator.h> #include <SkMatrix.h> @@ -28,8 +29,19 @@ namespace uirenderer { struct DirtyStack; class RenderNode; +class Matrix4; -class DamageAccumulator { +class IDamageAccumulator { +public: + virtual void pushTransform(const RenderNode* transform) = 0; + virtual void pushTransform(const Matrix4* transform) = 0; + virtual void popTransform() = 0; + virtual void dirty(float left, float top, float right, float bottom) = 0; +protected: + virtual ~IDamageAccumulator() {} +}; + +class DamageAccumulator : public IDamageAccumulator { PREVENT_COPY_AND_ASSIGN(DamageAccumulator); public: DamageAccumulator(); @@ -37,20 +49,44 @@ public: // Push a transform node onto the stack. This should be called prior // to any dirty() calls. Subsequent calls to dirty() - // will be affected by the node's transform when popNode() is called. - void pushNode(const RenderNode* node); + // will be affected by the transform when popTransform() is called. + virtual void pushTransform(const RenderNode* transform); + virtual void pushTransform(const Matrix4* transform); + // Pops a transform node from the stack, propagating the dirty rect - // up to the parent node. - void popNode(); - void dirty(float left, float top, float right, float bottom); + // up to the parent node. Returns the IDamageTransform that was just applied + virtual void popTransform(); + + virtual void dirty(float left, float top, float right, float bottom); void finish(SkRect* totalDirty); private: + void pushCommon(); + void applyMatrix4Transform(DirtyStack* frame); + void applyRenderNodeTransform(DirtyStack* frame); + LinearAllocator mAllocator; DirtyStack* mHead; }; +class NullDamageAccumulator : public IDamageAccumulator { + PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator); +public: + virtual void pushTransform(const RenderNode* transform) { } + virtual void pushTransform(const Matrix4* transform) { } + virtual void popTransform() { } + virtual void dirty(float left, float top, float right, float bottom) { } + + ANDROID_API static NullDamageAccumulator* instance(); + +private: + NullDamageAccumulator() {} + ~NullDamageAccumulator() {} + + static NullDamageAccumulator sInstance; +}; + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index 2268386..9f2014f 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -417,6 +417,8 @@ void Matrix4::mapPoint(float& x, float& y) const { } void Matrix4::mapRect(Rect& r) const { + if (isIdentity()) return; + if (isSimple()) { MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index e33a001..1c5c578 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -147,6 +147,7 @@ public: data[kTranslateX] += x; data[kTranslateY] += y; data[kTranslateZ] += z; + mType |= kTypeUnknown; } else { // Doing a translation will only affect the translate bit of the type // Save the type diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index c2f6df8..83ad76f 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -15,6 +15,7 @@ */ #define ATRACE_TAG ATRACE_TAG_VIEW +#define LOG_TAG "RenderNode" #include "RenderNode.h" @@ -111,26 +112,20 @@ void RenderNode::prepareTree(TreeInfo& info) { prepareTreeImpl(info); } -static inline void pushNode(RenderNode* self, TreeInfo& info) { - if (info.damageAccumulator) { - info.damageAccumulator->pushNode(self); - } -} - -static inline void popNode(TreeInfo& info) { - if (info.damageAccumulator) { - info.damageAccumulator->popNode(); - } -} - void RenderNode::damageSelf(TreeInfo& info) { - if (info.damageAccumulator && isRenderable() && properties().getAlpha() > 0) { - info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); + if (isRenderable() && properties().getAlpha() > 0) { + if (properties().getClipToBounds()) { + info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); + } else { + // Hope this is big enough? + // TODO: Get this from the display list ops or something + info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX); + } } } void RenderNode::prepareTreeImpl(TreeInfo& info) { - pushNode(this, info); + info.damageAccumulator->pushTransform(this); if (info.mode == TreeInfo::MODE_FULL) { pushStagingChanges(info); evaluateAnimations(info); @@ -140,7 +135,7 @@ void RenderNode::prepareTreeImpl(TreeInfo& info) { evaluateAnimations(info); } prepareSubTree(info, mDisplayListData); - popNode(info); + info.damageAccumulator->popTransform(); } class PushAnimatorsFunctor { @@ -173,14 +168,14 @@ void RenderNode::pushStagingChanges(TreeInfo& info) { if (mDirtyPropertyFields) { mDirtyPropertyFields = 0; damageSelf(info); - popNode(info); + info.damageAccumulator->popTransform(); mProperties = mStagingProperties; - pushNode(this, info); // We could try to be clever and only re-damage if the matrix changed. // However, we don't need to worry about that. The cost of over-damaging // here is only going to be a single additional map rect of this node // plus a rect join(). The parent's transform (and up) will only be // performed once. + info.damageAccumulator->pushTransform(this); damageSelf(info); } if (mNeedsDisplayListDataSync) { @@ -217,7 +212,7 @@ void RenderNode::evaluateAnimations(TreeInfo& info) { // property push and just damage self before and after animators are run damageSelf(info); - popNode(info); + info.damageAccumulator->popTransform(); AnimateFunctor functor(this, info); std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd; @@ -226,7 +221,7 @@ void RenderNode::evaluateAnimations(TreeInfo& info) { mProperties.updateMatrix(); info.out.hasAnimations |= mAnimators.size(); - pushNode(this, info); + info.damageAccumulator->pushTransform(this); damageSelf(info); } @@ -243,8 +238,11 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]); } for (size_t i = 0; i < subtree->children().size(); i++) { - RenderNode* childNode = subtree->children()[i]->mDisplayList; + DrawDisplayListOp* op = subtree->children()[i]; + RenderNode* childNode = op->mDisplayList; + info.damageAccumulator->pushTransform(&op->mTransformFromParent); childNode->prepareTreeImpl(info); + info.damageAccumulator->popTransform(); } } } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 393d4ea..f0f6e7c 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -39,6 +39,7 @@ #include <androidfw/ResourceTypes.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "Matrix.h" #include "DeferredDisplayList.h" @@ -125,6 +126,10 @@ public: return mDisplayListData && mDisplayListData->hasDrawOps; } + bool hasProjectionReceiver() const { + return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0; + } + const char* getName() const { return mName.string(); } diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 6163df5..5f7d4e3 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -44,7 +44,6 @@ RenderProperties::PrimitiveFields::PrimitiveFields() , mPivotX(0), mPivotY(0) , mLeft(0), mTop(0), mRight(0), mBottom(0) , mWidth(0), mHeight(0) - , mScrollX(0), mScrollY(0) , mPivotExplicitlySet(false) , mMatrixOrPivotDirty(false) , mCaching(false) { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index c294f38..b012fc5 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -366,22 +366,6 @@ public: return false; } - bool setScrollX(int scrollX) { - return RP_SET(mPrimitiveFields.mScrollX, scrollX); - } - - bool setScrollY(int scrollY) { - return RP_SET(mPrimitiveFields.mScrollY, scrollY); - } - - int getScrollX() const { - return mPrimitiveFields.mScrollX; - } - - int getScrollY() const { - return mPrimitiveFields.mScrollY; - } - bool setCaching(bool caching) { return RP_SET(mPrimitiveFields.mCaching, caching); } @@ -481,7 +465,6 @@ private: float mPivotX, mPivotY; int mLeft, mTop, mRight, mBottom; int mWidth, mHeight; - int mScrollX, mScrollY; bool mPivotExplicitlySet; bool mMatrixOrPivotDirty; bool mCaching; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 2096f98..fd78f8e 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -18,6 +18,7 @@ #include <utils/Timers.h> +#include "DamageAccumulator.h" #include "utils/Macros.h" namespace android { @@ -25,7 +26,6 @@ namespace uirenderer { class BaseRenderNodeAnimator; class AnimationListener; -class DamageAccumulator; class AnimationHook { public: @@ -62,7 +62,7 @@ public: , frameTimeMs(0) , animationHook(NULL) , prepareTextures(mode == MODE_FULL) - , damageAccumulator(0) + , damageAccumulator(NullDamageAccumulator::instance()) {} const TraversalMode mode; @@ -71,7 +71,8 @@ public: // TODO: Remove this? Currently this is used to signal to stop preparing // textures if we run out of cache space. bool prepareTextures; - DamageAccumulator* damageAccumulator; + // Must not be null + IDamageAccumulator* damageAccumulator; struct Out { Out() |