本文涉及的源代碼基于 Android-7.1.1r。
一、Android GUI 框架
SurfaceFlinger 是 Android GUI 的核心,但是從 OpenGL_ES 的角度來看,它也只是個“應用程序”。Android 的顯示系統大致框架圖下圖所示:
下面就“由下向上”來逐一分析該框架。
(1) 顯示驅動
Linux 內核提供了統一的 framebuffer 顯示驅動。設備節點是 /dev/graphics/fb* 或 /dev/fb*,而 fb0 表示第一個 Monitor,當前系統實現中只用到了一個顯示屏。
(2) HAL 層
Android 的 HAL 層提供了 Gralloc,包括 fb 和 gralloc 兩個設備。
- fb 負責打開內核中的 framebuffer,初始化配置,并提供 post,setSwapIntervel 等操作接口;
- gralloc 用于管理幀緩沖區的分配和釋放。
HAL 層還包含另一個重要模塊 —— “Composer”,它為廠商自定制“UI合成”提供了接口。Composer 的直接使用者是 SurfaceFlinger 中的 HWComposer,HWComposer 除了負責管理 Composer 的 HAL 模塊外,還負責 VSync 信號(軟件、硬件)的產生和控制。
(3) FramebufferNativeWindow
FramebufferNativeWindow 是負責 OpenGL ES(通用函數庫) 在 Android 平臺上本地化的中介之一,它將 Android 的窗口系統與 OpenGL ES 產生聯系,為 OpenGL ES 配置本地窗口的是 EGL。
(4) EGL
EGL 負責為 OpenGL ES 配合本地窗口。OpenGL ES 更多的只是一個接口協議,具體實現即可以采用軟件,也可以采用硬件實現,而 EGL 會去讀取 egl.cfg,并根據用戶的設置來動態加載 libagl(軟件實現)或是 libhgl(硬件實現)。
(5) DisplayDevice
SurfaceFlinger 中持有一個成員數組 mDisplays 用來描述系統中支持的各種"顯示設備",具體有那些 Display 是由 SurfaceFlinger 在 readyToRun 中進行判斷并賦值的。DisplayDevice 在初始化的時候會調用 eglGetDisplay,eglCreateWindowSurface 等接口,并利用 EGL 來完成 OpenGL ES 環境的搭建。
(6) OpenGL ES 模塊
很多模塊都可以調用 OpenGL ES 提供的 API,其中就包括 SurfaceFLinger 和 DisplayerDevice。
與 OpenGL ES 的相關的模塊分為以下幾類:
- 配置類:幫助 OpenGL ES 完成配置,包括 EGL,DisplayHardware 都屬這一類。
- 依賴類:OpenGL ES 要運行的起來所依賴的“本地化”的東西,在上圖中指的就是 FramebufferNativeWindow。
- 使用類:使用 OpenGL ES 的用戶,如 DisplayDevice 即扮演了使用者,又扮演了構建 OpenGL ES 的配置者。
二、HAL
HAL 是子系統(顯示系統、音頻系統)與 Linux 內核驅動之間的統一接口。
HAL 需要解決以下問題:
- 硬件的抽象;
- 接口的穩定;
- 靈活的使用。
(1) 硬件抽象
HAL 多數使用 C 語言編寫,而 C 語言不是面向對象的,所以具體的“繼承”關系就沒有像 C++ 或是 Java 這類的面向對象的語言表現的那么直接。在 C 語言中要實現類似的“繼承”關系,只需要讓子類的第一個成員變量是父類結構即可。以 Gralloc 為例,它就是 hw_module_t 的子類,代碼如下:
typedef struct gralloc_module_t {
struct hw_module_t common; // 父類
// 結構體中定義函數指針(結構體中不能定義函數)
int (*registerBuffer)(struct gralloc_module_t const* module,
buffer_handle_t handle);
int (*unregisterBuffer)(struct gralloc_module_t const* module,
buffer_handle_t handle);
int (*lock)(struct gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h,
void** vaddr);
int (*unlock)(struct gralloc_module_t const* module,
buffer_handle_t handle);
int (*perform)(struct gralloc_module_t const* module,
int operation, ... );
int (*lock_ycbcr)(struct gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h,
struct android_ycbcr *ycbcr);
int (*lockAsync)(struct gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h,
void** vaddr, int fenceFd);
int (*unlockAsync)(struct gralloc_module_t const* module,
buffer_handle_t handle, int* fenceFd);
int (*lockAsync_ycbcr)(struct gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h,
struct android_ycbcr *ycbcr, int fenceFd);
void* reserved_proc[3];
} gralloc_module_t;
(2) 接口的穩定
HAL 中的接口必須是穩定不變的,Android 系統中已經預定好了這些接口,如下圖所示(源碼位置:hardware/libhardware/include/hardware):
(3) 靈活的使用
硬件生產商只要按照 Android 提供的硬件要求來實現 HAL 接口,手機開發商只需要移植硬件生產商提供的 HAL 庫就可以了。
三、Android 終端顯示設備 ———— Gralloc 和 Framebuffer
Framebuffer 是 Linux 內核提供的圖形硬件的抽象描述,它占用了系統內存的一部分,是一塊包含屏幕顯示信息的緩沖區。在 Android 中,Framebuffer 提供的設備文件節點是 /dev/graphics/fb*。這里以 sony Xperia 為例,它的 fb 節點如下圖所示:
Android 的子系統不會直接使用內核驅動,而是由 HAL 層來間接引用底層框架。顯示系統也是一樣,它通過 HAL 層來做操作幀緩沖區,而完成這一中介任務的就是 Gralloc。
3.1、Gralloc 模塊的加載
Gralloc 對應的模塊是在 FramebufferNativeWindow(GUI 結構圖中位于 Grlloc 上方) 的構造函數中加載的(在 Androdi-7.1.1 中是通過 Gralloc1.cpp 進行加載的),即:
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); //
hw_get_module 是上層使用(FramebufferNativeWindow)者加載 HAL 庫的入口。lib 庫有以下幾種形式;
gralloc.[ro.hardware].so
gralloc.[ro.product.board].so
gralloc.[ro.board.platform].so
gralloc.[ro.arch].so
當以上文件都不存在時,就使用默認的:
gralloc.default.so
源碼位置:hardware/libhardware/modules/gralloc/,由 gralloc.cpp,framebuffer.cpp 和 mapper.cpp 三個主要文件編譯而成。
3.2、Gralloc 提供的接口
Gralloc 是 hw_module_t 的子類,hw_module_t 代碼如下:
typedef struct hw_module_t {
uint32_t tag;
uint16_t module_api_version;
#define version_major module_api_version
uint16_t hal_api_version;
#define version_minor hal_api_version
const char *id;
const char *name;
const char *author;
struct hw_module_methods_t* methods; // hw_module_t 中必須提供
void* dso;
#ifdef __LP64__
uint64_t reserved[32-7];
#else
uint32_t reserved[32-7];
#endif
} hw_module_t;
typedef struct hw_module_methods_t {
// 函數指針,用于打開設備
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
任何硬件設備的 HAL 庫都必須實現 hw_module_methods_t,該結構體中只有一個函數指針變量,也就是 open,由于函數體內不能定義函數,所以這里使用了函數指針。當上層使用者調用 hw_get_module 時,系統首先會在指定目錄下加載正確的 HAL 庫,然后通過 open 函數打開指定的設備。這里 open 方法對應的實現是 gralloc_device_open()@gralloc.cpp。open接口可以可以幫助上層使用者打開兩種設備:
- define GRALLOC_HARDWARE_FB0:主屏
- define GRALLOC_HARDWARE_GPU0:負責圖形緩沖區的分配和釋放
define 前有“#”,由于格式問題,這里沒有打出。
下面看 gralloc_device_open() 的實現:
// gralloc.cpp
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
// 打開 gralloc 設備或是打開 fb 設備。
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
gralloc_context_t *dev;
dev = (gralloc_context_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free = gralloc_free;
*device = &dev->device.common;
status = 0;
} else {
status = fb_device_open(module, name, device); // 打開 Framebuffer
}
return status;
}
下面看 framebuffer 設備的打開過程:
int fb_device_open(hw_module_t const* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
/* initialize our state here */
fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev)); // 分配 hw_device_t 空間,這只是一個“殼”
memset(dev, 0, sizeof(*dev)); // 初始化
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
// 核心接口
dev->device.common.close = fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0;
private_module_t* m = (private_module_t*)module;
status = mapFrameBuffer(m); // 內存映射 mmap
if (status >= 0) {
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
int format = (m->info.bits_per_pixel == 32)
? (m->info.red.offset ? HAL_PIXEL_FORMAT_BGRA_8888 : HAL_PIXEL_FORMAT_RGBX_8888)
: HAL_PIXEL_FORMAT_RGB_565;
const_cast<uint32_t&>(dev->device.flags) = 0;
const_cast<uint32_t&>(dev->device.width) = m->info.xres;
const_cast<uint32_t&>(dev->device.height) = m->info.yres;
const_cast<int&>(dev->device.stride) = stride;
const_cast<int&>(dev->device.format) = format;
const_cast<float&>(dev->device.xdpi) = m->xdpi;
const_cast<float&>(dev->device.ydpi) = m->ydpi;
const_cast<float&>(dev->device.fps) = m->fps;
const_cast<int&>(dev->device.minSwapInterval) = 1;
const_cast<int&>(dev->device.maxSwapInterval) = 1;
*device = &dev->device.common; // 核心
}
}
return status;
}
其中 fb_context_t 是 framebuffer 內部使用的一個類,它包含了眾多信息,而最終返回的 device 只是其內部的 device.common。這種“通用和差異”并存的編碼風格在 HAL 層非常常見。
fb_context_t 唯一的成員就是 framebuffer_device_t,這是對 frambuffer 設備的統一描述。
struct fb_context_t {
framebuffer_device_t device;
};
一個標準的 fb 設備通常要提供如下的函數實現:
int(post)(struct framebuffer_device_t dev, buffer_handle_t buffer);
將 buffer 數據 post 到顯示屏上。要求 buffer 必須與屏幕尺寸一致,并且沒有被 locked。這樣的話
buffer 內容將在下一次 VSYNC 中被顯示出來。int(setSwapInterval)(struct framebuffer_device_t window, int interval);
設置兩個緩沖區交換的時間間隔int(setUpdateRect)(struct framebuffer_device_t window, int left, int top, int width, int height);
設置刷新區域,需要 framebuffer 驅動支持“update-on-demand”。也就是說在這個區域外的數據很可能
被認為無效。
framebuffer_device_t 中的重要成員變量:
typedef struct framebuffer_device_t {
struct hw_device_t common;
const uint32_t flags; // 用來記錄系統幀緩沖區的標志
const uint32_t width; // 用來描述設備顯示屏的寬度
const uint32_t height; // 用來描述設備顯示屏的高度
const int stride; // 用來描述設備顯示屏的一行有多少個像素點
const int format; // 用來描述系統幀緩沖區的像素格式
const float xdpi; // 用來描述設備顯示屏在寬度上的密度
const float ydpi; // 用來描述設備顯示屏在高度上的密度
const float fps; // 用來描述設備顯示屏的刷新頻率
const int minSwapInterval; // 用來描述幀緩沖區交換前后兩個圖形緩沖區的最小時間間隔
const int maxSwapInterval; // 用來描述幀緩沖區交換前后兩個圖形緩沖區的最大時間間隔
int reserved[8];//保留
// 用來設置幀緩沖區交換前后兩個圖形緩沖區的最小和最大時間間隔
int (*setSwapInterval)(struct framebuffer_device_t* window,int interval);
// 用來設置幀緩沖區的更新區域
int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);
// 用來將圖形緩沖區buffer的內容渲染到幀緩沖區中去
int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
// 用來通知 fb 設備,圖形緩沖區的組合工作已經完成
int (*compositionComplete)(struct framebuffer_device_t* dev);
void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len);
int (*enableScreen)(struct framebuffer_device_t* dev, int enable);
// 保留
void* reserved_proc[6];
} framebuffer_device_t;
變 量 | 描 述 |
---|---|
uint32_t flags | 標志位,指示framebuffer 的屬性配置 |
uint32_t width; uint32_t height; | framebuffer 的寬和高,以像素為單位 |
int format | framebuffer 的像素格式,比如:HAL_PIXEL_FORMAT_RGBA_8888,HAL_PIXEL_FORMAT_RGBX_8888,HAL_PIXEL_FORMAT_RGB_888,HAL_PIXEL_FORMAT_RGB_565 等等 |
float xdpi;float ydpi; | x和y軸的密度(pixel per inch) |
float fps | 屏幕的每秒刷新頻率,假如無法正常從設備獲取的話,默認設置為 60Hz |
int minSwapInterval;int maxSwapInterval; | 該 framebuffer 支持的最小和最大緩沖交換時間 |
我們以下面簡圖來小結對 Gralloc 的分析:
[圖片上傳失敗...(image-f944da-1542183518291)]
四、Android 本地窗口
Native Window為OpenGL與本地窗口系統之間搭建了橋梁。整個GGUI系統至少需要兩種本地窗口:
- 面向管理者(SurfaceFlinger)
SurfaceFlinger 是系統中所有 UI 界面的管理者,需要直接或間接的持有“本地窗口”,此本地窗口是
FramebufferNativeWindow(4.2+ 后被廢棄)。 - 面向應用程序
這類本地窗口是 Surface。
正常情況按照 SDK 向導生成 APK 應用程序,是采用 Skia 等第三方圖形庫,而對于希望使用 OpenGL ES 來完成復雜界面渲染的應用開發者來說,Android 也提供封裝的 GLSurfaceView(或其他方式)來實現圖形顯示。
4.1、FramebufferNativeWindow
EGL 需要根據本地窗口來為 OpenGL/OpenGL ES 創造環境,但是不論哪一類本地窗口都需要和“本地窗口類型”保持一致。
// /frameworks/native/opengl/include/EGL/eglplatform.h
...
typedef HWND EGLNativeWindowType;
#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
#elif defined(__ANDROID__) || defined(ANDROID) // Android 系統
struct ANativeWindow;
struct egl_native_pixmap_t;
typedef struct ANativeWindow* EGLNativeWindowType;
...
#elif defined(__unix__) // unix 系統
...
typedef Window EGLNativeWindowType;
#else
#error "Platform not recognized"
#endif
...
EGLNativeWindowType 在不同系統中對應不同的數據類型,而在 Android 中對應的是 ANativeWindow 指針。
// /system/core/include/system/window.h
struct ANativeWindow
{
...
const uint32_t flags; // 與 Surface 或 update 有關的屬性
const int minSwapInterval; // 最小交換時間間隔
const int maxSwapInterval; // 最大交換時間間隔
const float xdpi; // 水平方向密度 dpi
const float ydpi; // 垂直方向密度 dpi
intptr_t oem[4];
...
// 設置交換時間
int (*setSwapInterval)(struct ANativeWindow* window,
int interval);
// 向本地窗口查詢相關信息
int (*query)(const struct ANativeWindow* window,
int what, int* value);
// 用于執行本地窗口的相關操作
int (*perform)(struct ANativeWindow* window,
int operation, ... );
int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer);
// EGL 通過該接口來申請 buffer
int (*dequeueBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer** buffer, int* fenceFd);
// EGL 對 buffer 渲染完成后就調用該接口,來 unlock 和 post buffer
int (*queueBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer, int fenceFd);
// 取消一個已經 dequeue 的 buffer
int (*cancelBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer, int fenceFd);
};
ANativeWindow 更像一份“協議”,規定了本地窗口的形態和功能。下面來分析 FramebufferNativeWindow 是如何履行“協議”的。
(1) FramebufferNativeWindow 構造函數
FramebufferNativeWindow 構造函數的功能包括:
- 加載 Gralloc 模塊(GRALLOC_HARDWARE_MODULE_ID)。
- 打開 fb 和 gralloc(gpu0) 設備,打開后由 fbDev 和 grDev 管理。
- 根據設備屬性為 FramebufferNativeWindow 賦初值。
- 根據 FramebufferNativeWindow 的實現來填充 ANativeWindow 中的“協議”。
- 其他必要的初始化。
所有申請到的緩沖區都由 FramebufferNativeWindow 中的 buffers[] 來記錄,每個元素是一個 NativeBuffer,該類繼承了 ANativeWindowBuffer, 該類的聲明如下:
// /system/core/include/system/window.h
typedef struct ANativeWindowBuffer
{
...
int width;
int height;
int stride;
int format;
int usage;
void* reserved[2];
buffer_handle_t handle; // 代表內存塊的句柄
void* reserved_proc[8];
} ANativeWindowBuffer_t;
(2) dequeueBuffer
int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer)
{
FramebufferNativeWindow* self = getSelf(window);
Mutex::Autolock _l(self->mutex);
// 從 FramebufferNativeWindow 對象中取出 fb 設備描述符,在構造 FramebufferNativeWindow 對象時,已經打開了 fb 設備
framebuffer_device_t* fb = self->fbDev;
// 計算當前申請的圖形緩沖區在 buffers 數組中的索引,同時將下一個申請的 buffe r的索引保存到 mBufferHead 中
int index = self->mBufferHead++;
// 如果申請的下一個 buffer 的索引大于或等于 buffer 總數,則將下一個申請的 buffer 索引設置為 0,這樣就實現了對 buffer 數組的循環管理
if (self->mBufferHead >= self->mNumBuffers)
self->mBufferHead = 0;
// 如果當前沒有空閑的 buffer,即 mNumFreeBuffers = 0,則線程睡眠等待 buffer 的釋放
while (!self->mNumFreeBuffers) {
self->mCondition.wait(self->mutex);
}
// 存在了空閑 buffer,線程被喚醒繼續執行,由于此時要申請一塊 buffer,因此空閑 buffer 的個數又需要減 1
self->mNumFreeBuffers--;
// 保存當前申請的 buffer 在緩沖區數組中的索引位置
self->mCurrentBufferIndex = index;
// 得到 buffer 數組中的 NativeBuffer 對象指針
*buffer = self->buffers[index].get();
return 0;
}
dequeueBuffer 函數就是從 FramebufferNativeWindow 創建的包含 2 個圖形緩沖區的緩沖區隊列 buffers 中取出一塊空閑可用的圖形 buffer,如果當前緩沖區隊列中沒有空閑的 buffer,則當前申請 buffer 線程阻塞等待,等待其他線程釋放圖形緩沖區。mNumFreeBuffers 用來描述可用的空閑圖形 buffer 個數,index 記錄當前申請 buffer 在圖形緩沖區隊列中的索引位置,mBufferHead 指向下一次申請的圖形 buffer 的位置,由于我們是循環利用兩個緩沖區的,所以如果這個變量的值超過 mNumBuffers,就需要置 0。也就是說 mBufferHead 的值永遠只能是 0或者 1。
4.2、SurfaceView
Surface 也繼承了 ANativeWindow:
class Surface: public ANativeObjectBase<ANativeWindow, Surface, RefBase>{ ... }
Surface 是面向 Android 系統中所有 UI 應用程序的,即它承擔著應用進程中的 UI 顯示需求。
Surface 需要面向上層實現(主要是 Java 層)提供繪制圖像的畫板。SurfaceFlinger 需要收集系統中所有應用程序繪制的圖像數據,然后集中顯示到物理屏幕上。Surface 需要扮演相應角色,本質上還是由 SurfaceFlinger 服務統一管理的,涉及到很多跨進程的通信細節。
下面來看 Surface 中的關鍵成員變量:
成員變量 | 說明 |
---|---|
sp<IGraphicsBufferProducer> mGraphicsBufferProducer | Surface 核心變量 |
BufferSlot mSlots[32] | Surface 內部存儲 buffer 的地方,BufferSlot 內不包括:GraphicsBuffer 和 dirtyRegion,當用戶 dequeue 時將申請內存 |
Surface 將通過 mGraphicBufferProducer 來獲取 buffer,這些緩沖區會被記錄在 mSlots 中數據中。mGraphicBufferProducer 這一核心成員的初始化流程如下:
- ViewRootImpl 持有一個 Java 層的 Surface 對象(mSurface)。
- ViewRootImpl 向 WindowManagerService 發起 relayout 請求,此時 mSurface 被賦予真正的有效值,
將輾轉生成的 SurfaceControl 通過S urface.copyFrom() 函數復制到 mSurface 中。
由此,Surface 由 SurfaceControl 管理,SurfaceControl 由 SurfaceComposerClient 創建。SurfaceComposerClient 獲得的匿名 Binder 是 ISurfaceComposer,其服務端實現是 SurfaceFlinger。而 Surface 依賴的 IGraphicBufferProducer 對象在 Service 端的實現是 BufferQueue。
class SurfaceFlinger :
public BinderService<SurfaceFlinger>, // 在 ServiceManager 中注冊為 SurfaceFlinger
public BnSurfaceComposer, // 實現的接口卻叫 ISurfaceComposer
Buffer,Consumer,Producer 是“生產者-消費者”模型中的 3 個參與對象,如何協調好它們的工作是應用程序能否正常顯示UI的關鍵。Buffer 是 BufferQueue,Producer 是應用程序,Consumer 是 SurfaceFlinger。
五、BufferQueue
To be continued ....