前言
本篇文章是自己的一個學習筆記,記錄了 Android 5.0 中 hwui 中的 RenderThread 的簡單工作流程。由于是學習筆記,所以其中一些細節不會太詳細,我只是將大概的流程走一遍,將其工作流標注出來,下次遇到問題的時候就可以知道去哪里查。
下圖是我用 Systrace 抓取的一個應用啟動的時候 RenderThread 的第一次 Draw 的 Trace 圖,從這里面的順序來看 RenderThread 的流程。熟悉應用啟動流程的話應該知道,只有當第一次 DrawFrame 完成之后,整個應用的界面才會顯示在手機上,在這之前,用戶看到的是應用的 StartingWindow 的界面。
從Java層說起
應用程序的每一幀是從接收到 VSYNC 信號開始進行計算和繪制的,這要從 Choreographer 這個類說起了,不過由于篇幅原因,我們直接看一幀的繪制調用關系鏈即可:
Choreographer 的 drawFrame 會調用到 ViewRootImpl 的 performTraversals 方法,而 performTraversals 方法最終會調用到performDraw() 方法, performDraw 又會調用到 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);
}
如果是走硬件繪制路線的話,則會走這一條先,之后就會調用 mHardwareRenderer 的 draw 方法,這里的 mHardwareRenderer 指的是 ThreadedRenderer ,其 Draw 函數如下:
@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();
}
}
這個函數里面的 updateRootDisplayList(view, callbacks) ;即 getDisplayList 操作。接下來就是比較重要的一個操作:
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
recordDuration, view.getResources().getDisplayMetrics().density);
可以看出這是一個阻塞操作,等Native層完成后,拿到返回值后才會進行下一步的操作。
Native層
其Native代碼在android_view_ThreadedRenderer.cpp中,對應的實現代碼如下:
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() 的實現如下:
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue(this);
mSignal.wait(mLock);
}
就是將一個 DrawFrameTask 放入到了 mRenderThread 中,其中 queue 方法實現如下:
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 函數則更為簡單,僅僅向管道的寫端寫入一個字符“W”,這樣管道的讀端就會因為有數據可讀而從等待狀態中醒來。
HWUI-RenderThread
接下來會到哪里去,我們首先要熟悉一下RenderThread,RenderThread是繼承自Thread的,這個Thread是utils/Thread.h,RenderThread的初始化函數
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 函數,我們來看 RenderThread 的 threadLoop 函數,這個函數比較重要:
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 循環是一個無限循環,而其中 pollOnce 是一個阻塞函數,直到我們上面調用了 mLooper->wake() 之后,會繼續往下走,走到 while 循環中:
while (RenderTask* task = nextTask(&nextWakeup)) {
task->run();
// task may have deleted itself, do not reference it again
}
會將 RenderTask 取出來執行其 run 方法,經過前面的流程我們知道這個 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 方法中的執行的方法即我們在最前面那張圖中所示的部分(即文章最前面那張圖),下面的流程就是那張圖中的函數調用,我們結合代碼和圖,一部分一部分來走整個 DrawFrame 的流程:
1. syncFrameState
第一個比較重要的函數是 syncFrameState ,從函數名就可以知道, syncFrameState 的作用就是同步 frame 信息,將 Java 層維護的 frame 信息同步到 RenderThread中。
Main Thread 和Render Thread 都各自維護了一份應用程序窗口視圖信息。各自維護了一份應用程序窗口視圖信息的目的,就是為了可以互不干擾,進而實現最大程度的并行。其中,Render Thread維護的應用程序窗口視圖信息是來自于 Main Thread 的。因此,當Main Thread 維護的應用程序窗口信息發生了變化時,就需要同步到 Render Thread 去。
所以查看代碼就可以知道有兩個 RenderNode,一個在 hwui 中,一個在 View 中。簡單來說,同步信息就是將 Java 層的 RenderNode 中的信息同步到 hwui 中的 RenderNode 中。 注意syncFrameState的返回值賦給了 canUnblockUiThread ,從名字可以看出這個 canUnblockUiThread 的作用是判斷是否喚醒 Main Thread ,也就是說如果返回為 true 的話,會提前喚醒主線程來執行其他的事情,而不用等到 draw 完成后再去喚醒 Main Thread。 這也是 Android 5.0 和 Android 4.x 最大的區別了。
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實現如下:
void CanvasContext::makeCurrent() {
// In the meantime this matches the behavior of GLRenderer, so it is not a regression
mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
}
mEglManager是一個EglManager對象,其實現為:
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。,則會執行eglMakeCurrent,來重新創建上下文。
makeCurrent之后,會調用mContext->prepareTree(info),其實現如下:
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);
然后會創建一個 mNativeProxy 指針,在 Native 層初始化一個 RenderProxy 對象,將 rootNodePtr 傳給 RenderProxy 對象,這樣在 RenderProxy 我們就可以得到這個對象的指針了。其中 CanvasContext 也是在 RenderProxy 對象初始化的時候被初始化的,初始化的時候將 rootNodePtr 傳給了 CanvasContext 對象。
我們之前提到 ThreadedRenderer 的 draw 方法中首先會調用updateRootDisplayList,即我們熟悉的 getDisplayList 。這個方法中,其實也分為兩個步驟,第一個步驟是 updateViewTreeDisplayList,第二個步驟是將根 Node 加入到 DrawOp 中:
canvas.insertReorderBarrier();
canvas.drawRenderNode(view.getDisplayList());
canvas.insertInorderBarrier();
其最終實現在
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 初始化是傳進來的。
其實現在 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); //同步當前正在處理的Render Node的Property
}
uint32_t animatorDirtyMask = 0;
if (CC_LIKELY(info.runAnimations)) {
animatorDirtyMask = mAnimatorManager.animate(info);//執行動畫相關的操作
}
prepareLayer(info, animatorDirtyMask);
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingDisplayListChanges(info); //同步當前正在處理的Render Node的Display List
}
prepareSubTree(info, mDisplayListData); //同步當前正在處理的Render Node的Display List引用的Bitmap,以及當前正在處理的Render Node的子Render Node的Display List等信息
pushLayerUpdate(info); //檢查當前正在處理的Render Node是否設置了Layer。如果設置了的話,就對這些Layer進行處理
info.damageAccumulator->popTransform();
TT_END_MARK();
}
這里所涉及到的進一步的具體操作大家可以自行去看代碼。
2. draw
執行完syncFrameState之后,接下來就是執行draw
if (CC_LIKELY(canDrawThisFrame)) {
context->draw();
}
CanvasContext的draw函數是一個核心函數,其位置在 frameworks/base/libs/hwui/OpenGLRenderer.cpp ,其實現如下:
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的實現
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主要是校驗參數的合法性。
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實現
//TODO:增加函數功能描述
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);
接下來就是調用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(); // 首先執行設置了 Layer 的子 Render Node 的繪制命令,以便得到一個對應的FBO
status = startFrame(); //執行一些諸如清理顏色繪沖區等基本操作
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,
可以看到,到了這里雖然只是開始,但是其實已經結束了,這個函數里面最重要的幾步:
renderNode->defer(deferStruct, 0); //進行重排序
flushLayers(); 首先執行設置了 Layer 的子 Render Node 的繪制命令,以便得到一個對應的FBO
status = deferredList.flush(*this, dirty) | status; //對deferredList中的繪制命令進行真正的繪制操作
這幾個是渲染部分真正的核心部分,其中的代碼細節需要自己去研究。老羅在這部分講的很細,有空可以去看看他的文章Android應用程序UI硬件加速渲染的Display List渲染過程分析.
2.4 swapBuffers
if (status & DrawGlInfo::kStatusDrew) {
swapBuffers();
}
其核心就是調用EGL的 eglSwapBuffers(mEglDisplay, surface), duration)函數。
2.5 FinishFrame
profiler().finishFrame();
主要是記錄時間信息。
總結
鑒于我比較懶,而且總結能力不如老羅,就直接把他的總結貼過來了。
RenderThread的總的流程如下:
- 將Main Thread維護的Display List同步到Render Thread維護的Display List去。這個同步過程由Render Thread執行,但是Main Thread會被阻塞住。
- 如果能夠完全地將Main Thread維護的Display List同步到Render Thread維護的Display List去,那么Main Thread就會被喚醒,此后Main Thread和Render Thread就互不干擾,各自操作各自內部維護的Display List;否則的話,Main Thread就會繼續阻塞,直到Render Thread完成應用程序窗口當前幀的渲染為止。
- Render Thread在渲染應用程序窗口的Root Render Node的Display List之前,首先將那些設置了Layer的子Render Node的Display List渲染在各自的一個FBO上,接下來再一起將這些FBO以及那些沒有設置Layer的子Render Node的Display List一起渲染在Frame Buffer之上,也就是渲染在從Surface Flinger請求回來的一個圖形緩沖區上。這個圖形緩沖區最終會被提交給Surface Flinger合并以及顯示在屏幕上。
第2步能夠完全將Main Thread維護的Display List同步到Render Thread維護的Display List去很關鍵,它使得Main Thread和Render Thread可以并行執行,這意味著Render Thread在渲染應用程序窗口當前幀的Display List的同時,Main Thread可以去準備應用程序窗口下一幀的Display List,這樣就使得應用程序窗口的UI更流暢。
注意最后一段,在 Android 4.x 時代,沒有RenderThread的時代,只有 Main Thread ,也就是說 必須要等到 Draw 完成后,才會去準備下一幀的數據,如下圖:
Android5.0 之后,如老羅所說,有兩種情況,
可以看到第二張圖中,Render Thread 并沒有繪制完成,但是由于其提前喚醒了 Main Thread ,所以 Main Thread 在下一個Vsync信號到來的時候,響應了Vsync事件,開始準備下一幀。
此時雖然由于第一幀繪制時間過長,導致掉了一幀,但是第二幀沒有收到任何影響。