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

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

Fully deferred displaylist replay

bug:8037003

A recursive drawDisplayList call is now entirely deferred before
playing back to the screen and issuing GL commands. This way, the
entire stream can be inspected, optimized, and batch work (such as
uploading textures) before issuing commands.

Additionally, this fixes an issue where operations draw could move
across restores corresponding to saveLayer(alpha). Those and other
similar cases (such as complex clipping, requiring the stencil) are
now treated as batching barriers, with the operations that change
renderer state in a way that's difficult to defer are just re-issued
at flush time.

Change-Id: Ie7348166662a5ad89fb9b1e87558334fb826b01e
parent 9105ce74
Loading
Loading
Loading
Loading
+2 −7
Original line number Diff line number Diff line
@@ -103,14 +103,9 @@ void Caches::initFont() {
void Caches::initExtensions() {
    if (mExtensions.hasDebugMarker()) {
        eventMark = glInsertEventMarkerEXT;
        if ((drawDeferDisabled || drawReorderDisabled)) {

        startMark = glPushGroupMarkerEXT;
        endMark = glPopGroupMarkerEXT;
        } else {
            startMark = startMarkNull;
            endMark = endMarkNull;
        }

    } else {
        eventMark = eventMarkNull;
        startMark = startMarkNull;
+243 −37
Original line number Diff line number Diff line
@@ -32,15 +32,15 @@
namespace android {
namespace uirenderer {

/////////////////////////////////////////////////////////////////////////////////
// Operation Batches
/////////////////////////////////////////////////////////////////////////////////

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

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

    void add(DrawOp* op) {
        // NOTE: ignore empty bounds special case, since we don't merge across those ops
@@ -48,8 +48,9 @@ public:
        mOps.add(op);
    }

    bool intersects(Rect& rect) {
    virtual 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
@@ -64,27 +65,217 @@ public:
        return false;
    }

    Vector<DrawOp*> mOps;
    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
        DEFER_LOGD("replaying draw batch %p", this);

        status_t status = DrawGlInfo::kStatusDone;
        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
        for (unsigned int i = 0; i < mOps.size(); i++) {
            DrawOp* op = mOps[i];

            renderer.restoreDisplayState(op->state, kStateDeferFlag_Draw);

#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
            renderer.eventMark(strlen(op->name()), op->name());
#endif
            status |= op->applyDraw(renderer, dirty, 0, op->state.mMultipliedAlpha);
            logBuffer.writeCommand(0, op->name());
        }
        return status;
    }

    inline int count() const { return mOps.size(); }
private:
    Vector<DrawOp*> mOps;
    Rect mBounds;
};

void DeferredDisplayList::clear() {
class StateOpBatch : public DrawOpBatch {
public:
    // creates a single operation batch
    StateOpBatch(StateOp* op) : mOp(op) {}

    bool intersects(Rect& rect) {
        // if something checks for intersection, it's trying to go backwards across a state op,
        // something not currently supported - state ops are always barriers
        CRASH();
        return false;
    }

    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
        DEFER_LOGD("replaying state op batch %p", this);
        renderer.restoreDisplayState(mOp->state, 0);

        // use invalid save count because it won't be used at flush time - RestoreToCountOp is the
        // only one to use it, and we don't use that class at flush time, instead calling
        // renderer.restoreToCount directly
        int saveCount = -1;
        mOp->applyState(renderer, saveCount);
        return DrawGlInfo::kStatusDone;
    }

private:
    StateOp* mOp;
};

class RestoreToCountBatch : public DrawOpBatch {
public:
    RestoreToCountBatch(int restoreCount) : mRestoreCount(restoreCount) {}

    bool intersects(Rect& rect) {
        // if something checks for intersection, it's trying to go backwards across a state op,
        // something not currently supported - state ops are always barriers
        CRASH();
        return false;
    }

    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
        DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
        renderer.restoreToCount(mRestoreCount);

        return DrawGlInfo::kStatusDone;
    }

private:
    /*
     * The count used here represents the flush() time saveCount. This is as opposed to the
     * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and
     * (saveCount + mCount) respectively). Since the count is different from the original
     * RestoreToCountOp, we don't store a pointer to the op, as elsewhere.
     */
    const int mRestoreCount;
};

/////////////////////////////////////////////////////////////////////////////////
// DeferredDisplayList
/////////////////////////////////////////////////////////////////////////////////

void DeferredDisplayList::resetBatchingState() {
    for (int i = 0; i < kOpBatch_Count; i++) {
        mBatchIndices[i] = -1;
    }
}

void DeferredDisplayList::clear() {
    resetBatchingState();
    mComplexClipStackStart = -1;

    for (unsigned int i = 0; i < mBatches.size(); i++) {
        delete mBatches[i];
    }
    mBatches.clear();
    mSaveStack.clear();
}

void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) {
    if (CC_UNLIKELY(disallowReorder)) {
        if (!mBatches.isEmpty()) {
            mBatches[0]->add(op);
/////////////////////////////////////////////////////////////////////////////////
// Operation adding
/////////////////////////////////////////////////////////////////////////////////

int DeferredDisplayList::getStateOpDeferFlags() const {
    // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save
    // the clip if we aren't recording a complex clip (and can thus trust it to be a rect)
    return recordingComplexClip() ? 0 : kStateDeferFlag_Clip;
}

int DeferredDisplayList::getDrawOpDeferFlags() const {
    return kStateDeferFlag_Draw | getStateOpDeferFlags();
}

/**
 * When an clipping operation occurs that could cause a complex clip, record the operation and all
 * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading
 * the clip from deferred state, we play back all of the relevant state operations that generated
 * the complex clip.
 *
 * Note that we don't need to record the associated restore operation, since operations at defer
 * time record whether they should store the renderer's current clip
 */
void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) {
    if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) {
        DEFER_LOGD("%p Received complex clip operation %p", this, op);

        // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded
        storeStateOpBarrier(renderer, op);

        if (!recordingComplexClip()) {
            mComplexClipStackStart = renderer.getSaveCount() - 1;
            DEFER_LOGD("    Starting complex clip region, start is %d", mComplexClipStackStart);
        }
    }
}

/**
 * For now, we record save layer operations as barriers in the batch list, preventing drawing
 * operations from reordering around the saveLayer and it's associated restore()
 *
 * In the future, we should send saveLayer commands (if they can be played out of order) and their
 * contained drawing operations to a seperate list of batches, so that they may draw at the
 * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame.
 *
 * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a
 * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set.
 */
void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer,
        SaveLayerOp* op, int newSaveCount) {
    DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d",
            this, op, op->getFlags(), newSaveCount);

    storeStateOpBarrier(renderer, op);
    mSaveStack.push(newSaveCount);
}

/**
 * Takes save op and it's return value - the new save count - and stores it into the stream as a
 * barrier if it's needed to properly modify a complex clip
 */
void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) {
    int saveFlags = op->getFlags();
    DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount);

    if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) {
        // store and replay the save operation, as it may be needed to correctly playback the clip
        DEFER_LOGD("    adding save barrier with new save count %d", newSaveCount);
        storeStateOpBarrier(renderer, op);
        mSaveStack.push(newSaveCount);
    }
}

/**
 * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw
 * the layer in the deferred list
 *
 * other save() commands which occur as children of a snapshot with complex clip will be deferred,
 * and must be restored
 *
 * Either will act as a barrier to draw operation reordering, as we want to play back layer
 * save/restore and complex canvas modifications (including save/restore) in order.
 */
void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount) {
    DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount);

    if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) {
        mComplexClipStackStart = -1;
        resetBatchingState();
    }

    if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) {
        return;
    }

    while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop();

    storeRestoreToCountBarrier(mSaveStack.size() + 1);
}

void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
    if (renderer.storeDisplayState(op->state, getDrawOpDeferFlags())) {
        return; // quick rejected
    }

    op->onDrawOpDeferred(renderer);

    if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
        // TODO: elegant way to reuse batches?
        DrawOpBatch* b = new DrawOpBatch();
        b->add(op);
        mBatches.add(b);
@@ -138,9 +329,41 @@ void DeferredDisplayList::add(DrawOp* op, bool disallowReorder) {
    targetBatch->add(op);
}

status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32_t flags,
        uint32_t level) {
    ATRACE_CALL();
void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
    DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size());

    renderer.storeDisplayState(op->state, getStateOpDeferFlags());
    mBatches.add(new StateOpBatch(op));
    resetBatchingState();
}

void DeferredDisplayList::storeRestoreToCountBarrier(int newSaveCount) {
    DEFER_LOGD("%p adding restore to count %d barrier, pos %d",
            this, newSaveCount, mBatches.size());

    mBatches.add(new RestoreToCountBatch(newSaveCount));
    resetBatchingState();
}

/////////////////////////////////////////////////////////////////////////////////
// Replay / flush
/////////////////////////////////////////////////////////////////////////////////

static status_t replayBatchList(Vector<DrawOpBatch*>& batchList,
        OpenGLRenderer& renderer, Rect& dirty) {
    status_t status = DrawGlInfo::kStatusDone;

    int opCount = 0;
    for (unsigned int i = 0; i < batchList.size(); i++) {
        status |= batchList[i]->replay(renderer, dirty);
        opCount += batchList[i]->count();
    }
    DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount);
    return status;
}

status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
    ATRACE_NAME("flush drawing commands");
    status_t status = DrawGlInfo::kStatusDone;

    if (isEmpty()) return status; // nothing to flush
@@ -148,29 +371,12 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty, int32
    DEFER_LOGD("--flushing");
    renderer.eventMark("Flush");

    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.restoreToCount(1);

            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++;
        }
    }
    status |= replayBatchList(mBatches, renderer, dirty);

    DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount);
    DEFER_LOGD("--flush complete, returning %x", status);

    renderer.restoreToCount(restoreTo);
    renderer.setDrawModifiers(restoreDrawModifiers);
    clear();
    return status;
}
+31 −4
Original line number Diff line number Diff line
@@ -26,10 +26,13 @@
namespace android {
namespace uirenderer {

class ClipOp;
class DrawOp;
class SaveOp;
class SaveLayerOp;
class StateOp;
class DrawOpBatch;
class OpenGLRenderer;
class SkiaShader;

class DeferredDisplayList {
public:
@@ -55,18 +58,42 @@ public:
     * 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);
    status_t flush(OpenGLRenderer& renderer, Rect& dirty);

    void addClip(OpenGLRenderer& renderer, ClipOp* op);
    void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount);
    void addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount);
    void addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount);

    /**
     * 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);
    void addDrawOp(OpenGLRenderer& renderer, DrawOp* op);

private:
    /*
     * Resets the batching back-pointers, creating a barrier in the operation stream so that no ops
     * added in the future will be inserted into a batch that already exist.
     */
    void resetBatchingState();

    void clear();

    void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op);
    void storeRestoreToCountBarrier(int newSaveCount);

    bool recordingComplexClip() const { return mComplexClipStackStart >= 0; }

    int getStateOpDeferFlags() const;
    int getDrawOpDeferFlags() const;

    /*
     *
     * at defer time, stores the savecount of save/saveLayer ops that were 
     */
    Vector<int> mSaveStack;
    int mComplexClipStackStart;

    Vector<DrawOpBatch*> mBatches;
    int mBatchIndices[kOpBatch_Count];
+103 −48
Original line number Diff line number Diff line
@@ -61,6 +61,12 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {

void DisplayList::clearResources() {
    mDisplayListData = NULL;

    mClipRectOp = NULL;
    mSaveLayerOp = NULL;
    mSaveOp = NULL;
    mRestoreToCountOp = NULL;

    delete mTransformMatrix;
    delete mTransformCamera;
    delete mTransformMatrix3D;
@@ -156,6 +162,13 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
        return;
    }

    // allocate reusable ops for state-deferral
    LinearAllocator& alloc = mDisplayListData->allocator;
    mClipRectOp = new (alloc) ClipRectOp();
    mSaveLayerOp = new (alloc) SaveLayerOp();
    mSaveOp = new (alloc) SaveOp();
    mRestoreToCountOp = new (alloc) RestoreToCountOp();

    mFunctorCount = recorder.getFunctorCount();

    Caches& caches = Caches::getInstance();
@@ -318,7 +331,7 @@ void DisplayList::updateMatrix() {
    }
}

void DisplayList::outputViewProperties(uint32_t level) {
void DisplayList::outputViewProperties(const int level) {
    updateMatrix();
    if (mLeft != 0 || mTop != 0) {
        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
@@ -358,10 +371,17 @@ void DisplayList::outputViewProperties(uint32_t level) {
    }
}

status_t DisplayList::setViewProperties(OpenGLRenderer& renderer, Rect& dirty,
        int32_t flags, uint32_t level, DeferredDisplayList* deferredList) {
    status_t status = DrawGlInfo::kStatusDone;
#if DEBUG_DISPLAYLIST
/*
 * For property operations, we pass a savecount of 0, since the operations aren't part of the
 * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
 * base saveCount (i.e., how RestoreToCount uses saveCount + mCount)
 */
#define PROPERTY_SAVECOUNT 0

template <class T>
void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
        const int level) {
#if DEBUG_DISPLAY_LIST
    outputViewProperties(level);
#endif
    updateMatrix();
@@ -381,86 +401,121 @@ status_t DisplayList::setViewProperties(OpenGLRenderer& renderer, Rect& dirty,
        }
    }
    if (mAlpha < 1 && !mCaching) {
        if (deferredList) {
            // flush since we'll either enter a Layer, or set alpha, both not supported in deferral
            status |= deferredList->flush(renderer, dirty, flags, level);
        }

        if (!mHasOverlappingRendering) {
            renderer.setAlpha(mAlpha);
        } else {
            // TODO: should be able to store the size of a DL at record time and not
            // have to pass it into this call. In fact, this information might be in the
            // location/size info that we store with the new native transform data.
            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
            if (mClipChildren) {
                flags |= SkCanvas::kClipToLayer_SaveFlag;
                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
            }
            renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
                    mMultipliedAlpha, flags);
            handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop,
                    mMultipliedAlpha, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT);
        }
    }
    if (mClipChildren && !mCaching) {
        if (deferredList && CC_UNLIKELY(!renderer.hasRectToRectTransform())) {
            // flush, since clip will likely be a region
            status |= deferredList->flush(renderer, dirty, flags, level);
        handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op),
                PROPERTY_SAVECOUNT);
    }
}

