BufferQueue 和 gralloc
理解 Android 圖形系統(tǒng),我們從場(chǎng)景背后的 BufferQueue 和 gralloc HAL 開始。
BufferQueue 類是 Android 中所有圖形的核心。它的角色很簡(jiǎn)單:連接產(chǎn)生圖形數(shù)據(jù)緩沖區(qū)的東西(生產(chǎn)者)和接受數(shù)據(jù)來顯示或進(jìn)一步處理的東西(消費(fèi)者)。幾乎所有在系統(tǒng)中移動(dòng)圖形數(shù)據(jù)緩沖區(qū)的東西都依賴于 BufferQueue。
gralloc 內(nèi)存分配器執(zhí)行緩沖區(qū)分配,且通過一個(gè)供應(yīng)商特有的 HAL 接口(參考 hardware/libhardware/include/hardware/gralloc.h)實(shí)現(xiàn)。alloc() 期待接收的參數(shù)為 (width, height, pixel format) 及一系列使用標(biāo)記(下面詳述)。
BufferQueue 生產(chǎn)者和消費(fèi)者
基本的用法很直接:生產(chǎn)者請(qǐng)求一塊空閑的緩沖區(qū) (dequeueBuffer()),指定一系列特性,包括寬度,高度,像素格式,和使用標(biāo)記。生產(chǎn)者填充緩沖區(qū),并將它返回給隊(duì)列 (queueBuffer())。隨后,消費(fèi)者獲得緩沖區(qū)(acquireBuffer()) 并使用緩沖區(qū)的內(nèi)容。當(dāng)消費(fèi)者完成時(shí),它將緩沖區(qū)返回給隊(duì)列(releaseBuffer())。
最近 Android 設(shè)備支持 同步框架,這使得系統(tǒng)可以與能夠異步處理圖形數(shù)據(jù)的硬件組件結(jié)合使用。比如,生產(chǎn)者可以提交一系列 OpenGL ES 繪制命令,然后在渲染完成之前加入輸出緩沖區(qū)隊(duì)列。緩沖區(qū)伴隨著內(nèi)容準(zhǔn)備就緒時(shí)發(fā)出信號(hào)的柵欄。當(dāng)緩沖區(qū)返回到空閑列表時(shí),第二個(gè)柵欄隨附緩沖區(qū),因此消費(fèi)者可以釋放緩沖區(qū),同時(shí)內(nèi)容仍在使用中。當(dāng)緩沖區(qū)移動(dòng)通過系統(tǒng)時(shí),這種方法可以提升延遲和吞吐量。
隊(duì)列的一些特性,比如它可以持有的最大緩沖區(qū)數(shù)量,由生產(chǎn)者和消費(fèi)者聯(lián)合決定。然而,BufferQueue 負(fù)責(zé)根據(jù)需要分配緩沖區(qū)。除非特性改變,否則緩沖區(qū)將保留;比如,如果生產(chǎn)者請(qǐng)求了一個(gè)大小不同的緩沖區(qū),老的緩沖區(qū)將釋放,新的緩沖區(qū)將根據(jù)需要分配。
生產(chǎn)者和消費(fèi)者可以位于不同的進(jìn)程中。當(dāng)前,消費(fèi)者總是創(chuàng)建并擁有數(shù)據(jù)結(jié)構(gòu)。在更老的版本中,只有生產(chǎn)者一端是 binder 化的 (比如生產(chǎn)者可以在一個(gè)遠(yuǎn)程進(jìn)程中,但消費(fèi)者必須位于隊(duì)列創(chuàng)建的進(jìn)程中)。Android 4.4 及之后的發(fā)行版采用了一個(gè)更加通用的實(shí)現(xiàn)。
BufferQueue 從來不拷貝緩沖區(qū)的內(nèi)容 (像那樣移動(dòng)大量數(shù)據(jù)將是非常低效的)。相反,緩沖區(qū)總是通過句柄傳遞。
gralloc HAL 使用標(biāo)記
gralloc 分配器不僅僅是另外一種在本地堆上分配內(nèi)存的方式;在某些情形下,分配的內(nèi)存可能不是高速緩存一致的,或者可能完全不能從用戶空間訪問。分配的性質(zhì)由使用標(biāo)記決定,這包括這樣的一些屬性:
- 從軟件訪問內(nèi)存的頻率有多高 (CPU)
- 從硬件訪問內(nèi)存的頻率有多高 (GPU)
- 內(nèi)存是否會(huì)被用作 OpenGL ES (GLES) 紋理
- 內(nèi)存是否會(huì)被視頻編碼器使用
比如,如果你的格式指定 RGBA 8888 像素,然后你指出緩沖區(qū)將從軟件訪問 (意味著你的應(yīng)用將直接接觸像素),然后分配器必須一個(gè)緩沖區(qū),其中每像素 4 個(gè)字節(jié),且以 R-G-B-A 的順序。相反,如果你說緩沖區(qū)將只從硬件訪問,并作為一個(gè) GLES 紋理,分配器可以做任何 GLES 驅(qū)動(dòng)想要的事情 - BGRA 順序,非線性布局,替代顏色格式,等等。允許硬件使用它喜歡的格式可以提升性能。
一些值在某一平臺(tái)上無法結(jié)合。比如,視頻編碼器標(biāo)記可以請(qǐng)求 YUV 像素,于是添加軟件訪問和指定 RGBA 8888 將失敗。
gralloc 分配器返回的句柄可以通過 Binder 在進(jìn)程之間傳遞。
使用systrace跟蹤BufferQueue
要真正理解圖形緩沖區(qū)如何移動(dòng),則使用 systrace。系統(tǒng)級(jí)的圖形代碼可以很好地進(jìn)行探索,就像許多相關(guān)的應(yīng)用程序框架代碼一樣。如何高效使用 systrace 的完整描述將需要一篇相當(dāng)長(zhǎng)的文檔。首先啟用 gfx,view 和 sched 標(biāo)簽。你也將在 trace 中看到 BufferQueues。如果你之前已經(jīng)在使用 systrace 了,你可能已經(jīng)看到過它們但可能不確定它們是什么。舉個(gè)例子,如果你在 Grafika's "Play video (SurfaceView)" 運(yùn)行時(shí)獲取 trace,標(biāo)簽為 SurfaceView 的行告訴你在任何給定時(shí)刻有多少緩沖區(qū)被加入隊(duì)列。
值在應(yīng)用活躍時(shí)增加 - MediaCodec 解碼器觸發(fā)幀的渲染 - 而在 SurfaceFlinger 工作,消費(fèi)緩沖區(qū)時(shí)減小。當(dāng)視頻的幀率為 30 fps 時(shí),隊(duì)列的值將在 0 到 1 之間變動(dòng),因?yàn)?~60 fps 顯示可以輕松地跟蹤源。(還要注意 SurfaceFlinger 只有在有工作要做時(shí)才喚醒,而不是美妙 60 次。系統(tǒng)努力試圖避免工作,并且如果沒有東西更新屏幕的話完全禁用 VSYNC。)
如果你切換到 Grafika's "Play video (TextureView)" 并獲取一個(gè) trace,你將看到標(biāo)簽為 com.android.grafika/com.android.grafika.PlayMovieActivity 的行。這是主 UI 層,它只是另一個(gè) BufferQueue。由于 TextureView 渲染到 UI 層 (而不是一個(gè)分離的層),你將在這里看到所有的視頻驅(qū)動(dòng)的更新。
關(guān)于 systrace 工具的更多信息,請(qǐng)參考 Systrace documentation。
原文
總結(jié)
以上是生活随笔為你收集整理的BufferQueue 和 gralloc的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 图形架构
- 下一篇: HiKey960 开发板 android