Android 12(S) 圖形顯示系統 - BufferQueue的工作流程(八)

二的次方發表於2022-03-20

題外話


最近總有一個感覺:在不斷學習中,越發的感覺自己的無知,自己是不是要從“愚昧之巔”掉到“絕望之谷”了,哈哈哈?

鄧寧-克魯格效應

Android 12(S) 圖形顯示系統 - BufferQueue的工作流程(八)

 

一、前言


前面的文章中已經講解了如何去建立一個Surface,也講了一些操作Surface的知識,接下來就是如何利用這個Surface進行繪圖呢?

在此開始講解buffer queue的工作流程,看看圖形資料是怎樣流轉的? 圖形緩衝區的申請和消費流程是怎樣的?有哪些核心類?等等問題在接下來的文章中陸續展開。

這篇文章中,先介紹一些基本概念的東西,幫助後續內容展開打下基礎。

  • 生產者與消費者模型
  • 關於圖形緩衝區佇列的核心類
  • BufferState介紹
  • BufferSlot介紹
  • 一些buffer陣列的介紹

二、生產者與消費者模型


在Android 12系統中,BLASTBufferQueue中完成buffer queue相關元件的初始化。整個生產消費模型都在客戶端,圖形緩衝區的出隊、入隊、獲取等操作都在客戶端完成,預示著生產者模型從遠端通訊變成了本地通訊。帶來的改變就是客戶端需要通過事務Transaction來向SF端提交Buffer與圖層的屬性。
 

三、關於圖形緩衝區佇列的核心類


先給出一個涉及到的相關類的關係圖,這幅圖並不完整,很多細節也沒有呈現出來,只是大概描述各元素間的關係,便於我們看到全貌。

其中幾個比較重要的類,也是後面出場次數比較多的有:

  • BLASTBufferQueue
  • BufferQueueCore
  • BufferQueueProducer
  • BufferQueueConsumer
  • Surface
  • SurfaceControl

 

四、BufferSlot介紹


原始碼

/frameworks/native/libs/gui/include/gui/BufferSlot.h

定義

BufferSlot理解為緩衝槽,一個存放buffer及其資訊的地方。這個結構體中主要有如下內容:

Android 12(S) 圖形顯示系統 - BufferQueue的工作流程(八)

我們主要看一下幾個成員變數:

  • 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() {...}

Android 12(S) 圖形顯示系統 - BufferQueue的工作流程(八)

狀態

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)的值來進行判斷的,如下表格就是各種狀態下各個變數的組合情況:

Android 12(S) 圖形顯示系統 - BufferQueue的工作流程(八)

 

六、幾個佇列/陣列大概解釋


在圖形緩衝區佇列的邏輯中,有幾處佇列、陣列,我們大概看一看他們代表了什麼意思。

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) 圖形顯示系統 - 開篇

 


Android 12(S) 圖形顯示系統 - BufferQueue的工作流程(八)

相關文章