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

Commit c3566d06 authored by Chris Craik's avatar Chris Craik
Browse files

DisplayList draw operation reordering

bug:8037003

The reordering enables similar operations to draw together, minimizing the
OpenGL state change operations that go inbetween draws. Eventually, multiple
complete canvas draw operations will be merged (into a single glDrawArrays call,
for example)

Reorders DisplayList draw operations when:

-They can move backwards in the command stream to be after similar
operations without violating draw ordering

-The OpenGLRenderer is in a simple, replayable state (no complex clip,
or filter/shadow etc)

Also adds two system properties to control the deferral/reordering:
    "debug.hwui.disable_draw_defer"
    "debug.hwui.disable_draw_reorder"
which can be set to "true" to control the display list manipulation

Change-Id: I5e89f3cb0ea2d2afd3e15c64d7f32b8406777a32
parent 101ae3bb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
		GammaFontRenderer.cpp \
		Caches.cpp \
		DisplayList.cpp \
		DeferredDisplayList.cpp \
		DisplayListLogBuffer.cpp \
		DisplayListRenderer.cpp \
		Dither.cpp \
+3 −0
Original line number Diff line number Diff line
@@ -74,6 +74,9 @@
// Turn on to enable additional debugging in the font renderers
#define DEBUG_FONT_RENDERER 0

// Turn on to log draw operation batching and deferral information
#define DEBUG_DEFER 0

// Turn on to dump display list state
#define DEBUG_DISPLAY_LIST 0

+176 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "OpenGLRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW

#include <utils/Trace.h>

#include "Debug.h"
#include "DisplayListOp.h"
#include "OpenGLRenderer.h"

#if DEBUG_DEFER
    #define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
#else
    #define DEFER_LOGD(...)
#endif

namespace android {
namespace uirenderer {

class DrawOpBatch {
public:
    DrawOpBatch() {
        mOps.clear();
    }

    ~DrawOpBatch() {
        mOps.clear();
    }

    void add(DrawOp* op) {
        // NOTE: ignore empty bounds special case, since we don't merge across those ops
        mBounds.unionWith(op->state.mBounds);
        mOps.add(op);
    }

    bool intersects(Rect& rect) {
        if (!rect.intersects(mBounds)) return false;
        for (unsigned int i = 0; i < mOps.size(); i++) {
            if (rect.intersects(mOps[i]->state.mBounds)) {
#if DEBUG_DEFER
                DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i],
                        mOps[i]->state.mBounds.left, mOps[i]->state.mBounds.top,
                        mOps[i]->state.mBounds.right, mOps[i]->state.mBounds.bottom);
                mOps[i]->output(2);
#endif
                return true;
            }
        }
        return false;
    }

    Vector<DrawOp*> mOps;
private:
    Rect mBounds;
};

void DeferredDisplayList::clear() {
    for (int i = 0; i < kOpBatch_Count; i++) {
        mBatchIndices[i] = -1;
    }
    for (unsigned int i = 0; i < mBatches.size(); i++) {
        delete mBatches[i];
    }
    mBatches.clear();
}

void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) {
    if (CC_UNLIKELY(disallowReorder)) {
        if (!mBatches.isEmpty()) {
            mBatches[0]->add(op);
            return;
        }
        DrawOpBatch* b = new DrawOpBatch();
        b->add(op);
        mBatches.add(b);
        return;
    }

    // disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge
    // the new op into it
    DrawOpBatch* targetBatch = NULL;
    int batchId = op->getBatchId();

    if (!mBatches.isEmpty()) {
        if (op->state.mBounds.isEmpty()) {
            // don't know the bounds for op, so add to last batch and start from scratch on next op
            mBatches.top()->add(op);
            for (int i = 0; i < kOpBatch_Count; i++) {
                mBatchIndices[i] = -1;
            }
#if DEBUG_DEFER
            DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
            op->output(2);
#endif
            return;
        }

        if (batchId >= 0 && mBatchIndices[batchId] != -1) {
            int targetIndex = mBatchIndices[batchId];
            targetBatch = mBatches[targetIndex];
            // iterate back toward target to see if anything drawn since should overlap the new op
            for (int i = mBatches.size() - 1; i > targetIndex; i--) {
                DrawOpBatch* overBatch = mBatches[i];
                if (overBatch->intersects(op->state.mBounds)) {
                    targetBatch = NULL;
#if DEBUG_DEFER
                    DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
                            targetIndex, i);
                    op->output(2);
#endif
                    break;
                }
            }
        }
    }
    if (!targetBatch) {
        targetBatch = new DrawOpBatch();
        mBatches.add(targetBatch);
        if (batchId >= 0) {
            mBatchIndices[batchId] = mBatches.size() - 1;
        }
    }
    targetBatch->add(op);
}

