Android 圖形系統(tǒng)(8)---- Bufferqueue

BufferQueue

BufferQueue要解決的是生產(chǎn)者和消費(fèi)者的同步問(wèn)題盔夜,應(yīng)用程序產(chǎn)生畫(huà)面缩擂,SurfaceFlinger 消費(fèi)畫(huà)面谷浅;SurfaceFlinger 生成畫(huà)面而HWC service 消費(fèi)畫(huà)面涂滴;用來(lái)存儲(chǔ)這些畫(huà)面的區(qū)域我們稱(chēng)為緩沖區(qū)胆建,為此需要按照下面需求設(shè)計(jì):

  1. 需要有緩沖區(qū)供生產(chǎn)者消費(fèi)者使用
  2. 生產(chǎn)者生產(chǎn)完成需要能夠通知到消費(fèi)者
  3. 生產(chǎn)者一般為app街州,消費(fèi)者是 sf,因此需要拷貝跨進(jìn)程通信的問(wèn)題

下面我們以應(yīng)用程序?yàn)樯a(chǎn)者玻孟,SurfaceFlinger 為消費(fèi)者為例唆缴,了解一下BufferQueue 的內(nèi)部設(shè)計(jì)

BufferState 的切換

在BufferQueue 的設(shè)計(jì)中,Buffer 存在下面幾種狀態(tài):
FREE:表示該Buffer 是空閑的黍翎,可以給到應(yīng)用程序面徽,由應(yīng)用程序來(lái)繪圖
DEQUEUED:表示Buffer 的控制權(quán)已經(jīng)交給了應(yīng)用程序側(cè),這個(gè)狀態(tài)下應(yīng)用程序可以在上面繪圖了
QUEUED:表示該Buffer已經(jīng)由應(yīng)用程序繪圖完成匣掸,Buffer 的控制權(quán)已經(jīng)回到SurfaceFlinger 了
ACQUIRED:表示該Buffer 已經(jīng)交給 HWC service去合成了趟紊,這個(gè)時(shí)候控制權(quán)已經(jīng)給到HWC service了
Buffer 的初始狀態(tài)是Free

  1. 當(dāng)生產(chǎn)者通過(guò)dequeueBuffer 來(lái)申請(qǐng)Buffer 成功時(shí),buffer 的狀態(tài)變?yōu)?QEQUEUED
  2. 應(yīng)用程序繪制完成后通過(guò)queueBuffer 把BufferState 狀態(tài)改為 QUEUED 狀態(tài)
  3. SurfaceFlinger 通過(guò) acquiredBuffer 把 Buffer 拿去給 HWC service合成碰酝,這時(shí)的Buffer 變?yōu)?ACQUIRED 狀態(tài)
  4. 合成完成后通過(guò) release buffer 把 Buffer 狀態(tài)改為FREE狀態(tài)霎匈,
    狀態(tài)切換圖如下圖所示:


    BBQ_base.jpg

從時(shí)間軸上一個(gè)Buffer 的變化過(guò)程是:
FREE->DEQUEUED->QUEUED->ACQUIRED->FREE

BBQ_timeline.jpg

BufferSlot

每一個(gè)應(yīng)用程序圖層在SurfaceFlinger 里稱(chēng)為一個(gè)Layer,每個(gè)Layer都有一個(gè)獨(dú)特的BufferQueue送爸,每個(gè) BufferQueue都會(huì)有多個(gè)Buffer铛嘱,目前Android 系統(tǒng)上單圖層最多支持 64個(gè)Buffer

BufferSlot 的定義如下:
framework/native/libs/gui/include/gui/BufferQueueDefs.h

namespace android {
    class BufferQueueCore;

    namespace BufferQueueDefs {
        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
    } // namespace BufferQueueDefs
} // namespace android

SlotsType 被 typedef 定義成了數(shù)組類(lèi)型,數(shù)組元素是BufferSlot袭厂,數(shù)組的大小為NUM_BUFFER_SLOTS(64)
BufferSlot 是對(duì)GraphicBuffer 的封裝墨吓,重要成員如下:

struct BufferSlot {
.....
    BufferState mBufferState;  //當(dāng)前的 Buffer 狀態(tài),F(xiàn)REE/DEQUEUED/QUEUED/ACQUIRED
    sp<GraphicBuffer> mGraphicBuffer; // 代表了Buffer 存儲(chǔ)空間
    uint64_t mFrameNumber; //表示這個(gè)slot被queued的編號(hào)纹磺,在應(yīng)用調(diào)dequeueBuffer申請(qǐng)slot時(shí)會(huì)參考該值
    sp<Fence> mFence;
}

64 個(gè) BufferSlot 可以分為兩部分帖烘,usedslot(使用中的) 和 unusedlot(未使用中的)
usedslot = active slot + unactive slot
而usedslot又可以分為 active slot 和 unactive slot,處在 DEQUEUED橄杨,QUEUED蚓让,ACQUIRED狀態(tài)的被稱(chēng)為active slot,剩下FREE狀態(tài)的稱(chēng)為unactive Slots讥珍;
所有activeSlots都是有人正在使用中的 slot,使用者可以是生產(chǎn)者也可以是消費(fèi)者

unactive slot = Free state slot = mFreeBuffers + mFreeSlots
而Free 狀態(tài)的slot 根據(jù)是否為其分配內(nèi)存可以分為mFreeBuffers 和 mFreeSlots

  • mFreeBuffers:已經(jīng)分配過(guò)內(nèi)存的
  • mFreeSlots:沒(méi)有分配過(guò)內(nèi)存的
    如果從代碼中看到mFreeSlots中拿出一個(gè) Bufferslot 那說(shuō)明這個(gè)Bufferslot 還沒(méi)有配置過(guò)GraphicBuffer的窄瘟,這個(gè)slot 可能第一次用到

Buffer 的分配流程

framework/native/libs/gui/Surface.cpp

  1. 應(yīng)用第一次dequeueBuffer前會(huì)通過(guò)connect接口和SurfaceFlinger 連接
int Surface::connect(
    int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
    ATRACE_CALL();
    ...
    int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);

}
  1. 調(diào)用 dequeueBuffer時(shí)會(huì)先調(diào)用requestBuffer
    result 標(biāo)志第一次會(huì)帶 BUFFER_NEEDS_REALLOCATION 標(biāo)志
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();//這里可以在systrace中看到
    ......
    //這里嘗試去dequeueBuffer,因?yàn)檫@時(shí)SurfaceFlinger對(duì)應(yīng)Layer的slot還沒(méi)有分配buffer,這時(shí)SurfaceFlinger會(huì)回復(fù)的flag會(huì)有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)志就會(huì)發(fā)出一次requestBuffer
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
        ......
    }
    ......
}
  1. 因?yàn)閼?yīng)用側(cè) Surface 對(duì)象中 包含的是 GraphicBufferProducer 的 binder client端對(duì)象衷佃,所以實(shí)際的調(diào)用是在
    Surfaceflinger 進(jìn)程的 server 端 BufferQueueProducer.cpp,注意dequeueBuffer 實(shí)際返回的是一個(gè)空間 slot
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,置個(gè)標(biāo)記
    }
    ......
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        ......
        //新創(chuàng)建一個(gè)新的GraphicBuffer給到對(duì)應(yīng)的slot
        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
               width, height, format, BQ_LAYER_COUNT, usage,
               {mConsumerName.string(), mConsumerName.size()});
        ......
               mSlots[*outSlot].mGraphicBuffer = graphicBuffer;//把GraphicBuffer給到對(duì)應(yīng)的slot
        ......
    }
    ......
    return returnFlags;//注意在應(yīng)用第一次請(qǐng)求buffer, dequeueBuffer返回時(shí)對(duì)應(yīng)的GraphicBuffer已經(jīng)創(chuàng)建完成并給到了對(duì)應(yīng)的slot上蹄葱,但返回給應(yīng)用的flags里還是帶有BUFFER_NEEDS_REALLOCATION標(biāo)記的
}

根據(jù) client 端的調(diào)用氏义,會(huì)繼續(xù)調(diào)用到requestBuffer

status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
    ATRACE_CALL();

    mSlots[slot].mRequestBufferCalled = true;
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}

tips: 為什么不在 dequeueBuffer時(shí),直接返回 GraphicBuffer 對(duì)象图云,而是返回一個(gè)空閑的 slot惯悠,再調(diào)用requestBuffer去獲得一個(gè) GraphicBuffer?
因?yàn)檫@個(gè)接口調(diào)用太頻繁了,比如在90FPS的設(shè)備上竣况,一秒鐘該接口要執(zhí)行90次克婶,太頻繁了,而且這個(gè)信息只需要傳遞一次就可以了,如果每次這個(gè)接口都要帶上GraphicBuffer的信息情萤,傳輸了很多冗余數(shù)據(jù)鸭蛙,所以不如加入一個(gè)新的api(requestBuffer)來(lái)完成GraphicBuffer傳遞的事情

GraphicBuffer 繼承了 Parcel 對(duì)象,可以通過(guò)Binder跨進(jìn)程傳遞

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末筋岛,一起剝皮案震驚了整個(gè)濱河市娶视,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌睁宰,老刑警劉巖肪获,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柒傻,居然都是意外死亡孝赫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)诅愚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)寒锚,“玉大人,你說(shuō)我怎么就攤上這事违孝∩睬埃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵雌桑,是天一觀的道長(zhǎng)喇喉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)校坑,這世上最難降的妖魔是什么拣技? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮耍目,結(jié)果婚禮上膏斤,老公的妹妹穿的比我還像新娘。我一直安慰自己邪驮,他們只是感情好莫辨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著毅访,像睡著了一般沮榜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喻粹,一...
    開(kāi)封第一講書(shū)人閱讀 49,856評(píng)論 1 290
  • 那天蟆融,我揣著相機(jī)與錄音,去河邊找鬼守呜。 笑死型酥,一個(gè)胖子當(dāng)著我的面吹牛山憨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冕末,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼萍歉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了档桃?” 一聲冷哼從身側(cè)響起枪孩,我...
    開(kāi)封第一講書(shū)人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藻肄,沒(méi)想到半個(gè)月后蔑舞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘹屯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年攻询,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片州弟。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钧栖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婆翔,到底是詐尸還是另有隱情拯杠,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布啃奴,位于F島的核電站潭陪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏最蕾。R本人自食惡果不足惜依溯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘟则。 院中可真熱鬧黎炉,春花似錦、人聲如沸醋拧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)趁仙。三九已至,卻和暖如春垦页,著一層夾襖步出監(jiān)牢的瞬間雀费,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工痊焊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盏袄,地道東北人忿峻。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辕羽,于是被迫代替她去往敵國(guó)和親逛尚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容