Android5.0中 hwui 中 RenderThread 工作流程

前言

本篇文章是自己的一個學(xué)習(xí)筆記,記錄了 Android 5.0 中 hwui 中的 RenderThread 的簡單工作流程。由于是學(xué)習(xí)筆記,所以其中一些細(xì)節(jié)不會太詳細(xì),我只是將大概的流程走一遍,將其工作流標(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 信號開始進(jìn)行計算和繪制的,這要從 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層完成后,拿到返回值后才會進(jìn)行下一步的操作。

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)用程序窗口視圖信息的目的,就是為了可以互不干擾,進(jìn)而實現(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) 又是最重要的?;氐絁ava層,我們知道 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 初始化是傳進(jìn)來的。

其實現(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進(jìn)行處理

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

這里所涉及到的進(jìn)一步的具體操作大家可以自行去看代碼。

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方法進(jìn)行繪制

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); //遞歸進(jìn)行重排操作

        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); //進(jìn)行重排序

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

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

這幾個是渲染部分真正的核心部分,其中的代碼細(xì)節(jié)需要自己去研究。老羅在這部分講的很細(xì),有空可以去看看他的文章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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容