android surfaceflinger研究----顯示系統, Surface機制, SurfaceFlinger loop

轉載自http://blog.csdn.net/windskier/article/details/7030732

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

    一.  android顯示系統的建立

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

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

@SurfaceFlinger.h

GraphicPlane                mGraphicPlanes[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.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內存管理

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

    1.3.1 屏幕設備支持page filp模式

    目前的android系統默認要求屏幕設備給系統映射2個屏幕大小的緩存區,以便支持page flip模式,如果屏幕設備支持page flip模式,那麼FramebufferNativeWindowbuffers將分別指向一個屏幕大小的屏幕設備緩存區。
    // 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模式

    在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

    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

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

    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文件內容
0 0 android
0 1 adreno200

    因此只要我們的移動設備芯片集成了GPU,並提供了對應的GL圖形庫,那麼我們就可以在我們的工程中device目錄下的egl.cfg文件中加入類似上面的配置,那麼我們的系統就會支持硬件加速。

如adreno200 GPU提供的GL圖形庫:
libGLESv1_CM_adreno200.so
libGLESv2_adreno200.so
libEGL_adreno200.so

    那麼假如我們的系統中軟硬件加速都支持了,那麼我們從代碼來看能不能讓用戶自由的選擇加速類型,我們帶着問題來研究一下代碼。

   3.1 OpenGL初始化

    在調用不管是軟件加速的還是硬件加速的OpenGL api之前,我們都需要把軟硬兩種模式的各自的OpenGL api提取出來,抽象出一個interface來供系統使用,這個過程我稱之爲OpenGL初始化過程。
    軟硬兩種模式的OpenGL api被分別指定到了一個全局數組的對應位置。
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個庫中獲得;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
    enum {
        EGL         = 0x01,
        GLESv1_CM   = 0x02,
        GLESv2      = 0x04
    };

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的類型
    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;
    }

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

    

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;

中,不同於gEGLImpl分開保存軟硬件api,所有的config,不論軟硬件的,均保存在gDisplay[0],因爲所有的config是以屏幕區分的,同一塊屏幕應該保存同一份config信息。

在提取出的openGL的config時,會保存到gDisplay[0].config中,在這兒有一個很tricky的實現它保證了硬件加速器的優先使用!
// sort our configurations so we can do binary-searches
        qsort(  dp->configs,
                dp->numTotalConfigs,
                sizeof(egl_config_t), cmp_configs);

最終,上述代碼會將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
    // initialize EGL
    EGLint attribs[] = {
            EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,
            EGL_NONE,           0,
            EGL_NONE
    };

3.3.3.2 滿足RGBA要求

在pixelflinger中,爲系統提供了各個pixel format的基本信息,RGBA值,字節數/pixel,位數/pixel。
system/core/libpixelflinger/format.cpp
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

android會根據pixelflinger的pixel format信息,去和openGL的config比較,得到想要的config。


selectConfigForPixelFormat()@frameworks/base/libs/ui/EGLUtils.cpp
    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

    要讓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 上下文

    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

    有了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
    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;
        }
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
        // 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之間的關係

由於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,通過
    // Unbind the context from this thread
    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
    // 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();

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


 

    前一篇文章介紹了android的顯示系統,這篇文章中,我們把視角往上層移動一下,研究一下framework是如何與surfaceflinger進行業務交互的。如何創建surface,如何顯示窗口等等,所有的這一切都是通過系統服務WindowManagerService與surfaceflinger來進行的。

    android中的Surface機制這一塊代碼寫的比較難理解,光叫Surface的類就有3個,因此本篇文章從兩部分來分析,首先,想要理解Surface機制,還是需要首先理清各個類之間的關係。其次,在理解了整個Surface機制的類關係之後,到時我們再結合前一篇文章中對顯示系統的介紹,研究一下一個Surface是如何和顯示系統建立起聯繫來的,這個聯繫主要是指Surface的顯示buffer的存儲管理。在下篇文章中,再分析SurfaceFlinger是如何將已經存儲了窗口圖形數據的Surface Buffer顯示到顯示系統中。。

1. Surface機制的靜態關係

    將這一部分叫做Surface機制,是有別於SurfaceFlinger而言的,android的圖形系統中,作爲C/S模型兩端的WMS和SurfaceFlinger是圖形系統業務的核心,但是不把WMS和SurfaceFlinger中間的這層聯繫搞清楚的話,是很難理解整個圖形系統的,在本文中我將兩者之間的這個聯繫關係稱之爲Surface機制,它的主要任務就是創建一個Surface,ViewRoot在這個Surface上描繪當前的窗口,SurfaceFlinger將這個Surface flinger(扔)給顯示系統將其呈現在硬件設備上。其實這裏這個Surface在不同的模塊中是以不同的形態存在的,唯一不變的就是其對應的顯示Buffer。


    


