android surfaceflinger研究----顯示系統

http://fuerbosi.iteye.com/blog/1535215

這周抽空研究了一下SurfaceFlinger,發現真正複雜的並不是SurfaceFlinger本身,而是android的display顯示系統,網上關於這部分的介紹有不少,本不打算寫的,但是發現還是記錄一下研究代碼的過程比較好,一是能夠幫助自己理清思路,另一個原因就是以後當這塊內容忘記的時候,能快速的通過這個記錄撿起來。

一. android顯示系統的建立

我們看SurfaceFlinger的定義就知道,它其實是一個Thread, 因此SurfaceFlinger的初始化工作就理所當然的放在了SurfaceFlinger線程中,詳見readyToRun()@SurfaceFlinger.cpp

SurfaceFlinger對於顯示的管理是通過一個或多個GraphicPlane對象(目前android只實現了一個)來管理的,

@SurfaceFlinger.h

  1. GraphicPlanemGraphicPlanes[1];
其實,GraphicPlane類只是一個wrapper層,目的是當android支持多個顯示系統時,通過該類來管裏各自的圖形系統,顯示系統真正的初始化工作是通過DisplayHardware類來初始化底層圖形系統的管理與顯示的。真正的圖形顯示系統的初始化在init()@DisplayHardware.cpp
目前,android支持一個圖形系統,這個圖形系統是全局的,surfaceflinger可以訪問,其他不通過surfaceflinger進行圖形處理的application也可以對其進行操作。


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。


  1. intfb_device_open(hw_module_tconst*module,constchar*name,
  2. hw_device_t**device)
  3. {
  4. intstatus=-EINVAL;
  5. if(!strcmp(name,GRALLOC_HARDWARE_FB0)){
  6. alloc_device_t*gralloc_device;
  7. status=gralloc_open(module,&gralloc_device);
  8. /*initializeourstatehere*/
  9. fb_context_t*dev=(fb_context_t*)malloc(sizeof(*dev));
  10. memset(dev,0,sizeof(*dev));
  11. /*initializetheprocs*/
  12. dev->device.common.tag=HARDWARE_DEVICE_TAG;
  13. private_module_t*m=(private_module_t*)module;
  14. status=mapFrameBuffer(m);
  15. }

在這個函數中,主要爲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

  1. /*
  2. *RequestNUM_BUFFERSscreens(atlest2forpageflipping)
  3. */
  4. info.yres_virtual=info.yres*NUM_BUFFERS;
  5. uint32_tflags=PAGE_FLIP;
  6. if(ioctl(fd,FBIOPUT_VSCREENINFO,&info)==-1){
  7. info.yres_virtual=info.yres;
  8. flags&=~PAGE_FLIP;
  9. LOGW("FBIOPUT_VSCREENINFOfailed,pageflippingnotsupported");
  10. }


3. 映射屏幕設備緩存區給fbDev設備符。

mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp

  1. /*
  2. *maptheframebuffer
  3. */
  4. interr;
  5. size_tfbSize=roundUpToPageSize(finfo.line_length*info.yres_virtual);
  6. module->framebuffer=newprivate_handle_t(dup(fd),fbSize,
  7. private_handle_t::PRIV_FLAGS_USES_PMEM);
  8. module->numBuffers=info.yres_virtual/info.yres;
  9. module->bufferMask=0;
  10. void*vaddr=mmap(0,fbSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
  11. if(vaddr==MAP_FAILED){
  12. LOGE("Errormappingtheframebuffer(%s)",strerror(errno));
  13. return-errno;
  14. }
  15. module->framebuffer->base=intptr_t(vaddr);
  16. memset(vaddr,0,fbSize);


1.2 grDev設備符

在爲framebuffer,也就是FramebufferNativeWindow申請內存之前,我們還要介紹一個概念,就是grDev設備符。

它雖然也叫設備符,但是它和具體的設備沒有直接關係,我們看它的類型就是知道了alloc_device_t,

沒錯,grDev設備符就是爲了FramebufferNativeWindow管理內存使用的。爲FramebufferNativeWindow提供了申請/釋放內存的接口。


1.3 FramebufferNativeWindow內存管理

FramebufferNativeWindow維護了2個buffer,
  1. sp<NativeBuffer>buffers[2];

1.3.1 屏幕設備支持page filp模式

目前的android系統默認要求屏幕設備給系統映射2個屏幕大小的緩存區,以便支持page flip模式,
如果屏幕設備支持page flip模式,
那麼FramebufferNativeWindow中buffers將分別指向一個屏幕大小的屏幕設備緩存區。
  1. //createa"fake"handlesforit
  2. intptr_tvaddr=intptr_t(m->framebuffer->base);
  3. private_handle_t*hnd=newprivate_handle_t(dup(m->framebuffer->fd),size,
  4. private_handle_t::PRIV_FLAGS_USES_PMEM|
  5. private_handle_t::PRIV_FLAGS_FRAMEBUFFER);
  6. //findafreeslot
  7. for(uint32_ti=0;i<numBuffers;i++){
  8. if((bufferMask&(1LU<<i))==0){
  9. m->bufferMask|=(1LU<<i);
  10. break;
  11. }
  12. vaddr+=bufferSize;
  13. }
  14. hnd->base=vaddr;
  15. hnd->offset=vaddr-intptr_t(m->framebuffer->base);
  16. *pHandle=hnd;

1.3.2 屏幕設備不支持page flip模式

mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp中可以得知,如果屏幕設備不支持page flip模式,那麼numBuffer值將爲1而不是2,那麼映射過來的屏幕緩存區將只有一個屏幕大小,不夠支持page flip模式,那麼此時將不使用這一個屏幕大小的屏幕緩存區,而改爲去dev/pmem設備去申請。

gralloc_alloc_framebuffer_locked()@hardware/msm7k/libgralloc-qsd8k/gpu.cpp

  1. constuint32_tbufferMask=m->bufferMask;
  2. constuint32_tnumBuffers=m->numBuffers;
  3. constsize_tbufferSize=m->finfo.line_length*m->info.yres;
  4. if(numBuffers==1){
  5. //Ifwehaveonlyonebuffer,weneverusepage-flipping.Instead,
  6. //wereturnaregularbufferwhichwillbememcpy'edtothemain
  7. //screenwhenpostiscalled.
  8. intnewUsage=(usage&~GRALLOC_USAGE_HW_FB)|GRALLOC_USAGE_HW_2D;
  9. returngralloc_alloc_buffer(bufferSize,newUsage,pHandle);
  10. }


2. 打開Overlay

同選擇gralloc library相似,根據屬性值來選擇何時的overlay庫,如果vendor廠商沒有提供overlay庫的話,那麼系統將使用默認的overlay庫overlay.default.so。同樣的我們獲得overlay庫的HAL_MODULE_INFO_SYM結構體,作爲系統調用overlay的接口。

  1. if(hw_get_module(OVERLAY_HARDWARE_MODULE_ID,&module)==0){
  2. overlay_control_open(module,&mOverlayEngine);
  3. }


3. 選擇OpenGL ES library(也即軟/硬件加速)

OpenGL (Open Graphics Library)[3] is a standard specification defining a cross-language, cross-platform API for writing applications that produce 2D and 3D computer graphics. The interface consists of over 250 different function calls which can be used to draw complex three-dimensional scenes from simple primitives. OpenGL was developed by Silicon Graphics Inc. (SGI) in 1992[4] and is widely used in CAD, virtual reality, scientific visualization, information visualization, flight simulation, and video games. OpenGL is managed by the non-profit technology consortium Khronos Group.。
android是默認支持OpenGL ES軟件加速的,library爲libGLES_android,源碼路徑爲frameworks\base\opengl\libagl;如果手機設備支持硬件加速的話,那麼複雜的圖像處理工作將交由GPU去處理,那麼效率將大大提高。但是如果系統真的存在硬件加速,它是如何選擇何時用軟件加速?何時用硬件加速的呢?
如何查看是否有GPU來實現硬件加速,很容易查看/system/lib/egl/egl.cfg文件內容
  1. 00android
  2. 01adreno200
因此只要我們的移動設備芯片集成了GPU,並提供了對應的GL圖形庫,那麼我們就可以在我們的工程中device目錄下的egl.cfg文件中加入類似上面的配置,那麼我們的系統就會支持硬件加速。
如adreno200 GPU提供的GL圖形庫:
  1. libGLESv1_CM_adreno200.so
  2. libGLESv2_adreno200.so
  3. libEGL_adreno200.so
那麼假如我們的系統中軟硬件加速都支持了,那麼我們從代碼來看能不能讓用戶自由的選擇加速類型,我們帶着問題來研究一下代碼。

3.1OpenGL初始化

在調用不管是軟件加速的還是硬件加速的OpenGL api之前,我們都需要把軟硬兩種模式的各自的OpenGL api提取出來,抽象出一個interface來供系統使用,這個過程我稱之爲OpenGL初始化過程。
軟硬兩種模式的OpenGL api被分別指定到了一個全局數組的對應位置。
frameworks/base/opengl/libs/EGL/egl.cpp
  1. staticegl_connection_tgEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
  1. enum{
  2. IMPL_HARDWARE=0,
  3. IMPL_SOFTWARE,
  4. IMPL_NUM_IMPLEMENTATIONS
  5. };

gEGLImpl[IMPL_HARDWARE]中保存着硬件圖形設備的OpenGL api地址,從
  1. libGLESv1_CM_adreno200.so
  2. libGLESv2_adreno200.so
  3. libEGL_adreno200.so
這3個庫中獲得;gEGLImpl[IMPL_SOFTWARE]中保存着軟件的OpenGL api地址,從libGLES_android.so中獲取。

這部分代碼在egl_init_drivers_locked()@frameworks/base/opengl/libs/EGL/egl.cpp

3.2 EGL和GLES api

在OpenGL的初始化過程中,OpenGL提供了兩套api,分別稱爲EGL和GLES。android在OPENGL初始化過程中,會將兩種不同的接口分開管理,從下面代碼中我們可以看到EGL和GLES api地址被存儲到了不同的位置。
@frameworks\base\opengl\libs\EGL\Loader.h
  1. enum{
  2. EGL=0x01,
  3. GLESv1_CM=0x02,
  4. GLESv2=0x04
  5. };
load_driver()@frameworks\base\opengl\libs\EGL\Loader.cpp

上面枚舉的EGL表示ELG api;GLESvq1_CM表示OpenGL ES 1.0的api;GLESv2表示OpenGL ES 2.0的api。
EGL api地址最終被存儲在gEGLImpl[].egl中;
GLESvq1_CM api地址最終被存儲在gEGLImpl[].hooks[GLESv1_INDEX]->gl中;
GLESv2 api地址最終被存儲在gEGLImpl[].hooks[GLESv2_INDEX]->gl中;

3.2.1 EGL api
EGL is an interface between Khronos rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system. It handles graphics context management, surface/buffer binding, and rendering synchronization and enables high-performance, accelerated, mixed-mode 2D and 3D rendering using other Khronos APIs.
上面引用了官方的定義,可以看出,EGL是系統和OPENGL ES之間的接口,它的聲明在文件frameworks\base\opengl\libs\EGL\egl_entries.in。


3.2.2 GLES
GLES纔是真正的OpenGL ES的api,它的聲明我們可以在frameworks\base\opengl\libs\entries.in找到。目前的android系統不但將EGL提供給系統使用,同時將GLES也提供給了系統使用,這個我們可以在最開始的顯示系統的結構圖中可以看到,surfacefliger和framework的opengl模塊均可以訪問EGL和GLES接口。

3.3 OpenGL config

每個OpenGL庫都根據不同的像素格式(pixel format)提供了一系統的config,android根據framebuffer中設置的像素格式來選擇合適的config,android根據中各config中的屬性信息來創建main surface和openGL上下文。

3.3.1 系統默認pixel format

當前的代碼分析是基於gingerbread的,在mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp中我們可以找到framebuffer的pixel format的類型
  1. if(info.bits_per_pixel==32){
  2. /*
  3. *ExplicitlyrequestRGBA_8888
  4. */
  5. /*Note:theGLdriverdoesnothavear=8g=8b=8a=0config,soifwedo
  6. *notusetheMDPforcomposition(i.e.hwcomposition==0),askfor
  7. *RGBAinsteadofRGBX.*/
  8. if(property_get("debug.sf.hw",property,NULL)>0&&atoi(property)==0)
  9. module->fbFormat=HAL_PIXEL_FORMAT_RGBX_8888;
  10. elseif(property_get("debug.composition.type",property,NULL)>0&&(strncmp(property,"mdp",3)==0))
  11. module->fbFormat=HAL_PIXEL_FORMAT_RGBX_8888;
  12. else
  13. module->fbFormat=HAL_PIXEL_FORMAT_RGBA_8888;
  14. }else{
  15. /*
  16. *Explicitlyrequest5/6/5
  17. */
  18. module->fbFormat=HAL_PIXEL_FORMAT_RGB_565;
  19. }

目前的移動設備都是真彩色,所以這裏我們認爲我們的屏幕設備支持的是HAL_PIXEL_FORMAT_RGBA_8888。

3.3.2 config初始化

所有的OpenGL庫提供的config,同樣需要將軟硬兩種模式的各自的OpenGL config提取出來供系統使用,如同OpenGL api地址一樣。OpenGL config提取出來後保存在另外一個全局變量
  1. staticegl_display_tgDisplay[NUM_DISPLAYS];
  1. //EGLDisplayareglobal,notattachedtoagiventhread
  2. constunsignedintNUM_DISPLAYS=1;
中,不同於gEGLImpl分開保存軟硬件api,所有的config,不論軟硬件的,均保存在gDisplay[0],因爲所有的config是以屏幕區分的,同一塊屏幕應該保存同一份config信息。

在提取出的openGL的config時,會保存到gDisplay[0].config中,在這兒有一個很tricky的實現,它保證了硬件加速器的優先使用!

  1. <strong></strong>//sortourconfigurationssowecandobinary-searches
  2. qsort(dp->configs,
  3. dp->numTotalConfigs,
  4. sizeof(egl_config_t),cmp_configs);<strong>
  5. </strong>
最終,上述代碼會將gDisplay[0].config中的配置按照先硬件的,後軟件的規則做一個總體的排序。

代碼在eglInitialize()@frameworks/base/opengl/libs/EGL/egl.cpp

3.3.3 config選擇

上文說到,android會根據framebuffer的pixel format信息來獲取對應的config,這個過程只選擇一個合適的config,選到爲止。

3.3.3.1 滿足屬性要求

並不是所有的config都可以被選擇,首先這個config的屬性需要滿足
init()@DisplayHardware.cpp
  1. //initializeEGL
  2. EGLintattribs[]={
  3. EGL_SURFACE_TYPE,EGL_WINDOW_BIT,
  4. EGL_NONE,0,
  5. EGL_NONE
  6. };

3.3.3.2 滿足RGBA要求

在pixelflinger中,爲系統提供了各個pixel format的基本信息,RGBA值,字節數/pixel,位數/pixel。
system/core/libpixelflinger/format.cpp
  1. staticGGLFormatconstgPixelFormatInfos[]=
  2. {//AlphaRedGreenBlue
  3. {0,0,{{0,0,0,0,0,0,0,0}},0},//PIXEL_FORMAT_NONE
  4. {4,32,{{32,24,8,0,16,8,24,16}},GGL_RGBA},//PIXEL_FORMAT_RGBA_8888
android會根據pixelflinger的pixel format信息,去和openGL的config比較,得到想要的config。

selectConfigForPixelFormat()@frameworks/base/libs/ui/EGLUtils.cpp
  1. EGLConfig*constconfigs=(EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs);
  2. if(eglChooseConfig(dpy,attrs,configs,numConfigs,&n)==EGL_FALSE){
  3. free(configs);
  4. returnBAD_VALUE;
  5. }
  6. constintfbSzA=fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA);
  7. constintfbSzR=fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED);
  8. constintfbSzG=fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN);
  9. constintfbSzB=fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE);
  10. inti;
  11. EGLConfigconfig=NULL;
  12. for(i=0;i<n;i++){
  13. EGLintr,g,b,a;
  14. EGLConfigcurr=configs[i];
  15. eglGetConfigAttrib(dpy,curr,EGL_RED_SIZE,&r);
  16. eglGetConfigAttrib(dpy,curr,EGL_GREEN_SIZE,&g);
  17. eglGetConfigAttrib(dpy,curr,EGL_BLUE_SIZE,&b);
  18. eglGetConfigAttrib(dpy,curr,EGL_ALPHA_SIZE,&a);
  19. if(fbSzA<=a&&fbSzR<=r&&fbSzG<=g&&fbSzB<=b){
  20. config=curr;
  21. break;
  22. }
  23. }