status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
        uint32_t level) {
    ATRACE_CALL();
    status_t status = DrawGlInfo::kStatusDone;

    if (isEmpty()) return status; // nothing to flush

    DEFER_LOGD("--flushing");
    DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
    int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
    int opCount = 0;
    for (unsigned int i = 0; i < mBatches.size(); i++) {
        DrawOpBatch* batch = mBatches[i];
        for (unsigned int j = 0; j < batch->mOps.size(); j++) {
            DrawOp* op = batch->mOps[j];

            renderer.restoreDisplayState(op->state);

#if DEBUG_DEFER
            op->output(2);
#endif
            status |= op->applyDraw(renderer, dirty, level,
                    op->state.mMultipliedAlpha >= 0, op->state.mMultipliedAlpha);
            opCount++;
        }
    }

    DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount);
    renderer.restoreToCount(restoreTo);
    renderer.setDrawModifiers(restoreDrawModifiers);
    clear();
    return status;
}

}; // namespace uirenderer
}; // namespace android
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H

#include <utils/Errors.h>
#include <utils/Vector.h>

#include "Matrix.h"
#include "Rect.h"

namespace android {
namespace uirenderer {

class DrawOp;
class DrawOpBatch;
class OpenGLRenderer;
class SkiaShader;

class DeferredDisplayList {
public:
    DeferredDisplayList() { clear(); }
    ~DeferredDisplayList() { clear(); }

    enum OpBatchId {
        kOpBatch_None = -1, // Don't batch
        kOpBatch_Bitmap,
        kOpBatch_Patch,
        kOpBatch_AlphaVertices,
        kOpBatch_Vertices,
        kOpBatch_AlphaMaskTexture,
        kOpBatch_Text,
        kOpBatch_ColorText,

        kOpBatch_Count, // Add other batch ids before this
    };

    bool isEmpty() { return mBatches.isEmpty(); }

    /**
     * Plays back all of the draw ops recorded into batches to the renderer.
     * Adjusts the state of the renderer as necessary, and restores it when complete
     */
    status_t flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
            uint32_t level);

    /**
     * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if
     * disallowReorder is false, respecting draw order when overlaps occur
     */
    void add(DrawOp* op, bool disallowReorder);

private:
    void clear();


    Vector<DrawOpBatch*> mBatches;
    int mBatchIndices[kOpBatch_Count];
};

}; // namespace uirenderer
}; // namespace android

#endif // ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
+21 −3
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include "Debug.h"
#include "DisplayList.h"
#include "DisplayListOp.h"
#include "DisplayListLogBuffer.h"
@@ -386,7 +387,8 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, uint32_t level) {
    }
}

status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) {
status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level,
        DeferredDisplayList* deferredList) {
    status_t drawGlStatus = DrawGlInfo::kStatusDone;

#if DEBUG_DISPLAY_LIST
@@ -401,6 +403,12 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
    int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
    DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "",
            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);

    if (mAlpha < 1 && !mCaching && CC_LIKELY(deferredList)) {
        // flush before a saveLayerAlpha/setAlpha
        // TODO: make this cleaner
        drawGlStatus |= deferredList->flush(renderer, dirty, flags, level);
    }
    setViewProperties(renderer, level);

    if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
@@ -418,8 +426,13 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
        Caches::getInstance().eventMark(strlen(op->name()), op->name());
#endif

        if (deferredList) {
            drawGlStatus |= op->replay(renderer, dirty, flags,
                    saveCount, level, mCaching, mMultipliedAlpha, *deferredList);
        } else {
            drawGlStatus |= op->replay(renderer, dirty, flags,
                    saveCount, level, mCaching, mMultipliedAlpha);
        }
        logBuffer.writeCommand(level, op->name());
    }

@@ -429,6 +442,11 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag

    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", (level + 1) * 2, "", this, mName.string(),
            drawGlStatus);

    if (!level && CC_LIKELY(deferredList)) {
        drawGlStatus |= deferredList->flush(renderer, dirty, flags, level);
    }

    return drawGlStatus;
}

Loading