1.1 ViewRoot和WMS共享Surface

    我們知道每個Activity都會有一個ViewRoot作爲Activity Window與WMS交互的接口,ViewRoot會繪製整個Activity的窗口View到Surface上,因此我們在ViewRoot中就有了創建Surface的需求。看一下代碼中的Surface的定義:

relayoutWindow()@ViewRoot.java

private final Surface mSurface = new Surface();

Surface()@Surface.java

public Surface() {
        if (DEBUG_RELEASE) {
            mCreationStack = new Exception();
        }
        mCanvas = new CompatibleCanvas();
    }

    由上面可以看出在ViewRoot中定義的Surface只是一個空殼,那麼真正的Surface是在哪裏被初始化的呢?大管家WMS中!當ViewRoot請求WMS relayout時,會將ViewSurface中的Surface交給WMS初始化。在WMS中,對應每個WindowState對象,在relayout窗口時,同樣會創建一個Surface,wms中的這個Surface會真正的初始化,然後再將這個WMS Surface複製給ViewRoot中的Surface。這麼實現的目的就是保證ViewRoot和WMS共享同一個Surface。ViewRoot對Surface進行繪製,WMS對這個Surface進行初始化及管理。很和諧!

relayoutWindow()@ViewRoot.java

int relayoutResult = sWindowSession.relayout(
                mWindow, params,
                (int) (mView.mMeasuredWidth * appScale + 0.5f),
                (int) (mView.mMeasuredHeight * appScale + 0.5f),
                viewVisibility, insetsPending, mWinFrame,
                mPendingContentInsets, mPendingVisibleInsets,
                mPendingConfiguration, mSurface);

relayoutWindow()@WindowManagerService.java

