Android5.0中 hwui 中 RenderThread 工作流程

前言

本篇文章是自己的一個學(xué)習(xí)筆記,記錄了 Android 5.0 中 hwui 中的 RenderThread 的簡單工作流程阎肝。由于是學(xué)習(xí)筆記肆良,所以其中一些細節(jié)不會太詳細,我只是將大概的流程走一遍卧波,將其工作流標(biāo)注出來,下次遇到問題的時候就可以知道去哪里查庇茫。

下圖是我用 Systrace 抓取的一個應(yīng)用啟動的時候 RenderThread 的第一次 Draw 的 Trace 圖港粱,從這里面的順序來看 RenderThread 的流程。熟悉應(yīng)用啟動流程的話應(yīng)該知道旦签,只有當(dāng)?shù)谝淮?DrawFrame 完成之后查坪,整個應(yīng)用的界面才會顯示在手機上,在這之前宁炫,用戶看到的是應(yīng)用的 StartingWindow 的界面偿曙。

RenderThread Draw first frame

從Java層說起

應(yīng)用程序的每一幀是從接收到 VSYNC 信號開始進行計算和繪制的,這要從 Choreographer 這個類說起了,不過由于篇幅原因淋淀,我們直接看一幀的繪制調(diào)用關(guān)系鏈即可:

繪制關(guān)系鏈

Choreographer 的 drawFrame 會調(diào)用到 ViewRootImpl 的 performTraversals 方法遥昧,而 performTraversals 方法最終會調(diào)用到performDraw() 方法覆醇, performDraw 又會調(diào)用到 draw(boolean fullRedrawNeeded) 方法,這個 draw 方法是 ViewRootImpl 的私有方法炭臭,和我們熟知的那個draw并不是同一個方法

            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                mIsAnimating = false;
                boolean invalidateRoot = false;
                if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
                    mHardwareYOffset = yOffset;
                    mHardwareXOffset = xOffset;
                    mAttachInfo.mHardwareRenderer.invalidateRoot();
                }
                mResizeAlpha = resizeAlpha;

                dirty.setEmpty();

                mBlockResizeBuffer = false;
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
}

如果是走硬件繪制路線的話永脓,則會走這一條先,之后就會調(diào)用 mHardwareRenderer 的 draw 方法,這里的 mHardwareRenderer 指的是 ThreadedRenderer 鞋仍,其 Draw 函數(shù)如下:

    @Override
    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
        attachInfo.mIgnoreDirtyState = true;
        long frameTimeNanos = mChoreographer.getFrameTimeNanos();
        attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;

        long recordDuration = 0;
        if (mProfilingEnabled) {
            recordDuration = System.nanoTime();
        }

        updateRootDisplayList(view, callbacks);

        if (mProfilingEnabled) {
            recordDuration = System.nanoTime() - recordDuration;
        }

        attachInfo.mIgnoreDirtyState = false;

        // register animating rendernodes which started animating prior to renderer
        // creation, which is typical for animators started prior to first draw
        if (attachInfo.mPendingAnimatingRenderNodes != null) {
            final int count = attachInfo.mPendingAnimatingRenderNodes.size();
            for (int i = 0; i < count; i++) {
                registerAnimatingRenderNode(
                        attachInfo.mPendingAnimatingRenderNodes.get(i));
            }
            attachInfo.mPendingAnimatingRenderNodes.clear();
            // We don't need this anymore as subsequent calls to
            // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
            attachInfo.mPendingAnimatingRenderNodes = null;
        }

        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
                recordDuration, view.getResources().getDisplayMetrics().density);
        if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
            attachInfo.mViewRootImpl.invalidate();
        }
    }

這個函數(shù)里面的 updateRootDisplayList(view, callbacks) ;即 getDisplayList 操作常摧。接下來就是比較重要的一個操作:

        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
                recordDuration, view.getResources().getDisplayMetrics().density);

可以看出這是一個阻塞操作,等Native層完成后威创,拿到返回值后才會進行下一步的操作落午。

Native層

