Android SurfaceFlinger中的SharedClient & threadLoop Paint--留UML圖用

Android SurfaceFlinger中的SharedClient -- 客戶端(Surface)和服務端(Layer)之間的顯示緩衝區管理

轉載自http://blog.csdn.net/mtv0312/article/details/6407388

     SurfaceFlinger在系統啓動階段作爲系統服務被加載。應用程序中的每個窗口,對應本地代碼中的Surface,而Surface又對應於SurfaceFlinger中的各個Layer,SurfaceFlinger的主要作用是爲這些Layer申請內存,根據應用程序的請求管理這些Layer顯示、隱藏、重畫等操作,最終由SurfaceFlinger把所有的Layer組合到一起,顯示到顯示器上。當一個應用程序需要在一個Surface上進行畫圖操作時,首先要拿到這個Surface在內存中的起始地址,而這塊內存是在SurfaceFlinger中分配的,因爲SurfaceFlinger和應用程序並不是運行在同一個進程中,如何在應用客戶端(Surface)和服務端(SurfaceFlinger - Layer)之間傳遞和同步顯示緩衝區?這正是本文要討論的內容。

Surface的創建過程

我們先看看Android如何創建一個Surface,下面的序列圖展示了整個創建過程。

                                                              圖一   Surface的創建過程

創建Surface的過程基本上分爲兩步:

1. 建立SurfaceSession

第一步通常只執行一次,目的是創建一個SurfaceComposerClient的實例,JAVA層通過JNI調用本地代碼,本地代碼創建一個SurfaceComposerClient的實例,SurfaceComposerClient通過ISurfaceComposer接口調用SurfaceFlinger的createConnection,SurfaceFlinger返回一個ISurfaceFlingerClient接口給SurfaceComposerClient,在createConnection的過程中,SurfaceFlinger創建了用於管理緩衝區切換的SharedClient,關於SharedClient我們下面再介紹,最後,本地層把SurfaceComposerClient的實例返回給JAVA層,完成SurfaceSession的建立。

2. 利用SurfaceSession創建Surface

JAVA層通過JNI調用本地代碼Surface_Init(),本地代碼首先取得第一步創建的SurfaceComposerClient實例,通過SurfaceComposerClient,調用ISurfaceFlingerClient接口的createSurface方法,進入SurfaceFlinger,SurfaceFlinger根據參數,創建不同類型的Layer,然後調用Layer的setBuffers()方法,爲該Layer創建了兩個緩衝區,然後返回該Layer的ISurface接口,SurfaceComposerClient使用這個ISurface接口創建一個SurfaceControl實例,並把這個SurfaceControl返回給JAVA層。

由此得到以下結果:

  • JAVA層的Surface實際上對應於本地層的SurfaceControl對象,以後本地代碼可以使用JAVA傳入的SurfaceControl對象,通過SurfaceControl的getSurface方法,獲得本地Surface對象;
  • Android爲每個Surface分配了兩個圖形緩衝區,以便實現Page-Flip的動作;
  • 建立SurfaceSession時,SurfaceFlinger創建了用於管理兩個圖形緩衝區切換的SharedClient對象,SurfaceComposerClient可以通過ISurfaceFlingerClient接口的getControlBlock()方法獲得這個SharedClient對象,查看SurfaceComposerClient的成員函數_init
void SurfaceComposerClient::_init(
        const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn)
{
    ......
    mClient = conn;
    if (mClient == 0) {
        mStatus = NO_INIT;
        return;
    }

    mControlMemory = mClient->getControlBlock();
    mSignalServer = sm;
    mControl = static_cast<SharedClient *>(mControlMemory->getBase());
}


 

獲得Surface對應的顯示緩衝區

