前言
本篇文章是自己的一個學(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 的界面偿曙。
從Java層說起
應(yīng)用程序的每一幀是從接收到 VSYNC 信號開始進行計算和繪制的,這要從 Choreographer 這個類說起了,不過由于篇幅原因淋淀,我們直接看一幀的繪制調(diào)用關(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ū)別了善延。
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
執(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的總的流程如下:
- 將Main Thread維護的Display List同步到Render Thread維護的Display List去旅东。這個同步過程由Render Thread執(zhí)行,但是Main Thread會被阻塞住十艾。
- 如果能夠完全地將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)前幀的渲染為止康吵。
- 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ù),如下圖:
Android5.0 之后枕扫,如老羅所說陪腌,有兩種情況,
可以看到第二張圖中铡原,Render Thread 并沒有繪制完成偷厦,但是由于其提前喚醒了 Main Thread ,所以 Main Thread 在下一個Vsync信號到來的時候燕刻,響應(yīng)了Vsync事件只泼,開始準(zhǔn)備下一幀。
此時雖然由于第一幀繪制時間過長卵洗,導(dǎo)致掉了一幀请唱,但是第二幀沒有收到任何影響。