[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 層和硬件層。