Surface surface = win.createSurfaceLocked();
                    if (surface != null) {
                        outSurface.copyFrom(surface);
                        win.mReportDestroySurface = false;
                        win.mSurfacePendingDestroy = false;
                        if (SHOW_TRANSACTIONS) Slog.i(TAG,
                                "  OUT SURFACE " + outSurface + ": copied");
                    } else {

1.2 SurfaceSession

    SurfaceSession可以認爲是創建Surface過程中,WMS和SurfaceFlinger之間的會話層,通過這個SurfaceSession實現了Surface的創建。

    

    SurfaceSession是JAVA層的概念,@SurfaceSession.java。它對應的native實體是一個SurfaceComposerClient對象。

    SurfaceComposerClient通過ComposerService類來獲得SurfaceFlinger的IBinder接口,但是光獲得SurfaceFlinger的IBinder接口是不夠的,要想請求SurfaceFlinger創建一個Surface,還需要向SurfaceFlinger獲得一個IBinder接口ISurfaceComposerClient,通過這個ISurfaceComposerClient來請求SurfaceFlinger創建一個Surface,爲什麼這麼繞呢,爲什麼不直接讓SurfaceFlinger創建Surface呢?

    站在SurfaceFlinger的角度來考慮,對於SurfaceFlinger來說,可能有多個Client來請求SurfaceFlinger的業務,每個Client可能會請求SurfaceFlinger創建多個Surface,那麼SurfaceFlinger本地需要提供一套機制來保存每個client請求創建的Surface,SurfaceFlinger通過爲每個client創建一個Client對象實現這個機制,並將這個Client的IBinder接口ISurfaceComposerClient返給SurfaceComposerClient對象。SurfaceComposerClient對象在通過ISurfaceComposerClient去請求創建Surface。

@SurfaceFlinger.h

class Client : public BnSurfaceComposerClient

@SurfaceComposerClient.cpp

void SurfaceComposerClient::onFirstRef()
{
    sp<ISurfaceComposer> sm(getComposerService());
    if (sm != 0) {
        sp<ISurfaceComposerClient> conn = sm->createConnection();
        if (conn != 0) {
            mClient = conn;
            Composer::addClient(this);
            mPrebuiltLayerState = new layer_state_t;
            mStatus = NO_ERROR;
        }
    }
}

   下圖描述了整個SurfaceSession的內部結構與工作流程。

其中藍色箭頭是SurfaceComposerClient通過ComposerService獲得SurfaceFlinger的IBinder接口ISurfaceComposer過程;

紅色箭頭表示SurfaceComposerClient通過IPC請求SurfaceFlinger創建Client的過程,並獲得Client的IBinder接口ISurfaceComposerClient;

綠色箭頭表示SurfaceComposerClient通過IPC請求Client創建Surface。



1.3 Surface的形態

    上一節我們分析了SurfaceSession的靜態結構,得知Surface的創建過程是通過SurfaceSession這個中間會話層去請求SurfaceFlinger去創建的,並且這篇文章中,我們說了半天Surface了,那麼究竟我們要創建的Surface究竟是什麼樣的一個東西呢,它的具體形態是什麼呢?這一小節我們就來分析以下Surface的形態。

1.3.1 client端Surface的形態

    首先,我們看一下Surface在WMS中定義的代碼

createSurfaceLocked()@WindowManagerService.java

                    mSurface = new Surface(
                            mSession.mSurfaceSession, mSession.mPid,
                            mAttrs.getTitle().toString(),
                            0, w, h, mAttrs.format, flags);

我們可以看到,它將SurfaceSession對象當作參數傳遞給了Surface的構造函數。往下看Surface的構造函數。

@Surface.java

    public Surface(SurfaceSession s,
            int pid, int display, int w, int h, int format, int flags)
        throws OutOfResourcesException {
        if (DEBUG_RELEASE) {
            mCreationStack = new Exception();
        }
        mCanvas = new CompatibleCanvas();
        init(s,pid,null,display,w,h,format,flags);
    }

    這個構造函數,不同於我們在ViewRoot中看到的Surface的構造函數,這個構造函數並不是一個空殼,它做了本地實體的初始化工作,因此這個Surface纔是一個真正的Suface。

Native 函數init回調到SurfaceComposerClient的createSurface()函數,往下的過程在上一節的圖中描述的很清楚,流程就不介紹了,同時我們先不管SurfaceFlinger爲SurfaceComposerClient創建的Surface到底是一個什麼東西,我們先看看SurfaceComposerClient爲WMS創建的是一個什麼東西?

@SurfaceComposerClient.cpp

sp<SurfaceControl> SurfaceComposerClient::createSurface(
        int pid,
        const String8& name,
        DisplayID display,
        uint32_t w,
        uint32_t h,
        PixelFormat format,
        uint32_t flags)
{
    sp<SurfaceControl> result;
    if (mStatus == NO_ERROR) {
        ISurfaceComposerClient::surface_data_t data;
        sp<ISurface> surface = mClient->createSurface(&data, pid, name,
                display, w, h, format, flags);
        if (surface != 0) {
            result = new SurfaceControl(this, surface, data, w, h, format, flags);
        }
    }
    return result;
}

    從上面的代碼我們可以看出,SurfaceComposerClient爲WMS返回的是一個SurfaceControl對象,這個SurfaceControl對象包含了surfaceFlinger爲SurfaceComposerClient創建的surface,這個surfaceFlinge創建的Surface在Client端的形態爲ISurface。這個過程下面分析SurfaceFlinger端的Surface形態時會看到。

    SurfaceControl類中還有一個非常重要的成員,它的類型也叫做Surface,定義在frameworks/base/libs/surfaceflinger/Surface.h。這個Surface提供了顯示Buffer的管理。在文章的後面再介紹。

@frameworks/base/libs/surfaceflinger_client/Surface.cpp

sp<Surface> SurfaceControl::getSurface() const
{
    Mutex::Autolock _l(mLock);
    if (mSurfaceData == 0) {
        mSurfaceData = new Surface(const_cast<SurfaceControl*>(this));
    }
    return mSurfaceData;
}

1.3.2 SurfaceFlinger端Surface形態

SurfaceFlinger::[email protected]  
    sp<Layer> normalLayer;
    switch (flags & eFXSurfaceMask) {
        case eFXSurfaceNormal:
            if (UNLIKELY(flags & ePushBuffers)) {
                layer = createPushBuffersSurface(client, d, w, h, flags);
            } else {
                normalLayer = createNormalSurface(client, d, w, h, flags, format);
                layer = normalLayer;
            }
            break;
        case eFXSurfaceBlur:
            layer = createBlurSurface(client, d, w, h, flags);
            break;
        case eFXSurfaceDim:
            layer = createDimSurface(client, d, w, h, flags);
            break;
    }

    if (layer != 0) {
        layer->initStates(w, h, flags);
        layer->setName(name);
        ssize_t token = addClientLayer(client, layer);

        surfaceHandle = layer->getSurface();
        if (surfaceHandle != 0) { 
            params->token = token;
            params->identity = surfaceHandle->getIdentity();
            params->width = w;
            params->height = h;
            params->format = format;
            if (normalLayer != 0) {
                Mutex::Autolock _l(mStateLock);
                mLayerMap.add(surfaceHandle->asBinder(), normalLayer);
            }
        }

    當client請求SurfaceFlinger創建Surface時,SurfaceFlinger首先根據WMS提供的窗口的屬性來一個命名爲Layer概念的對象,然後再根據Layer創建它的子類對象LayerBaseClient::Surface。此時第三個名爲Surface類出現了,下一節我們來介紹一下這個Layer的概念。

   

1.4 Layer       

    

1.4.1 Layer的分類

    目前,android中有4中Layer類型,如上圖所示。  

    1. Layer, 普通的Layer,它爲每個Client端請求的Surface創建顯示Buffer。

    2. LayerBuffer,這種Layer它並不會創建顯示Buffer,它只是使用已有的Buffer作爲顯示Buffer,如Camera的preview;

    3. LayerBlur,這種Layer也不會創建顯示Buffer,它只是將通過這個Layer將原來FrameBuffer上的數據進行模糊處理;

    4. LayerDim,這種Layer也不會創建顯示Buffer,它只是將通過這個Layer將原來FrameBuffer上的數據進行暗淡處理;


     從這中Layer看出,我們分析的重點就是第一種Layer,下面我們着重分析一下普通的Layer。Layer的具體業務我們在下一篇文章中分析

1.4.2 Layer的管理

    上文我們在分析SurfaceSession的時候,也分析過,一個Client可能會創建多個Surface,也就是要創建多個Layer,那麼SurfaceFlinger端如何管理這個寫個Layer呢?SurfaceFlinger維護了2個Vector來管理Layer。

    第一種方式,我們知道SurfaceFlinger會爲每個SurfaceSession創建一個Client對象,這第一種方式就是將所有爲某一個SurfacSession創建的Layer保存在它對應的Client對象中。

SurfaceFlinger::createSurface()@SurfaceFlinger.cpp

        ssize_t token = addClientLayer(client, layer);

    第二種方式,將所有的創建的普通的Layer保存起來,以便Client Surface在請求實現Buffer時能夠辨識Client Surface對應的Layer。

SurfaceFlinger::createSurface()@SurfaceFlinger.cpp

        mLayerMap.add(surfaceHandle->asBinder(), normalLayer);

2. Surface 顯示Buffer的存儲管理

    在前文介紹Client端的Surface形態的內容時,我們提到SurfaceControl中還會維護一個名爲Surface對象,它定義在 frameworks/base/libs/surfaceflinger/Surface.h中,它負責向LayerBaseClient::Surface請求顯示Buffer,同時將顯示Buffer交給JAVA Surface的Canvas去繪製窗口,我們稱這個Surface爲Client Surface

2.1 窗口繪製

    我們先從ViewRoot中分析一下,它是如何顯示窗口View的,如何用到Client Surface請求的顯示Buffer的。

draw()@ViewRoot.java

Canvas canvas;
            try {
                int left = dirty.left;
                int top = dirty.top;
                int right = dirty.right;
                int bottom = dirty.bottom;
                canvas = surface.lockCanvas(dirty);

                if (left != dirty.left || top != dirty.top || right != dirty.right ||
                        bottom != dirty.bottom) {
                    mAttachInfo.mIgnoreDirtyState = true;
                }

                // TODO: Do this in native
                canvas.setDensity(mDensity);

  上面的代碼顯示,JAVA Surface 會lock canvas。而Client Surface的創建就在這個過程中,即下面代碼中的第一行getSurface().我們先不管Client Surface的創建,先看看Canvas是如何與Client Surface的顯示Buffer關聯的。

 

static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
{
    const sp<Surface>& surface(getSurface(env, clazz));
    if (!Surface::isValid(surface))
        return 0;


    SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas);
    SkBitmap bitmap;
    ssize_t bpr = info.s * bytesPerPixel(info.format);
    bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);
    if (info.format == PIXEL_FORMAT_RGBX_8888) {
        bitmap.setIsOpaque(true);
    }
    if (info.w > 0 && info.h > 0) {
        bitmap.setPixels(info.bits);
    } else {
        // be safe with an empty bitmap.
        bitmap.setPixels(NULL);
    }
    nativeCanvas->setBitmapDevice(bitmap);
    
    SkRegion clipReg;
    if (dirtyRegion.isRect()) { // very common case
        const Rect b(dirtyRegion.getBounds());
        clipReg.setRect(b.left, b.top, b.right, b.bottom);
    } else {
        size_t count;
        Rect const* r = dirtyRegion.getArray(&count);
        while (count) {
            clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);
            r++, count--;
        }
    }

    nativeCanvas->clipRegion(clipReg);
    
    int saveCount = nativeCanvas->save();
    env->SetIntField(clazz, so.saveCount, saveCount);

    if (dirtyRect) {
        const Rect& bounds(dirtyRegion.getBounds());
        env->SetIntField(dirtyRect, ro.l, bounds.left);
        env->SetIntField(dirtyRect, ro.t, bounds.top);
        env->SetIntField(dirtyRect, ro.r, bounds.right);
        env->SetIntField(dirtyRect, ro.b, bounds.bottom);
    }
    
    return canvas;
}

    上面的代碼,我們可以看出,Canvas的Bitmap設備的設置了Client Surface的顯示Buffer爲其Bitmap pixel存儲空間。