其Native代碼在android_view_ThreadedRenderer.cpp中,對應(yīng)的實現(xiàn)代碼如下:

static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density) {
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density);
}

RenderProxy的路徑位于frameworks/base/libs/hwui/renderthread/RenderProxy.cpp

int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
        float density) {
    mDrawFrameTask.setDensity(density);
    return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
}

其中 mDrawFrameTask 是一個 DrawFrameTask 對象肚豺,其路徑位于frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp溃斋,其中drawFrame代碼:

int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
    mSyncResult = kSync_OK;
    mFrameTimeNanos = frameTimeNanos;
    mRecordDurationNanos = recordDurationNanos;
    postAndWait();

    // Reset the single-frame data
    mFrameTimeNanos = 0;
    mRecordDurationNanos = 0;

    return mSyncResult;
}

其中 postAndWait() 的實現(xiàn)如下:

void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue(this);
    mSignal.wait(mLock);
}

就是將一個 DrawFrameTask 放入到了 mRenderThread 中,其中 queue 方法實現(xiàn)如下:

void RenderThread::queue(RenderTask* task) {
    AutoMutex _lock(mLock);
    mQueue.queue(task);
    if (mNextWakeup && task->mRunAt < mNextWakeup) {
        mNextWakeup = 0;
        mLooper->wake();
    }
}

其中 mQueue 是一個 TaskQueue 對象,其

void TaskQueue::queue(RenderTask* task) {
    // Since the RenderTask itself forms the linked list it is not allowed
    // to have the same task queued twice
    LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!");
    if (mTail) {
        // Fast path if we can just append
        if (mTail->mRunAt <= task->mRunAt) {
            mTail->mNext = task;
            mTail = task;
        } else {
            // Need to find the proper insertion point
            RenderTask* previous = 0;
            RenderTask* next = mHead;
            while (next && next->mRunAt <= task->mRunAt) {
                previous = next;
                next = next->mNext;
            }
            if (!previous) {
                task->mNext = mHead;
                mHead = task;
            } else {
                previous->mNext = task;
                if (next) {
                    task->mNext = next;
                } else {
                    mTail = task;
                }
            }
        }
    } else {
        mTail = mHead = task;
    }
}

接著看 RenderThread 之前的 queue 方法吸申,

void Looper::wake() {
    ssize_t nWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite != 1) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

wake 函數(shù)則更為簡單梗劫,僅僅向管道的寫端寫入一個字符“W”,這樣管道的讀端就會因為有數(shù)據(jù)可讀而從等待狀態(tài)中醒來截碴。

HWUI-RenderThread

接下來會到哪里去梳侨,我們首先要熟悉一下RenderThread,RenderThread是繼承自Thread的日丹,這個Thread是utils/Thread.h,RenderThread的初始化函數(shù)

RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
        , mNextWakeup(LLONG_MAX)
        , mDisplayEventReceiver(0)
        , mVsyncRequested(false)
        , mFrameCallbackTaskPending(false)
        , mFrameCallbackTask(0)
        , mRenderState(NULL)
        , mEglManager(NULL) {
    mFrameCallbackTask = new DispatchFrameCallbacks(this);
    mLooper = new Looper(false);
    run("RenderThread");
}

其 run 方法在 Thread 中有說明:

    // Start the thread in threadLoop() which needs to be implemented.
    virtual status_t    run(    const char* name = 0,
                                int32_t priority = PRIORITY_DEFAULT,
                                size_t stack = 0);

即啟動 threadLoop 函數(shù)走哺,我們來看 RenderThread 的 threadLoop 函數(shù),這個函數(shù)比較重要:

bool RenderThread::threadLoop() {
#if defined(HAVE_PTHREADS)
    setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
#endif
    initThreadLocals();

    int timeoutMillis = -1;
    for (;;) {
        int result = mLooper->pollOnce(timeoutMillis);
        LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
                "RenderThread Looper POLL_ERROR!");

        nsecs_t nextWakeup;
        // Process our queue, if we have anything
        while (RenderTask* task = nextTask(&nextWakeup)) {
            task->run();
            // task may have deleted itself, do not reference it again
        }
        if (nextWakeup == LLONG_MAX) {
            timeoutMillis = -1;
        } else {
            nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
            if (timeoutMillis < 0) {
                timeoutMillis = 0;
            }
        }

        if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
            drainDisplayEventQueue(true);
            mFrameCallbacks.insert(
                    mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
            mPendingRegistrationFrameCallbacks.clear();
            requestVsync();
        }
    }

    return false;
}

可以看到哲虾,一個 for 循環(huán)是一個無限循環(huán)丙躏,而其中 pollOnce 是一個阻塞函數(shù),直到我們上面調(diào)用了 mLooper->wake() 之后束凑,會繼續(xù)往下走彼哼,走到 while 循環(huán)中:

while (RenderTask* task = nextTask(&nextWakeup)) {
            task->run();
            // task may have deleted itself, do not reference it again
        }

會將 RenderTask 取出來執(zhí)行其 run 方法,經(jīng)過前面的流程我們知道這個 RenderTask 是一個 DrawFrameTask 湘今,其run方法如下:

void DrawFrameTask::run() {
    ATRACE_NAME("DrawFrame");

    mContext->profiler().setDensity(mDensity);
    mContext->profiler().startFrame(mRecordDurationNanos);

    bool canUnblockUiThread;
    bool canDrawThisFrame;
    {
        TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
        canUnblockUiThread = syncFrameState(info);
        canDrawThisFrame = info.out.canDrawThisFrame;
    }

    // Grab a copy of everything we need
    CanvasContext* context = mContext;

    // From this point on anything in "this" is *UNSAFE TO ACCESS*
    if (canUnblockUiThread) {
        unblockUiThread();
    }

    if (CC_LIKELY(canDrawThisFrame)) {
        context->draw();
    }

    if (!canUnblockUiThread) {
        unblockUiThread();
    }
}

RenderThread.DrawFrame

上面說到了 DrawFrameTask 的 run 方法,這里 run 方法中的執(zhí)行的方法即我們在最前面那張圖中所示的部分(即文章最前面那張圖)剪菱,下面的流程就是那張圖中的函數(shù)調(diào)用摩瞎,我們結(jié)合代碼和圖,一部分一部分來走整個 DrawFrame 的流程:

1. syncFrameState

第一個比較重要的函數(shù)是 syncFrameState 孝常,從函數(shù)名就可以知道旗们, syncFrameState 的作用就是同步 frame 信息,將 Java 層維護的 frame 信息同步到 RenderThread中构灸。

Main Thread 和Render Thread 都各自維護了一份應(yīng)用程序窗口視圖信息上渴。各自維護了一份應(yīng)用程序窗口視圖信息的目的,就是為了可以互不干擾,進而實現(xiàn)最大程度的并行稠氮。其中曹阔,Render Thread維護的應(yīng)用程序窗口視圖信息是來自于 Main Thread 的。因此隔披,當(dāng)Main Thread 維護的應(yīng)用程序窗口信息發(fā)生了變化時赃份,就需要同步到 Render Thread 去。

所以查看代碼就可以知道有兩個 RenderNode奢米,一個在 hwui 中抓韩,一個在 View 中。簡單來說鬓长,同步信息就是將 Java 層的 RenderNode 中的信息同步到 hwui 中的 RenderNode 中谒拴。 注意syncFrameState的返回值賦給了 canUnblockUiThread ,從名字可以看出這個 canUnblockUiThread 的作用是判斷是否喚醒 Main Thread 涉波,也就是說如果返回為 true 的話英上,會提前喚醒主線程來執(zhí)行其他的事情,而不用等到 draw 完成后再去喚醒 Main Thread怠蹂。 這也是 Android 5.0 和 Android 4.x 最大的區(qū)別了善延。