4. 創建main surface

要讓OpenGL進行圖形處理,那麼需要在OpenGL中創建一個openGL surface。代碼在eglCreateWindowSurface()@frameworks/base/opengl/libs/EGL/egl.cpp
調用當前的config所處的openGL庫的api來創建surface。通過validate_display_config()方法來獲取當前config的openGL api。
創建的surface會和FramebufferNativeWindow關聯到一起。

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

有了surface,有了FramebufferNativeWindow,有了context,基本上與圖形系統相關的概念都有了,下一步就是把這幾個概念關聯起來,在創建surface時已經將surface和FramebufferNativeWindow關聯了起來。
eglMakeCurrent()@frameworks/base/opengl/libs/EGL/egl.cpp

6.1 多線程支持

OpenGL 提供了多線程的支持,有以下2點的支持:
1. 一個Context只能被一個線程使用,不能存在多個線程使用同一個context。因此在多線層操作中使用到了TLS技術,即Thread-local storage,來保證context被唯一使用。
makeCurrent()@frameworks/base/opengl/libs/libagl/egl.cpp
  1. ogles_context_t*current=(ogles_context_t*)getGlThreadSpecific();
  2. if(gl){
  3. egl_context_t*c=egl_context_t::context(gl);
  4. if(c->flags&egl_context_t::IS_CURRENT){
  5. if(current!=gl){
  6. //itisanerrortosetacontextcurrent,ifit'salready
  7. //currenttoanotherthread
  8. return-1;
  9. }
  10. }else{
  11. if(current){
  12. //markthecurrentcontextasnotcurrent,andflush
  13. glFlush();
  14. egl_context_t::context(current)->flags&=~egl_context_t::IS_CURRENT;
  15. }
  16. }
  17. if(!(c->flags&egl_context_t::IS_CURRENT)){
  18. //Thecontextisnotcurrent,makeitcurrent!
  19. setGlThreadSpecific(gl);
  20. c->flags|=egl_context_t::IS_CURRENT;
  21. }
2. 在同一進程中,對於不同的線程對OpenGL庫的訪問,可能使用的GLES api version不同,同樣可以使用TLS技術來保證多線程過程中,不同線程調用各自的GLES api。
前面我們介紹過GLES api地址被存放在gEGLImpl[].hooks[VERSION]->gl中,因此爲保證多線程支持,android將gEGLImpl[].hooks[VERSION]保存到了TLS中,這樣就實現了不同線程各自調用各自版本的GLES api。
eglMakeCurrent()@frameworks/base/opengl/libs/EGL/egl.cpp
  1. //cur_chastobevalidhere(butcouldbeterminated)
  2. if(ctx!=EGL_NO_CONTEXT){
  3. setGlThreadSpecific(c->cnx->hooks[c->version]);
  4. setContext(ctx);
  5. _c.acquire();
  6. }else{
  7. setGlThreadSpecific(&gHooksNoContext);
  8. setContext(EGL_NO_CONTEXT);
  9. }

儘管openGL 實現了多線程的支持,目前我從代碼中別沒有找到多線程的使用。

6.2 設置surface和context之間的關係

由於vendor廠商提供的GPU的GLES庫是不可見的,因此以libGLES_android.so軟件加速爲例來說明這個過程。
contex中保存着兩個surface,read和draw,多少情況下這兩個surface爲同一個surface。
設置FramebufferNativeWindow中Buffers[2]之一爲surface的數據區, 通過connect()和bindDrawSurface()。最終的形態如下圖所示:



在init()@DisplayHardware.cpp中,在綁定surface和context之後,馬上在當前線程中unbind了context,通過
  1. //Unbindthecontextfromthisthread
  2. eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT);
這麼做的目的應該是支持多display系統中的特殊處理,目的是當系統有多個display系統的話,那麼surfaceflinger就會去定義多個DisplayHardware對象,那麼爲了保證下一個DisplayHardware對象的創建不受影響,在當前的DisplayHardware創建完成後,將context從當前的進程中unbind掉。
不過沒關係,在所有的DisplayHardware創建完成之後,surfaceflinger會重新bind 主Display系統的context和surface。
readyToRun()@SurfaceFlinger.cpp
  1. //initializeprimaryscreen
  2. //(otherdisplayshouldbeinitializedinthesamemanner,but
  3. //asynchronously,astheycouldcomeandgo.Noneofthisissupported
  4. //yet).
  5. constGraphicPlane&plane(graphicPlane(dpy));
  6. constDisplayHardware&hw=plane.displayHardware();
  7. constuint32_tw=hw.getWidth();
  8. constuint32_th=hw.getHeight();
  9. constuint32_tf=hw.getFormat();
  10. hw.makeCurrent();



下圖爲這個圖形系統的類圖結構。

發佈了2 篇原創文章 · 獲贊 7 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章