1. 程式人生 > >Android Display System --- Surface Flinger

Android Display System --- Surface Flinger

轉自: http://www.cnblogs.com/Caiqinghua/archive/2010/08/03/1791388.html

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 來專門儲存需要顯示的內容。

    在這裡還存在一個問題,那就是當存在圖形重合的時候應該如何處理呢,而且可能有些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的過程:


   

    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 建立過程的類圖 :

 

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

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


   

     為應用程式建立一個 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 的統一識別符號以及 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 的資料來源自其他地方,只是 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 上來畫圖,還一個就是畫圖以後如何通知 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 。

一般來說畫圖的過程需要重繪 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 這個服務在建立的時候會啟動一個監聽的執行緒,這個執行緒負責每次視窗更新時候的處理,下面我們來仔細看看這個執行緒的事件的處理,大致就是下面的這個圖:

    
  
 

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

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

4.1 、 handleConsoleEvent

當接收到 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 內顯示了。