Android中SurfaceFlinger工作原理

概念

SurfaceFlinger是一個系統服務,如:audioflinger,audiopolicyservice等等,系統的主要服務通過這個文章進行瞭解,Android的系統服務一覽。這個系統服務主要實現瞭Surface的建立、控制、管理等功能。換種說法就是,在Android 的實現中它是一個service,提供系統范圍內的surface composer功能,它能夠將各種應用程序的2D、3D surface進行組合。

原理分析

讓我們首先看一下下面的屏幕簡略圖:

每個應用程序可能對應著一個或者多個圖形界面,而每個界面我們就稱之為一個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 。

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。

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 。

 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 client = new SurfaceComposerClient();

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

sp surface = client->createSurface(getpid(), 0, 320, 240, PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers);

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

sp isurface = Test::getISurface(surface);

//overlay 方式下就創建 overlay ,然後就可以使用 overlay 的接口瞭

sp ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565);

sp 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 中畫圖瞭,這裡就面對著下面幾個問題瞭:

  • a、是怎麼知道在哪個 buffer 上來畫圖?
  • b、就是畫圖以後如何通知 SurfaceFlinger 來進行 flip ?
  • c、除瞭畫圖之外,如果我們移動窗口以及改變窗口大小的時候,如何告訴 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 內顯示瞭。

到此這篇關於Android中SurfaceFlinger工作原理的文章就介紹到這瞭。希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: