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

Commit 2554b09f authored by Chris Craik's avatar Chris Craik Committed by android-build-merger
Browse files

Merge "Improve multi-window render clipping logic" into nyc-dev

am: c79c3246

* commit 'c79c3246':
  Improve multi-window render clipping logic

Change-Id: I4be0f6cefba71f7928fec559481fe389d9de800b
parents ed5bea39 c79c3246
Loading
Loading
Loading
Loading
+6 −9
Original line number Diff line number Diff line
@@ -270,15 +270,12 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
            mLastXOffset = xOffset;
            mLastYOffset = yOffset;

            // Only clip the content to the bounds if we are not fullscreen. In the other case, we
            // actually need to draw outside these.
            if (mResizeMode == RESIZE_MODE_FREEFORM) {
            // Inform the renderer of the content's new bounds
            mRenderer.setContentDrawBounds(
                    mLastXOffset,
                    mLastYOffset,
                    mLastXOffset + mLastContentWidth,
                    mLastYOffset + mLastCaptionHeight + mLastContentHeight);
            }

            // If this was the first call and redrawLocked got already called prior
            // to us, we should re-issue a redrawLocked now.
+108 −44
Original line number Diff line number Diff line
@@ -31,18 +31,16 @@
namespace android {
namespace uirenderer {

FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
FrameBuilder::FrameBuilder(const SkRect& clip,
        uint32_t viewportWidth, uint32_t viewportHeight,
        const std::vector< sp<RenderNode> >& nodes,
        const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches)
        : mCanvasState(*this)
        const LightGeometry& lightGeometry, Caches& caches)
        : mStdAllocator(mAllocator)
        , mLayerBuilders(mStdAllocator)
        , mLayerStack(mStdAllocator)
        , mCanvasState(*this)
        , mCaches(caches)
        , mLightRadius(lightGeometry.radius)
        , mDrawFbo0(!nodes.empty()) {
    ATRACE_NAME("prepare drawing commands");

    mLayerBuilders.reserve(layers.entries().size());
    mLayerStack.reserve(layers.entries().size());
        , mDrawFbo0(true) {

    // Prepare to defer Fbo0
    auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
@@ -51,7 +49,31 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
    mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
            clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
            lightGeometry.center);
}

FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers,
        const LightGeometry& lightGeometry, Caches& caches)
        : mStdAllocator(mAllocator)
        , mLayerBuilders(mStdAllocator)
        , mLayerStack(mStdAllocator)
        , mCanvasState(*this)
        , mCaches(caches)
        , mLightRadius(lightGeometry.radius)
        , mDrawFbo0(false) {
    // TODO: remove, with each layer on its own save stack

    // Prepare to defer Fbo0 (which will be empty)
    auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1));
    mLayerBuilders.push_back(fbo0);
    mLayerStack.push_back(0);
    mCanvasState.initializeSaveStack(1, 1,
            0, 0, 1, 1,
            lightGeometry.center);

    deferLayers(layers);
}

void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) {
    // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
    // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
    for (int i = layers.entries().size() - 1; i >= 0; i--) {
@@ -76,10 +98,45 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
            restoreForLayer();
        }
    }
}

void FrameBuilder::deferRenderNode(RenderNode& renderNode) {
    renderNode.computeOrdering();

    mCanvasState.save(SaveFlags::MatrixClip);
    deferNodePropsAndOps(renderNode);
    mCanvasState.restore();
}

void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) {
    renderNode.computeOrdering();

    mCanvasState.save(SaveFlags::MatrixClip);
    mCanvasState.translate(tx, ty);
    mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
            SkRegion::kIntersect_Op);
    deferNodePropsAndOps(renderNode);
    mCanvasState.restore();
}

static Rect nodeBounds(RenderNode& node) {
    auto& props = node.properties();
    return Rect(props.getLeft(), props.getTop(),
            props.getRight(), props.getBottom());
}

void FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
        const Rect& contentDrawBounds) {
    if (nodes.size() < 1) return;
    if (nodes.size() == 1) {
        if (!nodes[0]->nothingToDraw()) {
            deferRenderNode(*nodes[0]);
        }
        return;
    }
    // It there are multiple render nodes, they are laid out as follows:
    // #0 - backdrop (content + caption)
    // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
    // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
    // #2 - additional overlay nodes
    // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
    // resizing however it might become partially visible. The following render loop will crop the
@@ -88,45 +145,52 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
    //
    // Additional nodes will be drawn on top with no particular clipping semantics.

    // The bounds of the backdrop against which the content should be clipped.
    Rect backdropBounds = contentDrawBounds;
    // Usually the contents bounds should be mContentDrawBounds - however - we will
    // move it towards the fixed edge to give it a more stable appearance (for the moment).
    // If there is no content bounds we ignore the layering as stated above and start with 2.
    int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;

    for (const sp<RenderNode>& node : nodes) {
        if (node->nothingToDraw()) continue;
        node->computeOrdering();
        int count = mCanvasState.save(SaveFlags::MatrixClip);
    // Backdrop bounds in render target space
    const Rect backdrop = nodeBounds(*nodes[0]);

        if (layer == 0) {
            const RenderProperties& properties = node->properties();
            Rect targetBounds(properties.getLeft(), properties.getTop(),
                              properties.getRight(), properties.getBottom());
            // Move the content bounds towards the fixed corner of the backdrop.
            const int x = targetBounds.left;
            const int y = targetBounds.top;
            // Remember the intersection of the target bounds and the intersection bounds against
            // which we have to crop the content.
            backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
            backdropBounds.doIntersect(targetBounds);
        } else if (layer == 1) {
            // We shift and clip the content to match its final location in the window.
            const float left = contentDrawBounds.left;
            const float top = contentDrawBounds.top;
            const float dx = backdropBounds.left - left;
            const float dy = backdropBounds.top - top;
            const float width = backdropBounds.getWidth();
            const float height = backdropBounds.getHeight();
            mCanvasState.translate(dx, dy);
            // It gets cropped against the bounds of the backdrop to stay inside.
            mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op);
        }

        deferNodePropsAndOps(*node);
        mCanvasState.restoreToCount(count);
        layer++;
    // Bounds that content will fill in render target space (note content node bounds may be bigger)
    Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
    content.translate(backdrop.left, backdrop.top);
    if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
        // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)

        // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
        // also fill left/top. Currently, both 2up and freeform position content at the top/left of
        // the backdrop, so this isn't necessary.
        if (content.right < backdrop.right) {
            // draw backdrop to right side of content
            deferRenderNode(0, 0, Rect(content.right, backdrop.top,
                    backdrop.right, backdrop.bottom), *nodes[0]);
        }
        if (content.bottom < backdrop.bottom) {
            // draw backdrop to bottom of content
            // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
            deferRenderNode(0, 0, Rect(content.left, content.bottom,
                    content.right, backdrop.bottom), *nodes[0]);
        }
    }

    if (!backdrop.isEmpty()) {
        // content node translation to catch up with backdrop
        float dx = contentDrawBounds.left - backdrop.left;
        float dy = contentDrawBounds.top - backdrop.top;

        Rect contentLocalClip = backdrop;
        contentLocalClip.translate(dx, dy);
        deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
    } else {
        deferRenderNode(*nodes[1]);
    }

    // remaining overlay nodes, simply defer
    for (size_t index = 2; index < nodes.size(); index++) {
        if (!nodes[index]->nothingToDraw()) {
            deferRenderNode(*nodes[index]);
        }
    }
}

+21 −20
Original line number Diff line number Diff line
@@ -37,8 +37,8 @@ class OffscreenBuffer;
class Rect;

/**
 * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing
 * them to be rendered.
 * Processes, optimizes, and stores rendering commands from RenderNodes and
 * LayerUpdateQueue, building content needed to render a frame.
 *
 * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
 * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
@@ -60,21 +60,21 @@ public:
        float radius;
    };

    // TODO: remove
    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
    FrameBuilder(const SkRect& clip,
            uint32_t viewportWidth, uint32_t viewportHeight,
            const std::vector< sp<RenderNode> >& nodes,
            const LightGeometry& lightGeometry,
            Caches& caches)
            : FrameBuilder(layers, clip, viewportWidth, viewportHeight,
                    nodes, lightGeometry, Rect(), caches) {}
            const LightGeometry& lightGeometry, Caches& caches);

    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
            uint32_t viewportWidth, uint32_t viewportHeight,
            const std::vector< sp<RenderNode> >& nodes,
            const LightGeometry& lightGeometry,
            const Rect &contentDrawBounds,
            Caches& caches);
    FrameBuilder(const LayerUpdateQueue& layerUpdateQueue,
            const LightGeometry& lightGeometry, Caches& caches);

    void deferLayers(const LayerUpdateQueue& layers);

    void deferRenderNode(RenderNode& renderNode);

    void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode);

    void deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
            const Rect& contentDrawBounds);

    virtual ~FrameBuilder() {}

@@ -223,8 +223,12 @@ private:
    MAP_DEFERRABLE_OPS(X)
#undef X

    // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
    LinearAllocator mAllocator;
    LinearStdAllocator<void*> mStdAllocator;

    // List of every deferred layer's render state. Replayed in reverse order to render a frame.
    std::vector<LayerBuilder*> mLayerBuilders;
    LsaVector<LayerBuilder*> mLayerBuilders;

    /*
     * Stack of indices within mLayerBuilders representing currently active layers. If drawing
@@ -238,7 +242,7 @@ private:
     * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
     * ops added to it.
    */
    std::vector<size_t> mLayerStack;
    LsaVector<size_t> mLayerStack;

    CanvasState mCanvasState;

@@ -246,9 +250,6 @@ private:

    float mLightRadius;

    // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
    LinearAllocator mAllocator;

    const bool mDrawFbo0;
};

+2 −1
Original line number Diff line number Diff line
@@ -244,7 +244,8 @@ void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bak

        if (CC_UNLIKELY(activeUnclippedSaveLayers.empty()
                && bakedState->computedState.opaqueOverClippedBounds
                && bakedState->computedState.clippedBounds.contains(repaintRect))) {
                && bakedState->computedState.clippedBounds.contains(repaintRect)
                && !Properties::debugOverdraw)) {
            // discard all deferred drawing ops, since new one will occlude them
            clear();
        }
+7 −4
Original line number Diff line number Diff line
@@ -356,9 +356,13 @@ void CanvasContext::draw() {

#if HWUI_NEW_OPS
    auto& caches = Caches::getInstance();
    FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
            mRenderNodes, mLightGeometry, mContentDrawBounds, caches);
    FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches);

    frameBuilder.deferLayers(mLayerUpdateQueue);
    mLayerUpdateQueue.clear();

    frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds);

    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
            mOpaque, mLightInfo);
    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -623,8 +627,7 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
#if HWUI_NEW_OPS
    static const std::vector< sp<RenderNode> > emptyNodeList;
    auto& caches = Caches::getInstance();
    FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
            emptyNodeList, mLightGeometry, mContentDrawBounds, caches);
    FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches);
    mLayerUpdateQueue.clear();
    BakedOpRenderer renderer(caches, mRenderThread.renderState(),
            mOpaque, mLightInfo);
Loading