本文涉及的源代码基于 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 ....