http://fuerbosi.iteye.com/blog/1535215
這周抽空研究了一下SurfaceFlinger,發現真正複雜的並不是SurfaceFlinger本身,而是android的display顯示系統,網上關於這部分的介紹有不少,本不打算寫的,但是發現還是記錄一下研究代碼的過程比較好,一是能夠幫助自己理清思路,另一個原因就是以後當這塊內容忘記的時候,能快速的通過這個記錄撿起來。
一. android顯示系統的建立
SurfaceFlinger對於顯示的管理是通過一個或多個GraphicPlane對象(目前android只實現了一個)來管理的,
@SurfaceFlinger.h
- GraphicPlanemGraphicPlanes[1];
1. FrameBuffer的建立
framebuffer,確切的是說是linux下的framebuffer,,它是linux圖形顯示系統中一個與圖形硬件無關的抽象層,user完全不用考慮我們的硬件設備,而僅僅使用framebuffer就可以實現對屏幕的操作。
android的framebuffer並沒有被SurfaceFlinger直接使用,而是在framebuffer外做了一層包裝,這個包裝就是FramebufferNativeWindow,我們來看一下FramebufferNativeWindow的創建過程。
我們的framebuffer是由一個設備符fbDev來表示的,它是FramebufferNativeWindow的一個成員,我們來分析一下對fbDev的處理過程。
1.1. fbDev設備符
1.1.1gralloc library
在這之前,先介紹一下gralloc library,它的形態如grallocBOARDPLATFORM.so,BOARDPLATFORM可以從屬性ro.board.platform中獲得,這篇文章中我們以Qualcommmsmx7x30爲例,也就是gralloc.msm7x30.so中,它的源路徑在hardware/msm7k/libgralloc-qsd8k。
framebuffer的初始化需要通過HALgralloc.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。
- intfb_device_open(hw_module_tconst*module,constchar*name,
- hw_device_t**device)
- {
- intstatus=-EINVAL;
- if(!strcmp(name,GRALLOC_HARDWARE_FB0)){
- alloc_device_t*gralloc_device;
- status=gralloc_open(module,&gralloc_device);
- /*initializeourstatehere*/
- fb_context_t*dev=(fb_context_t*)malloc(sizeof(*dev));
- memset(dev,0,sizeof(*dev));
- /*initializetheprocs*/
- 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
- /*
- *RequestNUM_BUFFERSscreens(atlest2forpageflipping)
- */
- info.yres_virtual=info.yres*NUM_BUFFERS;
- uint32_tflags=PAGE_FLIP;
- if(ioctl(fd,FBIOPUT_VSCREENINFO,&info)==-1){
- info.yres_virtual=info.yres;
- flags&=~PAGE_FLIP;
- LOGW("FBIOPUT_VSCREENINFOfailed,pageflippingnotsupported");
- }
3. 映射屏幕設備緩存區給fbDev設備符。
mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp
- /*
- *maptheframebuffer
- */
- interr;
- size_tfbSize=roundUpToPageSize(finfo.line_length*info.yres_virtual);
- module->framebuffer=newprivate_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("Errormappingtheframebuffer(%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模式
- //createa"fake"handlesforit
- intptr_tvaddr=intptr_t(m->framebuffer->base);
- private_handle_t*hnd=newprivate_handle_t(dup(m->framebuffer->fd),size,
- private_handle_t::PRIV_FLAGS_USES_PMEM|
- private_handle_t::PRIV_FLAGS_FRAMEBUFFER);
- //findafreeslot
- for(uint32_ti=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
- constuint32_tbufferMask=m->bufferMask;
- constuint32_tnumBuffers=m->numBuffers;
- constsize_tbufferSize=m->finfo.line_length*m->info.yres;
- if(numBuffers==1){
- //Ifwehaveonlyonebuffer,weneverusepage-flipping.Instead,
- //wereturnaregularbufferwhichwillbememcpy'edtothemain
- //screenwhenpostiscalled.
- intnewUsage=(usage&~GRALLOC_USAGE_HW_FB)|GRALLOC_USAGE_HW_2D;
- returngralloc_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(也即軟/硬件加速)
- 00android
- 01adreno200
- libGLESv1_CM_adreno200.so
- libGLESv2_adreno200.so
- libEGL_adreno200.so
3.1OpenGL初始化
frameworks/base/opengl/libs/EGL/egl.cpp
- staticegl_connection_tgEGLImpl[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){
- /*
- *ExplicitlyrequestRGBA_8888
- */
- /*Note:theGLdriverdoesnothavear=8g=8b=8a=0config,soifwedo
- *notusetheMDPforcomposition(i.e.hwcomposition==0),askfor
- *RGBAinsteadofRGBX.*/
- if(property_get("debug.sf.hw",property,NULL)>0&&atoi(property)==0)
- module->fbFormat=HAL_PIXEL_FORMAT_RGBX_8888;
- elseif(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{
- /*
- *Explicitlyrequest5/6/5
- */
- module->fbFormat=HAL_PIXEL_FORMAT_RGB_565;
- }
3.3.2 config初始化
所有的OpenGL庫提供的config,同樣需要將軟硬兩種模式的各自的OpenGL config提取出來供系統使用,如同OpenGL api地址一樣。OpenGL config提取出來後保存在另外一個全局變量- staticegl_display_tgDisplay[NUM_DISPLAYS];
- //EGLDisplayareglobal,notattachedtoagiventhread
- constunsignedintNUM_DISPLAYS=1;
- <strong></strong>//sortourconfigurationssowecandobinary-searches
- qsort(dp->configs,
- dp->numTotalConfigs,
- sizeof(egl_config_t),cmp_configs);<strong>
- </strong>
3.3.3 config選擇
3.3.3.1 滿足屬性要求
- //initializeEGL
- EGLintattribs[]={
- EGL_SURFACE_TYPE,EGL_WINDOW_BIT,
- EGL_NONE,0,
- EGL_NONE
- };
3.3.3.2 滿足RGBA要求
- staticGGLFormatconstgPixelFormatInfos[]=
- {//AlphaRedGreenBlue
- {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
- EGLConfig*constconfigs=(EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs);
- if(eglChooseConfig(dpy,attrs,configs,numConfigs,&n)==EGL_FALSE){
- free(configs);
- returnBAD_VALUE;
- }
- constintfbSzA=fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA);
- constintfbSzR=fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED);
- constintfbSzG=fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN);
- constintfbSzB=fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE);
- inti;
- EGLConfigconfig=NULL;
- for(i=0;i<n;i++){
- EGLintr,g,b,a;
- EGLConfigcurr=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 上下文
AnOpenGL contextrepresents 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 aframebuffer 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){
- //itisanerrortosetacontextcurrent,ifit'salready
- //currenttoanotherthread
- return-1;
- }
- }else{
- if(current){
- //markthecurrentcontextasnotcurrent,andflush
- glFlush();
- egl_context_t::context(current)->flags&=~egl_context_t::IS_CURRENT;
- }
- }
- if(!(c->flags&egl_context_t::IS_CURRENT)){
- //Thecontextisnotcurrent,makeitcurrent!
- setGlThreadSpecific(gl);
- c->flags|=egl_context_t::IS_CURRENT;
- }
- //cur_chastobevalidhere(butcouldbeterminated)
- 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之間的關係
- //Unbindthecontextfromthisthread
- eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT);
- //initializeprimaryscreen
- //(otherdisplayshouldbeinitializedinthesamemanner,but
- //asynchronously,astheycouldcomeandgo.Noneofthisissupported
- //yet).
- constGraphicPlane&plane(graphicPlane(dpy));
- constDisplayHardware&hw=plane.displayHardware();
- constuint32_tw=hw.getWidth();
- constuint32_th=hw.getHeight();
- constuint32_tf=hw.getFormat();
- hw.makeCurrent();
下圖爲這個圖形系統的類圖結構。