[Android禪修之路] 解讀 GraphicBuffer 之 Framework 層

語言: CN / TW / HK

theme: channing-cyan

[Android禪修之路] 解讀 GraphicBuffer 之 Framework 層

Android禪修之路

Android禪修之路 解讀 GraphicBuffer 開篇

一 前言

GraphicBuffer 是 SurfaceFlinger 中一塊重要的內容, 它涉及到了我們應用程式的資料是如何和SurfaceFlinger進行傳遞的。

在介紹 GraphicBuffer 之前,我們先提出這樣一個問題:我們應用程式的介面資料,是如何傳遞給 SurfaceFlinger 進行合成和顯示的。是 Binder 嗎?顯然不是,Binder 傳遞不了這麼大的資料。那麼是共享記憶體嗎,早期的介面資料的確是通過這種方式傳遞的,但是那已經是很早之前了。

前面我們介紹了SurfaceFlinger中的生產者和消費者模型, 在生產者申請 buffer 的時候, 如果拿到的 Slot 沒有和 GraphicBuffer 進行繫結, 那麼就會先建立一個 GraphicBuffer , 然後進行繫結

從這一篇開始,我們就來探究 GraphicBuffer 的工作原理。

1.1 GraphicBuffer 的建立

在前面介紹 dequeueBuffer 申請緩衝區的時候, 我們還說了一種邏輯, 那就是從 BufferQueue 中拿到的 slot 沒有關聯的 GraphicBuffer , 那麼這種情況下還需要單獨建立 GraphicBuffer ,這裡我們就來看看 GraphicBuffer 的建立邏輯

cpp [frameworks/native/libs/gui/BufferQueueProducer.cpp] if (returnFlags & BUFFER_NEEDS_REALLOCATION) { // 如果拿到的緩衝區的 flag 為 BUFFER_NEEDS_REALLOCATION ,就建立一個 GraphicBuffer sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( width, height, format, BQ_LAYER_COUNT, usage, {mConsumerName.string(), mConsumerName.size()}); }

在之前解讀 BufferQueue的時候介紹了 BufferQueue 中的生產者和消費者,以及最重要的四個函式,其中的生產者在生成內容的時候,就會有這麼一個過程; 1. 生產者向 BufferQueue 中申請 slot(緩衝槽) 2. 生產者拿到 slot,但是 slot 並沒有關聯對應的 GraphicBuffer(緩衝區) 3. 生產者建立一個緩衝區,並將它與緩衝槽相關聯。

如上程式碼,就是步驟2中的一個片段。接下來,我們看 GraphicBuffer 建立時的具體流程。

1.2 GraphicBuffer的建構函式

```cpp [frameworks/native/libs/ui/GraphicBuffer.cpp] GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inUsage, std::string requestorName) : GraphicBuffer(inWidth, inHeight, inFormat, 1, static_cast(inUsage), requestorName) { }

GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, std::string requestorName) : GraphicBuffer() { mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage, std::move(requestorName)); } ```

GraphicBuffer 的建構函式非常簡單, 它只是呼叫了一個初始化函式 initWithSize。

1.3 GraphicBuffer::initWithSize

```cpp status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, std::string requestorName) { // 獲取一個 GraphicBufferAllocator 物件, 這個物件是一個單例 // GraphicBufferAllocator 主要負責 GraphicBuffer 的記憶體分配 GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); uint32_t outStride = 0; // 分配一塊制定寬高的 GraphicBuffer status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount, inUsage, &handle, &outStride, mId, std::move(requestorName)); if (err == NO_ERROR) { // 通過 GraphicBufferMapper 將這塊 GraphicBuffer 的引數記錄下來 // GraphicBufferMapper 負責的是 GraphicBuffer 的記憶體對映 mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); // 初始化引數 width = static_cast(inWidth); height = static_cast(inHeight); format = inFormat; layerCount = inLayerCount; usage = inUsage; usage_deprecated = int(usage); stride = static_cast(outStride); } return err; }

```

這裡的初始化函式中,建立了一個 GraphicBufferAllocator 物件,這個我們申請的 GraphicBuffer 記憶體,其實是通過 GraphicBufferAllocator 這個物件進行分配的。

從這裡開始,就需要注意了,因為在建立 GraphicBuffer 並分配記憶體的時候,會通過一個個物件不斷的呼叫,從 Framework 層一直到最後的硬體層。

首先我們來看 GraphicBufferAllocator 這個類。

二 GraphicBufferAllocator

GraphicBufferAllocator 它是一個單例,外部使用時可以通過它來為 GraphicBuffer 來分配記憶體,Android 系統是為了遮蔽不同硬體平臺的差異性,所以使用它來為外部提供一個統一的介面。

2.1 GraphicBufferAllocator::allocate

GraphicBufferAllocator::allocate 函式就是分配記憶體的具體函式, 它又通過呼叫一個 mAllocator 物件的 allocate 來分配記憶體, 這個 mAllocator 是一個 GrallocAllocator 的指標物件。GrallocAllocator 定義在frameworks/native/libs/ui/include/ui/Gralloc.h,它有幾個實現分別是 定義在 Gralloc3.h 中的 Gralloc3Allocator , 和定義在 Gralloc2.h 中的 Gralloc2Allocator

cpp [frameworks/native/libs/ui/GraphicBufferAllocator.cpp] status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride, uint64_t /*graphicBufferId*/, std::string requestorName) { // 如果寬或者高為0, 則將寬高設定為1 if (!width || !height) width = height = 1; // 如果圖層的數量少於1, 則將圖層的數量設定為1 if (layerCount < 1) layerCount = 1; // 移除呼叫者中的無效位 usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); // 分配記憶體,使用的是 GrallocAllocator 指標,根據不同的版本有哦不同的實現, // 這裡我們假設它的實現是 Gralloc3Allocator status_t error = mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle); if (error == NO_ERROR) { // 初始化引數 Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); uint32_t bpp = bytesPerPixel(format); alloc_rec_t rec; rec.width = width; rec.height = height; rec.stride = *stride; rec.format = format; rec.layerCount = layerCount; rec.usage = usage; rec.size = static_cast<size_t>(height * (*stride) * bpp); rec.requestorName = std::move(requestorName); list.add(*handle, rec); return NO_ERROR; } else { return NO_MEMORY; } }

GraphicBufferAllocator 有兩個實現類, 我們來看它其中的一個實現類 Gralloc3Allocator

2.2 Gralloc3Allocator

GrallocAllocator 有多個實現版本,Gralloc3Allocator 就是實現 Gralloc3 的版本

2.2.1 Gralloc3Allocator的定義

在 Gralloc3Allocator 定義的建構函式中,只有一個引數,就是 Gralloc3Mapper。這個引數在後面做記憶體分配的時候會用上,接著來看它建構函式具體的實現

```c [frameworks/native/libs/ui/include/ui/Gralloc3.h]

class Gralloc3Allocator : public GrallocAllocator { public: // Gralloc3Allocator 的建構函式需要傳遞一個 Gralloc3Mapper,因為分配記憶體依賴對映器 mapper Gralloc3Allocator(const Gralloc3Mapper& mapper);

bool isLoaded() const override;

std::string dumpDebugInfo() const override;

// 它只有一個函式,就是分配記憶體
status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
                  uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
                  buffer_handle_t* outBufferHandles) const override;

private: const Gralloc3Mapper& mMapper; // 真正實現分配記憶體的則是 mAllocator,這是通過 HAL 呼叫。 // HAL 層呼叫使用的通訊方式就是 HIDL,其實和我們使用的 AIDL 是一樣的原理。 sp mAllocator; };

```

2.2.2 Gralloc3Allocator的建構函式

cpp [frameworks/native/libs/ui/Gralloc3.cpp] Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) { mAllocator = IAllocator::getService(); if (mAllocator == nullptr) { return; } }

Gralloc3Allocator 的建構函式會傳遞一個 Gralloc3Mapper 作為引數, 並將它賦值給 mMapper , 當然, 其中最關鍵的還是通過一個 IAllocator::getService() 獲取到了一個 mAllocator 物件, 這個 mAllocator 物件就是 hardware::graphics::allocator::V3_0::IAllocator 的指標。

看到這裡,我們大概就能猜到,這和我們常見的程序間獲取服務的方式很相似,在我們在應用中使用系統服務時,也是先通過 getService 拿到註冊好的服務,然後再通過這個 Bp 物件,呼叫對應的服務函式。

2.2.3 Gralloc3Allocator的allocate

然後我們看 Gralloc3Allocator 分配記憶體的函式