bitmap.setPixels(info.bits);

    這樣Canvas的繪製空間就有了。下一步就該繪製窗口了。

draw()@ViewRoot.java

                    try {
                        canvas.translate(0, -yoff);
                        if (mTranslator != null) {
                            mTranslator.translateCanvas(canvas);
                        }
                        canvas.setScreenDensity(scalingRequired
                                ? DisplayMetrics.DENSITY_DEVICE : 0);
                        mView.draw(canvas);
                    }

其中ViewRoot中的mView爲整個窗口的DecorView。


2.2 Client Surface的初始化

    Client Surface的創建是從ViewRoot首次Lock canvas時進行的,這麼做的目的可能也是爲了節約空間,減少不必要的開支。

    Client Surface的初始化和顯示Buffer的管理過程比較複雜,下圖給出了這一部分的一個靜態結構圖,有些東西從圖上表現不出來,下面我簡單的介紹一下。



2.2.1 SharedClient

    SharedClient是這一部分實現的關鍵所在,它並不是一個每個Client Surface創建時都會被創建的,整個系統中只有一個SharedClient對象,並且它是在共享內存上創建的,下面代碼中可以看出,UserClient在初始化時,提供了一個MemoryHeapBase來供SharedClient創建,MemoryHeapBase是創建的共享內存。

@SurfaceFlinger.cpp

UserClient::UserClient(const sp<SurfaceFlinger>& flinger)
    : ctrlblk(0), mBitmap(0), mFlinger(flinger)
{
    const int pgsize = getpagesize();
    const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));

    mCblkHeap = new MemoryHeapBase(cblksize, 0,
            "SurfaceFlinger Client control-block");

    ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());
    if (ctrlblk) { // construct the shared structure in-place.
        new(ctrlblk) SharedClient;
    }
}

    SharedClient對象的主要目的其實很簡單,就是爲系統提供了SharedBufferStack::NUM_LAYERS_MAX(GB上爲31)個SharedBufferStack。也就是目前系統同時支持31個Client Surface的創建。關於SharedBufferStack下面再做介紹。

   爲什麼需要將SharedClient設計爲共享內存呢?每個Client Surface需要的SharedBufferStack寄存在SharedClient中,而對於每個SharedBufferStack,一方面,Client Surface需要對它進行一些區域尺寸等的設置;另一方面,在render時,Layer需要獲得當前Client Surfce對應的SharedBufferStack中獲得區域尺寸等設置信息。

class [email protected]

    SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ];

2.2.2 SharedBufferStack

    SharedBufferStack在這個模塊中所處的地位在上一小節中介紹了,下面主要介紹一下它的作用。
    1. 設置當前窗口要顯示的區域等信息;
class SharedBufferStack@SharedBufferStack.h
    status_t setDirtyRegion(int buffer, const Region& reg);
    status_t setCrop(int buffer, const Rect& reg);
    status_t setTransform(int buffer, uint8_t transform);
    2. android的圖形系統中提供了兩個顯示Buffer,從上圖中我們可以看出Client Surface有2個GraphicBuffer,2個Buffer其中一個顯示,稱之爲Front Buffer,另外一個交給ViewRoot去繪製窗口,稱之爲Back Buffer。等BackBuffer繪製完成,SurfaceFlinger在將兩者調換,這樣就大大提高了顯示的效率,具體過程下篇文章介紹。
    而SharedBufferStack第二個很重要的作用就是提供了一套機制來實現這個調換的過程,以保證提供給ViewRoot的Buffer符合當前Buffer輪轉的要求。通過SharedBufferClient::tail和 
class SharedBufferStack@SharedBufferStack.h
    volatile int32_t head;      // server's current front buffer
    volatile int32_t available; // number of dequeue-able buffers

這幾個變量的值來確定Client Surface中GraphicBuffer的索引,其中SharedBufferClient::tail記錄的是BackBuffer的索引;SharedBufferStack::head記錄的是FrontBuffer的索引。



2.2.3 Client Surace GraphicBuffer的請求

    這裏將Client Surface的GraphicBuffer的創建過程以時序圖的形式展現出來。


    這裏需要注意的是,Client Surface的2個GraphicBuffer只有在lock()時纔會去創建,而不是在Client Surface被創建的時候創建的。

 

    上一篇文章介紹了整個Surface機制(我是這麼稱呼的,主要是Surface的建立,Surface的顯示存儲的管理),同時我們也介紹過了整個顯示系統,那麼這篇文章就介紹一下SurfaceFlinger 這個核心服務層的機制。

    從代碼中我們可以看出SurfaceFlinger 是一個thread,運行在system_server進程中,並且其threadLoop()方法的返回值爲true,因此它是一個循環的loop。這樣保證了SurfaceFlinger業務的循環週期性。

    首先,先來個綜述,下圖是我總結的一個SurfaceFlinger結構的概括圖:


1. SurfaceFlinger的同步

    SurfaceFlinger 並不是時時刻刻都在執行業務中,當WMS請求SurfaceFlinger創建Surface,或者WMS對Surface進行屬性設置時,我們希望此時的SurfaceFlinger並不進行顯示操作,以保證對Surface的線程保護,因此SurfaceFlinger 的loop中實現了同步機制。

  1. waitForEvent();  
    waitForEvent();

    主要的同步情況有如下幾種,當然也有其他一些要求SurfaceFlinger同步的情況,不夠對於研究SurfaceFlinger就不太重要了

    1. 創建Surface同步

    假如當前只有一個Client,比如WMS請求SufaceFlinger創建一個Surface,那麼此時應該保持SurfaceFlinger loop處在block狀態,因爲這個過程涉及到對一些成員變量的處理,爲了保證同步而需要hold住整個loop。

    2. 設置Surface屬性或SurfaceFlinger屬性同步

    創建完Surface之後,WMS會請求SurfaceFlinger對其Layer進行屬性設置或者對SurfaceFlinger的屬性進行設置,如上面概括圖中SurfaceComposerClient中的函數接口。

    3. Surface繪製同步

    當ViewRoot對Surface進行繪製時,同樣需要將SurfaceFlinger hold住,當整個窗口繪製完成之後,再向SurfaceFlinger發送signal信號。如下面時序圖所示。

    

    4. freeze/unfreeze同步

   當每個Activity啓動的時候,AMS都會請求WMS freeze整個屏幕,當Activity啓動之後,再unfreeze整個屏幕,我猜測這麼做的目的是爲了保證在Activity以及Activity的窗口在創建過程中,對Activity窗口的Surface進行的線程保護,以免出現屏幕的閃爍等用戶體驗較差的現象。


2. Layer存儲

    在SurfaceFlinger中,Layer是怎麼樣存儲的呢?所有的Layer,不論是那個Client創建的Layer,均保存在一個名爲layersSortedByZ的變量中,也就是說WMS請求創建的Surface的Layer和其他Client請求創建的Layer都保存在layersSortedByZ中,但是layersSortedByZ保存過程中則遵守一定的規則。下面代碼中的do_compare揭示了這個規則。

@SurfaceFlinger.h

    class LayerVector : public SortedVector< sp<LayerBase> > {
    public:
        LayerVector() { }
        LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }
        virtual int do_compare(const void* lhs, const void* rhs) const {
            const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));
            const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));
            // sort layers by Z order
            uint32_t lz = l->currentState().z;
            uint32_t rz = r->currentState().z;
            // then by sequence, so we get a stable ordering
            return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);
        }
    };

  每次向layersSortedByZ中添加新的Layer,都會做一次排序,按照規則將其放在合適的位置。

    1. 首先,按照Layer的Z-order值來排序,Z-order值小的,放在layersSortedByZ低索引值位置;

    2. 其次,如果兩個Layer Z-order值相同,sequence值小的,放在layersSortedByZ低索引值位置;

    Z-order值如何確定?

    WMS根據不同的Window Type來確定Z-order值,Z-order = LAYER*TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET。

根據下面代碼中的不同的Window Type的LAYER值,可以確定Z-order值,例如TYPE_APPLICATION窗口,其

Z-order = 2*10000+1000 = 21000。

    @PhoneWindowManager.java

   // wallpaper is at the bottom, though the window manager may move it.
    static final int WALLPAPER_LAYER = 2;
    static final int APPLICATION_LAYER = 2;
    static final int PHONE_LAYER = 3;
    static final int SEARCH_BAR_LAYER = 4;
    static final int STATUS_BAR_PANEL_LAYER = 5;
    static final int SYSTEM_DIALOG_LAYER = 6;
    // toasts and the plugged-in battery thing
    static final int TOAST_LAYER = 7;
    static final int STATUS_BAR_LAYER = 8;
    // SIM errors and unlock.  Not sure if this really should be in a high layer.
    static final int PRIORITY_PHONE_LAYER = 9;
    // like the ANR / app crashed dialogs
    static final int SYSTEM_ALERT_LAYER = 10;
    // system-level error dialogs
    static final int SYSTEM_ERROR_LAYER = 11;
    // on-screen keyboards and other such input method user interfaces go here.
    static final int INPUT_METHOD_LAYER = 12;
    // on-screen keyboards and other such input method user interfaces go here.
    static final int INPUT_METHOD_DIALOG_LAYER = 13;
    // the keyguard; nothing on top of these can take focus, since they are
    // responsible for power management when displayed.
    static final int KEYGUARD_LAYER = 14;
    static final int KEYGUARD_DIALOG_LAYER = 15;
    // things in here CAN NOT take focus, but are shown on top of everything else.
    static final int SYSTEM_OVERLAY_LAYER = 16;
    static final int SECURE_SYSTEM_OVERLAY_LAYER = 17;

    sequence值如何確定?

    sequence值是根據Layer的創建的順序來維護這個序列值,下面代碼中的LayerBase的構造函數中的sequence值,每創建一個Layer,sSequence加一賦值給sequence。