雖然在SurfaceFlinger在創建Layer時已經爲每個Layer申請了兩個緩衝區,但是此時在JAVA層並看不到這兩個緩衝區,JAVA層要想在Surface上進行畫圖操作,必須要先把其中的一個緩衝區綁定到Canvas中,然後所有對該Canvas的畫圖操作最後都會畫到該緩衝區內。下圖展現了綁定緩衝區的過程:

                                                                            圖二  綁定緩衝區的過程

    開始在Surface畫圖前,Surface.java會先調用lockCanvas()來得到要進行畫圖操作的Canvas,lockCanvas會進一步調用本地層的Surface_lockCanvas,本地代碼利用JAVA層傳入的SurfaceControl對象,通過getSurface()取得本地層的Surface對象,接着調用該Surface對象的lock()方法,lock()返回了改Surface的信息,其中包括了可用緩衝區的首地址vaddr,該vaddr在Android的2D圖形庫Skia中,創建了一個bitmap,然後通過Skia庫中Canvas的API:Canvas.setBitmapDevice(bitmap),把該bitmap綁定到Canvas中,最後把這個Canvas返回給JAVA層,這樣JAVA層就可以在該Canvas上進行畫圖操作,而這些畫圖操作最終都會畫在以vaddr爲首地址的緩衝區中。

    再看看在Surface的lock()方法中做了什麼:

  • dequeueBuffer(&backBuffer)獲取backBuffer
    • SharedBufferClient->dequeue()獲得當前空閒緩衝區的編號
    • 通過緩衝區編號獲得真正的GraphicBuffer:backBuffer
    • 如果還沒有對Layer中的buffer進行映射(Mapper),getBufferLocked通過ISurface接口重新重新映射
  • 獲取frontBuffer
  • 根據兩個Buffer的更新區域,把frontBuffer的內容拷貝到backBuffer中,這樣保證了兩個Buffer中顯示內容的同步
  • backBuffer->lock() 獲得backBuffer緩衝區的首地址vaddr
  • 通過info參數返回vaddr

釋放Surface對應的顯示緩衝區

畫圖完成後,要想把Surface的內容顯示到屏幕上,需要把Canvas中綁定的緩衝區釋放,並且把該緩衝區從變成可投遞(因爲默認只有兩個buffer,所以實際上就是變成了frontBuffer),SurfaceFlinger的工作線程會在適當的刷新時刻,把系統中所有的frontBuffer混合在一起,然後通過OpenGL刷新到屏幕上。下圖展現瞭解除綁定緩衝區的過程:

                                                                 圖三  解除綁定緩衝區的過程

  • JAVA層調用unlockCanvasAndPost
  • 進入本地代碼:Surface_unlockCanvasAndPost
  • 本地代碼利用JAVA層傳入的SurfaceControl對象,通過getSurface()取得本地層的Surface對象
  • 綁定一個空的bitmap到Canvas中
  • 調用Surface的unlockAndPost方法
    • 調用GraphicBuffer的unlock(),解鎖緩衝區
    • 在queueBuffer()調用了SharedBufferClient的queue(),把該緩衝區更新爲可投遞狀態

SharedClient 和 SharedBufferStack

從前面的討論可以看到,Canvas綁定緩衝區時,要通過SharedBufferClient的dequeue方法取得空閒的緩衝區,而解除綁定並提交緩衝區投遞時,最後也要調用SharedBufferClient的queue方法通知SurfaceFlinger的工作線程。實際上,在SurfaceFlinger裏,每個Layer也會關聯一個SharedBufferServer,SurfaceFlinger的工作線程通過SharedBufferServer管理着Layer的緩衝區,在SurfaceComposerClient建立連接的階段,SurfaceFlinger就已經爲該連接創建了一個SharedClient 對象,SharedClient 對象中包含了一個SharedBufferStack數組,數組的大小是31,每當創建一個Surface,就會佔用數組中的一個SharedBufferStack,然後SurfaceComposerClient端的Surface會創建一個SharedBufferClient和該SharedBufferStack關聯,而SurfaceFlinger端的Layer也會創建SharedBufferServer和SharedBufferStack關聯,實際上每對SharedBufferClient/SharedBufferServer是控制着同一個SharedBufferStack對象,通過SharedBufferStack,保證了負責對Surface的畫圖操作的應用端和負責刷新屏幕的服務端(SurfaceFlinger)可以使用不同的緩衝區,並且讓他們之間知道對方何時鎖定/釋放緩衝區。

SharedClient和SharedBufferStack的代碼和頭文件分別位於:

/frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp

/frameworks/base/include/private/surfaceflinger/SharedBufferStack.h

 

 

                                                                       圖四    客戶端和服務端緩衝區管理

     繼續研究SharedClient、SharedBufferStack、SharedBufferClient、SharedBufferServer的誕生過程。

    1. SharedClient

  •     在createConnection階段,SurfaceFlinger創建Client對象:
sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()
{
    Mutex::Autolock _l(mStateLock);
    uint32_t token = mTokens.acquire();

    sp<Client> client = new Client(token, this);
    if (client->ctrlblk == 0) {
        mTokens.release(token);
        return 0;
    }
    status_t err = mClientsMap.add(token, client);
    if (err < 0) {
        mTokens.release(token);
        return 0;
    }
    sp<BClient> bclient =
        new BClient(this, token, client->getControlBlockMemory());
    return bclient;
}
  • 再進入Client的構造函數中,它分配了4K大小的共享內存,並在這塊內存上構建了SharedClient對象:
Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)
    : ctrlblk(0), cid(clientID), mPid(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;
    }
}
  • 回到createConnection中,通過Client的getControlBlockMemory()方法獲得共享內存塊的IMemoryHeap接口,接着創建ISurfaceFlingerClient的子類BClient,BClient的成員變量mCblk保存了IMemoryHeap接口指針;
  • 把BClient返回給SurfaceComposerClient,SurfaceComposerClient通過ISurfaceFlingerClient接口的getControlBlock()方法獲得IMemoryHeap接口指針,同時保存在SurfaceComposerClient的成員變量mControlMemory中;
  • 繼續通過IMemoryHeap接口的getBase ()方法獲取共享內存的首地址,轉換爲SharedClient指針後保存在SurfaceComposerClient的成員變量mControl中;
  • 至此,SurfaceComposerClient的成員變量mControl和SurfaceFlinger::Client.ctrlblk指向了同一個內存塊,該內存塊上就是SharedClient對象。 

    2. SharedBufferStack、SharedBufferServer、SharedBufferClient

    SharedClient對象中有一個SharedBufferStack數組:

    SharedBufferStack surfaces[ NUM_LAYERS_MAX ];

    NUM_LAYERS_MAX 被定義爲31,這樣保證了SharedClient對象的大小正好滿足4KB的要求。創建一個新的Surface時,進入SurfaceFlinger的createSurface函數後,先取在createConnection階段創建的Client對象,通過Client在0--NUM_LAYERS_MAX 之間取得一個尚未被使用的編號,這個編號實際上就是SharedBufferStack數組的索引:

int32_t id = client->generateId(pid);

然後以Client對象和索引值以及其他參數,創建不同類型的Layer對象,一普通的Layer對象爲例:

layer = createNormalSurfaceLocked(client, d, id,
                        w, h, flags, format);

在createNormalSurfaceLocked中創建Layer對象:

sp<Layer> layer = new Layer(this, display, client, id);

構造Layer時會先構造的父類LayerBaseClient,LayerBaseClient中創建了SharedBufferServer對象,SharedBufferStack 數組的索引值和SharedClient被傳入SharedBufferServer對象中。

LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
        const sp<Client>& client, int32_t i)
    : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i),
      mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
{
    lcblk = new SharedBufferServer(
            client->ctrlblk, i, NUM_BUFFERS,
            mIdentity);
}

    自此,Layer通過lcblk成員變量(SharedBufferServer)和SharedClient共享內存區建立了關聯,並且每個Layer對應於SharedBufferStack 數組中的一項。

    回到SurfaceFlinger的客戶端Surface.cpp中,Surface的構造函數如下:

Surface::Surface(const sp<SurfaceControl>& surface)
    : mClient(surface->mClient), mSurface(surface->mSurface),
      mToken(surface->mToken), mIdentity(surface->mIdentity),
      mFormat(surface->mFormat), mFlags(surface->mFlags),
      mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL),
      mWidth(surface->mWidth), mHeight(surface->mHeight)
{
    mSharedBufferClient = new SharedBufferClient(
            mClient->mControl, mToken, 2, mIdentity);

    init();
}

SharedBufferClient構造參數mClient->mControl就是共享內存塊中的SharedClient對象,mToken就是SharedBufferStack 數組索引值。

到這裏我們終於知道,Surface中的mSharedBufferClient成員和Layer中的lcblk成員(SharedBufferServer),通過SharedClient中的同一個SharedBufferStack,共同管理着Surface(Layer)中的兩個緩衝區。

 

