最近由于工作的需要,要学习一下android上的显示系统,包括android的UI及Video显示,以便指导android HAL层及linux相关驱动开发,使方案更加合理。
在此之前,我仅知道android是通过HAL中的Gralloc模块去访问framebuffer(帧缓冲区),但不清楚framework上的一层是如何使用Gralloc模块的,更不清楚video数据是如何显示出来的。OK!!!哪么就先从framework层中的surfaceflinger是如何访问控制framebuffer入手吧。本文分为三个部分:
1:相关几个类及HAL层的相关设备.
在显示系统framework的surfaceflinger服务通过GraphicPlane对像来描述系统显示屏,也就是控制硬件的帧缓冲区framebuffer。GraphicPlane类内部有DisplayHardware对像,而DisplayHardware类内部又嵌有FramebufferNativeWindow对像,而对像FramebufferNativeWindow就是真正描述硬件帧缓冲区的。类FramebufferNativeWindow其实就是控制着一个叫Gralloc的HAL模块,这一HAL模块包含两个设备gralloc_device及framebuffer_device_t。
模块Gralloc负责注册图形缓冲区。所谓的注册图形缓冲区,就是指当使用的进程跟分配时候的进程不一致的时候,需要重新进行一次内存映射,以使当前的进程能够使用该内在区域。
设备gralloc用以分配图形缓冲区。分配图形缓冲区,有两个类,一类是使用硬件帧缓冲区的内存,一类是使用ashmem匿名共享内存作为图形缓冲区,在以下情况中会使用到匿名共享内存作为图形缓冲区:(1):fb驱动只提供一个显示buffer。(2):在分配的时候没有使用GRALLOC_USAGE_HW_FB标志。分配图形缓冲区的同时也会完成内存的映射。
设备framebuffer负责渲染图形缓冲区,通过设备的回调函数device.post也就是fb_post,fb_post()函数内部将会有一些系统调用,控制底层驱动,完成图形缓冲区到帧缓冲的显示动作。
下面我们来看看GraphicPlane,DisplayHardware,FramebufferNativeWindow这三个framework层的三个类的关系及其实现。
2:相关的类在surfaceflinger初始化过程
在surfaceflinger服务启动过程中会对上面几个类的对像进行初始化。
具体实现在frameworks/base/services/surfaceflinger/surfaceflinger.cpp 函数:status_t SurfaceFlinger::readyToRun()
在函数的开始部分初始化了SurfaceFlinger类里编号为0的GraphicPlane对像,以及创建了DisplayHardware对像,再用所创建的DisplayHardware对像设置为编号0的GraphicPlane对像的活动的DisplayHardware。
GraphicPlane对像的初始化主要是调用其成员函数GraphicPlane::setDisplayHardware(DisplayHardware *hw)。该成员函数使用DisplayHardware 对像初始了其内部变量成员,例如:显示屏的宽mDisplayWidth,显示屏的长mDisplayHeight,及显示屏的旋转矩阵mDisplayTransform。
SurfaceFlinger类成员函数readyToRun()内创建DisplayHardware对像,在创建过程中会调用DisplayHardware类的构造函数。DisplayHardware对像的初始化调用其类成员函数init()。在init()函数里,首先创建了一个FramebufferNativeWindow的对像,并保存在DisplayHardware类的成员变量mNativeWindow中,用以管理硬件帧缓冲区,并通过FramebufferNativeWindow对像来获得帧缓冲区的一个信息。接着加载一个overlay的HAL模块。并非所有平台都有overlay模块,在Android中我们把overlay看成一个特殊的surface,一般用来显示视频或者camera,这样做的好处就是能使用overlay硬件直接merge视频显示。再接下来调用eglGetDisplay和eglInitialize函数来获得和初始化OpengGL库的默认显示屏,接着再调用EGLUtils::selectConfigForNativeWindow函数来获得前面所创建的一个FramebufferNativeWindow对象所描述的系统主绘图表面的配置信息,并且保存在EGLConfig对象config。有了这些配置信息之后,接下来就可以在硬件帧缓冲区上面创建系统的主绘图表面。调用函数eglCreateWindowSurface来创建系统的主绘图表面。系统的主绘图表面是直接在硬件帧缓冲区上创建的,用来渲染系统的UI,即负责合成和渲染所有应用程序的UI。调用函数eglCreateContext来创建系统的主绘图表面的上下文。有了这个上下文之后,OpenGL库就能够在前面所创建的系统主绘图表面上渲染系统的UI了。eglMakeCurrent来将它所里面的绘图表面以及绘图上下文设置为绘图表面以及绘图上下文。别外在函数最后看到 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);是用来解除当前进程绑定的主绘图表面的上下文。最后在SurfaceFlinger类的成员函数readyToRun接下来还会调用DisplayHardware的成员函数makeCurrent来将它所里面的绘图表面以及绘图上下文设置为SurfaceFlinger服务的UI渲染线程的绘图表面以及绘图上下文。
系统的硬件帧缓冲区在初始化完成之后,SurfaceFlinger服务以后就可以调用用来描述它的一个DisplayHardware对象的成员函数flip来在它上面渲染系统的UI。这个函数主要就是调用OpenGL库中的函数eglSwapBuffers来将系统的UI渲染到系统的主绘图表面上去的,即渲染到系统的硬件帧缓冲区上去的。
接下来看看FramebufferNativeWindow类的构造函数的实现。首先调用函数hw_get_module来将HAL层中的Gralloc模块加载到当前进程来,并且调用函数framebuffer_open和gralloc_open分别打开Gralloc模块中的fb设备和gralloc设备。接下来创建FramebufferNativeWindow类的成员变量buffers所描述的一个图形缓冲区数组。每一个图形缓冲区都使用一个NativeBuffer对象来描述,并且这些图形缓冲区都是通过调用HAL层中的Gralloc模块的gralloc设备的成员函数alloc来分配的。注意,在分配图形缓冲区时,指定的标志,即第5个参数的值为GRALLOC_USAGE_HW_FB。这意味着FramebufferNativeWindow类所管理的图形缓冲区都是直接在硬件帧缓冲区上分配的,而不是在匿名共享内存中分配的。接着设置FramebufferNativeWindow的父类ANativeWindow的成员函数setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform,它们都是OpenGL的回调接口,分别指向FramebufferNativeWindow类的静态成员函数setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform。其中员函数dequeueBuffer和queueBuffer是用来为OpenGL库分配和渲染图形缓冲区。
FramebufferNativeWindow类的成员函数dequeueBuffer的作用是获取空闲的图形缓冲区,在获得FramebufferNativeWindow对象self之后,我们就可以在它内部的图形缓冲区数组buffers中获取下一个空闲图形缓冲区。
FramebufferNativeWindow类的成员函数queueBuffer目标就是要将参数buffer所描述的图形缓冲区渲染到硬件帧缓冲区中去,获取FramebufferNativeWindow对象self的成员变量fbDev所描述的一个fb设备。有了这个fb设备之后, 我们就可以调用它的成员函数post来将参数buffer所描述的图形缓冲区渲染到硬件帧缓冲区中去。
3:相关的数据是如何导入到framebuffer中的。
SurfaceFlinger服务的UI渲染线程是以SurfaceFlinger类的成员函数threadLoop为线程执行体的,即SurfaceFlinger服务的UI渲染线程会不断地循环执行SurfaceFlinger类的成员函数threadLoop。其中threadLoop在有需要更新硬件帧缓冲的时候会调用SurfaceFlinger类的成员函数postFramebuffer,而该函数又是调用DisplayHardware对象的成员函数flip,在flip函数里最终通过调用EGL的API函数eglSwapBuffers完成系统UI的渲染。通过跟踪Android自已的软件OpenGL库libGLES_android.so,也就是Android自身的OpenGL EGL API的实现,frameworks/base/opengl/libagl/egl.cpp,eglSwapBuffers()其实就是调用egl_window_surface_v2_t类的成员函数swapBuffers(),函数内部回调了nativeWindow类的dequeueBuffer()。其实早就DisplayHardware对像初始化函数init()里调用EGL的API eglCreateWindowSurface,createWindowSurface函数内部创建类egl_window_surface_v2_t,并用其所传参数 NativeWindowType window,初始化egl_window_surface_v2_t类的成员nativeWindow。eglCreateWindowSurface所传入的参数NativeWindowType window就是 DisplayHardware成员函数init(),开头部分所创建的FramebufferNativeWindow对像:mNativeWindow = new FramebufferNativeWindow();