一. android顯示系統的建立
SurfaceFlinger對於顯示的管理是通過一個或多個GraphicPlane對象(目前android只實現了一個)來管理的,
@SurfaceFlinger.h
- GraphicPlane mGraphicPlanes[1];
1. FrameBuffer的建立
framebuffer,確切的是說是linux下的framebuffer,,它是linux圖形顯示系統中一個與圖形硬件無關的抽象層,user完全不用考慮我們的硬件設備,而僅僅使用framebuffer就可以實現對屏幕的操作。
android的framebuffer並沒有被SurfaceFlinger直接使用,而是在framebuffer外做了一層包裝,這個包裝就是FramebufferNativeWindow,我們來看一下FramebufferNativeWindow的創建過程。
我們的framebuffer是由一個設備符fbDev來表示的,它是FramebufferNativeWindow的一個成員,我們來分析一下對fbDev的處理過程。
1.1. fbDev設備符
1.1.1 gralloc library
在這之前,先介紹一下gralloc library,它的形態如grallocBOARDPLATFORM.so, BOARDPLATFORM可以從屬性ro.board.platform中獲得,這篇文章中我們以Qualcomm msmx7x30為例,也就是gralloc.msm7x30.so中,它的源路徑在hardware/msm7k/libgralloc-qsd8k。
framebuffer的初始化需要通過HAL gralloc.msm7x30.so 來完成與底層硬件驅動的適配,但是gralloc library並不是平台無關的,不同的vendor可能會實現自己的gralloc library,因此為了保證在創建framebuffer時能夠平台無關,android只能是動態的判斷並使用當前的gralloc library,android通過從gralloc library中再抽象出一個hw_module_t結構來供使用,它為framebuffer的初始化提供了需要的gralloc.msm7x30.so業務。因此通過這個hw_module_t結構我們就不需要知道當前系統使用的到底是哪個gralloc library。按規定,所有gralloc library中的這個結構體被命名為HAL_MODULE_INFO_SYM(HMI)。當前分析的系統中,HAL_MODULE_INFO_SYM在hardware/msm7k/libgralloc-qsd8k/galloc.cpp。
1.1.2 打開fbDev設備符
下面看如何打開 打開fbDev設備符。通過HAL_MODULE_INFO_SYM提供的gralloc.msm7x30.so的接口我們調用到了fb_device_open()@hardware/msm7k/libgralloc-qsd8kframebuffer.cpp。
- 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)) {
- alloc_device_t* gralloc_device;
- status = gralloc_open(module, &gralloc_device);
- /* initialize our state here */
- fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
- memset(dev, 0, sizeof(*dev));
- /* initialize the procs */
- dev->device.common.tag = HARDWARE_DEVICE_TAG;
- private_module_t* m = (private_module_t*)module;
- status = mapFrameBuffer(m);
- }
在這個函數中,主要為fbDev設備符指定一個fb_context_t實例,並通過函數mapFrameBuffer()對設備節點/dev/graphics/fb0進行操作,操作的目的有:
1.獲得屏幕設備的信息,並將屏幕信息保存在HAL_MODULE_INFO_SYM(上面代碼中的module)中。
2. 向/dev/graphics/fb0請求page flip模式,page
flip模式需要至少2個屏幕大小的buffer,page flip模式在后面介紹。目前android系統中設置為2個屏幕大小的buffer。當然屏幕設備可能不支持page flip模式。
mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp
- /*
- * Request NUM_BUFFERS screens (at lest 2 for page flipping)
- */
- info.yres_virtual = info.yres * NUM_BUFFERS;
- uint32_t flags = PAGE_FLIP;
- if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
- info.yres_virtual = info.yres;
- flags &= ~PAGE_FLIP;
- LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
- }
3. 映射屏幕設備緩存區給fbDev設備符。
mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp
- /*
- * map the framebuffer
- */
- int err;
- size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);
- module->framebuffer = new private_handle_t(dup(fd), fbSize,
- private_handle_t::PRIV_FLAGS_USES_PMEM);
- module->numBuffers = info.yres_virtual / info.yres;
- module->bufferMask = 0;
- void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
- if (vaddr == MAP_FAILED) {
- LOGE("Error mapping the framebuffer (%s)", strerror(errno));
- return -errno;
- }
- module->framebuffer->base = intptr_t(vaddr);
- memset(vaddr, 0, fbSize);
1.2 grDev設備符
在為framebuffer,也就是FramebufferNativeWindow申請內存之前,我們還要介紹一個概念,就是grDev設備符。它雖然也叫設備符,但是它和具體的設備沒有直接關系,我們看它的類型就是知道了alloc_device_t,沒錯,grDev設備符就是為了FramebufferNativeWindow管理內存使用的。為FramebufferNativeWindow提供了申請/釋放內存的接口。
1.3 FramebufferNativeWindow內存管理
- sp<NativeBuffer> buffers[2];
1.3.1 屏幕設備支持page filp模式
- // create a "fake" handles for it
- intptr_t vaddr = intptr_t(m->framebuffer->base);
- private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size,
- private_handle_t::PRIV_FLAGS_USES_PMEM |
- private_handle_t::PRIV_FLAGS_FRAMEBUFFER);
- // find a free slot
- for (uint32_t i=0 ; i<numBuffers ; i++) {
- if ((bufferMask & (1LU<<i)) == 0) {
- m->bufferMask |= (1LU<<i);
- break;
- }
- vaddr += bufferSize;
- }
- hnd->base = vaddr;
- hnd->offset = vaddr - intptr_t(m->framebuffer->base);
- *pHandle = hnd;
1.3.2 屏幕設備不支持page flip模式
gralloc_alloc_framebuffer_locked()@hardware/msm7k/libgralloc-qsd8k/gpu.cpp
- const uint32_t bufferMask = m->bufferMask;
- const uint32_t numBuffers = m->numBuffers;
- const size_t bufferSize = m->finfo.line_length * m->info.yres;
- if (numBuffers == 1) {
- // If we have only one buffer, we never use page-flipping. Instead,
- // we return a regular buffer which will be memcpy'ed to the main
- // screen when post is called.
- int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D;
- return gralloc_alloc_buffer(bufferSize, newUsage, pHandle);
- }
2. 打開Overlay
- if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
- overlay_control_open(module, &mOverlayEngine);
- }
3. 選擇OpenGL ES library(也即軟/硬件加速)
- 0 0 android
- 0 1 adreno200
- libGLESv1_CM_adreno200.so
- libGLESv2_adreno200.so
- libEGL_adreno200.so
3.1 OpenGL初始化
frameworks/base/opengl/libs/EGL/egl.cpp
- static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
- enum {
- IMPL_HARDWARE = 0,
- IMPL_SOFTWARE,
- IMPL_NUM_IMPLEMENTATIONS
- };
gEGLImpl[IMPL_HARDWARE]中保存着硬件圖形設備的OpenGL api地址,從
- libGLESv1_CM_adreno200.so
- libGLESv2_adreno200.so
- libEGL_adreno200.so
3.2 EGL和GLES api
- enum {
- EGL = 0x01,
- GLESv1_CM = 0x02,
- GLESv2 = 0x04
- };

3.3 OpenGL config
3.3.1 系統默認pixel format
- if(info.bits_per_pixel == 32) {
- /*
- * Explicitly request RGBA_8888
- */
- /* Note: the GL driver does not have a r=8 g=8 b=8 a=0 config, so if we do
- * not use the MDP for composition (i.e. hw composition == 0), ask for
- * RGBA instead of RGBX. */
- if (property_get("debug.sf.hw", property, NULL) > 0 && atoi(property) == 0)
- module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888;
- else if(property_get("debug.composition.type", property, NULL) > 0 && (strncmp(property, "mdp", 3) == 0))
- module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888;
- else
- module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888;
- } else {
- /*
- * Explicitly request 5/6/5
- */
- module->fbFormat = HAL_PIXEL_FORMAT_RGB_565;
- }
3.3.2 config初始化
所有的OpenGL庫提供的config,同樣需要將軟硬兩種模式的各自的OpenGL config提取出來供系統使用,如同OpenGL api地址一樣。OpenGL config提取出來后保存在另外一個全局變量- static egl_display_t gDisplay[NUM_DISPLAYS];
- // EGLDisplay are global, not attached to a given thread
- const unsigned int NUM_DISPLAYS = 1;
- <strong> </strong> // sort our configurations so we can do binary-searches
- qsort( dp->configs,
- dp->numTotalConfigs,
- sizeof(egl_config_t), cmp_configs);<strong>
- </strong>
3.3.3 config選擇
3.3.3.1 滿足屬性要求
- // initialize EGL
- EGLint attribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_NONE, 0,
- EGL_NONE
- };
3.3.3.2 滿足RGBA要求
- static GGLFormat const gPixelFormatInfos[] =
- { // Alpha Red Green Blue
- { 0, 0, {{ 0, 0, 0, 0, 0, 0, 0, 0 }}, 0 }, // PIXEL_FORMAT_NONE
- { 4, 32, {{32,24, 8, 0, 16, 8, 24,16 }}, GGL_RGBA }, // PIXEL_FORMAT_RGBA_8888
format信息,去和openGL的config比較,得到想要的config。
- EGLConfig* const configs = (EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs);
- if (eglChooseConfig(dpy, attrs, configs, numConfigs, &n) == EGL_FALSE) {
- free(configs);
- return BAD_VALUE;
- }
- const int fbSzA = fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA);
- const int fbSzR = fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED);
- const int fbSzG = fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN);
- const int fbSzB = fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE);
- int i;
- EGLConfig config = NULL;
- for (i=0 ; i<n ; i++) {
- EGLint r,g,b,a;
- EGLConfig curr = configs[i];
- eglGetConfigAttrib(dpy, curr, EGL_RED_SIZE, &r);
- eglGetConfigAttrib(dpy, curr, EGL_GREEN_SIZE, &g);
- eglGetConfigAttrib(dpy, curr, EGL_BLUE_SIZE, &b);
- eglGetConfigAttrib(dpy, curr, EGL_ALPHA_SIZE, &a);
- if (fbSzA <= a && fbSzR <= r && fbSzG <= g && fbSzB <= b) {
- config = curr;
- break;
- }
- }
4. 創建main surface
5. 創建 OpenGL ES 上下文
An OpenGL context represents many things. A context stores all of the state associated with this instance of OpenGL. It represents the (potentially visible) default framebufferthat rendering commands will draw to when not drawing to a framebuffer object. Think of a context as an object that holds all of OpenGL; when a context is destroyed, OpenGL is destroyed.
http://www.opengl.org/wiki/OpenGL_context
具體的創建過程專業術語太多,也沒有仔細研究不再介紹。
6. 綁定context和surface
6.1 多線程支持
- ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific();
- if (gl) {
- egl_context_t* c = egl_context_t::context(gl);
- if (c->flags & egl_context_t::IS_CURRENT) {
- if (current != gl) {
- // it is an error to set a context current, if it's already
- // current to another thread
- return -1;
- }
- } else {
- if (current) {
- // mark the current context as not current, and flush
- glFlush();
- egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
- }
- }
- if (!(c->flags & egl_context_t::IS_CURRENT)) {
- // The context is not current, make it current!
- setGlThreadSpecific(gl);
- c->flags |= egl_context_t::IS_CURRENT;
- }
- // cur_c has to be valid here (but could be terminated)
- if (ctx != EGL_NO_CONTEXT) {
- setGlThreadSpecific(c->cnx->hooks[c->version]);
- setContext(ctx);
- _c.acquire();
- } else {
- setGlThreadSpecific(&gHooksNoContext);
- setContext(EGL_NO_CONTEXT);
- }
盡管openGL 實現了多線程的支持,目前我從代碼中別沒有找到多線程的使用。
6.2 設置surface和context之間的關系

- // Unbind the context from this thread
- eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- // initialize primary screen
- // (other display should be initialized in the same manner, but
- // asynchronously, as they could come and go. None of this is supported
- // yet).
- const GraphicPlane& plane(graphicPlane(dpy));
- const DisplayHardware& hw = plane.displayHardware();
- const uint32_t w = hw.getWidth();
- const uint32_t h = hw.getHeight();
- const uint32_t f = hw.getFormat();
- hw.makeCurrent();
下圖為這個圖形系統的類圖結構。