syncFrameState
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
    mContext->makeCurrent();
    Caches::getInstance().textureCache.resetMarkInUse();

    for (size_t i = 0; i < mLayers.size(); i++) {
        mContext->processLayerUpdate(mLayers[i].get());
    }
    mLayers.clear();
    mContext->prepareTree(info);

    if (info.out.hasAnimations) {
        if (info.out.requiresUiRedraw) {
            mSyncResult |= kSync_UIRedrawRequired;
        }
    }
    // If prepareTextures is false, we ran out of texture cache space
    return info.prepareTextures;
}

首先是makeCurrent,這里的mContext是一個CanvasContext對象城侧,其makeCurrent實現(xiàn)如下:

void CanvasContext::makeCurrent() {
    // In the meantime this matches the behavior of GLRenderer, so it is not a regression
    mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
}

mEglManager是一個EglManager對象易遣,其實現(xiàn)為:

bool EglManager::makeCurrent(EGLSurface surface) {
    if (isCurrent(surface)) return false;

    if (surface == EGL_NO_SURFACE) {
        // If we are setting EGL_NO_SURFACE we don't care about any of the potential
        // return errors, which would only happen if mEglDisplay had already been
        // destroyed in which case the current context is already NO_CONTEXT
        TIME_LOG("eglMakeCurrent", eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    } else {
        EGLBoolean success;
        TIME_LOG("eglMakeCurrent", success = eglMakeCurrent(mEglDisplay, surface, surface, mEglContext));
        if (!success) {
            LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
                (void*)surface, egl_error_str());
        }
    }
    mCurrentSurface = surface;
    return true;
}

這里會判斷mCurrentSurface == surface,如果成立嫌佑,則不用再初始化操作豆茫,如果是另外一個surface。屋摇,則會執(zhí)行eglMakeCurrent揩魂,來重新創(chuàng)建上下文。

makeCurrent之后炮温,會調(diào)用mContext->prepareTree(info)火脉,其實現(xiàn)如下:

void CanvasContext::prepareTree(TreeInfo& info) {
    mRenderThread.removeFrameCallback(this);

    info.damageAccumulator = &mDamageAccumulator;
    info.renderer = mCanvas;
    if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
        info.canvasContext = this;
    }
    mAnimationContext->startFrame(info.mode);
    mRootRenderNode->prepareTree(info);
    mAnimationContext->runRemainingAnimations(info);

    if (info.canvasContext) {
        freePrefetechedLayers();
    }

    int runningBehind = 0;
    // TODO: This query is moderately expensive, investigate adding some sort
    // of fast-path based off when we last called eglSwapBuffers() as well as
    // last vsync time. Or something.
    TIME_LOG("nativeWindowQuery", mNativeWindow->query(mNativeWindow.get(),
            NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind));
    info.out.canDrawThisFrame = !runningBehind;

    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
        if (!info.out.requiresUiRedraw) {
            // If animationsNeedsRedraw is set don't bother posting for an RT anim
            // as we will just end up fighting the UI thread.
            mRenderThread.postFrameCallback(this);
        }
    }
}

其中 mRootRenderNode->prepareTree(info) 又是最重要的∑馄。回到Java層倦挂,我們知道 ThreadedRenderer 在初始化時,初始化了一個指針

long rootNodePtr = nCreateRootRenderNode();

這個RootRenderNode也就是一個根Node担巩,

mRootNode = RenderNode.adopt(rootNodePtr);

然后會創(chuàng)建一個 mNativeProxy 指針方援,在 Native 層初始化一個 RenderProxy 對象,將 rootNodePtr 傳給 RenderProxy 對象涛癌,這樣在 RenderProxy 我們就可以得到這個對象的指針了犯戏。其中 CanvasContext 也是在 RenderProxy 對象初始化的時候被初始化的送火,初始化的時候?qū)?rootNodePtr 傳給了 CanvasContext 對象。

我們之前提到 ThreadedRenderer 的 draw 方法中首先會調(diào)用updateRootDisplayList先匪,即我們熟悉的 getDisplayList 种吸。這個方法中,其實也分為兩個步驟胚鸯,第一個步驟是 updateViewTreeDisplayList骨稿,第二個步驟是將根 Node 加入到 DrawOp 中:

canvas.insertReorderBarrier();
canvas.drawRenderNode(view.getDisplayList());
canvas.insertInorderBarrier();

其最終實現(xiàn)在

status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
    LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");

    // dirty is an out parameter and should not be recorded,
    // it matters only when replaying the display list
    DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform());
    addRenderNodeOp(op);

    return DrawGlInfo::kStatusDone;
}

再回到我們之前的 CanvasContext.prepareTree 中提到的 mRootRenderNode->prepareTree(info),這時候這里的 mRootRenderNode 就是 CanvasContext 初始化是傳進來的姜钳。

其實現(xiàn)在 RenderNode.cpp 中:

void RenderNode::prepareTree(TreeInfo& info) {
    prepareTreeImpl(info);
}

void RenderNode::prepareTreeImpl(TreeInfo& info) {
    TT_START_MARK(getName());
    info.damageAccumulator->pushTransform(this);

    if (info.mode == TreeInfo::MODE_FULL) {
        pushStagingPropertiesChanges(info); //同步當(dāng)前正在處理的Render Node的Property
    }
    uint32_t animatorDirtyMask = 0;
    if (CC_LIKELY(info.runAnimations)) {
        animatorDirtyMask = mAnimatorManager.animate(info);//執(zhí)行動畫相關(guān)的操作
    }
    prepareLayer(info, animatorDirtyMask);
    if (info.mode == TreeInfo::MODE_FULL) {
        pushStagingDisplayListChanges(info);  //同步當(dāng)前正在處理的Render Node的Display List
    }
    prepareSubTree(info, mDisplayListData); //同步當(dāng)前正在處理的Render Node的Display List引用的Bitmap坦冠,以及當(dāng)前正在處理的Render Node的子Render Node的Display List等信息
    pushLayerUpdate(info); //檢查當(dāng)前正在處理的Render Node是否設(shè)置了Layer。如果設(shè)置了的話哥桥,就對這些Layer進行處理

    info.damageAccumulator->popTransform();
    TT_END_MARK();
}

這里所涉及到的進一步的具體操作大家可以自行去看代碼辙浑。

2. draw

Draw

執(zhí)行完syncFrameState之后,接下來就是執(zhí)行draw

    if (CC_LIKELY(canDrawThisFrame)) {
        context->draw();
    }

CanvasContext的draw函數(shù)是一個核心函數(shù)拟糕,其位置在 frameworks/base/libs/hwui/OpenGLRenderer.cpp 判呕,其實現(xiàn)如下:

void CanvasContext::draw() {
    profiler().markPlaybackStart();

    SkRect dirty;
    mDamageAccumulator.finish(&dirty);

    ......

    status_t status;
    if (!dirty.isEmpty()) {
        status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
                dirty.fRight, dirty.fBottom, mOpaque);
    } else {
        status = mCanvas->prepare(mOpaque);
    }

    Rect outBounds;
    status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);

    profiler().draw(mCanvas);

    mCanvas->finish();

    profiler().markPlaybackEnd();

    if (status & DrawGlInfo::kStatusDrew) {
        swapBuffers();
    }

    profiler().finishFrame();

    /// M: enable to get overdraw count
    if (CC_UNLIKELY(g_HWUI_debug_overdraw)) {
        if (!mDebugOverdrawLayer) {
            mDebugOverdrawLayer = LayerRenderer::createRenderLayer(mRenderThread.renderState(),
                mCanvas->getWidth(), mCanvas->getHeight());
        } else if (mDebugOverdrawLayer->layer.getWidth() != mCanvas->getWidth() ||
                   mDebugOverdrawLayer->layer.getHeight() != mCanvas->getHeight()) {
            if (!LayerRenderer::resizeLayer(mDebugOverdrawLayer, mCanvas->getWidth(), mCanvas->getHeight())) {
                LayerRenderer::destroyLayer(mDebugOverdrawLayer);
                mDebugOverdrawLayer = NULL;
            }
        }

    ......
}

2.1 eglBeginFrame

