題外話
最近總有一個感覺:在不斷學習中,越發的感覺自己的無知,自己是不是要從“愚昧之巔”掉到“絕望之谷”了,哈哈哈?
鄧寧-克魯格效應
一、前言
前面的文章中已經講解了如何去建立一個Surface,也講了一些操作Surface的知識,接下來就是如何利用這個Surface進行繪圖呢?
在此開始講解buffer queue的工作流程,看看圖形資料是怎樣流轉的? 圖形緩衝區的申請和消費流程是怎樣的?有哪些核心類?等等問題在接下來的文章中陸續展開。
這篇文章中,先介紹一些基本概念的東西,幫助後續內容展開打下基礎。
- 生產者與消費者模型
- 關於圖形緩衝區佇列的核心類
- BufferState介紹
- BufferSlot介紹
- 一些buffer陣列的介紹
二、生產者與消費者模型
三、關於圖形緩衝區佇列的核心類
先給出一個涉及到的相關類的關係圖,這幅圖並不完整,很多細節也沒有呈現出來,只是大概描述各元素間的關係,便於我們看到全貌。
其中幾個比較重要的類,也是後面出場次數比較多的有:
- BLASTBufferQueue
- BufferQueueCore
- BufferQueueProducer
- BufferQueueConsumer
- Surface
- SurfaceControl
四、BufferSlot介紹
原始碼
/frameworks/native/libs/gui/include/gui/BufferSlot.h
定義
BufferSlot理解為緩衝槽,一個存放buffer及其資訊的地方。這個結構體中主要有如下內容:
我們主要看一下幾個成員變數:
- mGraphicBuffer代表一塊圖形緩衝區GraphicBuffer,用於儲存繪製圖形的資料;
- mBufferState型別為BufferState,標記當前buffer slot所處的狀態;
- mNeedsReallocation,是否需要重新分配這個buffer;
- mFence,用於資源同步
關於mFence的解釋,原始碼中有以斷詳細的註釋,我覺得很值得讀一讀:
// mFence is a fence which will signal when work initiated by the
// previous owner of the buffer is finished. When the buffer is FREE,
// the fence indicates when the consumer has finished reading
// from the buffer, or when the producer has finished writing if it
// called cancelBuffer after queueing some writes. When the buffer is
// QUEUED, it indicates when the producer has finished filling the
// buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
// passed to the consumer or producer along with ownership of the
// buffer, and mFence is set to NO_FENCE.
sp<Fence> mFence;
用我蹩腳的英文->中文,我大概直譯一下:
1. mFence是一個圍欄,當buffer的前所有者的工作(即對這個buffer的處理操作)完成時,它會發出訊號;
2. 當buffer處於FREE狀態時,fence指示consumer何時已完成從buffer的讀取,或者如果producer在寫入一些東西后呼叫了cancelBuffer,此時fence指示producer何時已完成寫入;
3. 當buffer處於QUEUED狀態時,它指示producer何時完成buffer的填充(資料寫好了,通知consumer使用);
4. 當buffer處於DEQUEUED/ACQUIRED狀態時,fence已連同buffer的所有權一起傳遞給consumer或producer,並且mFence設定為NO_FENCE;
建構函式,預設其mGraphicBuffer是nullptr,即沒有繫結GraphicBuffer,也就是沒有分配實際的圖形快取了。
BufferSlot()
: mGraphicBuffer(nullptr),
mEglDisplay(EGL_NO_DISPLAY),
mBufferState(),
mRequestBufferCalled(false),
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
mFence(Fence::NO_FENCE),
mAcquireCalled(false),
mNeedsReallocation(false) {
}
五、BufferState介紹
原始碼
/frameworks/native/libs/gui/include/gui/BufferSlot.h
定義
BufferState用於跟蹤記錄一個buffer slot(緩衝槽)所處的狀態。如下這個類圖描述了BufferState中定義的基本內容:
- 用於描述緩衝區狀態的3個uint32_t變數(mDequeueCount/mQueueCount/mAcquireCount)和1個bool變數(mShared);
- 用於查詢緩衝區狀態的函式,isXXX();
- 用於改變/設定緩衝區狀態的函式,比如 void dequeue() {...} and void queue() {...}
狀態
BufferState用於跟蹤記錄一個buffer slot(緩衝槽)所處的狀態。一個buffer可以處於以下5種狀態之一。
狀態 | 說明 |
FREE |
此狀態下buffer可以被producer通過dequeued獲取; slot被BufferQueue所擁有,producer呼叫dequeueBuffer獲取該buffer後其狀態轉為DEQUEUED |
DEQUEUED |
此狀態表示該buffer已經被producer通過dequeued獲取到,但還沒有被queue或cancel。 一旦與該buffer相關聯的fence發出訊號,producer就可以修改buffer的內容了。 這種狀態下slot屬於producer所有,當呼叫queueBuffer or attachBuffer後可轉為QUEUED狀態,或呼叫cancelBuffer or detachBuffer轉為FREE狀態 |
QUEUED |
此狀態表示該buffer已經被producer填充資料,入佇列讓consumer使用。 buffer內容可能會在有限的時間內繼續修改,因此在相關fence發出訊號之前,不得訪問內容。 此時slot歸BufferQueue所有,buffer狀態可以轉為ACQUIRED(via acquireBuffer) 或FREE(另一個buffer非同步模式下入佇列) |
ACQUIRED |
此狀態表示該buffer被consumer取得。fence訊號發出後,消費者就可以訪問其內容了。 slot被consumer所擁有。 當呼叫releaseBuffer (or detachBuffer)可以轉為FREE |
SHARED | 表示此緩衝區正在共享緩衝區模式下使用(還沒太理解這個) |
在顯示系統中,實現流暢的繪製和顯示,一般的buffer大致會經過如下這個流程:
FREE -> DEQUEUED -> QUEUED -> ACQUIRED -> FREE
如下圖描述的狀態轉換的基本邏輯:
如何辨別當前狀態?
狀態是根據3個uint32_t變數(mDequeueCount/mQueueCount/mAcquireCount)和1個bool變數(mShared)的值來進行判斷的,如下表格就是各種狀態下各個變數的組合情況:
六、幾個佇列/陣列大概解釋
在圖形緩衝區佇列的邏輯中,有幾處佇列、陣列,我們大概看一看他們代表了什麼意思。
BufferQueue最多可以跟蹤的buffer的數量
/frameworks/native/libs/ui/include/ui/BufferQueueDefs.h
// BufferQueue will keep track of at most this value of buffers.
// Attempts at runtime to increase the number of buffers past this
// will fail.
static constexpr int NUM_BUFFER_SLOTS = 64;
SlotsType的定義--儲存64個BufferSlot的陣列
/frameworks/native/libs/gui/include/gui/BufferQueueDefs.h
namespace BufferQueueDefs {
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
} // namespace BufferQueueDefs
BufferQueueCore中的buffer slot陣列
/frameworks/native/libs/gui/include/gui/BufferQueueCore.h
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
// allocated for a slot when requestBuffer is called with that slot's index.
BufferQueueDefs::SlotsType mSlots;
// mQueue is a FIFO of queued buffers used in synchronous mode.
// 定義 typedef Vector<BufferItem> Fifo;
Fifo mQueue;
// mFreeSlots contains all of the slots which are FREE and do not currently
// have a buffer attached.
std::set<int> mFreeSlots;
// mFreeBuffers contains all of the slots which are FREE and currently have
// a buffer attached.
std::list<int> mFreeBuffers;
// mUnusedSlots contains all slots that are currently unused. They should be
// free and not have a buffer attached.
std::list<int> mUnusedSlots;
// mActiveBuffers contains all slots which have a non-FREE buffer attached.
std::set<int> mActiveBuffers;
- mSlots :BufferSlot陣列,預設大小是64個,這個陣列會被對映到BufferQueueProducer/BufferQueueConsuer類中;
- mQueue :BufferItem型別的陣列,Producer呼叫queueBuffer後,其實就是queue到這個陣列裡面;
- mFreeSlots :沒有繫結GraphicBuffer且狀態為FREE的BufferSlot集合;
- mFreeBuffers :繫結了GraphicBuffer且狀態為FREE的BufferSlot集合;
- mUnusedSlots:代表當前沒有使用的 BufferSlot 集合,這個和mFreeSlots有什麼差異,還沒搞懂。。。
- mActiveBuffers :繫結了GraphicBuffer且狀態為非FREE的BufferSlot集合;
Tips:
mFreeSlots/mFreeBuffers/mUnusedSlots/mActiveBuffers儲存的都是int型別的index,根據這個index去mSlots中獲取對應的BufferSlot及GraphicBuffer.
我的理解之所以劃分出這麼多不同的陣列,都是為了給 BufferSlot 分類,以便獲取 GraphicBuffer 時更加高效。
BufferQueueProducer中的buffer slot 陣列
看器建構函式:
BufferQueueProducer.cpp 檔案定義
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
bool consumerIsSurfaceFlinger) :
mCore(core),
mSlots(core->mSlots),
BufferQueueProducer.h 標頭檔案定義
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
BufferQueueDefs::SlotsType& mSlots;
BLASTBufferQueue::createBufferQueue中,例項化一個BufferQueueProducer物件,其建構函式在初始化成員變數時,在會直接將前面建立好的 BufferQueueCore 和 mSlots 賦值到 的成員變數mSlots中。
BufferQueueProducer::mSlots 是 BufferQueueCore::mSlots的對映/引用,其實就是一個東東!
BufferQueueConsumer中的buffer slot陣列
BufferQueueConsumer.cpp中的定義:
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
mConsumerName() {}
BufferQueueConsumer.h中的定義:
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
BufferQueueDefs::SlotsType& mSlots;
BLASTBufferQueue::createBufferQueue中,例項化一個BufferQueueConsumer物件,其建構函式在初始化成員變數時,在會直接將前面建立好的 BufferQueueCore 和 mSlots 賦值到 的成員變數mSlots中。
BufferQueueConsumer::mSlots 是 BufferQueueCore::mSlots的對映/引用,其實就是一個東東!
Tips:
BufferQueueProducer和BufferQueueConsumer是BufferQueueCore的友元類,所以可以直接訪問其私有成員。
七、小結
這篇文章主要是講了一些零碎的概念,這些小的知識點理解後,對於後續理解 生產者 - 緩衝區佇列 - 消費者 執行的邏輯十分有幫助。
下一篇中將會講解buffer queue的運作流程&buffer是怎樣在其中流轉的。
必讀:
Android 12(S) 圖形顯示系統 - 開篇