喚醒渲染線程
Java層,ThreadedRenderer的draw方法俊鱼,首先記錄樹形結(jié)構每個視圖節(jié)點繪制操作,在底層DisplayListData保存,然后JNI的#nSyncAndDrawFrame方法實現(xiàn)同步忽妒。
根據(jù)指針找到底層RenderProxy代理對象。RenderProxy的#syncAndDrawFrame方法兼贸。
int RenderProxy::syncAndDrawFrame() {
return mDrawFrameTask.drawFrame();
}
DrawFrameTask任務段直,繪制幀
int DrawFrameTask::drawFrame() {
mSyncResult = kSync_OK;
mSyncQueued = systemTime(CLOCK_MONOTONIC);
postAndWait();
return mSyncResult;
}
postAndWait方法,讓主線程等待溶诞,通過Singal的#wait方法坷牛,在幀繪制任務中,會調(diào)用unblockUiThread方法很澄,喚醒主線程。
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue(this);
mSignal.wait(mLock);
}
RenderThread繼承Thread颜及,單例甩苛,內(nèi)部有一個任務隊列TaskQueue,queue方法俏站,將該幀繪制任務DrawFrameTask加入隊列讯蒲。
void RenderThread::queue(RenderTask* task) {
AutoMutex _lock(mLock);
mQueue.queue(task);
if (mNextWakeup && task->mRunAt < mNextWakeup) {
mNextWakeup = 0;
mLooper->wake();
}
}
幀繪制任務DrawFrameTask,繼承RenderTask肄扎,插入隊列TaskQueue墨林,判斷下次喚醒的時間,喚醒渲染線程犯祠。渲染線程通過Looper機制休眠旭等。
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
... {
mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
run("RenderThread");
}
RenderThread構造方法,創(chuàng)建一個Looper衡载,線程啟動搔耕,threadLoop循環(huán)。
bool RenderThread::threadLoop() {
setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
initThreadLocals();
int timeoutMillis = -1;
for (;;) {//循環(huán)
int result = mLooper->pollOnce(timeoutMillis);
nsecs_t nextWakeup;
while (RenderTask* task = nextTask(&nextWakeup)) {
task->run();//喚醒后痰娱,隊列順序執(zhí)行
}
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;
}
}
...
}
return false;
}
渲染線程在Looper的#pollOnce方法處無限期休眠弃榨,主線程向隊列插入幀渲染任務時,Looper的#wake方法喚醒渲染線程梨睁,隊列的RenderTask任務依次執(zhí)行鲸睛。
渲染任務
渲染線程,兩個功能坡贺。
1官辈,同步幀狀態(tài)箱舞,syncFrameState方法。
2钧萍,繪制褐缠,draw方法。
void DrawFrameTask::run() {
bool canUnblockUiThread;
bool canDrawThisFrame;
{
TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
//任務內(nèi)部的CanvasContext
CanvasContext* context = mContext;
if (canUnblockUiThread) {//喚醒主線程
unblockUiThread();
}
if (CC_LIKELY(canDrawThisFrame)) {
context->draw();
}
if (!canUnblockUiThread) {
unblockUiThread();
}
}
一次硬件渲染任務整流程圖风瘦。
同步幀狀態(tài)
一個任務队魏,就是一幀數(shù)據(jù),syncFrameState方法万搔,同步幀狀態(tài)和數(shù)據(jù)胡桨。
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
mRenderThread->timeLord().vsyncReceived(vsync);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse(mContext);
//第一步,遍歷DeferredLayerUpdater數(shù)組Vector
//觸發(fā)CanvasContext的processLayerUpdate方法瞬雹。
for (size_t i = 0; i < mLayers.size(); i++) {
mContext->processLayerUpdate(mLayers[i].get());
}
//每次push結(jié)束后清理數(shù)組
mLayers.clear();
//第二步
mContext->prepareTree(info, mFrameInfo, mSyncQueued);
if (info.out.hasAnimations) {
if (info.out.requiresUiRedraw) {
mSyncResult |= kSync_UIRedrawRequired;
}
}
//如果返回的是false昧谊,說明我們耗盡了textureCache的空間
return info.prepareTextures;
}
Layers數(shù)組分析
分析一下什么樣的視圖會將Layer加入DrawFrameTask的Layers數(shù)組?
DrawFrameTask的#pushLayerUpdate方法酗捌,負責向數(shù)組添加元素呢诬,追溯到Java層。ThreadedRenderer的#pushLayerUpdate方法胖缤。
@Override
void pushLayerUpdate(HardwareLayer layer) {
nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}
通過JNI#方法尚镰,觸發(fā)RenderProxy的#pushLayerUpdate方法。
void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
mDrawFrameTask.pushLayerUpdate(layer);
}
因此哪廓,由Java層ThreadedRenderer的pushLayerUpdate方法發(fā)起狗唉,向Layers數(shù)組添加元素,是DeferredLayerUpdater類型的Layer涡真。那么分俯,這是什么樣的視圖呢?
下面是兩個HardwareLayer#方法哆料。
public void setSurfaceTexture(SurfaceTexture surface) {
nSetSurfaceTexture(mFinalizer.get(), surface, false);
mRenderer.pushLayerUpdate(this);//mRenderer即ThreadedRenderer缸剪。
}
public void updateSurfaceTexture() {
nUpdateSurfaceTexture(mFinalizer.get());
mRenderer.pushLayerUpdate(this);
}
都觸發(fā)ThreadedRenderer的#pushLayerUpdate方法,TextureView視圖有一個HardwareLayer东亦,對應底層DeferredLayerUpdater橄登,封裝真正的Layer。
HardwareLayer結(jié)構圖讥此。
TextureView視圖繪制時拢锹,利用ThreadedRenderer將HardwareLayer底層DeferredLayerUpdater加入DrawFrameTask的Layer數(shù)組。遍歷數(shù)組針對TextureView視圖萄喳。對于普通視圖卒稳,數(shù)組是空的。
針對TextureView視圖處理
遍歷每一個DeferredLayerUpdater他巨,CanvasContext#processLayerUpdate方法充坑。
void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
bool success = layerUpdater->apply();
if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
//mCanvas是OpenGLRenderer减江。
mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
}
}
將deferredUpdateScheduled標志的Layer加入OpenGLRenderer的Layer數(shù)組。
此標志從哪里來呢捻爷?TextureView視圖的Layer有此標志嗎辈灼?
View視圖有三種LayerType。
LAYER_TYPE_NONE也榄,
LAYER_TYPE_SOFTWARE巡莹,
LAYER_TYPE_HARDWARE。
底層對應None = 0甜紫,Software = 1降宅,RenderLayer = 2。
View的#setLayerType方法設置LayerType囚霸。
TextureView忽略LayerType類型腰根,默認是LAYER_TYPE_HARDWARE。
從源碼中可知拓型,Layer#updateDeferred方法設置deferredUpdateScheduled標志额嘿,Layer渲染后重置。此方法僅在RenderNode#pushLayerUpdate中調(diào)用劣挫,由RenderNode內(nèi)部Layer觸發(fā)岩睁。
LayerType是RenderLayer。
因此:上層專門設置LAYER_TYPE_HARDWARE的視圖才會在RenderNode內(nèi)部創(chuàng)建Layer揣云,并有此標志。目前看來冰啃,TextureView視圖底層Layer沒有deferredUpdateScheduled標志邓夕,不會被加入OpenGLRenderer的Layer數(shù)組。
總結(jié)
第一步阎毅,處理和TextureView視圖相關焚刚,底層Layer,普通視圖無HardwareLayer扇调,因此矿咕,DrawFrameTask數(shù)組是空。
第二步 準備ViewTree的數(shù)據(jù)
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
int64_t syncQueued) {
mRenderThread.removeFrameCallback(this);
//當前幀F(xiàn)rameInfo無SkippedFrame標志
//說明之前的幀沒有被跳過狼钮,mCurrentFrameInfo指向緩沖區(qū)新節(jié)點碳柱。
if (!wasSkipped(mCurrentFrameInfo)) {
mCurrentFrameInfo = &mFrames.next();//RingBuffer<FrameInfo, 120>循環(huán)緩沖區(qū)
}
//若之前的幀被跳過,繼續(xù)用當前指向的節(jié)點進行初始化熬芜。
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
mCurrentFrameInfo->markSyncStart();
//初始化info信息
info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas;
info.canvasContext = this;
mAnimationContext->startFrame(info.mode);
//底層根節(jié)點RenderNode準備莲镣,樹形結(jié)構遍歷
mRootRenderNode->prepareTree(info);
mAnimationContext->runRemainingAnimations(info);
//不存在mNativeWindow退出,無法繪制該幀canDrawThisFrame設false
int runningBehind = 0;
mNativeWindow->query(mNativeWindow.get(),
NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
info.out.canDrawThisFrame = !runningBehind;
if (!info.out.canDrawThisFrame) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
}
if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
if (!info.out.requiresUiRedraw) {
mRenderThread.postFrameCallback(this);
}
}
}
CanvasContext的底層根節(jié)點RootRenderNode涎拉,在CanvasContext構造方法初始化瑞侮。在上層ThreadedRenderer保存指針的圆,RootRenderNode繼承RenderNode。
void RenderNode::prepareTree(TreeInfo& info) {
bool functorsNeedLayer = Properties::debugOverdraw;
prepareTreeImpl(info, functorsNeedLayer);
}
從根節(jié)點開始半火,遞歸遍歷越妈。
void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
info.damageAccumulator->pushTransform(this);
//還有一種模式MODE_RT_ONLY
if (info.mode == TreeInfo::MODE_FULL) {
//屬性改變,賦值給mProperties钮糖。
pushStagingPropertiesChanges(info);
}
bool willHaveFunctor = false;
if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayListData) {
willHaveFunctor = !mStagingDisplayListData->functors.isEmpty();
} else if (mDisplayListData) {
willHaveFunctor = !mDisplayListData->functors.isEmpty();
}
bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
willHaveFunctor, functorsNeedLayer);
prepareLayer(info, animatorDirtyMask);
if (info.mode == TreeInfo::MODE_FULL) {
//賦值給mDisplayListData梅掠。
pushStagingDisplayListChanges(info);
}
prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
pushLayerUpdate(info);
info.damageAccumulator->popTransform();
}
prepareTree準備樹形結(jié)構的流程如下圖所示。
prepareSubTree方法,遞歸遍歷子節(jié)點挠蛉。
pushLayerUpdate方法,RenderNode節(jié)點的內(nèi)部Layer處理谴古。
void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer,
DisplayListData* subtree) {
//該節(jié)點的DisplayListData存在
if (subtree) {
//獲取TextureCahe對象
TextureCache& cache = Caches::getInstance().textureCache;
//數(shù)據(jù)中Functor集合
info.out.hasFunctors |= subtree->functors.size();
for (size_t i = 0; info.prepareTextures && i <
subtree->bitmapResources.size(); i++) {
//若有一個texture失敗,是0稠歉,賦值prepareTextures,不再遍歷怒炸。
info.prepareTextures = cache.prefetchAndMarkInUse(
info.canvasContext, subtree->bitmapResources[i]);
}
for (size_t i = 0; i < subtree->children().size(); i++) {
DrawRenderNodeOp* op = subtree->children()[i];
RenderNode* childNode = op->mRenderNode;
info.damageAccumulator->pushTransform(&op->mTransformFromParent);
bool childFunctorsNeedLayer = functorsNeedLayer
|| op->mRecordedWithPotentialStencilClip;
childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
info.damageAccumulator->popTransform();
}
}
}
- 遍歷SkBitmap數(shù)組,查看每個Bitmap是否準備好紋理阅羹。
SkBitmap數(shù)組元素從哪里來呢勺疼?
畫布繪制Bitmap,DisplayListCanvas#drawBitmap方法捏鱼。
void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
//refBitmap會向mDisplayListData的bitmapResources數(shù)組中保存一份localBitmap
bitmap = refBitmap(*bitmap);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
}
inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
alloc().autoDestroy(localBitmap);
mDisplayListData->bitmapResources.push_back(localBitmap);
return localBitmap;
}
增加一個DrawBitmapOp操作,向數(shù)組保存一個SkBitmap轨淌。
-
prefetchAndMarkInUse方法,返回Bitmap的Texture递鹉。
根據(jù)piexlRef的stableId在LruCache緩存查找Texture藏斩,若未查到,判斷是否可創(chuàng)建灾茁,若加上Bitmap大小后大于緩存設計的大小谷炸,刪除未使用的舊Texture禀挫,若仍大于最大值局服,無法創(chuàng)建新Texture,返回null套硼。
創(chuàng)建Texture時衫画,先glGenTextures,生成紋理對象索引缠导,保存在texture的id,bindTexture綁定僻造,glTexImage2D生成2D紋理將綁定Bitmap像素地址。最后竹挡,新Texture存入LruCache緩存立膛。
bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) {
Texture* texture = getCachedTexture(bitmap, AtlasUsageType::Use);
if (texture) {//texture不為0
texture->isInUse = ownerToken;
}
return texture;
}
若所有Texture存在揪罕,在syncFrameState結(jié)束后宝泵,返回info.prepareTextures標志true,它會影響喚醒主線程的時機。
- 遍歷DisplayListData的DrawRenderNodeOp數(shù)組罢坝。底層子RenderNode節(jié)點。遞歸prepareTreeImpl方法嘁酿。
DrawRenderNodeOp數(shù)組是如何加入的呢?
追溯到Java層娱仔,DisplayListCanvas#drawRenderNode方法將子節(jié)點RenderNode寫入游桩。底層創(chuàng)建一個DrawRenderNodeOp操作耐朴,封裝被繪制視圖的底層RenderNode節(jié)點盹憎,將操作加入到DrawRenderNodeOp數(shù)組筛峭。
RenderNode#pushLayerUpdate方法陪每。
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
if (CC_LIKELY(layerType != LayerType::RenderLayer) ||
CC_UNLIKELY(!isRenderable())) {
if (CC_UNLIKELY(mLayer)) {
LayerRenderer::destroyLayer(mLayer);
mLayer = nullptr;
}
return;
}
bool transformUpdateNeeded = false;
if (!mLayer) {
//內(nèi)部Layer不存在,由LayerRenderer創(chuàng)建挂签。
mLayer = LayerRenderer::createRenderLayer(info.renderState,
getWidth(), getHeight());
applyLayerPropertiesToLayer(info);
damageSelf(info);
transformUpdateNeeded = true;
}
...
if (dirty.intersect(0, 0, getWidth(), getHeight())) {
dirty.roundOut(&dirty);
//初始化Layer的RenderNode盼产,deferredUpdateScheduled標志
//初始化渲染器render為LayerRenderer。
mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop,
dirty.fRight, dirty.fBottom);
}
if (info.renderer && mLayer->deferredUpdateScheduled) {
info.renderer->pushLayerUpdate(mLayer);//push進數(shù)組
}
...
}
滿足兩個條件執(zhí)行辆飘。
LayerType必須是RenderLayer類型(Java層,View#setLayerType方法設置)蜈项。
DisplayListData不是空。
否則退出方法侥衬。
初始化創(chuàng)建RenderNode內(nèi)部Layer跑芳,TreeInfo的OpenGLRenderer,將該Layer加入OpenGLRenderer的Layer數(shù)組博个,等待OpenGLRenderer#updateLayers更新。
總結(jié)
1共耍,從底層根節(jié)點RootRenderNode開始虑灰,執(zhí)行prepareTreeImpl方法痹兜,prepareSubTree進入子視圖,從當前RenderNode的DisplayListData中查找子視圖RenderNode節(jié)點对湃,遞歸底層子視圖節(jié)點RenderNode的prepareTreeImpl方法崖叫。
2熟尉,底層根節(jié)點RootRenderNode并非頂層視圖節(jié)點,而是上層ThreadedRenderer內(nèi)部RenderNode對應的底層節(jié)點剧包。
3往果,上層畫布觸發(fā)過drawBitmap時疆液,存在SkBitmap數(shù)組陕贮。
4,上層設置LAYER_TYPE_HARDWARE的視圖肮之,底層RenderNode內(nèi)部創(chuàng)建一個Layer。
到這里眶明,就結(jié)束了根RenderNode的prepareTreeImpl方法筐高,并遞歸遍歷子節(jié)點的prepareTreeImpl。繼續(xù)回到CanvasContext的prepareTree柑土。
接下來是NativeWindow#query方法蜀肘。
那么何時初始化NativeWindow呢?
追溯到Java層稽屏,ViewRootImpl#performTraversals,調(diào)用ThreadedRenderer的initialize方法狐榔,該方法傳入由Wms初始化的Surface。
@Override
boolean initialize(Surface surface) throws OutOfResourcesException {
mInitialized = true;
updateEnabledState(surface);
boolean status = nInitialize(mNativeProxy, surface);
return status;
}
根據(jù)上層Surface獲取底層NativeWindow轿偎。從JNI#觸發(fā)RenderProxy#initialize方法被廓,到CanvasContext的initialize方法。
bool CanvasContext::initialize(ANativeWindow* window) {
setSurface(window);
mCanvas = new OpenGLRenderer(mRenderThread.renderState());
mCanvas->initProperties();
return true;
}
初始化OpenGLRenderer萝玷。
利用NativeWindow創(chuàng)建一個mEglSurface昆婿。
void CanvasContext::setSurface(ANativeWindow* window) {
mNativeWindow = window;
...
if (window) {
mEglSurface = mEglManager.createSurface(window);
}
}
NativeWindow是Java層Surface的底層對象蜓斧。
query方法的目的是查詢生產(chǎn)者和消費者緩沖區(qū)的運行情況。
NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND的官方解釋為是否消費者比生產(chǎn)者對緩沖區(qū)的處理落后大于1個緩沖區(qū)看疙,runningBehind返回結(jié)果直奋,如果runningBehind為false說明未落后能庆,則info.out.canDrawThisFrame置為true脚线,說明該幀可以繪制,否則canDrawThisFrame設為false跳過該幀渠旁。
總結(jié)
總結(jié):
主線程負責App視圖繪制記錄船逮,在App,每一次樹形視圖繪制都是一個渲染任務傻唾,渲染線程專注于與gpu交互。
第二步伪煤,prepareTree邏輯是繪制準備凛辣,將屬性與DisplayList數(shù)據(jù)交接,DrawRenderNodeOp操作對應節(jié)點遞歸處理,RenderNode必要時初始化Layer蝗敢,準備NativeWindow查詢是否該幀需要跳過寿谴。
syncFrameState同步結(jié)束,返回info.prepareTextures結(jié)果,賦值canUnblockUiThread码泞。
若info.prepareTextures標志余寥,喚醒主線程宋舷。Signal#signal()。
否則莲兢,說明存在未準備好的Texture续膳。先執(zhí)行CanvasContext#draw方法,再喚醒主線程坟岔。
任重而道遠