AndroidDisplay System --- SurfaceFlinger
SurfaceFlinger是Android multimedia的一個部分,在Android的實現(xiàn)中它是一個service,提供系統(tǒng)范圍內(nèi)的surface composer功能,它能夠?qū)⒏鞣N應用程序的2D、3D surface進行組合。在具體講SurfaceFlinger之前,我們先來看一下有關(guān)顯示方面的一些基礎(chǔ)知識。
1、原理分析
讓我們首先看一下下面的屏幕簡略圖:
每個應用程序可能對應著一個或者多個圖形界面,而每個界面我們就稱之為一個surface,或者說是window,在上面的圖中我們能看到4個surface,一個是home界面,還有就是紅、綠、藍分別代表的3個surface,而兩個button實際是home surface里面的內(nèi)容。在這里我們能看到我們進行圖形顯示所需要解決的問題:
a、首先每個surface在屏幕上有它的位置,以及大小,然后每個surface里面還有要顯示的內(nèi)容,內(nèi)容,大小,位置這些元素在我們改變應用程序的時候都可能會改變,改變時應該如何處理?
b、然后就各個surface之間可能有重疊,比如說在上面的簡略圖中,綠色覆蓋了藍色,而紅色又覆蓋了綠色和藍色以及下面的home,而且還具有一定透明度。這種層之間的關(guān)系應該如何描述?
我們首先來看第二個問題,我們可以想象在屏幕平面的垂直方向還有一個Z軸,所有的surface根據(jù)在Z軸上的坐標來確定前后,這樣就可以描述各個surface之間的上下覆蓋關(guān)系了,而這個在Z軸上的順序,圖形上有個專業(yè)術(shù)語叫Z-order。
對于第一個問題,我們需要一個結(jié)構(gòu)來記錄應用程序界面的位置,大小,以及一個buffer來記錄需要顯示的內(nèi)容,所以這就是我們surface的概念,surface實際我們可以把它理解成一個容器,這個容器記錄著應用程序界面的控制信息,比如說大小啊,位置啊,而它還有buffer來專門存儲需要顯示的內(nèi)容。
在這里還存在一個問題,那就是當存在圖形重合的時候應該如何處理呢,而且可能有些surface還帶有透明信息,這里就是我們SurfaceFlinger需要解決問題,它要把各個surface組合(compose/merge)成一個main Surface,最后將Main Surface的內(nèi)容發(fā)送給FB/V4l2 Output,這樣屏幕上就能看到我們想要的效果。
在實際中對這些Surface進行merge可以采用兩種方式,一種就是采用軟件的形式來merge,還一種就是采用硬件的方式,軟件的方式就是我們的SurfaceFlinger,而硬件的方式就是Overlay。
2、OverLay
因為硬件merge內(nèi)容相對簡單,我們首先來看overlay。Overlay實現(xiàn)的方式有很多,但都需要硬件的支持。以IMX51為例子,當IPU向內(nèi)核申請FB的時候它會申請3個FB,一個是主屏的,還一個是副屏的,還一個就是Overlay的。簡單地來說,Overlay就是我們將硬件所能接受的格式數(shù)據(jù)和控制信息送到這個Overlay FrameBuffer,由硬件驅(qū)動來負責merge Overlay buffer和主屏buffer中的內(nèi)容。
一般來說現(xiàn)在的硬件都只支持一個Overlay,主要用在視頻播放以及camera preview上,因為視頻內(nèi)容的不斷變化用硬件Merge比用軟件Merge要有效率得多,下面就是使用Overlay和不使用Overlay的過程:
SurfaceFlinger中加入了Overlay hal,只要實現(xiàn)這個Overlay hal可以使用overlay的功能,這個頭文件在:/hardware/libhardware/include/harware/Overlay.h,可以使用FB或者V4L2 output來實現(xiàn),這個可能是我們將來工作的內(nèi)容。實現(xiàn)Overlay hal以后,使用Overlay接口的sequence就在:/frameworks/base/libs/surfaceflinger/tests/overlays/Overlays.cpp,這個sequnce是很重要的,后面我們會講到。
不過在實際中我們不一定需要實現(xiàn)Overlay hal,如果了解硬件的話,可以在驅(qū)動中直接把這些信息送到OverlayBuffer,而不需要走上層的Android。Fsl現(xiàn)在的Camerapreview就是采用的這種方式,而且我粗略看了r3補丁的內(nèi)容,應該在opencore的視頻播放這塊也實現(xiàn)了Overlay。
3、SurfaceFlinger
現(xiàn)在就來看看最復雜的SurfaceFlinger,首先要明確的是SurfaceFlinger只是負責mergeSurface的控制,比如說計算出兩個Surface重疊的區(qū)域,至于Surface需要顯示的內(nèi)容,則通過skia,opengl和pixflinger來計算。所以我們在介紹SurfaceFlinger之前先忽略里面存儲的內(nèi)容究竟是什么,先弄清楚它對merge的一系列控制的過程,然后再結(jié)合2D,3D引擎來看它的處理過程。
3.1、Surface的創(chuàng)建過程
前面提到了每個應用程序可能有一個或者多個Surface,我們需要一些數(shù)據(jù)結(jié)構(gòu)來存儲我們的窗口信息,我們還需要buffer來存儲我們的窗口內(nèi)容, 而且最主要的是我們應該確定一個方案來和SurfaceFlinger來交互這些信息,讓我們首先看看下面的Surface創(chuàng)建過程的類圖:
在IBinder左邊的就是客戶端部分,也就是需要窗口顯示的應用程序,而右邊就是我們的Surface Flinger service。創(chuàng)建一個surface分為兩個過程,一個是在SurfaceFlinger這邊為每個應用程序(Client)創(chuàng)建一個管理結(jié)構(gòu),另一個就是創(chuàng)建存儲內(nèi)容的buffer,以及在這個buffer上的一系列畫圖之類的操作。
因為SurfaceFlinger要管理多個應用程序的多個窗口界面,為了進行管理它提供了一個Client類,每個來請求服務的應用程序就對應了一個Client。因為surface是在SurfaceFlinger創(chuàng)建的,必須返回一個結(jié)構(gòu)讓應用程序知道自己申請的surface信息,因此SurfaceFlinger將Client創(chuàng)建的控制結(jié)構(gòu)per_client_cblk_t經(jīng)過BClient的封裝以后返回給SurfaceComposerClient,并向應用程序提供了一組創(chuàng)建和銷毀surface的操作:
為應用程序創(chuàng)建一個Client以后,下面需要做的就是為這個Client分配Surface,Flinger為每個Client提供了8M的空間,包括控制信息和存儲內(nèi)容的buffer。在說創(chuàng)建surface之前首先要理解layer這個概念,回到我們前面看的屏幕簡略圖,實際上每個窗口就是z軸上的一個layer,layer提供了對窗口控制信息的操作,以及內(nèi)容的處理(調(diào)用opengl或者skia),也就是說SurfaceFlinger只是控制什么時候應該進行這些信息的處理以及處理的過程,所有實際的處理都是在layer中進行的,可以理解為創(chuàng)建一個Surface就是創(chuàng)建一個Layer。不得不說Android這些亂七八糟的名字,讓我繞了很久……
創(chuàng)建Layer的過程,首先是由這個應用程序的Client根據(jù)應用程序的pid生成一個唯一的layer ID,然后根據(jù)大小,位置,格式啊之類的信息創(chuàng)建出Layer。在Layer里面有一個嵌套的Surface類,它主要包含一個ISurfaceFlingerClient::Surface_data_t,包含了這個Surace的統(tǒng)一標識符以及buffer信息等,提供給應用程序使用。最后應用程序會根據(jù)返回來的ISurface信息等創(chuàng)建自己的一個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,一般的應用程序在創(chuàng)建surface的時候都是采用的這樣的layer,了解Normal Layer可以讓我們知道Android進行display過程中的一些基礎(chǔ)原理。Normal Layer為每個Surface分配兩個buffer:front buffer和back buffer,這個前后是相對的概念,他們是可以進行Flip的。Front buffer用于SurfaceFlinger進行顯示,而Back buffer用于應用程序進行畫圖,當Back buffer填滿數(shù)據(jù)(dirty)以后,就會flip,back buffer就變成了front buffer用于顯示,而front buffer就變成了back buffer用來畫圖,這兩個buffer的大小是根據(jù)surface的大小格式動態(tài)變化的。這個動態(tài)變化的實現(xiàn)我沒仔細看,可以參照:/frameworks/base/lib/surfaceflinger/layer.cpp中的setbuffers()。
兩個buffer flip的方式是Android display中的一個重要實現(xiàn)方式,不只是每個Surface這么實現(xiàn),最后寫入FB的main surface也是采用的這種方式。
LayerBuffer也是將來必定會用到的一個Layer,個人覺得也是最復雜的一個layer,它不具備render buffer,主要用在camera preview / video playback上。它提供了兩種實現(xiàn)方式,一種就是post buffer,另外一種就是我們前面提到的overlay,Overlay的接口實際上就是在這個layer上實現(xiàn)的。不管是overlay還是post buffer都是指這個layer的數(shù)據(jù)來源自其他地方,只是post buffer是通過軟件的方式最后還是將這個layer merge主的FB,而overlay則是通過硬件merge的方式來實現(xiàn)。與這個layer緊密聯(lián)系在一起的是ISurface這個接口,通過它來注冊數(shù)據(jù)來源,下面我舉個例子來說明這兩種方式的使用方法:
前面幾個步驟是通用的:
//要使用Surfaceflinger的服務必須先創(chuàng)建一個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()這個函數(shù)的調(diào)用時具有權(quán)限限制的,必須在Surface.h中打開:/framewoks/base/include/ui/Surface.h
sp<ISurface> isurface = Test::getISurface(surface);
//overlay方式下就創(chuàng)建overlay,然后就可以使用overlay的接口了
sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565);
sp<Overlay> verlay = new Overlay(ref);
//post buffer方式下,首先要創(chuàng)建一個buffer,然后將buffer注冊到ISurface上
ISurface::BufferHeap buffers(w, h, w, h,
PIXEL_FORMAT_YCbCr_420_SP,
transform,
0,
mHardware->getPreviewHeap());
mSurface->registerBuffers(buffers);
3.2、應用程序?qū)Υ翱诘目刂坪彤媹D
Surface創(chuàng)建以后,應用程序就可以在buffer中畫圖了,這里就面對著兩個問題了,一個是怎么知道在哪個buffer上來畫圖,還一個就是畫圖以后如何通知SurfaceFlinger來進行flip。除了畫圖之外,如果我們移動窗口以及改變窗口大小的時候,如何告訴SurfaceFlinger來進行處理呢?在明白這些問題之前,首先我們要了解SurfaceFlinger這個服務是如何運作的:
從類圖中可以看到SurfaceFlinger是一個線程類,它繼承了Thread類。當創(chuàng)建SurfaceFlinger這個服務的時候會啟動一個SurfaceFlinger監(jiān)聽線程,這個線程會一直等待事件的發(fā)生,比如說需要進行sruface flip,或者說窗口位置大小發(fā)生了變化等等,一旦產(chǎn)生這些事件,SurfaceComposerClient就會通過IBinder發(fā)出信號,這個線程就會結(jié)束等待處理這些事件,處理完成以后會繼續(xù)等待,如此循環(huán)。
SurfaceComposerClient和SurfaceFlinger是通過SurfaceFlingerSynchro這個類來同步信號的,其實說穿了就是一個條件變量。監(jiān)聽線程等待條件的值變成OPEN,一旦變成OPEN就結(jié)束等待并將條件置成CLOSE然后進行事件處理,處理完成以后再繼續(xù)等待條件的值變成OPEN,而Client的Surface一旦改變就通過IBinder通知SurfaceFlinger將條件變量的值變成OPEN,并喚醒等待的線程,這樣就通過線程類和條件變量實現(xiàn)了一個動態(tài)處理機制。
了解了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.
所以應用程序先調(diào)用lockSurface()鎖定layer的swapstate,并獲得畫圖的buffer然后就可以在上面進行畫圖了,完成以后就會調(diào)用unlockSurfaceAndPost()來通知SurfaceFlinger進行Flip。或者僅僅調(diào)用unlockSurface()而不通知SurfaceFlinger。
一般來說畫圖的過程需要重繪Surface上的所有像素,因為一般情況下顯示過后的像素是不做保存的,不過也可以通過設定來保存一些像素,而只繪制部分像素,這里就涉及到像素的拷貝了,需要將Front buffer的內(nèi)容拷貝到Back buffer。在SurfaceFlinger服務實現(xiàn)中像素的拷貝是經(jīng)常需要進行的操作,而且還可能涉及拷貝過程的轉(zhuǎn)換,比如說屏幕的旋轉(zhuǎn),翻轉(zhuǎn)等一系列操作。因此Android提供了拷貝像素的hal,這個也可能是我們將來需要實現(xiàn)的,因為用硬件完成像素的拷貝,以及拷貝過程中可能的矩陣變換等操作,比用memcpy要有效率而且節(jié)省資源。這個HAL頭文件在:/hardware/libhardware/hardware/include/copybit.h
窗口狀態(tài)變化的處理是一個很復雜的過程,首先要說明一下,SurfaceFlinger只是執(zhí)行Windowsmanager的指令,由Windows manager來決定什么是偶改變大小,位置,設置透明度,以及如何調(diào)整layer之間的順序,SurfaceFlinger僅僅只是執(zhí)行它的指令。PS:Windows Manager是java層的一個服務,提供對所有窗口的管理功能,這部分的內(nèi)容我沒細看過,覺得是將來需要了解的內(nèi)容。
窗口狀態(tài)的變化包括位置的移動,窗口大小,透明度,z-order等等,首先我們來了解一下SurfaceComposerClient是如何和SurfaceFlinger來交互這些信息的。當應用程序需要改變窗口狀態(tài)的時候它將所有的狀態(tài)改變信息打包,然后一起發(fā)送給SurfaceFlinger,SurfaceFlinger改變這些狀態(tài)信息以后,就會喚醒等待的監(jiān)聽線程,并設置一個標志位告訴監(jiān)聽線程窗口的狀態(tài)已經(jīng)改變了,必須要進行處理,在Android的實現(xiàn)中,這個打包的過程就是一個Transaction,所有對窗口狀態(tài)(layer_state_t)的改變都必須在一個Transaction中。
到這里應用程序客戶端的處理過程已經(jīng)說完了,基本分為兩個部分,一個就是在窗口畫圖,還一個就是窗口狀態(tài)改變的處理。
4、SurfaceFlinger的處理過程
了解了Flinger和客戶端的交互,我們再來仔細看看SurfaceFlinger的處理過程,前面已經(jīng)說過了SurfaceFlinger這個服務在創(chuàng)建的時候會啟動一個監(jiān)聽的線程,這個線程負責每次窗口更新時候的處理,下面我們來仔細看看這個線程的事件的處理,大致就是下面的這個圖:
先大致講一下Android組合各個窗口的原理:Android實際上是通過計算每一個窗口的可見區(qū)域,就是我們在屏幕上可見的窗口區(qū)域(用Android的詞匯來說就是visibleRegionScreen ),然后將各個窗口的可見區(qū)域畫到一個主layer的相應部分,最后就拼接成了一個完整的屏幕,然后將主layer輸送到FB顯示。在將各個窗口可見區(qū)域畫到主layer過程中涉及到一個硬件實現(xiàn)和一個軟件實現(xiàn)的問題,如果是軟件實現(xiàn)則通過Opengl重新畫圖,其中還包括存在透明度的alpha計算;如果實現(xiàn)了copybit hal的話,可以直接將窗口的這部分數(shù)據(jù)直接拷貝過來,并完成可能的旋轉(zhuǎn),翻轉(zhuǎn),以及alhpa計算等。
下面來看看Android組合各個layer并送到FB顯示的具體過程:
4.1、handleConsoleEvent
當接收到signal或者singalEvent事件以后,線程就停止等待開始對Client的請求進行處理,第一個步驟是handleConsoleEvent,這個步驟我看了下和/dev/console這個設備有關(guān),它會取得屏幕或者釋放屏幕,只有取得屏幕的時候才能夠在屏幕上畫圖。
4.2、handleTransaction
前面提到過,窗口狀態(tài)的改變只能在一個Transaction中進行。因為窗口狀態(tài)的改變可能造成本窗口和其他窗口的可見區(qū)域變化,所以就必須重新來計算窗口的可見區(qū)域。在這個處理子過程中Android會根據(jù)標志位來對所有layer進行遍歷,一旦發(fā)現(xiàn)哪個窗口的狀態(tài)發(fā)生了變化就設置標志位以在將來重新計算這個窗口的可見區(qū)域。在完成所有子layer的遍歷以后,Android還會根據(jù)標志位來處理主layer,舉個例子,比如說傳感器感應到手機橫過來了,會將窗口橫向顯示,此時就要重新設置主layer的方向。
4.3、handlePageFlip
這里會處理每個窗口surface buffer之間的翻轉(zhuǎn),根據(jù)layer_state_t的swapsate來決定是否要翻轉(zhuǎn),當swapsate的值是eNextFlipPending是就會翻轉(zhuǎn)。處理完翻轉(zhuǎn)以后它會重新計算每個layer的可見區(qū)域,這個重新計算的過程我還沒看太明白,但大致是一個這么的過程:
從Z值最大的layer開始計算,也就是說從最上層的layer計算,去掉本身的透明區(qū)域和覆蓋在它上面的不透明區(qū)域,得到的就是這個layer的可見區(qū)域。然后這個layer的不透明區(qū)域就會累加到不透明覆蓋區(qū)域,這個layer的可見區(qū)域會放入到主layer的可見區(qū)域,然后計算下一個layer,直到計算完所有的layer的可見區(qū)域。這中間的計算是通過定義在skia中的一種與或非的圖形邏輯運算實現(xiàn)的,類似我們數(shù)學中的與或非邏輯圖。
4.4、handleRepaint
計算出每個layer的可見區(qū)域以后,這一步就是將所有可見區(qū)域的內(nèi)容畫到主layer的相應部分了,也就是說將各個surface buffer里面相應的內(nèi)容拷貝到主layer相應的buffer,其中可能還涉及到alpha運算,像素的翻轉(zhuǎn),旋轉(zhuǎn)等等操作,這里就像我前面說的可以用硬件來實現(xiàn)也可以用軟件來實現(xiàn)。在使用軟件的opengl做計算的過程中還會用到PixFlinger來做像素的合成,這部分內(nèi)容我還沒時間來細看。
4.5、postFrameBuffer
最后的任務就是翻轉(zhuǎn)主layer的兩個buffer,將剛剛寫入的內(nèi)容放入FB內(nèi)顯示了。