SurfaceFlinger繼承了Thread類,自然也繼承了Thread類的threadLoop方法,SurfaceFlinger工作線程的主代碼都在threadLoop()方法中。工作線程啓動後,基類Thread會循環地調用threadLoop方法,SurfaceFlinger的threadLoop()主要是要完成系統中各個Layer(Surface)進行混合(compose),然後不停地把一幀幀混合好的圖像數據傳送到顯示設備中。

 

threadLoop的流程

                 圖一   threadLoop流程

1. handleConsoleEvents

handleConsoleEvent目前沒有深入瞭解,貌似只是處理顯示設備進入休眠狀態或者從休眠中喚醒時,改變SufaceFlinger的狀態,然後threadLoop的後續流程會根據相應的狀態來決定是否繼續給顯示設備傳送顯示數據。

2. handleTransaction

因爲Layer的混合是在線程中進行的,而混合的過程中,應用程序或者系統可能會改變Layer的狀態,例如屏幕旋轉、增加或刪除Layer、某個Layer可見或不可見,爲了使這些變動不會破壞當前正在進行的混合動作,SurfaceFlinger維護着兩個Layer列表:

  • mCurrentState.layersSortedByZ   ---- 當前系統最新的Layer列表
  • mDrawingState.layersSortedByZ  ---- 本次混合操作使用的Layer列表

handleTransaction就是根據Layer列表的這些狀態的變化,計算是否有可見區域內需要更新,並設置狀態變量mVisibleRegionsDirty,然後把mCurrentState賦值給mDrawingState,最後釋放已經被丟棄(ditch)的Layer

  • 上一次混合過程中,可能應用程序釋放了一個Layer,可是mDrawingState正在使用,不能馬上銷燬,所以要等到本次混合前才能做出銷燬的動作。

  • 如果Layer的大小有變化並且可見,Layer的handleTransaction將會重新分配緩衝區,並且凍結SurfaceFlinger後續的混合操作,也就是屏幕的內容本次將不會刷新,直到下一個循環的handlePageFlip階段才解除凍結。

3. handlePageFlip

該階段會遍歷各個Layer,在每個Layer中,取得並鎖住該Layer的frontBuffer,然後利用frontBuffer中的圖像數據生成該Layer的2D貼圖(Texture),並且計算更新區域,爲後續的混合操作做準備。

                                圖二   handlePageFlip處理流程

Layer的lockPageFlip()首先通過SharedBufferServer類的成員變量lcblk,調用retireAndLock取得該Layer當前可用的frontBuffer,然後通過reloadTexture方法生成openGL ES的紋理貼圖,最後通過unlockPageFlip完成更新區域的Layer座標到屏幕座標的變換。

handleRepaint

 handleRepaint真正實現Layer混合的階段,下圖是handleRepaint的處理流程:

                                                     圖三  handleRepaint 的處理流程

 

handleRepaint首先重置了openGL的觀察矩陣,然後遍歷mDrawingState.layersSortedByZ 中的Layer列表,調用每個Layer的onDraw方法,在onDraw方法中,會調用drawWithOpenGL()方法,將在handlePageFlip階段生成的貼圖混合到OpenGL的主表面,最後handleRepaint把需要刷新的區域清除。

unlockClients

unlockClients只是遍歷各個Layer並調用各個Layer的finishPageFlip方法。finishPageFlip會進一步調用SharedBufferServer的unlock()方法:(關於SharedBufferSever,請參考本人以下博文的SharedClient 和 SharedBufferStack一節)

void Layer::finishPageFlip()
{
    status_t err = lcblk->unlock( mFrontBufferIndex );
    LOGE_IF(err!=NO_ERROR, 
            "layer %p, buffer=%d wasn't locked!",
            this, mFrontBufferIndex);
}


lcblk->unlock( mFrontBufferIndex )會把Layer的frontBuffer解除鎖定。

postFramebuffer

 進入postFramebuffer階段,OpenGL主表面已經準備好了混合完成的圖像數據,postFramebuffer只是簡單地調用hw.flip(),hw.flip()進一步調用了eglSwapBuffers完成主表面的切換,這樣屏幕上的圖像就會更新爲新的數據。

 

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