```cpp status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t outStride, buffer_handle_t outBufferHandles) const {

// 定義一個緩衝區的描述資訊 descriptorInfo , 並將緩衝區的相關引數都封裝到這個描述資訊 descriptorInfo 中
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);

BufferDescriptor descriptor;
// 通過之前建構函式中的 mMapper 物件來建立一個描述資訊
status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
                                          static_cast<void*>(&descriptor));
if (error != NO_ERROR) {
    return error;
}

// 通過 mAllocator 這個 Bp 物件來呼叫對應的系統服務
// 呼叫到 hardware::graphics::allocator::V3_0::IAllocator 的 allocate 函式
auto ret = mAllocator->allocate(descriptor, bufferCount,
                                [&](const auto& tmpError, const auto& tmpStride,
                                    const auto& tmpBuffers) {
                                    error = static_cast<status_t>(tmpError);
                                    if (tmpError != Error::NONE) {
                                        return;
                                    }

                                    // import buffers
                                    for (uint32_t i = 0; i < bufferCount; i++) {
                                        error = mMapper.importBuffer(tmpBuffers[i],
                                                                     &outBufferHandles[i]);
                                        if (error != NO_ERROR) {
                                            for (uint32_t j = 0; j < i; j++) {
                                                mMapper.freeBuffer(outBufferHandles[j]);
                                                outBufferHandles[j] = nullptr;
                                            }
                                            return;
                                        }
                                    }
                                    *outStride = tmpStride;
                                });

// 確保核心驅動程式看到 BC_FREE_BUFFER 並立即關閉fds
hardware::IPCThreadState::self()->flushCommands();
return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);

}

`` Gralloc3Allocator 的記憶體分配主要用到了兩個物件,分別是繼承自Gralloc::GraphicBufferMapper的 mMapper,和繼承自GraphicBufferAllocator` 的 mAllocator。

mAllocator 是通過 HAL 建立的物件, 這個 IAllocator 是一個 HIDL 介面, HIDL 其實就是 hardware 層的 AIDL , 它和我們應用層的 AIDL 其實是一樣的, 在我們應用層, 如果想使用 AIDL 進行跨程序通訊, 需要定義一個 aidl 字尾名的檔案, 而在系統的 HAL 層, 如果想使用 HIDL 進行跨進行通訊也是類似, 需要定義一個 hal 字尾名的檔案.。

例如 IAllocator 這個介面,其實就定義中 hardware/interfaces/graphics/allocator/3.0/ 路徑下的 IAllocator.hal 中

```hal [hardware/interfaces/graphics/allocator/3.0/IAllocator.hal] package android.hardware.grap[email protected]; import [email protected];

// 這是一個介面 interface IAllocator { // 除錯相關的方法,不關心 dumpDebugInfo() generates (string debugInfo);

// 按照描述符的屬性分配緩衝區
allocate(BufferDescriptor descriptor, uint32_t count)
    generates (Error error,
               uint32_t stride,
               vec<handle> buffers);

}; ```

IAllocator.hal 定義的方法很簡單, 它只有一個 allocate 函式,這個就是我們用來分配記憶體的函式。我們會拿到 Bp 物件來進行呼叫,具體的實現則是在 Bn 中。

它實際上就是呼叫到了 HAL 部分的 allocate,這其實就有些像我們呼叫系統函式時的過程,也是通過 AIDL 完成的,只不過這裡就是通過 HIDL 完成的。接下來我們需要知道這個 HAL 是如何完成工作的。

注意, 我們之前通過 IAllocator 呼叫了兩個函式, 其中還有一個函式是 getService ,而這裡的 hal 檔案中我們並沒有看到這個函式, 那麼這個函式是哪裡來的呢 其實在編譯的時候, 還會生成一個檔案 AllocatorAll.cpp , 這個檔案就包含了我們之前呼叫的 getService

2.2.4 getService

```cpp [out/soong/.intermediates/hardware/interfaces/graphics/allocator/3.0/[email protected]_genc++/gen/android/hardware/graphics/allocator/3.0/AllocatorAll.cpp] ::android::sp IAllocator::getService(const std::string &serviceName, const bool getStub) { return ::android::hardware::details::getServiceInternal(serviceName, true, getStub); }

```

到此,Framework 部分的內容就全部介紹完了,還是比較簡單的,因為真正的工作都是發生在 HAL 層和硬體層。