努比亞技術(shù)團隊原創(chuàng)內(nèi)容爽待,轉(zhuǎn)載請務(wù)必注明出處融痛。
Android畫面顯示流程分析(1)
Android畫面顯示流程分析(2)
Android畫面顯示流程分析(3)
Android畫面顯示流程分析(4)
Android畫面顯示流程分析(5)
5. BufferQueue
BufferQueue要解決的是生產(chǎn)者和消費者的同步問題菩混,應(yīng)用程序生產(chǎn)畫面鸯两,SurfaceFlinger消費畫面宣吱;SurfaceFlinger生產(chǎn)畫面而HWC Service消費畫面哼转。用來存儲這些畫面的存儲區(qū)我們稱其為幀緩沖區(qū)buffer, 下面我們以應(yīng)用程序作為生產(chǎn)者明未,SurfaceFlinger作為消費者為例來了解一下BufferQueue的內(nèi)部設(shè)計。
5.1. Buffer State的切換
在BufferQueue的設(shè)計中壹蔓,一個buffer的狀態(tài)有以下幾種:
FREE :表示該buffer可以給到應(yīng)用程序趟妥,由應(yīng)用程序來繪畫
DEQUEUED:表示該buffer的控制權(quán)已經(jīng)給到應(yīng)用程序側(cè),這個狀態(tài)下應(yīng)用程序可以在上面繪畫了
QUEUED: 表示該buffer已經(jīng)由應(yīng)用程序繪畫完成佣蓉,buffer的控制權(quán)已經(jīng)回到SurfaceFlinger手上了
ACQUIRED:表示該buffer已經(jīng)交由HWC Service去合成了披摄,這時控制權(quán)已給到HWC Service了
Buffer的初始狀態(tài)為FREE, 當(dāng)生產(chǎn)者通過dequeueBuffer來申請buffer成功時亲雪,buffer狀態(tài)變?yōu)榱薉EQUEUED狀態(tài), 應(yīng)用畫圖完成后通過queueBuffer把buffer狀態(tài)改到QUEUED狀態(tài)行疏, 當(dāng)SurfaceFlinger通過acquireBuffer操作把buffer拿去給HWC Service合成匆光, 這時buffer狀態(tài)變?yōu)锳CQUIRED狀態(tài),合成完成后通過releaseBuffer把buffer狀態(tài)重新改為FREE狀態(tài)酿联。狀態(tài)切換如下圖所示:
從時間軸上來看一個buffer的狀態(tài)總是這樣循環(huán)變化:
FREE->DEQUEUED->QUEUED->ACQUIRED->FREE
應(yīng)用程序在DEQUEUED狀態(tài)下繪畫终息,而HWC Service在狀態(tài)為ACQUIRED狀態(tài)下做合成:
5.2. BufferSlot
每一個應(yīng)用程序的圖層在SurfaceFlinger里稱為一個Layer, 而每個Layer都擁有一個獨立的BufferQueue, 每個BufferQueue都有多個Buffer,Android 系統(tǒng)上目前支持每個Layer最多64個buffer, 這個最大值被定義在frameworks/native/gui/BufferQueueDefs.h贞让, 每個buffer用一個結(jié)構(gòu)體BufferSlot來代表周崭。
每個BufferSlot里主要有如下重要成員:
struct BufferSlot{
......
BufferState mBufferState;//代表當(dāng)前Buffer的狀態(tài) FREE/DEQUEUED/QUEUED/ACQUIRED
....
sp<GraphicBuffer> mGraphicBuffer;//代表了真正的buffer的存儲空間
......
uint64_t mFrameNumber;//表示這個slot被queued的編號,在應(yīng)用調(diào)dequeueBuffer申請slot時會參考該值
......
sp<Fence> mFence;//在Fence一章再來看它的作用
.....
}
64個BufferSlot可以分成兩個部分喳张,used Slots和Unused Slots, 這個比較好理解续镇,就是使用中的和未被使用的,而Used Slots又可以分為Active Slots和UnActive Slots, 處在DEQUEUED, QUEUED, ACQUIRED狀態(tài)的被稱為Active Slots, 剩下FREE狀態(tài)的稱為UnActive Slots, 所以所有Active Slots都是正在有人使用中的slot, 使用者可能是生產(chǎn)者也可能是消費者销部。而FREE狀態(tài)的Slot根據(jù)是否已經(jīng)為其分配過內(nèi)存來分成兩個部分摸航, 一是已經(jīng)分配過內(nèi)存的,在Android源碼中稱為mFreeBuffers, 沒有分配過內(nèi)存的稱為mFreeSlots, 所以如果我們在代碼中看到是從mFreeSlots里拿出一個BufferSlot那說明這個BufferSlot是還沒有配置GraphicBuffer的, 這個slot可能是第一次被使用到舅桩。其分類如下圖所示:
我們來看一下酱虎,應(yīng)用上幀時SurfaceFlinger是如何管理分配這些Slot的。
應(yīng)用側(cè)對圖層buffer的操作接口是如下文件:
frameworks/native/libs/gui/Surface.cpp
應(yīng)用第一次dequeueBuffer前會通過connect接口和SurfaceFlinger建立“連接”:
int Surface::connect(int api, const sp<IProducerListener>&listener, bool reportBufferRemoval){
ATRACE_CALL();//應(yīng)用第一次上幀前可以在trace 中看到這個
......
int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);//這里通過binder調(diào)用和SurfaceFlinger建立聯(lián)系
......
}
應(yīng)用在第一次dequeueBuffer時會先調(diào)用requestBuffer:
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();//這里可以在systrace中看到
......
//這里嘗試去dequeueBuffer,因為這時SurfaceFlinger對應(yīng)Layer的slot還沒有分配buffer,這時SurfaceFlinger會回復(fù)的flag會有BUFFER_NEEDS_REALLOCATION
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
reqFormat, reqUsage, &mBufferAge,
enableFrameTimestamps?&frameTimestamps:nullptr);
......
if((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
......
//這里檢查到dequeueBuffer返回的結(jié)果里帶有BUFFER_NEEDS_REALLOCATION標(biāo)志就會發(fā)出一次requestBuffer
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
......
}
......
}
在SurfaceFlinger這端擂涛,第一次收到dequeueBuffer時發(fā)現(xiàn)分配出來的slot沒有GraphicBuffer读串, 這時會去申請對應(yīng)的buffer:
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
if ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))//檢查是否已分配了GraphicBuffer
{
......
returnFlags |= BUFFER_NEEDS_REALLOCATION;//發(fā)現(xiàn)需要分配buffer,置個標(biāo)記
}
......
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
......
//新創(chuàng)建一個新的GraphicBuffer給到對應(yīng)的slot
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
......
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;//把GraphicBuffer給到對應(yīng)的slot
......
}
......
return returnFlags;//注意在應(yīng)用第一次請求buffer, dequeueBuffer返回時對應(yīng)的GraphicBuffer已經(jīng)創(chuàng)建完成并給到了對應(yīng)的slot上,但返回給應(yīng)用的flags里還是帶有BUFFER_NEEDS_REALLOCATION標(biāo)記的
}
應(yīng)用側(cè)收到帶有BUFFER_NEEDS_REALLOCATION標(biāo)記的返回結(jié)果后就會調(diào)requestBuffer來獲取對應(yīng)buffer的信息:
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
......
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
從上面可以看出requestBuffer的主要作用就是把GraphicBuffer傳遞到應(yīng)用側(cè)撒妈,這里思考一個問題恢暖,既然SurfaceFlinger在響應(yīng)dequeueBuffer時就已經(jīng)為slot新創(chuàng)建了GraphicBuffer, 為什么還需要應(yīng)用側(cè)再次調(diào)用requestBuffer時再把GraphicBuffer傳給應(yīng)用呢? 為什么dequeueBuffer不直接返回呢狰右?這不是多花費一次跨進程通信的時間嗎杰捂? 為什么設(shè)計成了這個樣子呢?
我們再來看一下應(yīng)用側(cè)接口dequeueBuffer的函數(shù)設(shè)計:
frameworks/native/libs/gui/IGraphicBufferProducer.h
virtual status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height,
PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps);
注意第一個參數(shù)只是返回一個int值棋蚌,它表示的是64個slot里的哪一個slot琼娘, 其他參數(shù)里也不會返回這個slot所對應(yīng)的GraphicBuffer的信息,但這個slot拿到應(yīng)用側(cè)后附鸽,應(yīng)用是要拿到確確實實的GraphicBuffer才能把共享內(nèi)存mmap到自已進程空間脱拼,才能在上面繪畫。而顯然這個接口的設(shè)計并不會帶來GraphicBuffer的信息坷备,那設(shè)計之初為什么不把這個信息放進來呢熄浓? 因為這個接口調(diào)用太頻繁了,比如在90FPS的設(shè)備上,一秒鐘該接口要執(zhí)行90次赌蔑,太頻繁了俯在,而且這個信息只需要傳遞一次就可以了,如果每次這個接口都要帶上GraphicBuffer的信息娃惯,傳輸了很多冗余數(shù)據(jù)跷乐,所以不如加入一個新的api(requestBuffer)來完成GraphicBuffer傳遞的事情.
應(yīng)用側(cè)在requestBuffer后會拿到GraphicBuffer的信息,然后會通過importBuffer在本進程內(nèi)通過binder傳過來的parcel包把GraphicBuffer重建出來:
frameworks/native/libs/ui/GraphicBuffer.cpp
status_t GraphicBuffer::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
......
if (handle != 0) {
buffer_handle_t importedHandle;
//獲取從SurfaceFlinger傳過來的buffer
status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height),
uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle);
......
}
......
}
如下圖所示趾浅,從App側(cè)看愕提,前三幀都會有requestBuffer, 都會有importBuffer,在第4幀時就沒有requestBuffer/importBuffer了皿哨,因為我們當(dāng)前系統(tǒng)一共使用了三個buffer,從systrace上可以看到這個區(qū)別:
當(dāng)一個surface被創(chuàng)建出來開始上幀時其流程如下圖所示浅侨,應(yīng)用所使用的畫布是在前三幀被分配出來的,從第四幀開始進入穩(wěn)定上幀期证膨,這時會重復(fù)循環(huán)利用前三次分配的buffer如输。
思考一個問題,在三個buffer的系統(tǒng)中一定是前三幀中觸發(fā)分配GraphicBuffer嗎央勒? 如果某個應(yīng)用有一個SurfaceView自已決定上幀的幀率不见,而這個幀率非常低,如低到一秒一幀崔步,那前三秒會把三個Buffer分配出來嗎稳吮?我們需要了解一下多buffer下SurfaceFlinger的管理策略是什么。
5.3. Buffer管理
前文提到了每個圖層Layer都有最多64個BufferSlot, 如下圖所示,每個BufferSlot都會記錄有自身的狀態(tài)(BufferState),以及自已的GraphicBuffer指針mGraphicBuffer.
但不是每個Layer都能使用到那么多刷晋,每個Layer最多可使用多少個Layer是在這里設(shè)置的:
frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp
void BufferQueueLayer::onFirstRef() {
......
// BufferQueueCore::mMaxDequeuedBufferCount is default to 1
if (!mFlinger->isLayerTripleBufferingDisabled()) {
mProducer->setMaxDequeuedBufferCount(2);//3 buffer時這里設(shè)為2盖高, 是因為在BufferQueueCore那里會+1
}
......
}
我們重新回憶下BufferSlot的幾個狀態(tài)慎陵,F(xiàn)REE 眼虱,代表該buffer可以給到應(yīng)用程序,由應(yīng)用程序來繪畫席纽, 這樣的Slot SurfaceFlinger會根據(jù)是否有給它分配有GraphicBuffer分到兩個隊列里捏悬, 有GraphicBuffer的分配到mFreeBuffers里, 沒有GraphicBuffer的分配到mFreeSlots里润梯; 當(dāng)應(yīng)用申請走一個Slot時过牙,該Slot狀態(tài)會切換到DEQUEUED狀態(tài),該Slot會被放入mActiveBuffers隊列里纺铭; 當(dāng)應(yīng)用繪畫完成后Slot狀態(tài)會切到QUEUED狀態(tài)寇钉,所有QUEUED狀態(tài)的Slot會被放入mQueue隊列里; 當(dāng)一個Slot被HWC Service拿去合成后狀態(tài)會變?yōu)锳CQUIRED舶赔, 這個Slot會被從mQueue隊列中取出放入mActiveBuffers隊列里扫倡;
我們先來看一個BufferSlot管理的場景:
Time1: 在上圖中,初始狀態(tài)下竟纳,有0撵溃, 1疚鲤, 2這三個BufferSlot, 由于它們都沒有分配過GraphicBuffer, 所以它們都位于mFreeSlots隊列里,當(dāng)應(yīng)用來dequeueBuffer時缘挑,SurfaceFlinger會先檢查在mFreeBuffers隊列中是否有Slot集歇, 如果有則直接分配該Slot給應(yīng)用。顯然此時mFreeBuffers里是空的语淘,這時Surfaceflinger會去mFreeSlots里去找出第一個Slot, 這時就找到了0號Slot, dequeueBuffer結(jié)束時應(yīng)用就拿到了0號Slot的使用權(quán)诲宇,于此同時SurfaceFlinger也會為0號Slot分配GraphicBuffer, 之后應(yīng)用將通過requestBuffer和importBuffer來獲取到該Slot的實際內(nèi)存空間。
應(yīng)用dequeueBuffer之后0號Slot切換到DEQUEUED狀態(tài)亏娜,并被放入mActiveBuffers列表焕窝。
Time2:應(yīng)用完成繪制后通過queueBuffer來提交繪制好的畫面,完成后0號Slot狀態(tài)變?yōu)镼UEUED狀態(tài)维贺,放入mQueue隊列它掂,此時1,2號Slot還停留在mFreeSlots隊列中溯泣。
Time3: 上面這個狀態(tài)會持續(xù)到下一個Vsync-sf信號到來虐秋,當(dāng)Vsync-sf信號到來時,SurfaceFlinger主線程會檢查mQueue隊列中是否有Slot, 有就意味著有應(yīng)用上幀垃沦,這時它會把該Slot從mQueue中取出放入mActiveBuffers隊列客给,并將Slot的狀態(tài)切換到ACQUIRED, 代表這個Slot已被拿去做畫面合成。那么這之后0號Slot被從mQueue隊列拿出放入mActivieBuffers里肢簿。
Time4:接下來應(yīng)用繼續(xù)調(diào)用dequeueBuffer申請buffer, 此時0號Slot在mActiveBuffers里靶剑,1,2號在mFreeSlots里池充,SurfaceFlinger仍然是先檢查mFreeBuffers里有沒有Slot, 發(fā)現(xiàn)還是沒有桩引,再檢查mFreeSlots里是否有,于是取出了1號Slot給到應(yīng)用側(cè)收夸,同時1號Slot狀態(tài)切換到DEQUEUED狀態(tài)坑匠, 放入mActiveBuffers里,
Time5:1號Slot應(yīng)用繪畫完畢卧惜,通過queueBuffer提交上來厘灼,這時1號Slot狀態(tài)由DEQUEUED狀態(tài)切換到了QUEUED狀態(tài),進入mQueue隊列咽瓷,之后將維持該狀態(tài)直到下一個Vsync-sf信號到來设凹。
Time6: 此時Vsync-sf信號到來,發(fā)現(xiàn)mQueue中有個Slot 1, 這時SurfaceFlinger主線程會把它取出茅姜,把狀態(tài)切換到ACQUIRED闪朱, 并放入mActiveBuffers里。
Time7:這時0號Slot HWC Service使用完畢,通過releaseBuffer還了回來监透,0號Slot的狀態(tài)將從ACQUIRED切換回FREE, Surfaceflinger會把它從mActivieBuffers里拿出來放入mFreeBuffers里桶错。注意這時放入的是mFreeBuffers里而不是mFreeSlots里,因為此時0號Slot是有GraphicBuffer的胀蛮。
在上述過程中SurfaceFlinger收到應(yīng)用dequeueBuffer請求時處在FREE狀態(tài)的Slot都還沒有分配過GraphicBuffer, 由之前的討論我們知道這通常發(fā)生在一個Surface的前幾幀時間內(nèi)院刁。如3 buffer下的前三幀。
我們再來看一下申請buffer時mFreeBuffers里有Slot時的情況:
Time11:當(dāng)下的狀態(tài)是0粪狼,1兩個Slot都在mFreeBuffers里退腥,2號Slot在mActiveBuffers里,這時應(yīng)用來dequeueBuffer
Time12: SurfaceFlinger仍然會先查看mFreeBuffers列表看是否有可用的Slot, 發(fā)現(xiàn)0號可用再榄,于是0號Slot狀態(tài)由FREE切換到DEQUEUED狀態(tài)狡刘,并被放入mActiveBuffers里
Time13:應(yīng)用對0號Slot的繪圖完成后提交上來,這時狀態(tài)從DEQUEUED切換到QUEUED狀態(tài)困鸥,0號Slot被放入mQueue隊列嗅蔬,之后會維持該狀態(tài)直到下一下Vsync-sf信號到來
Time14:這時Vsync-sf信號到來,SurfaceFlinger主線程中檢查mQueue隊列中是否有Slot, 發(fā)現(xiàn)0號Slot疾就, 于是通過 aquireBuffer操作把0號Slot狀態(tài)切換到ACQUIRED
這個過程中應(yīng)用申請buffer時已經(jīng)有處于FREE狀態(tài)的Slot是分配過GraphicBuffer的澜术,這種情況多發(fā)生在Surface的穩(wěn)定上幀期。
再來關(guān)注一下acquireBuffer和releaseBuffer的過程:
Time 23: 當(dāng)前狀態(tài)mQueue里有兩個buffer
Time 24:Vsync-sf信號到達猬腰,從mQueue隊列里取走了0號Slot,
Time 25: 再一次Vsync-sf到來鸟废,這時SurfaceFlinger會先查看mQueue隊列是否有buffer,發(fā)現(xiàn)有2號Slot姑荷, 會先取走2號Slot
Time 26: 此時0號Slot已經(jīng)被HWC Service使用完畢盒延,需要把Slot還回來,0號Slot在此刻進入mFreeBuffers隊列鼠冕。
這里需要注意的是兩個時序:
- 每次Vsync-sf信號到來時總是先查看mQueue隊列看是否有Layer上幀添寺,然后才會走到releaseBuffer把HWC Service使用的Slot回收回來
- 本次Vsync-sf被aquireBuffer取走的Slot總是會在下一個Vsync-sf時才會被release回來
由上述過程不難看出,如果應(yīng)用上幀速度較慢供鸠,比如其上幀周期時長大于兩倍屏幕刷新周期時畦贸,每次應(yīng)用來dequeueBuffer時前一次queueBuffer的BufferSlot都已經(jīng)被release回來了陨闹,這時總會在mFreeBuffers里找到可用的楞捂,那么就不需要三個Slot都分配出GraphicBuffer.
在應(yīng)用上幀過程中所涉及到的BufferSlot我們可以通過systrace來觀察:
這兩個圖中顯示可以從systrace中看到每次dequeueBuffer和acquireBuffer所操作到的Slot是哪個,當(dāng)然releaseBuffer也可以在systrace上找到:
從trace里我們還應(yīng)注意到趋厉,releaseBuffer是在postComposition里調(diào)用到的寨闹,這段代碼如下:
frameworks/native/services/surfaceflinger/surfaceflinger.cpp
void SurfaceFlinger::postComposition(){
ATRACE_CALL();
......
for(auto& layer:mLayersWithQueuedFrames){//這里只要主線程執(zhí)行到這個postComposition函數(shù)就一定會讓集合中的layer去執(zhí)行releasePendingBuffer, 而這個releasePendingBuffer里就會調(diào)用到releaseBuffer
layer->releasePendingBuffer(dequeueReadyTime);
}
......
}
mLayersWithQueuedFrames里的Layer是在這里被加入進來的:
bool SurfaceFlinger::handlePageFlip(){
......
mDrawingState.traverse([&](Layer* layer){
.......
if(layer != nullptr && layer->hasReadyFrame()){//這里是判斷這個Layer是否有buffer更新,也就是mLayersWithQueuedFrames里放的是有上幀的layer
......
mLayersWithQueuedFrames.push_back(layer);
......
}
.......
});
......
}
在Layer的releasePendingBuffer里會把對應(yīng)的Slot的狀態(tài)切到FREE狀態(tài)君账,切換到FREE狀態(tài)后繁堡,是很可能被應(yīng)用dequeueBuffer獲取到的,那么怎么能確定buffer已經(jīng)被HWC Service使用完了呢?如果HWC Service還沒有使用完成椭蹄,而應(yīng)用申請到了這個buffer闻牡,buffer中的數(shù)據(jù)會出錯,怎么解決這個問題呢绳矩,這就要靠我們下一章要討論的Fence來解決罩润。
我們再從幀數(shù)據(jù)更新的流程上來看下bufferSlot的管理,從systrace(屏幕刷新率為90HZ)上可以觀察到的應(yīng)用上幀的全景圖:
首先應(yīng)用(這里是以一個SurfaceView上幀為例)通過dequeueBuffer拿到了BufferSlot 0, 開始第1步繪圖翼馆,繪圖完成后通過queueBuffer將Slot 0提交到SurfaceFlinger, 下一個Vsync-sf信號到達后割以,開始第2步圖層處理,這時SurfaceFlinger通過aquireBuffer把Slot 0拿去給到HWC Service应媚,與此同時進入第3步HWC Service開始把多個圖層做合成严沥,合成完成后通過libdrm提供的接口通知DRM模塊通過DSI傳輸給DDIC, Panel 通過Disp Scan Gram把圖像顯示到屏幕。
5.4. 代碼接口
以應(yīng)用為生產(chǎn)者SurfaceFlinger為消費者為例中姜,BufferQueue的Slot管理核心代碼如BufferQueueCore消玄、BufferQueueProducer、BufferQueueConsumer組成丢胚, 生產(chǎn)者這邊還有一個Surface它是應(yīng)用側(cè)操作BufferQueue的接口:
相關(guān)代碼路徑如下:
Surface.cpp (frameworks\native\libs\gui)
BufferQueueCore.cpp (frameworks\native\libs\gui)
BufferQueueProducer.cpp (frameworks\native\libs\gui)
BufferQueueConsumer.cpp (frameworks\native\libs\gui)
IGraphicBufferProducer.cpp (frameworks\native\libs\gui)
IGraphicBufferConsumer.cpp (frameworks\native\libs\gui)
IConsumerListener.h (frameworks\native\libs\gui\include\gui)
由于Android規(guī)定莱找,BufferQueue的buffer必須是在Consumer側(cè)來分配,所以BufferQueue的核心Slot管理代碼是在SurfaceFlinger進程空間內(nèi)執(zhí)行的嗜桌,它們關(guān)系可以用如下圖來表示:
相關(guān)代碼路徑:
IGraphicBufferProducer用來規(guī)定了BufferQueue向生產(chǎn)者提供的接口有哪些奥溺,比如請求buffer用到的dequeueBuffer, 提交buffer用到的queueBuffer等等:
class IGraphicBufferProducer : public RefBase {
......
virtual status_t connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) = 0;
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
QueueBufferOutput* output) = 0;
virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) = 0;
......
}
connect接口是在開始時上幀前調(diào)用一次,主要用來讓生產(chǎn)者和消費者溝通一些參數(shù)骨宠,比如api 版本浮定,buffer的尺寸,個數(shù)等层亿; disconnect用于在生產(chǎn)者不再生產(chǎn)斷開連接桦卒,用以通知消費端清理一些資源。
IGraphicBufferConsumer則規(guī)定了消費者和BufferQueueCore的接口有哪些匿又,比如查詢從mQueue隊列中取出buffer方灾,和還buffer到BufferQueue:
class IGraphicBufferConsumer : public RefBase {
......
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) = 0;
virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display,
EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0;
......
}
5.5. 本章小結(jié)
讓我們用一張圖來總結(jié)說明一下在Triple Buffer下應(yīng)用連續(xù)上幀過程中三個buffer的使用情況,以及在此過程中應(yīng)用碌更, SurfaceFlinger是如何配合的:
應(yīng)用在每個Vsync信號到來后都會通過dequeueBuffer/queueBuffer來申請buffer和提交繪圖數(shù)據(jù)裕偿,Surfaceflinger都會在下一個vsync信號到來時取走buffer去做合成和顯示, 并在下一下個vsync時將buffer還回來痛单,再次循環(huán)嘿棘。
6. Fence
Fence這個英文單詞通常代表柵欄,籬笆,圍墻旭绒,代表了此處是否可以通行鸟妙。它是內(nèi)核提供的不同硬件間同步機制焦人,在userspace層我們可以將它視為是一把鎖,它代表了某個硬件對共享資源的占用情況重父。
6.1. 為什么要有Fence
一般凡是共享的資源都要建立一個同步機制來管理花椭,比如在多線程編程中對臨界資源的通過加鎖實現(xiàn)互斥訪問,再比如BufferQueue中Surfaceflinger和應(yīng)用對共享內(nèi)存(幀緩沖)的訪問中有bufferstate來標(biāo)識共享內(nèi)存控制權(quán)的方法來做同步房午。沒有同步機制的無序訪問極可能造成數(shù)據(jù)混亂个从。
上面圖中的BufferState的方式只是解決了在CPU管理之下,當(dāng)下共享內(nèi)存的控制權(quán)歸屬問題歪沃,但當(dāng)共享資源是在兩個硬件之中時嗦锐,情況就不同了,比如當(dāng)一個幀緩沖區(qū)共享內(nèi)存給到GPU時沪曙,GPU并不清楚CPU還有沒有在使用它奕污,同樣地,當(dāng)GPU在使用共享內(nèi)存時液走,CPU也不清楚GPU是否已使用完畢碳默,如下面這個例子:
CPU調(diào)用OpenGL函數(shù)繪圖過程的一個簡化版流程如上圖所示,首先CPU側(cè)調(diào)用glClear清空畫布缘眶,再調(diào)用glXXX()來畫各種各樣的畫面嘱根,對于CPU來講在glXXX()執(zhí)行完畢后,它的繪圖工作已經(jīng)完成了巷懈。但其實glXXX()的具體工作是由GPU來完成的该抒,CPU側(cè)的glXXX()只是在向GPU傳達任務(wù)而已,任務(wù)傳達完并不意味著任務(wù)已經(jīng)完成了顶燕。真正任務(wù)做完是在GPU把glXXX()所對應(yīng)的工作做完才是真正的任務(wù)完成了凑保。從CPU下達完任務(wù)到GPU完成任務(wù)間存在時差,而且這個時差受GPU工作頻率影響并不是一個定值涌攻。在OpenGL的語境中CPU可以通過glFilish()來等待GPU完成所有工作欧引,但這顯然浪費了CPU本可以并行工作的時間,這段時間CPU沒有用來做別的事情恳谎。
在上面的例子中CPU下達了要在畫布上繪畫的指令給GPU, 而GPU什么時候畫完時間是不確定的芝此,這里的畫布就是共享資源,CPU和GPU的工作完全是異步的因痛。Fence提供了一種方式來處理不同硬件對共享資源的訪問控制婚苹。
我們可以這樣來理解Fence的工作原理: Fence是一個內(nèi)核driver, 對一個Fence對象有兩種操作, signal和wait, 當(dāng)生產(chǎn)者(App)向GPU下達了很多繪圖指令(drawCall)后GPU開始工作婚肆,這里CPU就認為繪圖工作已經(jīng)完成了租副,之后把創(chuàng)建的Fence對象通過binder通知給消費者(SurfaceFlinger),SurfaceFlinger收到通知后坐慰,此時SurfaceFlinger并不知道GPU是否已經(jīng)繪圖完畢较性,即GPU是否已對共享資源訪問完畢用僧,消費者先通過Fence對象的wait方法等待,如果GPU繪圖完成會調(diào)用Fence的signal赞咙, 這時消費者就會從Fence對象的wait方法中跳出责循。即wait方法結(jié)束時就是GPU工作完成時。這個signal由kernel driver來完成攀操。有了Fence的情況下院仿,CPU在完成自已的工作后就可以繼續(xù)做別的事情,到了真正要使用共享資源時再通過Fence wait來和GPU同步速和,盡最大可能做到了讓不同硬件并行工作歹垫。
6.2. 與BufferQueue協(xié)作方式
我們以App(productor)和SurfaceFlinger(Consumer)間的交互來看下Fence在其中的作用:
首先App通過dequeueBuffer獲得某一Slot的使用權(quán),這時Slot的狀態(tài)切換到DEQUEUED狀態(tài)颠放,隨著dequeueBuffer函數(shù)返回的還有一個releaseFence對象排惨,這時因為releaseFence還沒有signaled, 這意味著雖然在CPU這邊已經(jīng)拿到了buffer的使用權(quán)碰凶,但別的硬件還在使用這個buffer, 這時的GPU還不能直接在上面繪畫暮芭,它要等releaseFence signaled后才能繪畫。 接下來我們先假設(shè)GPU的工作花費的時間較長欲低,在它完成之前CPU側(cè)APP已經(jīng)完成了queueBuffer動作辕宏,這時Slot的狀態(tài)已切換為QUEUED狀態(tài),或者vsync已經(jīng)到來狀態(tài)變?yōu)锳CQUIRED狀態(tài)砾莱, 這在CPU側(cè)代表該buffer給HWC去合成了趁仙,但這時HWC的硬件MDP還不能去讀里面的數(shù)據(jù),它還需要等待acauireFence的signaled信號蕴坪,只有等到了acquireFence的signaled信號才代表GPU的繪畫工作真正做完了隘马,GPU已經(jīng)完成了對幀緩沖區(qū)的訪問,這時HWC 的硬件才能去讀幀緩沖區(qū)的數(shù)據(jù)扫步,完成圖層合成的工作魔策。
同樣地,當(dāng)SurfaceFlinger執(zhí)行到releaseBuffer時河胎,并不能代表HWC 已經(jīng)完全完成合成工作了闯袒,很有可能它還在讀取緩沖區(qū)的內(nèi)容做合成, 但不妨礙releaseBuffer的流程執(zhí)行游岳,雖然HWC還在使用緩沖區(qū)做合成政敢,但幀緩沖區(qū)的Slot有可能被應(yīng)用申請走變成DEQUEUED狀態(tài),雖然Slot是DEQUEUED狀態(tài)這時GPU并不能直接存取它胚迫,它要等代表著HWC使用完畢的releaseFence的signaled信號喷户。
應(yīng)用側(cè)申請buffer的同時會獲取到一個fence對象(releaseFence):
frameworks/native/libs/gui/Surface.cpp
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
.....
sp<Fence> fence;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
reqFormat, reqUsage, &mBufferAge,
enableFrameTimestamps ? &frameTimestamps
: nullptr);
.....
}
對應(yīng)SurfaceFlinger側(cè):
frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
ATRACE_CALL();
.......
*outFence = (mCore->mSharedBufferMode &&
mCore->mSharedBufferSlot == found) ?
Fence::NO_FENCE : mSlots[found].mFence;//把Slot里記錄的mFence對象返回出去,就是應(yīng)用側(cè)拿到的releaseFence
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;//不妨思考下這里為什么可以清成NO_FENCE访锻?
.......
}
應(yīng)用側(cè)上幀時要創(chuàng)建一個fence來代表GPU的功能還在進行中褪尝,提交buffer的同時把fence對象傳給SurfaceFlinger:
frameworks/native/libs/gui/Surface.cpp
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
......
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);//創(chuàng)建一個fence, 這個就是SurfaceFlinger側(cè)的acquireFence
......
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,//將fence放入input參數(shù)
static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
mTransform ^ mStickyTransform, fence, mStickyTransform,
mEnableFrameTimestamps);
......
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);//把這個fence傳給surfaceflinger
......
}
對應(yīng)的SurfaceFlinger側(cè)從binder里獲取到應(yīng)用側(cè)傳來的fence對象(這個稱為acquireFence):
frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
ATRACE_CALL();
......
sp<Fence> acquireFence;
......
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
......
mSlots[slot].mFence = acquireFence;//queueBuffer完成時Slot的mFence放的是acquireFence
......
}
我們來通過systrace觀察一個因GPU工作時間太長闹获,從而讓DRM工作線程卡在等Fence的情況:
如上圖所示,complete_commit函數(shù)(從上面4.3章我們了解過這個函數(shù)是執(zhí)行SOC準備傳輸數(shù)據(jù)到DDIC的過程)執(zhí)行時前面有一段時間是陷于等待狀態(tài)了河哑,那么它在等誰呢避诽,從圖中所示我們可以看出它在等下73026號fence的signal信號。這種情況說明drm內(nèi)部的dma要去讀這miHoYo.yuanshen這個應(yīng)用的buffer時發(fā)現(xiàn)應(yīng)用的GPU還沒有把畫面畫完璃谨,它不得不等待它畫完才能開始讀取沙庐,但既然都已經(jīng)送到crtc_commit了,至少在CPU這側(cè)佳吞,該Slot的BufferState已經(jīng)是ACQUIRED狀態(tài)拱雏。
6.3 本章小結(jié)
在本章節(jié)中我們了解了不同硬件間同步工作的一種方法,了解了Fence在App畫面更新過程中的使用情況底扳。