前面讲解了那么多的基础知识,现在 我们可以进入界面合成流程的分析了。下面是一个手机APP的界面图: 在其中有很多个应用程序,从图上的标记,我们就知道了4个APP,那么这些APP的界面时怎么合成的呢?有两种方法: 1.在FrameBuffer上一次画出每个APP,然后在FrameBuffer上显示出来。 比如这对这四个APP:先获得一个FrameBuffer,先画出分别画出APP1,2,3,如下:
然后把FrameBuffer推送给LCD。
2.各个APP写入不同的window,通过硬件合成后,在LED上直接显示(硬件方式)。
软件显示首先我们来讲解第一种方法,使用openGL,其可以使用软件合成也能通过GPU合成。我们打开SurfaceFlinger.cpp找到其中的init函数:
void SurfaceFlinger::init() {
mHwc = new HWComposer(this);
loadHwcModule();
/*获得HW文件,如果不能获得,则不会设置mHwc*/
hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0)
int err = hwc_open_1(module, &mHwc);
mHwc = NULL;
如果不通过硬件合成单元,这返回失败mHwc = NULL。那么是怎么判断系统是否存在硬件合成单元的呢?在HWComposer这个类中存在initCheck函数:
status_t HWComposer::initCheck() const {
return mHwc ? NO_ERROR : NO_INIT;
}
如果mHwc 被设置了,说明其有硬件合成放单元,否则代表没有。下面我们查看SurfaceFlinger.cpp中handleMessageRefresh方法,我们知道这在这个函数之中刷新界面的。
void SurfaceFlinger::handleMessageRefresh() {
setUpHWComposer();
/*如果存在硬件合成单元(以后进行详细讲解)*/
if (hwc.initCheck() == NO_ERROR) {
.....
/*对于使用openGL刷新界面我们需要着重关注这个函数*/
doComposition();
在讲解doComposition之前,我们测试一下其内部会实现哪些动作:在FramerBuffer依次画出每个应用程序,然后提交给LCD。 1.绘画出每一个Layer 2.suapBuffer 如下图: 下面我们进入doComposition函数的分析,下面是他详细的流程图:
doComposition()
/*对所有显示器进行循环*/
for (size_t dpy=0 ; dpyisDisplayOn()) {
// repaint the framebuffer (if needed)
/*重新绘画framebuffer*/
doDisplayComposition(hw, dirtyRegion);
/*合成多个surface*/
doComposeSurfaces(hw, dirtyRegion);
/*如果使用openGL*/
if (hasGlesComposition) {
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
/*如果使用硬件合成单元*/
if (hasHwcComposition)
.......
// we start with the whole screen area
/*确定要绘制的区域*/
const Region bounds(hw->getBounds());
/*把区域写成黑色*/
drawWormhole(hw, region);
/*对于每一个layer*/
for (size_t i=0 ; idraw(hw, clip);
onDraw(hw, clip, false);
drawWithOpenGL(hw, clip, useIdentityTransform);
/*对每个显示器使用drawWithOpenGL函数之后执行doDisplayComposition*/
doDisplayComposition()
doComposeSurfaces(hw, dirtyRegion)
// swap buffers (presentation)
hw->swapBuffers(getHwComposer());
可以看到,其最终调用到drawWithOpenGL函数,其上的swapBuffers在DisplayDevice.cpp中实现:
void DisplayDevice::swapBuffers(HWComposer& hwc) const {
success = eglSwapBuffers(mDisplay, mSurface);
d->swapBuffers();
/*当前的buffer*/
previousBuffer = buffer;
/*把当前的buffer放入队列中*/
nativeWindow->queueBuffer(nativeWindow, buffer, -1);
/*获得一个新的buffer*/
nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR)
通过上面的分析我们可以绘画出以下流程: