40
http://blog.csdn.net/yili_xie/article/details/4803527 Android Display System --- Surface Flinger SurfaceFlinger 是 Android multimedia 是 是是 一, Android 是是是是是是是 service 系系 是是是是 surface composer 是 系系 是是是 2D 3D surface 是是是是 是是 SurfaceFlinger 是 是是是 ,一一系系 系系 1 系系 是是 是是是是是是是是是是 一: 是是是是是是是是是是是是是 一,一 surface window 4 是 surface ,一 home 是 是是是 ,、、 3 是 surface button 是是是home surface 是是是是是是是是 系系 是是是 a surface 是是是是是是 是是是是是是 ,, surface 是 是 是 是是是 是是 ,,, ,? b surface 是是是是是是 是是是是是是是是是 ,,,home 是是是 ,一。? 是是是是是是是是是是 ,一 Z 是 是是是 surface 是是是 Z 是 surface 是 Z 是 Z-order 是是是 是是是是是是是是是是是是是是是是是是是是 一,一,,一 buffer 是 surface 是是是 surface 是 是是是是是 一,,,, buffer 是是是是是是是是是是是是

Surfaceflinger Overlay

  • View
    141

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Surfaceflinger Overlay

http://blog.csdn.net/yili_xie/article/details/4803527

Android Display System --- Surface Flinger

     SurfaceFlinger 是 Android multimedia 的一個部分,在 Android 的實現中它是一個 service ,提供系統 範圍內的 surface composer 功能,它能夠將各種應用 程式的 2D 、3D surface 進行組合。在具體講 SurfaceFlinger

之前,我們先來看一下有關顯示方面的一些基礎 知識 。 1 、原理 分析 讓我們首先看一下下面的螢幕簡略圖:

 

 

每個應用程式可能對應著一個或者多個圖形介面,而每個介面我們就稱之為一個 surface ,或者說是 window ,在上面的圖中我們能看到 4 個 surface

,一個是 home 介面,還有就是紅、綠、藍分別代表的 3 個 surface ,而兩個 button 實際是 home surface 裡面的內容。在這裡我們能看到我們進行圖形顯示所需要解決 的問題:     a 、首先每個 surface 在螢幕上有它的位置,以及大小,然後每個 surface

裡面還有要顯示的內容,內容,大小,位置 這些元素 在我們改變應用程式的時候都可能會改變,改變時應該如何處理 ?

b 、然後就各個 surface 之間可能有重疊,比如說在上面的簡略圖中,綠色覆蓋了藍色,而紅色又覆蓋了綠色和藍色以及下面的 home ,而且還具有一定透明度。這種層之間的關係應該如何描述?

Page 2: Surfaceflinger Overlay

我們首先來看第二個問題,我們可以想像在螢幕平面的垂直方向還有一個Z 軸,所有的 surface 根據在 Z 軸上的座標來確定前後,這樣就可以描述各個surface 之間的上下覆蓋關係了,而這個在 Z 軸上的順序,圖形上有個專業術語叫 Z-order 。     對於第一個問題,我們需要一個結構來記錄應用程式介面的位置,大小,以及一個 buffer 來記錄需要顯示的內容,所以這就是我們 surface 的概念,surface 實際我們可以把它理解成一個容器,這個容器記錄著應用程式介面的控制資訊,比如說大小啊,位置啊,而它還有 buffer 來專門存儲需要顯示的內容。     在這裡還存在一個問題,那就是當存在圖形重合的時候應該如何處理呢,而且可能有些 surface 還帶有透明資訊,這裡就是我們 SurfaceFlinger 需要解決問題,它要把各個 surface 組合(compose/merge) 成一個 main Surface

,最後將 Main Surface 的內容發送給 FB/V4l2 Output ,這樣螢幕上就能看到我們想要的效果。     在實際中對這些 Surface 進行 merge 可以採用兩種方式,一種就是採用軟體的形式來 merge ,還一種就是採用硬體的方式,軟體的方式就是我們的SurfaceFlinger ,而硬體的方式就是 Overlay 。 2 、OverLay

     因為硬體 merge 內容相對簡單,我們首先來看 overlay 。 Overlay 實現的方式有很多,但都需要硬體的支援。以 IMX51 為例子,當 IPU 向內核申請FB 的時候它會申請 3 個 FB ,一個是主屏的,還一個是副屏的,還一個就是Overlay 的。 簡單地來說,Overlay 就是我們將硬體所能接受的格式資料 和控制資訊送到這個 Overlay FrameBuffer,由硬體驅動來負責 merge

Overlay buffer 和主屏 buffer 中的內容。     一般來說現在的硬體都只支援一個 Overlay,主要用在視頻播放以及camera preview 上,因為視頻內容的不斷變化用硬體 Merge 比用軟體Merge 要有效率得多,下面就是使用 Overlay 和不使用 Overlay 的過程:  

Page 3: Surfaceflinger Overlay

  

 

    SurfaceFlinger 中加入了 Overlay hal,只要實現這個 Overlay hal 可以使用 overlay 的功能,這個頭檔 在:/hardware/libhardware/include/harware/Overlay.h,可以使用 FB 或者V4L2 output 來實現,這個可能是我們將來工作的內容。實現 Overlay hal 以後,使用 Overlay 介面的 sequence 就在 : /frameworks/base/libs/surfaceflinger/tests/overlays/Overlays.cpp,這個 sequnce 是很重要的,後面我們會講到。     不過在實際中我們不一定需要實現 Overlay hal,如果瞭解硬體的話,可以在驅動中直接把這些資訊送到 Overlay Buffer,而不需要走上層的Android。Fsl 現在的 Camera preview 就是採用的這種方式,而且我粗略看了 r3 補丁的內容,應該在 opencore 的視頻播放這塊也實現了 Overlay。 3、SurfaceFlinger

     現在就來看看最複雜的 SurfaceFlinger,首先要明確的是 SurfaceFlinger

只是負責 merge Surface 的控制,比如說計算出兩個 Surface 重疊的區域,至於 Surface 需要顯示的內容,則通過 skia,opengl 和 pixflinger 來計算。 所以我們在介紹 SurfaceFlinger 之前先忽略裡面存儲的內容究竟是什麼,先弄清楚它對 merge 的一系列控制的過程,然後再結合 2D ,3D 引擎來看它的處理過程。 3.1 、Surface 的創建過程     前面提到了每個應用程式可能有一個或者多個 Surface , 我們需要一些資料結構來存儲我們的視窗資訊,我們還需要 buffer 來存儲我們的視窗內容, 而且最主要的是我們應該確定一個方案 來和 SurfaceFlinger 來交互這些資訊,讓我們首先看看下面的 Surface 創建過程的類圖 :

Page 4: Surfaceflinger Overlay

 

在 IBinder 左邊的就是用戶端部分,也就是需要視窗顯示的應用程式,而右邊就是我們的 Surface Flinger service 。 創建一個 surface 分為兩個過程,一個是在 SurfaceFlinger 這邊為每個應用程式(Client) 創建一個管理 結構,另一個就是創建存儲內容的 buffer ,以及在這個 buffer 上的一系列畫圖之類的操作。

因為 SurfaceFlinger 要管理多個應用程式的多個視窗介面,為了進行管理它提供了一個 Client 類,每個來請求服務的應用程式就對應了一個 Client 。因為 surface 是在 SurfaceFlinger 創建的,必須返回一個結構讓應用程式知道自己申請的 surface 資訊,因此 SurfaceFlinger 將 Client 創建的控制結構per_client_cblk_t 經過 BClient 的封裝以後返回給SurfaceComposerClient ,並向應用程式提供了一組創建和銷毀 surface 的操作:

Page 5: Surfaceflinger Overlay

上層啟動一個新的圖形會話,首先要建立一個 SurfaceComposerClient物件,用來創建圖形會話用戶端。SurfaceComposerClient 在其構造函數中調用getComposerService(),返回 ISurfaceComposer 介面,在調用ISurfaceComposer:createConnection(),通過 binder 和 SurfaceFlinger庫通訊,最終 SurfaceFlinger庫返回一個 ISurfaceFlingerClient 介面。

    為應用程式創建一個 Client 以後,下面需要做的就是為這個 Client 分配 Surface , Flinger 為每個 Client 提供了 8M 的空間 ,包括控制資訊和存儲內容的 buffer 。在說創建 surface 之前首先要理解 layer 這個概念,回到我們前面看的螢幕簡略圖,實際上每個視窗就是 z 軸上的一個 layer , layer 提供了對視窗控制資訊的操作,以及內容的處理 ( 調用 opengl 或者 skia) ,也就是說 SurfaceFlinger 只是控制什麼時候應該進行這些資訊的處理以及處理的過程,所有實際的處理都是在 layer 中進行的,可以理解為創建一個 Surface 就是創建一個 Layer 。不得不說 Android 這些亂七八糟的名字,讓我繞了很久…… 創建 Layer 的過程,首先是由這個應用程式的 Client 根據應用程式的 pid

生成一個唯一的 layer ID ,然後根據大小,位置,格式啊之類的資訊創建出 Layer 。在 Layer 裡面有一個嵌套的 Surface 類,它主要包含一個 ISurfaceFlingerClient::Surface_data_t ,包含了這個 Surace 的統一識別字以及

Page 6: Surfaceflinger Overlay

buffer 資訊等,提供給應用程式使用。最後應用程式會根據返回來的 ISurface

資訊等創建自己的一個 Surface 。

 

 

Android 提供了 4 種類型的 layer 供選擇,每個 layer 對應一種類型的視窗,並對應這種視窗相應的操作: Layer , LayerBlur , LayerBuffer , LayerDim

。不得不說再說 Android 起的亂七八糟的名字, LayerBuffer 很容易讓人理解成是 Layer 的 Buffer ,它實際上是一種 Layer 類型。各個 Layer 的效果大家可以參考 Surface.java 裡面的描述: /frameworks/base/core/java/android/view/surface.java 。這裡要重點說一下兩種 Layer ,一個是 Layer (norm layer) ,另一個是 LayerBuffer 。

Norm Layer 是 Android 種使用最多的一種 Layer ,一般的應用程式在創建 surface 的時候都是採用的這樣的 layer ,瞭解 Normal Layer 可以讓我們知道 Android 進行 display 過程中的一些基礎原理。 Normal Layer 為每個 Surface

分配兩個 buffer : front buffer 和 back buffer ,這個前後是相對的概念,他們是可以進行 Flip 的。 Front buffer 用於 SurfaceFlinger 進行顯示,而 Back

buffer 用於應用程式進行畫圖,當 Back buffer 填滿資料 (dirty) 以後,就會 flip

, back buffer 就變成了 front buffer 用於顯示,而 front buffer 就變成了 back

buffer 用來畫圖,這兩個 buffer 的大小是根據 surface 的大小格式動態變化的。這個動態變化的實現我沒仔細看,可以參照 : /frameworks/base/lib/surfaceflinger/layer.cpp 中的 setbuffers() 。

兩個 buffer flip 的方式是 Android display 中的一個重要實現方式,不只是每個 Surface 這麼實現,最後寫入 FB 的 main surface 也是採用的這種方式。

LayerBuffer 也是將來必定會用到的一個 Layer ,個人覺得也是最複雜的一個 layer ,它不具備 render buffer ,主要用在 camera preview / video

playback 上。它提供了兩種實現方式,一種就是 post buffer ,另外一種就是我們前面提到的 overlay , Overlay 的介面實際上就是在這個 layer 上實現的。不管是 overlay 還是 post buffer 都是指這個 layer 的資料來源自其他地方,只

Page 7: Surfaceflinger Overlay

是 post buffer 是通過軟體的方式最後還是將這個 layer merge 主的 FB ,而 overlay 則是通過硬體 merge 的方式來實現。與這個 layer 緊密聯繫在一起的是 ISurface 這個介面,通過它來註冊資料來源,下面我舉個例子來說明這兩種方式的使用方法:

 

前面幾個步驟是通用的:  

// 要使用 Surfaceflinger 的服務必須先創建一個 client sp<SurfaceComposerClient> client = new

SurfaceComposerClient();

// 然後向 Surfaceflinger 申請一個 Surface , surface 類型為 PushBuffers

sp<Surface> surface = client->createSurface(getpid(), 0, 320,

240,

PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers);

// 然後取得 ISurface 這個介面, getISurface() 這個函數的調用時具有許可權限制的,必須在 Surface.h 中打開: /framewoks/base/include/ui/Surface.h

sp<ISurface> isurface = Test::getISurface(surface);

 

//overlay 方式下就創建 overlay ,然後就可以使用 overlay 的介面了 sp<OverlayRef> ref = isurface->createOverlay(320, 240,

PIXEL_FORMAT_RGB_565);

sp<Overlay> verlay = new Overlay(ref);

 

//post buffer 方式下,首先要創建一個 buffer ,然後將 buffer 註冊到 ISurface 上

ISurface::BufferHeap buffers(w, h, w, h,

                                         

PIXEL_FORMAT_YCbCr_420_SP,

                                         transform,

                                         0,

                                         mHardware-

>getPreviewHeap());

mSurface->registerBuffers(buffers);

3.2 、應用 程式對視窗的控制和畫圖 Surface 創建以後,應用程式就可以在 buffer 中畫圖了,這裡就面對著兩個問題

了,一個是怎麼知道在哪個 buffer 上來畫圖,還一個就是畫圖以後如何通知

Page 8: Surfaceflinger Overlay

SurfaceFlinger 來進行 flip 。除了畫圖之外,如果我們移動視窗以及改變視窗大小的時候,如何告訴 SurfaceFlinger 來進行處理呢 ?在明白這些問題之前,首先我們要瞭解 SurfaceFlinger 這個服務 是如何運作的: 從類圖中可以看到 SurfaceFlinger 是一個執行緒類,它繼承了 Thread 類。當創

建 SurfaceFlinger 這個服務的時候會啟動一個 SurfaceFlinger 監聽執行緒,這個執行緒會一直等待事件的發生,比如說需要進行 sruface flip ,或者說視窗位置大小發生了變化等等,一旦產生這些事件, SurfaceComposerClient 就會通過 IBinder 發出信號,這個執行緒就會結束等待處理這些事件,處理完成以後會繼續等待,如此迴圈。

SurfaceComposerClient 和 SurfaceFlinger 是通過 SurfaceFlingerSynchro 這個類來同步信號的,其實說穿了就是一個條件變數。監聽執行緒等待條件的值變成 OPEN ,一旦變成 OPEN 就結束等待並將條件置成 CLOSE 然後進行事件處理,處理完成以後再繼續等待條件的值變成 OPEN ,而 Client 的 Surface

一旦改變就通過 IBinder 通知 SurfaceFlinger 將條件變數的值變成 OPEN ,並喚醒等待的執行緒,這樣就通過執行緒類和條件變數實現了一個動態處理機制。

瞭解了 SurfaceFlinger 的事件機制我們再回頭看看前面提到的問題了。首先在對 Surface 進行畫圖之前必須鎖定 Surface 的 layer ,實際上就是鎖定了 Layer_cblk_t 裡的 swapstate 這個變數。 SurfaceComposerClient 通過 swapsate 的值來確定要使用哪個 buffer 畫圖,如果 swapstate 是下面的值就會阻塞 Client ,就不翻譯了直接 copy 過來:

// We block the client if:

// eNextFlipPending:  we've used both buffers already, so we

need to

//                    wait for one to become availlable.

// eResizeRequested:  the buffer we're going to acquire is

being

//                    resized. Block until it is done.

// eFlipRequested && eBusy: the buffer we're going to acquire

is

//                    currently in use by the server.

// eInvalidSurface:   this is a special case, we don't block

in this

//                    case, we just return an error.

所以應用程式先調用 lockSurface() 鎖定 layer 的 swapstate ,並獲得畫圖的 buffer 然後就可以在上面進行畫圖了,完成以後就會調用 unlockSurfaceAndPost() 來通知 SurfaceFlinger 進行 Flip 。或者僅僅調用 unlockSurface() 而不通知 SurfaceFlinger 。

Page 9: Surfaceflinger Overlay

一般來說畫圖的過程需要重繪 Surface 上的所有圖元,因為一般情況下顯示過後的圖元是不做保存的,不過也可以通過設定來保存一些圖元,而只繪製部分圖元,這裡就涉及到圖元的拷貝了,需要將 Front buffer 的內容拷貝到 Back buffer 。在 SurfaceFlinger 服務實現中圖元的拷貝是經常需要進行的操作,而且還可能涉及拷貝過程的轉換,比如說螢幕的旋轉,翻轉等一系列操作。因此 Android 提供了拷貝圖元的 hal ,這個也可能是我們將來需要實現的,因為用硬體完成圖元的拷貝,以及拷貝過程中可能的矩陣變換等操作,比用 memcpy 要有效率而且節省資源。這個 HAL 頭文件 在: /hardware/libhardware/hardware/include/copybit.h

視窗狀態變化的處理是一個很複雜的過程,首先要說明一下, SurfaceFlinger 只是執行 Windows manager 的指令,由 Windows manager 來決定什麼是偶改變大小,位置,設置 透明度,以及如何調整 layer 之間的順序, SurfaceFlinger 僅僅只是執行它的指令。 PS : Windows Manager 是 java 層的一個服務,提供對所有視窗的管理 功能,這部分的內容我沒細看過,覺得是將來需要瞭解的內容。

視窗狀態的變化包括位置的移動,視窗大小,透明度, z-order 等等,首先我們來瞭解一下 SurfaceComposerClient 是如何和 SurfaceFlinger 來交互這些資訊的。當應用程式需要改變視窗狀態的時候它將所有的狀態改變資訊打包,然後一起發送給 SurfaceFlinger , SurfaceFlinger 改變這些狀態資訊以後,就會喚醒等待的監聽執行緒,並設置一個標誌位元告訴監聽執行緒視窗的狀態已經改變了,必須要進行處理,在 Android 的實現中,這個打包的過程就是一個 Transaction ,所有對視窗狀態 (layer_state_t) 的改變都必須在一個 Transaction 中。

到這裡應用程式用戶端的處理過程已經說完了,基本分為兩個部分,一個就是在視窗畫圖,還一個就是視窗狀態改變的處理。  

4 、 SurfaceFlinger 的處理過程 瞭解了 Flinger 和用戶端的交互,我們再來仔細看看 SurfaceFlinger 的處理過

程,前面已經說過了 SurfaceFlinger 這個服務在創建的時候會啟動一個監聽的執行緒,這個執行緒負責每次視窗更新時候的處理,下面我們來仔細看看這個執行緒的事件的處理,大致就是下面的這個圖:

Page 10: Surfaceflinger Overlay

 

先大致講一下 Android 組合各個視窗的原理 : Android 實際上是通過計算每一個視窗的可見區域,就是我們在螢幕上可見的視窗區域 ( 用 Android 的詞彙來說就是 visibleRegionScreen ) ,然後將各個視窗的可見區域畫到一個主 layer 的相應部分,最後就拼接成了一個完整的螢幕,然後將主 layer 輸送到 FB 顯示。在將各個視窗可見區域畫到主 layer 過程中涉及到一個硬體實現和一個軟體實現的問題,如果是軟體實現則通過 Opengl 重新畫圖,其中還包括存在透明度的 alpha 計算;如果實現了 copybit hal

的話,可以直接將視窗的這部分數據 直接拷貝過來,並完成可能的旋轉,翻轉,以及 alhpa 計算等。

下面來看看 Android 組合各個 layer 並送到 FB 顯示的具體過程:  

4.1 、 handleConsoleEvent

Page 11: Surfaceflinger Overlay

當接收到 signal 或者 singalEvent 事件以後,執行緒就停止等待開始對 Client 的請求進行處理,第一個步驟是 handleConsoleEvent ,這個步驟我看了下和 /dev/console 這個設備有關,它會取得螢幕或者釋放螢幕,只有取得螢幕的時候才能夠在螢幕上畫圖。  

4.2 、 handleTransaction

前面提到過,視窗狀態的改變只能在一個 Transaction 中進行。因為視窗狀態的改變可能造成本視窗和其他視窗的可見區域變化,所以就必須重新來計算視窗的可見區域。在這個處理子過程中 Android 會根據標誌位元來對所有 layer 進行遍歷,一旦發現哪個視窗的狀態發生了變化就設置標誌位元以在將來重新計算這個視窗的可見區域。在完成所有子 layer 的遍歷以後, Android 還會根據標誌位元來處理主 layer ,舉個例子,比如說感測器感應到手機橫過來了,會將視窗橫向顯示,此時就要重新設置主 layer 的方向。  

4.3 、 handlePageFlip

    這裡會處理每個視窗 surface buffer 之間的翻轉,根據 layer_state_t 的 swapsate 來決定是否要翻轉,當 swapsate 的值是 eNextFlipPending 是就會翻轉。處理完翻轉以後它會重新計算每個 layer 的可見區域,這個重新計算的過程我還沒看太明白,但大致是一個這麼的過程: 從 Z 值最大的 layer 開始計算,也就是說從最上層的 layer 計算,去掉本身的透明

區域和覆蓋在它上面的不透明區域,得到的就是這個 layer 的可見區域。然後這個 layer 的不透明區域就會累加到不透明覆蓋區域,這個 layer 的可見區域會放入到主 layer 的可見區域,然後計算下一個 layer ,直到計算完所有的 layer 的可見區域。這中間的計算是通過定義在 skia 中的一種與或非的圖形邏輯運算實現的,類似我們數學中的與或非邏輯圖。  

4.4 、 handleRepaint

計算出每個 layer 的可見區域以後,這一步就是將所有可見區域的內容畫到主 layer 的相應部分了,也就是說將各個 surface buffer 裡面相應的內容拷貝到主 layer 相應的 buffer ,其中可能還涉及到 alpha 運算,圖元的翻轉,旋轉等等操作,這裡就像我前面說的可以用硬體來實現也可以用軟體來實現。在使用軟體的 opengl 做計算的過程中還會用到 PixFlinger 來做圖元的合成,這部分內容我還沒時間來細看。  

4.5 、 postFrameBuffer

最後的任務就是翻轉主 layer 的兩個 buffer ,將剛剛寫入的內容放入 FB 內顯示了。

Page 12: Surfaceflinger Overlay

http://www.meegozu.com/thread-428-1-1.html

Android 圖形系統之 libui

1 libui 簡介libui 是 Android 圖形庫的本地框架,負責提供圖形介面(Surface)的框架。libui 不僅僅負責圖形介面框架,還提供處理事件輸入、攝像頭輸出、Overlay 顯示等框架,是整個圖形使用者交互(GUI)系統的中樞。標頭檔位置 /frameworks/base/include/ui

原始檔案文章 /frameworks/base/libs/ui

編譯生成動態連結程式庫 libui.so

2 libui 庫包含內容1)Camera 相關框架及底層介面2)Event / Key event事件處理3)Overlay 相關框架和底層介面4)定義一些圖形顯示相關資料結構(Rect、Region 和 PixelFormat)5)Framebuffer管理和顯存分配6)Surface 圖形介面框架本文將主要分析顯示控制機制及圖形介面框架。3 Framebuffer 管理和顯存分配3.1 設備初始化libui 定義了 FramebufferNativeWindow類,在構造函數中載入 Gralloc 硬體模組,並通過該模組提供的介面打開 Linux 的 Framebuffer設備,同時將打開Gralloc模組。設備打開後,將初始化 FramebufferNativeWindow 的參數,將顯示緩衝區設置為 2,同時向系統申請兩塊 NativeBuffer 結構用於存儲顯示裝置的雙緩衝,再調用 Gralloc模組的 alloc 介面分配顯存空間。以下為部分初始化代碼:

1.    if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)

== 0) {

2.         err = framebuffer_open(module, &fbDev);

3.         err = gralloc_open(module, &grDev);

4.         // initialize the buffer FIFO

5.         mNumBuffers = 2;

6.         mNumFreeBuffers = 2;

7.         mBufferHead = mNumBuffers-1;

8.         buffers[0] = new NativeBuffer(

Page 13: Surfaceflinger Overlay

9.                 fbDev->width, fbDev->height, fbDev->format,

GRALLOC_USAGE_HW_FB);

10.         buffers[1] = new NativeBuffer(

11.                 fbDev->width, fbDev->height, fbDev->format,

GRALLOC_USAGE_HW_FB);

12.         err = grDev->alloc(grDev, fbDev->width, fbDev->height, fbDev-

>format,

13.                 GRALLOC_USAGE_HW_FB, &buffers[0]->handle, &buffers[0]-

>stride);

14.         err = grDev->alloc(grDev, fbDev->width, fbDev->height, fbDev-

>format,

15.                 GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]-

>stride);

16.     ......

複製代碼

3.2 共用式分配顯存相對於 Gralloc模組採用獨立記憶體空間作為顯示緩衝區,libui 提供了一套通過 ashmem機制,和系統主存共用分配顯存的方案。這種方案適用於實體記憶體較少且對顯示要求低的設備。該策略通過巨集定義GRALLOC_USAGE_HW_MASK 進行判斷,如果標誌位元沒有被置位元,則初始化 sw_gralloc_handle_t 結構體。sw_gralloc_handle_t::alloc 負責申請共用記憶體,根據圖形格式計算需要申請的圖形尺寸,計算公式為 size = bpp * w * h,另外需要保證 size 為 PAGE_SIZE 對齊。調用ashmem_create_region函數申請空間,調用 ashmem_set_prot_region設置參數,最後用 mmap獲取申請空間的記憶體位址。

1.     int fd = ashmem_create_region("sw-gralloc-buffer", size);

2.     int prot = PROT_READ;

3.     if (usage & GRALLOC_USAGE_SW_WRITE_MASK)

4.         prot |= PROT_WRITE;

5.     ashmem_set_prot_region(fd, prot) ;

6.     void* base = mmap(0, size, prot, MAP_SHARED, fd, 0);

複製代碼

Page 14: Surfaceflinger Overlay

sw_gralloc_handle_t::registerBuffer 會判斷當前進程是否與 sw_gralloc_handle_t 相同,若不相同需要重新mmap,獲取新的記憶體位址。

1.     if (hnd->pid != getpid()) {

2.         void* base = mmap(0, hnd->size, hnd->prot, MAP_SHARED, hnd-

>fd, 0);

3.         hnd->base = intptr_t(base);

4.     }

4 libui 中的 Surface 圖形介面框架libui庫提供了 Surface 圖形介面框架,包括上層應用的調用介面和於 SurfaceFlinger庫的通信介面,圖形介面的具體實現由 SurfaceFlinger庫完成。Android 上層應用的調用介面主要包含 SurfaceComposerClient、SurfaceControl 和 Surface三個主要資料結構。4.1 創建 SurfaceComposerClient 用戶端上層啟動一個新的圖形會話,首先要建立一個 SurfaceComposerClient物件,用來創建圖形會話用戶端。SurfaceComposerClient 在其構造函數中調用 getComposerService(),返回ISurfaceComposer 介面,在調用 ISurfaceComposer:createConnection(),通過 binder

和 SurfaceFlinger庫通訊,最終 SurfaceFlinger庫返回一個 ISurfaceFlingerClient 介面。

//D:\repos\pydtd\pydtd_device\frameworks\base\libs\

surfaceflinger_client\SurfaceComposerClient.cpp

Page 15: Surfaceflinger Overlay

4.2 創建一個 Surface

創建 SurfaceComposerClient物件後,可以調用 createSurface()創建一個新的 Surface,實際上是調用 ISurfaceFlingerClient 介面的 createSurface(),通過 binder 進入SurfaceFlinger創建真正的顯示 Layer。創建成功後將返回一個 SurfaceControl類型的物件。

//D:\repos\pydtd\pydtd_device\frameworks\base\libs\

surfaceflinger_client\SurfaceComposerClient.cpp

JAVA 層的 Surface 實際上對應於本地層的 SurfaceControl物件,以後本地代碼可以使用 JAVA傳入的

SurfaceControl物件,通過 SurfaceControl 的 getSurface 方法,獲得本地 Surface物件;

Page 16: Surfaceflinger Overlay

4.3 獲取 Surface 和 ISurface

SurfaceControl物件可使用 getSurface()獲取 Surface物件。這兩個資料結構分工不同,SurfaceControl 主要負責設置圖形介面的參數,Surface 則提供了控制圖形介面的介面。Surface 可以通過 getISurface()獲取伺服器端 ISurface 的介面,利用 ISurface 的介面,通過 binder 處理序間通訊機制和伺服器端傳輸 Surface 資料。

4.4 用戶端和服務端模型圖中 Server 下層即 SurfaceFlinger 實現,這裡沒有列出。解釋一下我理解的客戶和服務端模型,上層圖形應用程式利用 SurfaceComposerClient、SurfaceControl 及 Surface物件向服務端申請 Surface,獲取控制 Surface 的介面 ISurfaceXXX,利用該介面提供的 Binder 進程通訊機制和服務端進行通訊和資料交換。服務端指 libui 提供的 Surface框架和libsurfaceflinger庫,系統啟動時將 SurfaceFlinger註冊為系統服務,負責處理所有用戶端擁有的 Surface,決定當前顯示內容及資料更新情況。

Page 17: Surfaceflinger Overlay

本文轉載自:

http://hi.baidu.com/aokikyon

引用文章:http://meegozu.com/thread-431-1-1.html

http://blog.csdn.net/yili_xie/archive/2009/11/12/4803527.aspx

http://blog.csdn.net/DroidPhone/article/details/5972568

Android SurfaceFlinger 中的 SharedClient -- 用戶端

(Surface)和服務端(Layer)之間的顯示緩衝區管理

SurfaceFlinger 在系統啟動階段作為系統服務被載入。應用程式中的每個視窗,對應本地代碼中的

Surface,而 Surface 又對應於 SurfaceFlinger 中的各個 Layer,SurfaceFlinger 的主要作用是為這些

Layer 申請記憶體,根據應用程式的請求管理這些 Layer 顯示、隱藏、重畫等操作,最終由

SurfaceFlinger 把所有的 Layer 組合到一起,顯示到顯示器上。當一個應用程式需要在一個 Surface 上進

行畫圖操作時,首先要拿到這個 Surface 在記憶體中的起始位址,而這塊記憶體是在 SurfaceFlinger 中分

配的,因為 SurfaceFlinger 和應用程式並不是運行在同一個進程中,如何在應用用戶端(Surface)和服

務端(SurfaceFlinger - Layer)之間傳遞和同步顯示緩衝區?這正是本文要討論的內容。

Surface 的創建過程

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

Page 18: Surfaceflinger Overlay

                                                             

                                                              圖一 Surface 的創建過程

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

1. 建立 SurfaceSession

第一步通常只執行一次,目的是創建一個 SurfaceComposerClient 的實例,JAVA 層通過 JNI調用本地代

碼,本地代碼創建一個 SurfaceComposerClient 的實例,SurfaceComposerClient 通過

ISurfaceComposer 介面調用 SurfaceFlinger 的 createConnection,SurfaceFlinger返回一個

ISurfaceFlingerClient 介面給 SurfaceComposerClient,在 createConnection 的過程中,SurfaceFlinger

創建了用於管理緩衝區切換的 SharedClient,關於 SharedClient 我們下面再介紹,最後,本地層把

SurfaceComposerClient 的實例返回給 JAVA 層,完成 SurfaceSession 的建立。

Page 19: Surfaceflinger Overlay

 

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:

 

view plaincopy to clipboardprint?

1. void SurfaceComposerClient::_init(  

2.         const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn)  

3. {  

4.     ......  

5.     mClient = conn;  

6.     if (mClient == 0) {  

7.         mStatus = NO_INIT;  

8.         return;  

9.     }  

10.   

11.     mControlMemory = mClient->getControlBlock();  

12.     mSignalServer = sm;  

13.     mControl = static_cast<SharedClient *>(mControlMemory->getBase());  

14. }  

Page 20: Surfaceflinger Overlay

獲得 Surface 對應的顯示緩衝區

雖然在 SurfaceFlinger 在創建 Layer 時已經為每個 Layer 申請了兩個緩衝區,但是此時在 JAVA 層並看不

到這兩個緩衝區,JAVA 層要想在 Surface 上進行畫圖操作,必須要先把其中的一個緩衝區綁定到 Canvas

中,然後所有對該 Canvas 的畫圖操作最後都會畫到該緩衝區內。下圖展現了綁定緩衝區的過程:

Page 21: Surfaceflinger Overlay

                                                                           

                                                                       

     圖二 綁定緩衝區的過程

    開始在 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

o SharedBufferClient->dequeue()獲得當前空閒緩衝區的編號

o 通過緩衝區編號獲得真正的 GraphicBuffer:backBuffer

o 如果還沒有對 Layer中的 buffer進行映射(Mapper),getBufferLocked通過 ISurface

介面重新重新映射

獲取 frontBuffer

根據兩個 Buffer 的更新區域,把 frontBuffer 的內容拷貝到 backBuffer 中,這樣保證了兩個 Buffer

中顯示內容的同步

backBuffer->lock() 獲得 backBuffer緩衝區的首地址 vaddr

通過 info參數返回 vaddr

Note: Ray,如果沒有 Dirty,可以直接將 frontbuffer 直接 Copy 到 backBuffer 中,然後將 backBuffer adder傳

D:\repos\pydtd\pydtd_device\frameworks\base\libs\surfaceflinger_client\ Surface.cpp

Page 22: Surfaceflinger Overlay

釋放 Surface 對應的顯示緩衝區

畫圖完成後,要想把 Surface 的內容顯示到螢幕上,需要把 Canvas 中綁定的緩衝區釋放,並且把該緩衝

區從變成可投遞(因為默認只有兩個 buffer,所以實際上就是變成了 frontBuffer),SurfaceFlinger 的工

作執行緒會在適當的刷新時刻,把系統中所有的 frontBuffer混合在一起,然後通過 OpenGL刷新到螢幕

上。下圖展現了解除綁定緩衝區的過程:

                                                        

Page 23: Surfaceflinger Overlay

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

JAVA 層調用 unlockCanvasAndPost

進入本地代碼:Surface_unlockCanvasAndPost

本地代碼利用 JAVA 層傳入的 SurfaceControl物件,通過 getSurface()取得本地層的 Surface 對

綁定一個空的 bitmap 到 Canvas 中

調用 Surface 的 unlockAndPost 方法

o 調用 GraphicBuffer 的 unlock(),解鎖緩衝區

o 在 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 也會創建

Page 24: Surfaceflinger Overlay

SharedBufferServer 和 SharedBufferStack 關聯,實際上每對 SharedBufferClient/SharedBufferServer 是

控制著同一個 SharedBufferStack物件,通過 SharedBufferStack,保證了負責對 Surface 的畫圖操作的

應用端和負責刷新螢幕的服務端(SurfaceFlinger)可以使用不同的緩衝區,並且讓他們之間知道對方何

時鎖定/釋放緩衝區。

SharedClient 和 SharedBufferStack 的代碼和標頭檔分別位於:

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

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

 

  安装Chrome浏览器下载资源送 30个下载分! 2011 中国移动开发者大会门票超低价团购!2011移动开发者大会亮点之二:七大论坛神秘嘉宾闪亮登场! “IT适合你吗?”智力挑战

Android SurfaceFlinger 中的 SharedClient -- 客户端

Page 25: Surfaceflinger Overlay

(Surface)和服务端(Layer)之间的显示缓冲区管理

分类: 移动开发之 Android 2010-10-28 18:59 8691人阅读 评论(18) 收藏 举报

     SurfaceFlinger 在系统启动阶段作为系统服务被加载。应用程序中的每个窗口,对应本地代码中的

Surface,而 Surface 又对应于 SurfaceFlinger 中的各个 Layer,SurfaceFlinger 的主要作用是为这些

Layer 申请内存,根据应用程序的请求管理这些 Layer显示、隐藏、重画等操作,最终由 SurfaceFlinger

把所有的 Layer组合到一起,显示到显示器上。当一个应用程序需要在一个 Surface 上进行画图操作时,

首先要拿到这个 Surface 在内存中的起始地址,而这块内存是在 SurfaceFlinger 中分配的,因为

SurfaceFlinger 和应用程序并不是运行在同一个进程中,如何在应用客户端(Surface)和服务端

(SurfaceFlinger - Layer)之间传递和同步显示缓冲区?这正是本文要讨论的内容。

Surface 的创建过程

我们先看看 Android 如何创建一个 Surface,下面的序列图展示了整个创建过程。

Page 26: Surfaceflinger Overlay

                                                              图一 Surface 的创建过程

创建 Surface 的过程基本上分为两步:

1. 建立 SurfaceSession

第一步通常只执行一次,目的是创建一个 SurfaceComposerClient 的实例,JAVA层通过 JNI调用本地代

码,本地代码创建一个 SurfaceComposerClient 的实例,SurfaceComposerClient 通过

ISurfaceComposer 接口调用 SurfaceFlinger 的 createConnection,SurfaceFlinger返回一个

ISurfaceFlingerClient 接口给 SurfaceComposerClient,在 createConnection 的过程中,SurfaceFlinger

Page 27: Surfaceflinger Overlay

创建了用于管理缓冲区切换的 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:

 

view plaincopy to clipboardprint?

1. void SurfaceComposerClient::_init(  

2.         const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn)  

3. {  

4.     ......  

5.     mClient = conn;  

6.     if (mClient == 0) {  

7.         mStatus = NO_INIT;  

8.         return;  

9.     }  

10.   

11.     mControlMemory = mClient->getControlBlock();  

12.     mSignalServer = sm;  

Page 28: Surfaceflinger Overlay

13.     mControl = static_cast<SharedClient *>(mControlMemory->getBase());  

14. }  

获得 Surface 对应的显示缓冲区

虽然在 SurfaceFlinger 在创建 Layer时已经为每个 Layer 申请了两个缓冲区,但是此时在 JAVA层并看不

到这两个缓冲区,JAVA层要想在 Surface 上进行画图操作,必须要先把其中的一个缓冲区绑定到 Canvas

中,然后所有对该Canvas 的画图操作最后都会画到该缓冲区内。下图展现了绑定缓冲区的过程:

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());}

Page 29: Surfaceflinger Overlay

                                                                            图二 绑定缓冲区的过程

    开始在 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

o SharedBufferClient->dequeue()获得当前空闲缓冲区的编号

Page 30: Surfaceflinger Overlay

o 通过缓冲区编号获得真正的 GraphicBuffer:backBuffer

o 如果还没有对 Layer中的 buffer进行映射(Mapper),getBufferLocked通过 ISurface

接口重新重新映射

获取 frontBuffer

根据两个 Buffer 的更新区域,把 frontBuffer 的内容拷贝到 backBuffer 中,这样保证了两个 Buffer

中显示内容的同步

backBuffer->lock() 获得 backBuffer缓冲区的首地址 vaddr

通过 info参数返回 vaddr

释放 Surface 对应的显示缓冲区

画图完成后,要想把 Surface 的内容显示到屏幕上,需要把 Canvas 中绑定的缓冲区释放,并且把该缓冲

区从变成可投递(因为默认只有两个 buffer,所以实际上就是变成了 frontBuffer),SurfaceFlinger 的工

作线程会在适当的刷新时刻,把系统中所有的 frontBuffer混合在一起,然后通过OpenGL刷新到屏幕上。

下图展现了解除绑定缓冲区的过程:

                                                                 图三 解除绑定缓冲区的过程

Page 31: Surfaceflinger Overlay

JAVA层调用 unlockCanvasAndPost

进入本地代码:Surface_unlockCanvasAndPost

本地代码利用 JAVA层传入的 SurfaceControl对象,通过 getSurface()取得本地层的 Surface对

绑定一个空的 bitmap 到 Canvas 中

调用 Surface 的 unlockAndPost 方法

o 调用 GraphicBuffer 的 unlock(),解锁缓冲区

o 在 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

  安装Chrome浏览器下载资源送 30个下载分! 2011 中国移动开发者大会门票超低价团购!2011移动开发者大会亮点之二:七大论坛神秘嘉宾闪亮登场! “IT适合你吗?”智力挑战

Android SurfaceFlinger 中的 SharedClient -- 客户端

(Surface)和服务端(Layer)之间的显示缓冲区管理

Page 32: Surfaceflinger Overlay

分类: 移动开发之 Android 2010-10-28 18:59 8691人阅读 评论(18) 收藏 举报

     SurfaceFlinger 在系统启动阶段作为系统服务被加载。应用程序中的每个窗口,对应本地代码中的

Surface,而 Surface 又对应于 SurfaceFlinger 中的各个 Layer,SurfaceFlinger 的主要作用是为这些

Layer 申请内存,根据应用程序的请求管理这些 Layer显示、隐藏、重画等操作,最终由 SurfaceFlinger

把所有的 Layer组合到一起,显示到显示器上。当一个应用程序需要在一个 Surface 上进行画图操作时,

首先要拿到这个 Surface 在内存中的起始地址,而这块内存是在 SurfaceFlinger 中分配的,因为

SurfaceFlinger 和应用程序并不是运行在同一个进程中,如何在应用客户端(Surface)和服务端

(SurfaceFlinger - Layer)之间传递和同步显示缓冲区?这正是本文要讨论的内容。

Surface 的创建过程

我们先看看 Android 如何创建一个 Surface,下面的序列图展示了整个创建过程。

Page 33: Surfaceflinger Overlay

                                                              图一 Surface 的创建过程

创建 Surface 的过程基本上分为两步:

1. 建立 SurfaceSession

第一步通常只执行一次,目的是创建一个 SurfaceComposerClient 的实例,JAVA层通过 JNI调用本地代

码,本地代码创建一个 SurfaceComposerClient 的实例,SurfaceComposerClient 通过

ISurfaceComposer 接口调用 SurfaceFlinger 的 createConnection,SurfaceFlinger返回一个

ISurfaceFlingerClient 接口给 SurfaceComposerClient,在 createConnection 的过程中,SurfaceFlinger

Page 34: Surfaceflinger Overlay

创建了用于管理缓冲区切换的 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:

 

view plaincopy to clipboardprint?

1. void SurfaceComposerClient::_init(  

2.         const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn)  

3. {  

4.     ......  

5.     mClient = conn;  

6.     if (mClient == 0) {  

7.         mStatus = NO_INIT;  

8.         return;  

9.     }  

10.   

11.     mControlMemory = mClient->getControlBlock();  

12.     mSignalServer = sm;  

Page 35: Surfaceflinger Overlay

13.     mControl = static_cast<SharedClient *>(mControlMemory->getBase());  

14. }  

获得 Surface 对应的显示缓冲区

虽然在 SurfaceFlinger 在创建 Layer时已经为每个 Layer 申请了两个缓冲区,但是此时在 JAVA层并看不

到这两个缓冲区,JAVA层要想在 Surface 上进行画图操作,必须要先把其中的一个缓冲区绑定到 Canvas

中,然后所有对该Canvas 的画图操作最后都会画到该缓冲区内。下图展现了绑定缓冲区的过程:

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());}

Page 36: Surfaceflinger Overlay

                                                                            图二 绑定缓冲区的过程

    开始在 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

o SharedBufferClient->dequeue()获得当前空闲缓冲区的编号

Page 37: Surfaceflinger Overlay

o 通过缓冲区编号获得真正的 GraphicBuffer:backBuffer

o 如果还没有对 Layer中的 buffer进行映射(Mapper),getBufferLocked通过 ISurface

接口重新重新映射

获取 frontBuffer

根据两个 Buffer 的更新区域,把 frontBuffer 的内容拷贝到 backBuffer 中,这样保证了两个 Buffer

中显示内容的同步

backBuffer->lock() 获得 backBuffer缓冲区的首地址 vaddr

通过 info参数返回 vaddr

释放 Surface 对应的显示缓冲区

画图完成后,要想把 Surface 的内容显示到屏幕上,需要把 Canvas 中绑定的缓冲区释放,并且把该缓冲

区从变成可投递(因为默认只有两个 buffer,所以实际上就是变成了 frontBuffer),SurfaceFlinger 的工

作线程会在适当的刷新时刻,把系统中所有的 frontBuffer混合在一起,然后通过OpenGL刷新到屏幕上。

下图展现了解除绑定缓冲区的过程:

                                                                 图三 解除绑定缓冲区的过程

Page 38: Surfaceflinger Overlay

JAVA层调用 unlockCanvasAndPost

进入本地代码:Surface_unlockCanvasAndPost

本地代码利用 JAVA层传入的 SurfaceControl对象,通过 getSurface()取得本地层的 Surface对

绑定一个空的 bitmap 到 Canvas 中

调用 Surface 的 unlockAndPost 方法

o 调用 GraphicBuffer 的 unlock(),解锁缓冲区

o 在 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

 

Page 39: Surfaceflinger Overlay

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

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

    1. SharedClient

    在 createConnection階段,SurfaceFlinger創建Client物件:

view plaincopy to clipboardprint?

1. sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()  

2. {  

3.     Mutex::Autolock _l(mStateLock);  

4.     uint32_t token = mTokens.acquire();  

5.   

6.     sp<Client> client = new Client(token, this);  

7.     if (client->ctrlblk == 0) {  

8.         mTokens.release(token);  

9.         return 0;  

Page 40: Surfaceflinger Overlay

10.     }  

11.     status_t err = mClientsMap.add(token, client);  

12.     if (err < 0) {  

13.         mTokens.release(token);  

14.         return 0;  

15.     }  

16.     sp<BClient> bclient =  

17.         new BClient(this, token, client->getControlBlockMemory());  

18.     return bclient;  

19. }  

再進入 Client 的構造函數中,它分配了 4K 大小的共用記憶體,並在這塊記憶體上構建了

SharedClient 對象:

view plaincopy to clipboardprint?

1. Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)  

2.     : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)  

3. {  

4.     const int pgsize = getpagesize();  

5.     const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));  

6.   

7.     mCblkHeap = new MemoryHeapBase(cblksize, 0,  

8.             "SurfaceFlinger Client control-block");  

9.   

10.     ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());  

11.     if (ctrlblk) { // construct the shared structure in-place.   

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;

Page 41: Surfaceflinger Overlay

12.         new(ctrlblk) SharedClient;  

13.     }  

14. }  

回到 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陣列的索引:

view plaincopy to clipboardprint?

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; }}

Page 42: Surfaceflinger Overlay

1. int32_t id = client->generateId(pid);  

  

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

view plaincopy to clipboardprint?

1. layer = createNormalSurfaceLocked(client, d, id,  

2.                         w, h, flags, format);  

在 createNormalSurfaceLocked 中創建 Layer 對象:

view plaincopy to clipboardprint?

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

構造 Layer 時會先構造的父類 LayerBaseClient,LayerBaseClient 中創建了 SharedBufferServer物件,

SharedBufferStack 陣列的索引值和 SharedClient被傳入 SharedBufferServer 對象中。

view plaincopy to clipboardprint?

1. LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,  

2.         const sp<Client>& client, int32_t i)  

3.     : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i),  

4.       mIdentity(uint32_t(android_atomic_inc(&sIdentity)))  

5. {  

6.     lcblk = new SharedBufferServer(  

7.             client->ctrlblk, i, NUM_BUFFERS,  

8.             mIdentity);  

9. }  

int32_t id = client->generateId(pid);

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

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

Page 43: Surfaceflinger Overlay

 

    自此,Layer 通過 lcblk 成員變數(SharedBufferServer)和 SharedClient共用記憶體區建立了關聯,並且

每個 Layer 對應於 SharedBufferStack 陣列中的一項。

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

view plaincopy to clipboardprint?

1. Surface::Surface(const sp<SurfaceControl>& surface)  

2.     : mClient(surface->mClient), mSurface(surface->mSurface),  

3.       mToken(surface->mToken), mIdentity(surface->mIdentity),  

4.       mFormat(surface->mFormat), mFlags(surface->mFlags),  

5.       mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL),  

6.       mWidth(surface->mWidth), mHeight(surface->mHeight)  

7. {  

8.     mSharedBufferClient = new SharedBufferClient(  

9.             mClient->mControl, mToken, 2, mIdentity);  

10.   

11.     init();  

12. }  

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);}

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();}

Page 44: Surfaceflinger Overlay

SharedBufferClient 構造參數mClient->mControl 就是共用區塊中的 SharedClient物件,mToken 就是

SharedBufferStack 陣列索引值。

到這裡我們終於知道,Surface 中的 mSharedBufferClient 成員和 Layer 中的 lcblk 成員

(SharedBufferServer),通過 SharedClient 中的同一個 SharedBufferStack,共同管理著

Surface(Layer)中的兩個緩衝區。

http://blog.csdn.net/droidphone/article/details/5982893

Android SurfaceFlinger 中的工作執行緒:threadLoop()