class DeferOperationHandler {
public:
    DeferOperationHandler(DeferStateStruct& deferStruct, int multipliedAlpha, int level)
        : mDeferStruct(deferStruct), mMultipliedAlpha(multipliedAlpha), mLevel(level) {}
    inline void operator()(DisplayListOp* operation, int saveCount) {
        operation->defer(mDeferStruct, saveCount, mLevel, mMultipliedAlpha);
    }
        renderer.clipRect(0, 0, mRight - mLeft, mBottom - mTop,
                SkRegion::kIntersect_Op);
private:
    DeferStateStruct& mDeferStruct;
    const int mMultipliedAlpha;
    const int mLevel;
};

void DisplayList::defer(DeferStateStruct& deferStruct, const int level) {
    DeferOperationHandler handler(deferStruct, mCaching ? mMultipliedAlpha : -1, level);
    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
}

class ReplayOperationHandler {
public:
    ReplayOperationHandler(ReplayStateStruct& replayStruct, int multipliedAlpha, int level)
        : mReplayStruct(replayStruct), mMultipliedAlpha(multipliedAlpha), mLevel(level) {}
    inline void operator()(DisplayListOp* operation, int saveCount) {
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
        replayStruct.mRenderer.eventMark(operation->name());
#endif
        operation->replay(mReplayStruct, saveCount, mLevel, mMultipliedAlpha);
    }
    return status;
private:
    ReplayStateStruct& mReplayStruct;
    const int mMultipliedAlpha;
    const int mLevel;
};

void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) {
    ReplayOperationHandler handler(replayStruct, mCaching ? mMultipliedAlpha : -1, level);

    replayStruct.mRenderer.startMark(mName.string());
    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
    replayStruct.mRenderer.endMark();

    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
            replayStruct.mDrawGlStatus);
}

status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level,
        DeferredDisplayList* deferredList) {
    status_t drawGlStatus = DrawGlInfo::kStatusDone;
/**
 * This function serves both defer and replay modes, and will organize the displayList's component
 * operations for a single frame:
 *
 * Every 'simple' operation that affects just the matrix and alpha (or other factors of
 * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
 * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
 * defer vs replay logic, per operation
 */
template <class T>
void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
    if (mSize == 0 || mAlpha <= 0) {
        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
        return;
    }

#if DEBUG_DISPLAY_LIST
    Rect* clipRect = renderer.getClipRect();
    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
            (level+1)*2, "", this, mName.string(), clipRect->left, clipRect->top,
            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
            clipRect->right, clipRect->bottom);
#endif

    renderer.startMark(mName.string());
    int restoreTo = renderer.getSaveCount();
    handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
            PROPERTY_SAVECOUNT);

    int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
    DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "",
    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);

    drawGlStatus |= setViewProperties(renderer, dirty, flags, level, deferredList);
    setViewProperties<T>(renderer, handler, level + 1);

    if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
        DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
        renderer.restoreToCount(restoreTo);
        renderer.endMark();
        return drawGlStatus;
        handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT);
        return;
    }

    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
    int saveCount = renderer.getSaveCount() - 1;
    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
        DisplayListOp *op = mDisplayListData->displayListOps[i];
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
        renderer.eventMark(strlen(op->name()), op->name());
#endif
        drawGlStatus |= op->replay(renderer, dirty, flags,
                saveCount, level, mCaching, mMultipliedAlpha, deferredList);

        handler(op, saveCount);
        logBuffer.writeCommand(level, op->name());
    }

    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
    handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT);
    renderer.restoreToCount(restoreTo);
    renderer.endMark();

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

}; // namespace uirenderer
+53 −6

File changed.

Preview size limit exceeded, changes collapsed.

Loading