首先來看eglBeginFrame的實現(xiàn)

void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
    makeCurrent(surface);
    if (width) {
        eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
    }
    if (height) {
        eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
    }
    eglBeginFrame(mEglDisplay, surface);
}

makeCurrent是用來管理上下文,eglBeginFrame主要是校驗參數(shù)的合法性送滞。

2.2 prepareDirty

    status_t status;
    if (!dirty.isEmpty()) {
        status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
                dirty.fRight, dirty.fBottom, mOpaque);
    } else {
        status = mCanvas->prepare(mOpaque);
    }

這里的mCanvas是一個OpenGLRenderer對象侠草,其prepareDirty實現(xiàn)

//TODO:增加函數(shù)功能描述
status_t OpenGLRenderer::prepareDirty(float left, float top,
        float right, float bottom, bool opaque) {
    setupFrameState(left, top, right, bottom, opaque);

    // Layer renderers will start the frame immediately
    // The framebuffer renderer will first defer the display list
    // for each layer and wait until the first drawing command
    // to start the frame
    if (currentSnapshot()->fbo == 0) {
        syncState();
        updateLayers();
    } else {
        return startFrame();
    }

    return DrawGlInfo::kStatusDone;
}

2.3 drawRenderNode

Rect outBounds;
status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);

接下來就是調(diào)用OpenGLRenderer的drawRenderNode方法進行繪制

status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
    status_t status;
    // All the usual checks and setup operations (quickReject, setupDraw, etc.)
    // will be performed by the display list itself
    if (renderNode && renderNode->isRenderable()) {
        // compute 3d ordering
        renderNode->computeOrdering();
        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { //判斷是否不重排序
            status = startFrame();
            ReplayStateStruct replayStruct(*this, dirty, replayFlags);
            renderNode->replay(replayStruct, 0);
            return status | replayStruct.mDrawGlStatus;
        }

        // 需要重新排序
        bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
        DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw);
        DeferStateStruct deferStruct(deferredList, *this, replayFlags);
        renderNode->defer(deferStruct, 0); //遞歸進行重排操作

        flushLayers(); // 首先執(zhí)行設(shè)置了 Layer 的子 Render Node 的繪制命令,以便得到一個對應(yīng)的FBO
        status = startFrame(); //執(zhí)行一些諸如清理顏色繪沖區(qū)等基本操作
        status = deferredList.flush(*this, dirty) | status;
        return status;
    }

    // Even if there is no drawing command(Ex: invisible),
    // it still needs startFrame to clear buffer and start tiling.
    return startFrame();
}

這里的 renderNode 是一個 Root Render Node犁嗅,

可以看到边涕,到了這里雖然只是開始,但是其實已經(jīng)結(jié)束了褂微,這個函數(shù)里面最重要的幾步:

renderNode->defer(deferStruct, 0); //進行重排序

flushLayers(); 首先執(zhí)行設(shè)置了 Layer 的子 Render Node 的繪制命令功蜓,以便得到一個對應(yīng)的FBO

status = deferredList.flush(*this, dirty) | status;   //對deferredList中的繪制命令進行真正的繪制操作

這幾個是渲染部分真正的核心部分,其中的代碼細節(jié)需要自己去研究宠蚂。老羅在這部分講的很細式撼,有空可以去看看他的文章Android應(yīng)用程序UI硬件加速渲染的Display List渲染過程分析.

2.4 swapBuffers

    if (status & DrawGlInfo::kStatusDrew) {
        swapBuffers();
    }

其核心就是調(diào)用EGL的 eglSwapBuffers(mEglDisplay, surface), duration)函數(shù)。

2.5 FinishFrame

    profiler().finishFrame();

主要是記錄時間信息求厕。

總結(jié)

