Android的surfaceflinger講解

來源:互聯網
上載者:User

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,如果瞭解硬體的話,可以在驅動中直接把這些資訊送到OverlayBuffer,而不需要走上層的Android。Fsl現在的Camerapreview就是採用的這種方式,而且我粗略看了r3補丁的內容,應該在opencore的視頻播放這塊也實現了Overlay。

 

3、SurfaceFlinger

   現在就來看看最複雜的SurfaceFlinger,首先要明確的是SurfaceFlinger只是負責mergeSurface的控制,比如說計算出兩個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內顯示了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

Tags Index: