Basic Use Model
BQ一開始的use model很直接, Producer利用dequeueBuffer從BQ要一塊GraphicBuffer, 資料寫入後, 利用queueBuffer將該Buffer放進BQ. Consumer需要資料時, 利用acquireBuffer從BQ取後一塊GraphicBuffer做處理, 處理完後, 利用releaseBuffer將該Buffer還給BQ.更進一步想, Producer與Consumer兩個產生與消耗Buffer的速度不一樣, 而且Buffer的數量也是有限, 不可能需要就Allocate, 因此有些限制就出現. 因此當Producer產生資料的速度比較快時, 就會遇到全部的Buffer都寫完資料, 但是Consumer還沒消耗完任何一個, 正常的情況Producer必須等Consumer釋放出Buffer才能繼續做事. 如果是Consumer比較消耗速度比較快, Consumer就必須等Producer. 我們可以用下面的圖來說明BQ基本的use model.
每一個GraphicBuffer在BQ裡都有一個Slot用來儲存該Buffer的位置, 而每一個Slot都有一個state, state有以下4種
- FREE
- DEQUEUED
Slot裡面的Buffer已經交給Producer, 當Producer使用完後, 它會利用queueBuffer將該Buffer還給BQ, 而該Slot的狀態會變成QUEUED. 但是Producer也可以不使用這塊Buffer, 利用cancelBuffer將該Buffer還給BQ, 但該Slot的狀態會設成FREE.
- QUEUED
在這個狀態下, BQ擁有這個Slot與Buffer, 只有Consumer可以利用acquireBuffer取得這個Slot與Buffer的所有權, 然後使用它. 當Consumer取得所有權後, 狀態會變成ACQUIRED.
- ACQUIRED
Consumer用完這個Buffer後, 必須將它還給BQ, 稍後Producer才能再利用這塊Buffer寫入資料. Consumer可以利用releaseBuffer將它的所有權交還給BQ, 它的狀態也會變成FREE供Producer稍後使用.
Sync Model
Basic Sync Model的運作有一個問題, 也就是當Producer或Consumer將GraphicBuffer還給BQ時, 必須確保Buffer已經處理完了, 但是其實這會造成performance的問題. 以GLES(OpenGL ES)當作Producer為例, CPU下完GLES command後, 不代表GPU完成了所有的動作, 從CPU端, 我們可以利用glFinish或其它方式等GPU做完才利用queueBuffer將Buffer交給BQ, 但這其實是在浪費CPU的resource, 我們可以延後這個等待的動作, 延後到Consumer真的拿到這塊Buffer時再做. 對Consumer也是一樣, 當Consumer利用releaseBuffer將Buffer還給BQ時, 它不用等這塊Buffer真的沒在用, 它可以先還給BQ, 當Producer拿到Buffer時再等待Buffer真的沒有使用後再寫入新的資料. Sync Model利用fence來做到這個功能. 在sync model裡, 每一個Slot都有一個fence物件, 當一個Slot的狀態是QUEUED時, 這個fence可以稱為acquire fence, 當它的狀態是FREE時, 可稱為release fence. Acquire fence指的是當Consumer利用acquireBuffer取得Buffer時, 需要等待fence完成才能使用, 而release fence指的是當Producer利用dequeueBuffer取得Buffer時, 需要等待fence完成才能開始寫入資料.
Attach/Detach Model
在現今的BQ interface裡, 我們可以觀察到有多出幾個function, 分別是attachBuffer, detachBuffer, 這兩個function不論在Producer或Consumer都可以觀察到. attachBuffer會從BQ裡找到一個FREE的Slot, 並與Producer或Consumer提供的GraphicBuffer聯結. 這與一般的dequeueBuffer不同, 在dequeueBuffer時, 如果FREE的Slot本身沒有GraphicBuffer的話, dequeuBuffer會利用allocator去產生, 但是attachBuffer則是Producer/Consumer提供一個已經存在的Buffer, 它的目的是要將一個GraphicBuffer放入BQ裡, 這個GraphicBuffer可以是其它BQ所產生, 也可以是Producer/Consumer利用allocator產生的. AOSP裡有一個StreamSplitter, 將接收到的GraphicBuffer, 交給多個Consumer去使用. StreamSplitter本身是一個Consumer, 但同時也是一個Producer. 當Consumer被通知有新的Buffer放入BQ時, 它會做以下的事mInput->acquireBuffer(&acquiredBuffer);
mInput->detachBuffer(acquiredBufer.mBuf);
foreach producer in all downstream BQ {
output->attachBuffer(&slot, acquiredBuffer.mGraphicBuffer);
output->queueBuffer(slot)
}
mInput就是StreamSplitter的Consumer端, 利用acquireBuffer取得可使用的Buffer後, 而利用detachBuffer將它從upstream BQ移除(Slot的狀態會從ACQUIRED變成FREE), upstream BQ就沒這個GraphicBuffer的存在. StreamSplitter再針對每個downstream BQ的producer端, 將取得的GraphicBuffer利用attachBuffer放入BQ裡的一個Slot, 這時候該Slot的狀態會從FREE變成ACQUIRED, 再利用queueBuffer放入downstream BQ裡. 利用attachBuffer/detachBuffer就可以簡單的將一個GraphicBuffer提供給多個BQ使用.
BQ Sample In SF
Android的每個monitor都會有一個EGLSurface, 這個EGLSurface是一個GraphicBuffer的Producer, SF會記錄每個monitor要顯示的Layer, 將每一個monitor的Layer做Composition後(可以用GLES或overlay), 通過BQ交給FramebufferSurface, FramebufferSurface收到新的畫面, 再通知HWComposer顯示在使用者面前.
GraphicBuffer
GraphicBuffer用來儲存Producer與Consumer之間要交換的資料, 它是ANativeWindowBuffer的subclass, 記錄著由gralloc HAL傳回的handle. gralloc HAL負責從Graphic Memory裡分配一塊記憶體, 傳回的handle代表這塊memory. Handle的type是native_handle_t, 基本上這個type是很多class的base class.typedef struct
{
int version;
int numFds;
int numInts;
int data[0];
} native_handle_t;
ANativeWindowBuffer記錄Buffer基本的屬性, 像是width, height, stride, format等, 最重要的是它記錄著一個native_handle_t的pointer.
typedef struct ANativeWindowBuffer
{
struct android_native_base_t common; // for reference counting
int width;
int height;
int stride;
int format;
int usage;
void* reserved[2];
buffer_handle_t handle; // alias of native_handle_t
...
}
而GraphicBuffer則是繼承自ANativeWindowBuffer與RefBase的class, 它除了簡化ANativeWindowBuffer的使用外, 也提供了對gralloc HAL的interface, 像是alloc, free與lockXXX/unlockXXX等等.
GraphicBuffer用了兩個helper class來使用gralloc HAL, 分別是GraphicBufferAllocator與GraphicBufferMapper. Allocator負責使用HAL的alloc/free, Mapper則是使用lockXXX/unlockXXX.
sp producer;
sp consumer;
BufferQueue::createBufferQueue(&producer, &consumer,
new GraphicBufferAlloc());
myConsumer c = new myConsumer(customer, ...);
myProducer p = new myProducer(producer, ...);
要注意的是, BufferQueueConsumer/Producer的用意並不是要我們去寫它們兩個的subclass, 而是用它們. 對Consumer而言, AOSP還提供ConsumerBase與GLConsumer可以使用, 一般來講, Consumer會繼承自這個這兩個Class. GLConsumer是ConsumerBase的subclass, 它可以將acqure的GraphicBuffer當作GLES的texture使用.
而GraphicBuffer則是繼承自ANativeWindowBuffer與RefBase的class, 它除了簡化ANativeWindowBuffer的使用外, 也提供了對gralloc HAL的interface, 像是alloc, free與lockXXX/unlockXXX等等.
GraphicBuffer用了兩個helper class來使用gralloc HAL, 分別是GraphicBufferAllocator與GraphicBufferMapper. Allocator負責使用HAL的alloc/free, Mapper則是使用lockXXX/unlockXXX.
BufferSlot
在BQ裡, 最多可以有64個Slot, 每個Slot有自已的State, 分別是FREE, QUEUED, DEQUEUED, ACQURED. 這些State是跟著Slot, 而不是GraphicBuffer. 當Consumer或Producer透過acquireBuffer或dequeueBuffer取得一個GraphicBuffer時, BQ也會提供一個Slot Index, 這個時候Slot會有相對應的GraphicBuffer, 當releaseBuffer, cancelBuffer或queueBuffer將Slot還給BQ時, GraphicBuffer也是跟著BufferSlot還給BQ. 但是利用attachBuffer/detachBuffer, 我們將跟Slot與GraphicBuffer的聯連移除. detachBuffer將GrahicBuffer從Slot中移除, Slot的狀態會變成FREE, 但下次這個Slot被dequeueBuffer選上時, 會再allocate一個GraphicBuffer. 被detach的GraphicBuffer可以繼續使用.BufferQueue Creation
AOSP提供了一個Helper class來幫助Programmer寫Producer與Consumer, 在開始研究這些Helper Class之前, 我們可以先了解一下如何使用. BufferQueue::createBufferQueue是一個static function, 它會產生BufferQueueConsumer與BufferQueueProducer的object, 並以IGraphicBufferConsumer與IGraphicBufferProducer的type傳回. BufferQueueConsumer/Producer也是Helper class, 用來簡化Programmer的effort.sp
sp
BufferQueue::createBufferQueue(&producer, &consumer,
new GraphicBufferAlloc());
myConsumer c = new myConsumer(customer, ...);
myProducer p = new myProducer(producer, ...);
要注意的是, BufferQueueConsumer/Producer的用意並不是要我們去寫它們兩個的subclass, 而是用它們. 對Consumer而言, AOSP還提供ConsumerBase與GLConsumer可以使用, 一般來講, Consumer會繼承自這個這兩個Class. GLConsumer是ConsumerBase的subclass, 它可以將acqure的GraphicBuffer當作GLES的texture使用.
Thank you, it's very clear.
回覆刪除