鑒于我比較懶著隆,而且總結(jié)能力不如老羅,就直接把他的總結(jié)貼過來了呀癣。
RenderThread的總的流程如下:

  1. 將Main Thread維護的Display List同步到Render Thread維護的Display List去旅东。這個同步過程由Render Thread執(zhí)行,但是Main Thread會被阻塞住十艾。
  1. 如果能夠完全地將Main Thread維護的Display List同步到Render Thread維護的Display List去,那么Main Thread就會被喚醒腾节,此后Main Thread和Render Thread就互不干擾忘嫉,各自操作各自內(nèi)部維護的Display List荤牍;否則的話,Main Thread就會繼續(xù)阻塞庆冕,直到Render Thread完成應(yīng)用程序窗口當(dāng)前幀的渲染為止康吵。
  1. Render Thread在渲染應(yīng)用程序窗口的Root Render Node的Display List之前,首先將那些設(shè)置了Layer的子Render Node的Display List渲染在各自的一個FBO上访递,接下來再一起將這些FBO以及那些沒有設(shè)置Layer的子Render Node的Display List一起渲染在Frame Buffer之上晦嵌,也就是渲染在從Surface Flinger請求回來的一個圖形緩沖區(qū)上。這個圖形緩沖區(qū)最終會被提交給Surface Flinger合并以及顯示在屏幕上拷姿。

第2步能夠完全將Main Thread維護的Display List同步到Render Thread維護的Display List去很關(guān)鍵惭载,它使得Main Thread和Render Thread可以并行執(zhí)行,這意味著Render Thread在渲染應(yīng)用程序窗口當(dāng)前幀的Display List的同時响巢,Main Thread可以去準(zhǔn)備應(yīng)用程序窗口下一幀的Display List描滔,這樣就使得應(yīng)用程序窗口的UI更流暢。

注意最后一段踪古,在 Android 4.x 時代含长,沒有RenderThread的時代,只有 Main Thread 伏穆,也就是說 必須要等到 Draw 完成后拘泞,才會去準(zhǔn)備下一幀的數(shù)據(jù),如下圖:

Paste_Image.png

Android5.0 之后枕扫,如老羅所說陪腌,有兩種情況,

Main Thread 和 Render Thread
Render Thread 提前喚醒了 Main Thread

可以看到第二張圖中铡原,Render Thread 并沒有繪制完成偷厦,但是由于其提前喚醒了 Main Thread ,所以 Main Thread 在下一個Vsync信號到來的時候燕刻,響應(yīng)了Vsync事件只泼,開始準(zhǔn)備下一幀。
此時雖然由于第一幀繪制時間過長卵洗,導(dǎo)致掉了一幀请唱,但是第二幀沒有收到任何影響。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末过蹂,一起剝皮案震驚了整個濱河市十绑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酷勺,老刑警劉巖本橙,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脆诉,居然都是意外死亡甚亭,警方通過查閱死者的電腦和手機贷币,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亏狰,“玉大人役纹,你說我怎么就攤上這事∠就伲” “怎么了促脉?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長策州。 經(jīng)常有香客問我瘸味,道長,這世上最難降的妖魔是什么抽活? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任硫戈,我火速辦了婚禮,結(jié)果婚禮上下硕,老公的妹妹穿的比我還像新娘丁逝。我一直安慰自己,他們只是感情好梭姓,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布霜幼。 她就那樣靜靜地躺著,像睡著了一般誉尖。 火紅的嫁衣襯著肌膚如雪罪既。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天铡恕,我揣著相機與錄音琢感,去河邊找鬼。 笑死探熔,一個胖子當(dāng)著我的面吹牛驹针,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诀艰,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼柬甥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了其垄?” 一聲冷哼從身側(cè)響起苛蒲,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绿满,沒想到半個月后臂外,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年漏健,在試婚紗的時候發(fā)現(xiàn)自己被綠了辜膝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡漾肮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茎毁,到底是詐尸還是另有隱情克懊,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布七蜘,位于F島的核電站谭溉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏橡卤。R本人自食惡果不足惜扮念,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碧库。 院中可真熱鬧柜与,春花似錦、人聲如沸嵌灰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沽瞭。三九已至迁匠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驹溃,已是汗流浹背城丧。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留豌鹤,地道東北人亡哄。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像傍药,于是被迫代替她去往敵國和親磺平。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容