@LayerBase.cpp

int32_t LayerBase::sSequence = 1;

LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
    : dpy(display), contentDirty(false),
      sequence(uint32_t(android_atomic_inc(&sSequence))),
      mFlinger(flinger),
      mNeedsFiltering(false),
      mOrientation(0),
      mLeft(0), mTop(0),
      mTransactionFlags(0),
      mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
      mInvalidate(0)
{
    const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
    mFlags = hw.getFlags();
    mBufferCrop.makeInvalid();
    mBufferTransform = 0;
}

3. 屬性更新

    這一節的所描述的實現都在函數handleTransactionLocked()中。

    從上面概括圖中可以看出,WMS可以對SurfaceFlinger進行屬性設置,也可以對當前的Surface對應的Layer進行屬性設置,因此handleTransactionLocked()函數就是對SurfaceFlinger屬性和設置了新屬性的Layer的屬性更新。

enum {
    eTransactionNeeded      = 0x01,
    eTraversalNeeded        = 0x02
};

    SurfaceFlinger根據這個枚舉值來確定handleTransactionLocked()需要更新SurfaceFlinger屬性還是layer屬性。

    如果SurfaceFlinger屬性被設置了新內容,則SurfaceFlinger會記錄標誌eTransactionNeeded;如果layer屬性被設置了新內容,那麼
SurfaceFlinger會記錄標誌eTraversalNeeded。handleTransactionLocked()通過記錄的標誌來執行各自的屬性得更新。‘

    這裏提到的屬性的更新,主要是看SurfaceFlinger或者laye新設置的屬性與舊的屬性相比,哪些屬性做了修改,然後

記錄下來,在接下來的SurfaceFlinger loop中使用新的屬性來顯示圖形。

    類SurfaceFlinger 和Layer中各自定義了兩個屬性的變量,其中mCurrentState爲新設置屬性,mDrawingState爲顯示圖形時用到的屬性,一般爲舊屬性。不過SurfaceFlinger 和Layer分別定義了不同的State類。

                State                   mCurrentState;
                State                   mDrawingState;

4. 圖形緩存

    這一部分的的實現在函數handlePageFlip()中。

    有這麼一種可能,當前顯示到顯示設備上的layer不止一個,而且layer是按照Z-Order的順序來疊加到OpenGL的surface上的,那麼這就需要layer的Z-Order值和座標來確定每個layer能夠被顯示的區域。

4.1 page flip

    前面一篇文章中介紹過,每個surface均有2個buffer供使用,一個作爲FronteBuffer供SurfaceFlinger去顯示,另外一個作爲BackBuffer供ViewRoot去繪製窗口。因此在顯示各個layer之前,我們需要做一個page flip過程,將當前的已經繪製了應用窗口的BackBuffer選擇爲FrontBuffer,用於顯示;將之前的已經顯示完成的FrontBuffer在重置爲BackBuffer供ViewRoot去繪製。

   而實現這個page flip的過程很簡單

lockPageFlip()@Layer.cpp

    ssize_t buf = lcblk->retireAndLock();

SharedBufferServer::RetireUpdate::operator()@SharedBufferStack.cpp

    head = (head + 1) % numBuffers;

    


4.2 紋理初始化

    爲每個Buffer的紋理進行初始化,爲當前的紋理創建一個EGLImageKHR,將當前的Buffer最爲該EGLImageKHR的源。這樣OpenGL就可以進行紋理映射。

lockPageFlip()@Layer.cpp

    /* a buffer was posted, so we need to call reloadTexture(), which
     * will update our internal data structures (eg: EGLImageKHR or
     * texture names). we need to do this even if mPostedDirtyRegion is
     * empty -- it's orthogonal to the fact that a new buffer was posted,
     * for instance, a degenerate case could be that the user did an empty
     * update but repainted the buffer with appropriate content (after a
     * resize for instance).
     */
    reloadTexture( mPostedDirtyRegion );

4.3 計算顯示區域

    通過layer的疊加,我們可以計算出總的顯示區域以及每個layer需要顯示的區域,它的實現在computeVisibleRegions()函數中。這個函數主要計算了layer疊加後的總的顯示區域,以及每個layer需要顯示的區域。整個的計算過程比較簡單,只是需要注意不透明區域的處理,computeVisibleRegions()需要計算出一個不透明區域,通過這個不透明區域驗證WMS提供給layer的區域是否正確。即下面代碼中的mWormholeRegion計算,mWormholeRegion爲屏幕區域減去不透明區域,正常情況mWormholeRegion應該爲空,即不透明區域範圍應該爲屏幕區域,如果不透明區域小雨屏幕區域,那麼說明當前的應用程序出現了設置的錯誤。今天有個網友就出現了這個問題。

