Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a447d29c authored by John Reck's avatar John Reck
Browse files

Fix DA bugs

 * Now aware of transform of DrawDisplayListOp
 * Supports projection

 Bug: 15539677
 Bug: 15506680

Change-Id: Ic16f482cd48c3add12e49eca529281be12b93491
parent f8333cc3
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -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);
+0 −1
Original line number Diff line number Diff line
@@ -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)) {
+0 −8
Original line number Diff line number Diff line
@@ -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 },
+121 −28
Original line number Diff line number Diff line
@@ -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();
    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(&mappedDirty, dirtyFrame->pendingDirty);
        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;
        }
    }
    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 {
            mappedDirty = dirtyFrame->pendingDirty;
            mapRect(frame->matrix4, *rect, rect);
        }
        frame = frame->prev;
    }
        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();
}

void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
    if (frame->pendingDirty.isEmpty()) {
        return;
    }
            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());

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

+42 −6
Original line number Diff line number Diff line
@@ -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 */

Loading