Merge "Fix DA bugs"
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user