Merge "Fix DA bugs"

This commit is contained in:
John Reck
2014-06-12 17:07:13 +00:00
committed by Android (Google) Code Review
12 changed files with 195 additions and 94 deletions

View File

@@ -841,13 +841,6 @@ public class RenderNode {
return nOffsetTopAndBottom(mNativeRenderNode, offset);
}
/**
* Sets the scroll position, this is used for damage calculations
*/
public void setScrollPosition(int x, int y) {
nSetScrollPosition(mNativeRenderNode, x, y);
}
/**
* Outputs the display list to the log. This method exists for use by
* tools to output display lists for selected nodes to the log.
@@ -906,7 +899,6 @@ public class RenderNode {
private static native boolean nSetRight(long renderNode, int right);
private static native boolean nSetTop(long renderNode, int top);
private static native boolean nSetLeft(long renderNode, int left);
private static native void nSetScrollPosition(long renderNode, int scrollX, int scrollY);
private static native boolean nSetCameraDistance(long renderNode, float distance);
private static native boolean nSetPivotY(long renderNode, float pivotY);
private static native boolean nSetPivotX(long renderNode, float pivotX);

View File

@@ -13738,7 +13738,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return;
}
renderNode.setScrollPosition(mScrollX, mScrollY);
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.isValid()
|| (!isLayer && mRecreateDisplayList)) {

View File

@@ -275,13 +275,6 @@ static jboolean android_view_RenderNode_offsetTopAndBottom(JNIEnv* env,
return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y);
}
static void android_view_RenderNode_setScrollPosition(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jint scrollX, jint scrollY) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
SET_AND_DIRTY(setScrollX, scrollX, RenderNode::GENERIC);
SET_AND_DIRTY(setScrollY, scrollY, RenderNode::GENERIC);
}
// ----------------------------------------------------------------------------
// RenderProperties - getters
// ----------------------------------------------------------------------------
@@ -517,7 +510,6 @@ static JNINativeMethod gMethods[] = {
{ "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
{ "nOffsetLeftAndRight", "(JF)Z", (void*) android_view_RenderNode_offsetLeftAndRight },
{ "nOffsetTopAndBottom", "(JF)Z", (void*) android_view_RenderNode_offsetTopAndBottom },
{ "nSetScrollPosition", "(JII)V", (void*) android_view_RenderNode_setScrollPosition },
{ "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering },
{ "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline },

View File

@@ -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();
}
}
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;
}
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());
}
} else {
mappedDirty.offset(props.getLeft(), props.getTop());
}
dirty(mappedDirty.fLeft, mappedDirty.fTop, mappedDirty.fRight, mappedDirty.fBottom);
}
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 {
ALOGW("Failed to find projection receiver? Dropping on the floor...");
}
frame->pendingDirty.setEmpty();
}
}

View File

@@ -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 */

View File

@@ -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]);

View File

@@ -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

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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()