handlePageFlip()

        const Region screenRegion(hw.bounds());
        if (visibleRegions) {
            Region opaqueRegion;
            computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);

            /*
             *  rebuild the visible layer list
             */
            mVisibleLayersSortedByZ.clear();
            const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
            size_t count = currentLayers.size();
            mVisibleLayersSortedByZ.setCapacity(count);
            for (size_t i=0 ; i<count ; i++) {
                if (!currentLayers[i]->visibleRegionScreen.isEmpty())
                    mVisibleLayersSortedByZ.add(currentLayers[i]);
            }

            mWormholeRegion = screenRegion.subtract(opaqueRegion);
            mVisibleRegionsDirty = false;
        }

    在computeVisibleRegions()疊加計算總的顯示範圍,layer的計算順序從上到下的過程計算的,也就是先計算Z-Order值較大的,顯示在最上層的layer開始往下計算。這麼做的好處就是能夠很好的計算出不透明區域的範圍。

    在SurfaceFlinger的區域相互之間的操作處理如下:


    

    

    


    

4.4 圖形緩存

    前面選擇了FrontBuffer、初始化了紋理、計算了layer的顯示區域,那麼下一步就該將Buffer內容進行圖形處理並保存到OpenGL緩存中。

    調用每個layer的draw函數來進行這個操作。如下面代碼所示。具體的圖形處理過程很複雜,完全交給OpenGL去處理,這裏我們就不去關心了。我們只需要知道最終經過圖形處理的內容會被緩存到OpenGL的緩存區中。

void SurfaceFlinger::composeSurfaces(const Region& dirty)
{
    if (UNLIKELY(!mWormholeRegion.isEmpty())) {
        // should never happen unless the window manager has a bug
        // draw something...
        drawWormhole();
    }
    const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
    const size_t count = layers.size();
    for (size_t i=0 ; i<count ; ++i) {
        const sp<LayerBase>& layer(layers[i]);
        const Region clip(dirty.intersect(layer->visibleRegionScreen));
        if (!clip.isEmpty()) {
            layer->draw(clip);
        }
    }
}

    從前面的顯示系統中,介紹過,Surface的緩存Buffer就是FramebufferNativeWindow中定義的2個Buffer,如果/dev/fb0讀取設備信息,如果設備支持page flip,那麼Surface的緩存Buffer即從/dev/fb0設備中申請;如果不支持,我們則需要從/dev/pmem中申請,同時/dev/fb0還會提供一個Buffer以便圖形最終的顯示。

    /dev/fb0不支持page flip模式

    

    /dev/fb0支持page flip模式

    

5. 圖形顯示

    當圖形內容被緩存到frameBuffer中後,最後的一步就是圖形顯示。代碼中很明確就是SurfaceFlinger loop中的postFramebuffer()函數了。

    這個函數最終回調到OpenGL的eglSwapBuffers()函數,這個函數主要有2個步驟(由於硬件加速代碼不可見,我們仍然以軟件加速爲例)

    1. 顯示當前緩存buffer中內容;

     首先,將原來的屏幕上的內容與最新需要顯示的內容進行區域相減,將原來的內容copy到當前的緩存buffer中;

EGLBoolean egl_window_surface_v2_t::swapBuffers()@frameworks\base\opengl\libagl\egl.cpp

    /*
     * Handle eglSetSwapRectangleANDROID()
     * We copyback from the front buffer 
     */
    if (!dirtyRegion.isEmpty()) {
        dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
        if (previousBuffer) {
            const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
            if (!copyBack.isEmpty()) {
                void* prevBits;
                if (lock(previousBuffer, 
                        GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
                    // copy from previousBuffer to buffer
                    copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
                    unlock(previousBuffer);
                }
            }
        }
        oldDirtyRegion = dirtyRegion;
    }

    其次,如果當前的緩存buffer是申請自/dev/fb0,那麼直接去顯示這個緩存區中內容;如果緩存buffer是申請自/dev/pmem,那麼需要將緩存buffer中內容拷貝到/dev/fb0 buffer中去,其結構如上一節所示。

    2. 對2個緩存buffer進行page flip(swap)操作。

    通過 queueBuffer()操作將將當前Buffer交還給FramebufferNativeWindow,同時調用fb_post進行圖形顯示。然後通過dequeueBuffer()操作獲得另外一個FramebufferNativeWindow的緩存Buffer,實現page flip(swap)操作。


 

    至此,整個的SurfaceFlinger的機制就分析完了。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章