void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
        ALOGW("Error: %s is drawing after destruction, size %d", getName(), mSize);
        CRASH();
    }
    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, %.0f, %.0f, %.0f",
            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
            clipRect->right, clipRect->bottom);
#endif

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

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

    setViewProperties<T>(renderer, handler, level + 1);

    if (mClipToBounds && renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
        DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
        handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
        renderer.restoreToCount(restoreTo);
        renderer.setOverrideLayerAlpha(1.0f);
        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
        op->output(level + 1);
#endif

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

    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
    handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
    renderer.restoreToCount(restoreTo);
    renderer.setOverrideLayerAlpha(1.0f);
}
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
    if (mDisplayList->isEmpty()) {
        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "",
                this, getName());
        return;
    }

#if HWUI_NEW_OPS
    const bool drawLayer = false;
#else
    const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
#endif
    // If we are updating the contents of mLayer, we don't want to apply any of
    // the RenderNode's properties to this issueOperations pass. Those will all
    // be applied when the layer is drawn, aka when this is true.
    const bool useViewProperties = (!mLayer || drawLayer);
    if (useViewProperties) {
        const Outline& outline = properties().getOutline();
        if (properties().getAlpha() <= 0
                || (outline.getShouldClip() && outline.isEmpty())
                || properties().getScaleX() == 0
                || properties().getScaleY() == 0) {
            DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
                    this, getName());
            return;
        }
    }

    handler.startMark(getName());

#if DEBUG_DISPLAY_LIST
    const Rect& clipRect = renderer.getLocalClipBounds();
    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
            handler.level() * 2, "", this, getName(),
            clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
#endif

    LinearAllocator& alloc = handler.allocator();
    int restoreTo = renderer.getSaveCount();
    handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
            PROPERTY_SAVECOUNT, properties().getClipToBounds());

    DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "",
            SaveFlags::MatrixClip, restoreTo);

    if (useViewProperties) {
        setViewProperties<T>(renderer, handler);
    }

#if HWUI_NEW_OPS
    LOG_ALWAYS_FATAL("legacy op traversal not supported");
#else
    bool quickRejected = properties().getClipToBounds()
            && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
    if (!quickRejected) {
        Matrix4 initialTransform(*(renderer.currentTransform()));
        renderer.setBaseTransform(initialTransform);

        if (drawLayer) {
            handler(new (alloc) DrawLayerOp(mLayer),
                    renderer.getSaveCount() - 1, properties().getClipToBounds());
        } else {
            const int saveCountOffset = renderer.getSaveCount() - 1;
            const int projectionReceiveIndex = mDisplayList->projectionReceiveIndex;
            for (size_t chunkIndex = 0; chunkIndex < mDisplayList->getChunks().size(); chunkIndex++) {
                const DisplayList::Chunk& chunk = mDisplayList->getChunks()[chunkIndex];

                std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
                buildZSortedChildList(chunk, zTranslatedNodes);

                issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren,
                        initialTransform, zTranslatedNodes, renderer, handler);

                for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
                    DisplayListOp *op = mDisplayList->getOps()[opIndex];
#if DEBUG_DISPLAY_LIST
                    op->output(handler.level() + 1);
#endif
                    handler(op, saveCountOffset, properties().getClipToBounds());

                    if (CC_UNLIKELY(!mProjectedNodes.empty() && projectionReceiveIndex >= 0 &&
                        opIndex == static_cast<size_t>(projectionReceiveIndex))) {
                        issueOperationsOfProjectedChildren(renderer, handler);
                    }
                }

                issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren,
                        initialTransform, zTranslatedNodes, renderer, handler);
            }
        }
    }
#endif

    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo);
    handler(new (alloc) RestoreToCountOp(restoreTo),
            PROPERTY_SAVECOUNT, properties().getClipToBounds());

    DISPLAY_LIST_LOGD("%*sDone (%p, %s)", handler.level() * 2, "", this, getName());
    handler.endMark();
}