在前面的一系列文章中,我們學習了Android應用程式與SurfaceFlinger服務的關系,以及SurfaceFlinger服務的啟動過程、初始化硬體幀緩沖區的過程、線程模型。SurfaceFlinger服務所做的一切都是為了給Android應用程式提服務的,即為Android應用程式渲染它們的UI。在本文中,我們就詳細分析SurfaceFlinger服務渲染Android應用程式UI的過程。
《Android系統源代碼情景分析》一書正在進擊的程式員網(http://0xcc0xcd.com)中連載,點選進入!
從前面Android系統Surface制的SurfaceFlinger服務的線程模型分析一文可以知道,SurfaceFlinger服務是通過它的UI渲染線程來将應用程式的UI渲染到硬體幀緩沖區中去的,是以,接下來我們就通過分析SurfaceFlinger服務的UI渲染線程的執行過程來分應用程式UI的渲染過程,這個過程如圖1所示。

圖1 SurfaceFlinger服務渲染應用程式UI的示意圖
從圖1就可以看出,SurfaceFlinger服務的UI渲染線程的執行過程如下所示:
1. 調用SurfaceFlinger類的成員函數handleConsoleEvents來處理控制台事件。
2. 調用SurfaceFlinger類的成員函數handleTransaction來處理系統顯示屏以及應用程式視窗的屬性變化,例如大小、旋轉方向變化等。
3. 調用SurfaceFlinger類的成員函數handlePageFlip來讓各個應用程式視窗設定它們目前所要渲染的圖形緩沖區。
4. 如果SurfaceFlinger服務在編譯的時候指定了USE_COMPOSITION_BYPASS宏,并且目前需要渲染的應用程式視窗隻有一個,那麼就會調用SurfaceFlinger類的成員函數handleBypassLayer來直接将這個應用程式視窗的圖形緩沖區渲染到硬體幀緩沖區中去,否則的話,就要調用SurfaceFlinger類的成員函數handleRepaint來合成所有的應用程式視窗的圖形緩沖區到一個主圖形緩沖區中去。
5. 調用SurfaceFlinger類的成員函數postFramebuffer将前面得到的主圖形緩沖區渲染到硬體幀緩沖區中去。
前面Android系統Surface制的SurfaceFlinger服務的線程模型分析一文中,我們已經分析過第1步的實作了,而通過前面Android應用程式與SurfaceFlinger服務的關系概述和學習計劃這一系列文章的學習,我們也已經了解了應用程式視窗的圖形緩沖區的建立過程,是以,接下來我們就在這些知識的基礎上來詳細分析第2步到第5的實作,即分别分析SurfaceFlinger類的成員函數handleTransaction、handlePageFlip、handleBypassLayer和postFramebuffer的實作。
1. handleTransaction
SurfaceFlinger類的成員函數handleTransaction是用來處理系統顯示屏以及應用程式視窗的屬性變化的,這個過程如圖2所示。
圖2 系統顯示屏以及應用程式視窗的屬性變化處理過程
這個過程可以分為6個步驟,接下來我們就詳細分析每一個步驟。
Step 1. SurfaceFlinger.handleTransaction
void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
Vector< sp<LayerBase> > ditchedLayers;
/*
* Perform and commit the transaction
*/
{ // scope for the lock
Mutex::Autolock _l(mStateLock);
const nsecs_t now = systemTime();
mDebugInTransaction = now;
handleTransactionLocked(transactionFlags, ditchedLayers);
mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
// here the transaction has been committed
}
/*
* Clean-up all layers that went away
* (do this without the lock held)
*/
const size_t count = ditchedLayers.size();
for (size_t i=0 ; i<count ; i++) {
if (ditchedLayers[i] != 0) {
//LOGD("ditching layer %p", ditchedLayers[i].get());
ditchedLayers[i]->ditch();
}
}
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
SurfaceFlinger類的成員函數handleTransaction是通過調用另外一個成員函數handleTransactionLocked來處理系統顯示屏以及應用程式視窗的屬性變化的,而SurfaceFlinger類的成員函數handleTransactionLocked在處理完成系統顯示屏以及應用程式視窗的屬性變化之後,會傳回系統中那些已經銷毀了的應用程式視窗。
從Android應用程式與SurfaceFlinger服務的關系概述和學習計劃這一系列文章可以知道,在SurfaceFlinger服務這一側,應用程式視窗一般是使用一個Layer對象來描述的,又由于Layer類是從LayerBase類繼承下來的,是以,我們可以那些已經銷毀了的應用程式視窗儲存在一個類型為sp<LayerBase>的向量ditchedLayers中。
SurfaceFlinger類的成員函數handleTransaction最後就調用儲存在向量ditchedLayers中的每一個LayerBase對象的成員函數dtich來執行被銷毀的應用程式視窗的清理操作,接下來我們就繼續分析SurfaceFlinger類的成員函數handleTransactionLocked,看看它是如何處理系統顯示屏以及應用程式視窗的屬性變化的。
Step 2. SurfaceFlinger.handleTransactionLocked
SurfaceFlinger類的成員函數handleTransactionLocked定義在檔案rameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,我們分三段來閱讀:
void SurfaceFlinger::handleTransactionLocked(
uint32_t transactionFlags, Vector< sp<LayerBase> >& ditchedLayers)
{
const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
const size_t count = currentLayers.size();
/*
* Traversal of the children
* (perform the transaction for each of them if needed)
*/
const bool layersNeedTransaction = transactionFlags & eTraversalNeeded;
if (layersNeedTransaction) {
for (size_t i=0 ; i<count ; i++) {
const sp<LayerBase>& layer = currentLayers[i];
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) continue;
const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion)
mVisibleRegionsDirty = true;
}
}
這段代碼用來處理應用程式視窗的屬性變化。
參數transactionFlags最開始是從SurfaceFlinger類的成員函數threadLoop傳進來的。從前面Android系統Surface制的SurfaceFlinger服務的線程模型分析一文可以知道,SurfaceFlinger類的成員函數threadLoop在調用另外一個成員函數handleTransaction來處理系統顯示屏以及應用程式視窗的屬性變化之前,首先會調用成員函數getTransactionFlags來檢查系統顯示屏或者應用程式視窗的屬性是否發生了變化。如果系統顯示屏的屬性發生了變化,那麼傳到這裡的參數transactionFlags的eTransactionNeeded位就會等于1,而如果有應用程式視窗的屬性發生了變化,那麼傳到這裡的參數transactionFlags的eTraversalNeeded位就會等于1。為了友善描述,我們假設系統顯示屏以及應用程式視窗的屬性都發生了變化。
SurfaceFlinger類的成員變量mCurrentState指向了一個State對象,用來描述SufaceFlinger服務的目前狀态,其中,這個State對象的成員變量layersSortedByZ是一個類型為LayerVector的向量,它裡面儲存了SufaceFlinger服務目前所需要渲染的應用程式視窗,而這些應用程式視窗都是使用一個LayerBase對象來描述的。
這段代碼首先獲得SufaceFlinger服務目前所需要渲染的應用程式視窗,接着再通過一個for循環來依次檢查每一個應用程式視窗的屬性是否發生了變化。如果某一個應用程式視窗的屬性被修改過,那麼調用用來描述這個應用程式視窗的一個LayerBase對象的成員函數getTransactionFlags得到的傳回值trFlags就不會等于0,在這種情況下,這段代碼就會調用這個LayerBase對象的成員函數doTransaction來處理對應的應用程式視窗的屬性變化。
在LayerBase類中,有一個類型為int32_t的成員變量mTransactionFlags,每當SurfaceFlinger服務修改某一個應用程式視窗的屬性時,都會将與其對應的LayerBase的成員變量mTransactionFlags的相應的位設定為1,這樣LayerBase類的成員函數getTransactionFlags就可以通過這個成員變量來判斷一個應用程式視窗的屬性是否發生變化了。
如果一個應用程式視窗發生的屬性變化是可見區域發生了改變,那麼對應的LayerBase對象的成員函數doTransaction的傳回值flags的Layer::eVisibleRegion位就會等于1。在這種情況下,這段代碼就會将 SurfaceFlinger類的成員變量mVisibleRegionsDirty的值設定為true,表示後面要重新計算各個應用程式視窗的可見區域。
為了友善描述,我們假設發生了屬性變化的應用程式視窗是一個普通類型的Surface,即用來描述它的實際是一個從LayerBase類繼承下來的Layer對象。在這種情況下,前面實際上調用了Layer類的成員函數doTransaction來處理一個應用程式視窗的屬性變化。在接下來的Step 3中,我們再詳細分析Layer類的成員函數doTransaction的實作,現在我們接着往下閱讀SurfaceFlinger類的成員函數handleTransactionLocked的代碼:
/*
* Perform our own transaction if needed
*/
if (transactionFlags & eTransactionNeeded) {
if (mCurrentState.orientation != mDrawingState.orientation) {
// the orientation has changed, recompute all visible regions
// and invalidate everything.
const int dpy = 0;
const int orientation = mCurrentState.orientation;
const uint32_t type = mCurrentState.orientationType;
GraphicPlane& plane(graphicPlane(dpy));
plane.setOrientation(orientation);
// update the shared control block
const DisplayHardware& hw(plane.displayHardware());
volatile display_cblk_t* dcblk = mServerCblk->displays + dpy;
dcblk->orientation = orientation;
dcblk->w = plane.getWidth();
dcblk->h = plane.getHeight();
mVisibleRegionsDirty = true;
mDirtyRegion.set(hw.bounds());
}
if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) {
// freezing or unfreezing the display -> trigger animation if needed
mFreezeDisplay = mCurrentState.freezeDisplay;
if (mFreezeDisplay)
mFreezeDisplayTime = 0;
}
if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
// layers have been added
mVisibleRegionsDirty = true;
}
// some layers might have been removed, so
// we need to update the regions they're exposing.
if (mLayersRemoved) {
mLayersRemoved = false;
mVisibleRegionsDirty = true;
const LayerVector& previousLayers(mDrawingState.layersSortedByZ);
const size_t count = previousLayers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<LayerBase>& layer(previousLayers[i]);
if (currentLayers.indexOf( layer ) < 0) {
// this layer is not visible anymore
ditchedLayers.add(layer);
mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);
}
}
}
}
這段代碼用來處理系統顯示屏的屬性變化。
在分析這段代碼之前,我們首先了解SurfaceFlinger類的另外一個成員變量mDrawingState的含義。SurfaceFlinger類的成員變量mDrawingState與前面所介紹的成員變量mCurrentState類似,它的類型也為State,不過它是用來描述SufaceFlinger服務的上一次渲染狀态的。通過這兩個成員變量的比較,我們就可以知道系統顯示屏的哪一個屬性發生了變化。
前面提到,當系統顯示屏的屬性發生了變化,那麼參數transactionFlags的eTransactionNeeded位就會等于1,在這種情況,這段代碼就需要完成四件事情。
第一件事情是判斷系統顯示屏的旋轉方向是否發生變化。State類的成員變量orientation用來描述顯示屏的方向,是以,當SurfaceFlinger類的成員變量mCurrentState所描述的一個State對象的成員變量orientation的值不等于SurfaceFlinger類的成員變量mDrawingState所描述的一個State對象的成員變量orientation的值時,就說明系統顯示屏的旋轉方向發生了變化。在這種情況下,我們就需要将系統顯示屏的旋轉方向設定為SurfaceFlinger類的成員變量mCurrentState所描述的一個State對象的成員變量orientation的值,這是通過調用編号為0的一個GraphicPlane對象的成員函數setOrientation來實作的。
SurfaceFlinger服務的UI渲染線程在初始化的過程中,除了會初始化硬體幀緩沖區之外,還會建立一個類型為surface_flinger_cblk_t的對象,用來描述系統顯示屏的資訊,例如大小和旋轉方向等,以便其它程序可以通過這個surface_flinger_cblk_t對象來獲得系統顯示屏的資訊。這個surface_flinger_cblk_t對象就儲存在SurfaceFlinger類的成員變量mServerCblk中。是以,當系統顯示屏的旋轉方向發生了變化時,我們還需要将變化後的旋轉方向儲存在SurfaceFlinger類的成員變量mServerCblk所描述的一個surface_flinger_cblk_t對象中。由于系統顯示屏的旋轉方向變化一般意味着寬度和高度也會發生變化,是以,我們還需要将旋轉發生變化後得到的系統顯示屏的寬度和高度值儲存在SurfaceFlinger類的成員變量mServerCblk所描述的一個surface_flinger_cblk_t對象中。
系統顯示屏的旋轉方向同時也意味着我們需要重新計算各個應用程式視窗的可見區域以及重新繪制整個顯示屏,是以,在這種情況下,我們還需要将SurfaceFlinger類的成員變量mVisibleRegionsDirty的值設定為true,以及将SurfaceFlinger類的成員變量mDirtyRegion的大小設定為整個顯示屏的大小,即将系統UI的髒區域設定為整個顯示屏的大小。
第二件事情是判斷系統顯示屏的當機狀态是否發生變化。State類的成員變量freezeDisplay用來描述顯示屏的當機狀态,是以,當SurfaceFlinger類的成員變量mCurrentState所描述的一個State對象的成員變量freezeDisplay的值不等于SurfaceFlinger類的成員變量mDrawingState所描述的一個State對象的成員變量freezeDisplay的值時,就說明系統顯示屏的當機狀态發生了變化。在這種情況下,我們就需要将SurfaceFlinger類的成員變量mFreezeDisplay的值設定為SurfaceFlinger類的成員變量mCurrentState所描述的一個State對象的成員變量freezeDisplay的值。如果顯示屏的是由解凍狀态變化為當機狀态的,那麼還需要将顯示屏的當機時間設定為0,即将SurfaceFlinger類的成員變量mFreezeDisplayTime的值設定為0,以便可以将顯示屏進入到當機狀态的最長時間設定為一個預設值,這一點可以參考前面Android系統Surface制的SurfaceFlinger服務的線程模型分析一文。
第三件事情是判斷是否新增了應用程式視窗。State類的成員變量layersSortedByZ是一個類型LayerVector的向量,裡面儲存的是SurfaceFlinger服務在某一個狀态下所擁有的應用程式視窗,是以,當SurfaceFlinger類的成員變量mCurrentState所描述的一個State對象的成員變量layersSortedByZ所指向的一個向量的大小值大于SurfaceFlinger類的成員變量mDrawingState所描述的一個State對象的成員變量layersSortedByZ所指向的一個向量的大小值時,就說明系統新增了應用程式視窗。在這種情況下,我們就需要将SurfaceFlinger類的成員變量mVisibleRegionsDirty的值設定為true,以表示我們需要重新計算各個應用程式視窗的可見區域。
第四件事情是判斷是否移除了某些應用程式視窗。SurfaceFlinger類的成員變量mLayersRemoved用來描述是否有應用程式視窗被移除了。如果有有應用程式視窗被移除的話,那麼這個成員變量的值就會等于true。在這種情況下,我們就需要是哪些應用程式視窗被移除了。計算的方法很簡單,如果一個應用程式視窗存在于SurfaceFlinger類的成員變量mDrawingState所描述的一個State對象的成員變量layersSortedByZ所指向的一個向量中,但是不存在于SurfaceFlinger類的成員變量mCurrentState所描述的一個State對象的成員變量layersSortedByZ所指向的一個向量中,那麼就說明這個應用程式視窗被移除了,是以,就需要将它儲存輸出參數ditchedLayers所描述的一個向量中,以便可以傳回給上一步來處理。SurfaceFlinger類的成員變量mDirtyRegionRemovedLayer用來描述那些被移除了的應用程式視窗所占用的區域,是以,每當我們移除一個應用程式視窗的時候,都需要将它所占用的區域增加到SurfaceFlinger類的成員變量mDirtyRegionRemovedLayer所描述的一個區域去。
當處理完成那些被移除的應用程式視窗之後,我們就需要将SurfaceFlinger類的成員變量mLayersRemoved的值設定為false,并且将SurfaceFlinger類的成員變量mVisibleRegionsDirty的值設定為true,以表示我們需要重新計算現存的各個應用程式視窗的可見區域。
處理完成系統顯示屏的屬性變化之後,我們接着向下閱讀SurfaceFlinger類的成員函數handleTransactionLocked的最後一行代碼:
commitTransaction();
}
這段代碼隻有一行,即調用SurfaceFlinger類的成員函數commitTransaction來告訴SurfaceFlinger服務,系統顯示屏以及各個應用程式視窗的屬性變化已經處理完畢,這時候SurfaceFlinger服務就可以切換狀态了。在後面的Step 6中,我們再詳細分析SurfaceFlinger類的成員函數commitTransaction的實作。
接下來,我們就繼續分析Layer類的成員函數doTransaction的實作,以便可以了解應用程式視窗的屬性變化的處理過程。
Step 3. Layer.doTransaction
uint32_t Layer::doTransaction(uint32_t flags)
{
const Layer::State& front(drawingState());
const Layer::State& temp(currentState());
const bool sizeChanged = (front.requested_w != temp.requested_w) ||
(front.requested_h != temp.requested_h);
if (sizeChanged) {
......
if (!isFixedSize()) {
// we're being resized and there is a freeze display request,
// acquire a freeze lock, so that the screen stays put
// until we've redrawn at the new size; this is to avoid
// glitches upon orientation changes.
if (mFlinger->hasFreezeRequest()) {
// if the surface is hidden, don't try to acquire the
// freeze lock, since hidden surfaces may never redraw
if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
mFreezeLock = mFlinger->getFreezeLock();
}
}
// this will make sure LayerBase::doTransaction doesn't update
// the drawing state's size
Layer::State& editDraw(mDrawingState);
editDraw.requested_w = temp.requested_w;
editDraw.requested_h = temp.requested_h;
// record the new size, form this point on, when the client request
// a buffer, it'll get the new size.
setBufferSize(temp.requested_w, temp.requested_h);
ClientRef::Access sharedClient(mUserClientRef);
SharedBufferServer* lcblk(sharedClient.get());
if (lcblk) {
// all buffers need reallocation
lcblk->reallocateAll();
}
} else {
// record the new size
setBufferSize(temp.requested_w, temp.requested_h);
}
}
if (temp.sequence != front.sequence) {
if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) {
// this surface is now hidden, so it shouldn't hold a freeze lock
// (it may never redraw, which is fine if it is hidden)
mFreezeLock.clear();
}
}
return LayerBase::doTransaction(flags);
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/Layer.cpp中。
和SurfaceFlinger服務類似,每一個應用程式視窗在内部也分别使用兩個類型為State的成員變量mDrawingState和mCurrentState來描述上一次的渲染狀态和下一次的渲染狀态。這兩個成員變量是從LayerBase類繼承下來的,用來描述應用程式視窗的Layer類可以分别通過從父類LayerBase繼承下來的成員函數drawingState和currentState來通路它們。注意,這裡所說的State類是定義在LayerBase類内部的,而SurfaceFlinger服務使用的State類是定義在SurfaceFlinger類内部的,它們是不一樣的。
Layer類的成員函數doTransaction首先調用從父類LayerBase繼承下來的成員函數drawingState和currentState來獲得目前正在處理的應用程式視窗的上一次的渲染狀态和下一次的渲染狀态,并且分别儲存在兩個類型為State的變量front和temp中。
State類的成員變量requested_w和requested_h分别用來描述應用程式視窗的寬度和高度,是以,當State變量front的成員變量requested_w和requested_h不等于State變量temp的成員變量requested_w和requested_h時,我們就會得到變量sizeChanged的值等于true,表示目前正在處理的應用程式視窗的大小發生了變化。在分析Layer類的成員函數doTransaction處理應用程式視窗的大小變化時,我們先介紹Layer類的成員變量mFixedSize的含義。
Layer類的成員變量mFixedSize是一個布爾變量,它的值可以通過Layer類的成員函數isFixedSize來獲得。從前面Android應用程式請求SurfaceFlinger服務渲染Surface的過程分析一文可以知道,當Android應用程式請求SurfaceFlinger服務配置設定一塊圖形緩沖區時, Layer類的成員函數requestBuffer就會被調用。這時候Android應用程式會傳遞兩個參數reqWidth和reqHeight過來,表示請求配置設定的圖形緩沖區的寬度和高度。這兩個參數是可以同時等于0的,表示使用預設的寬度和高度值來建立所請求的圖形緩沖區。這兩個預設的寬度和高度值即等于目前所處理的應用程式視窗的寬度和高度值,而後者的寬度和高度值是在其建立的時候指定的,這一點可以參考Android應用程式請求SurfaceFlinger服務建立Surface的過程分析一文。
Layer類的成員函數requestBuffer的參數reqWidth和reqHeight的值等于0意味着什麼呢?從前面Android應用程式請求SurfaceFlinger服務渲染Surface的過程分析一文還可以知道,Android應用程式在請求SurfaceFlinger服務配置設定一塊圖形緩沖區之前,會通過在Surface類内部定義的BufferInfo類的成員函數validateBuffer來檢查目前所處理的應用程式視窗的大小是否發生了變化。如果發生了變化,那麼Android應用程式就會忽略掉緩存自己一側的圖形緩沖區,而去SurfaceFlinger服務請求新的圖形緩沖區,因為那些緩存的圖形緩沖區由于與它們所關聯的應用程式視窗大小發生了變化而變為無效了。但是有一種特殊情況,在Android應用程式這一側,用來描述應用程式視窗的Surface類可以不維護應用程式視窗的大小值。在這種情況下,Surface類就會将與它所關聯的應用程式視窗的大小值設定為0,這意味着Android應用程式每次為這個應用程式視窗向SurfaceFlinger服務請求配置設定圖形緩沖區之前,都認為這個應用程式視窗的大小值沒有發生變化,同時傳遞給Layer類的成員函數requestBuffer的參數reqWidth和reqHeight的值會等于0。事實上,一個應用程式視窗的大小是随時都可以發生變化的,比如,我們可以通過調用用來在Android應用程式和SurfaceFlinger服務建立連接配接的一個類型為Client的Binder對象的成員函數setState來改變一個應用程式視窗的大小,而一旦一個應用程式視窗的大小發生了變化,我們正在分析Layer類的成員函數doTransaction就會被調用。
從上面的分析就可以得出一個結論,Layer類的成員函數doTransaction在處理應用程式視窗大小變化時,需要考慮Android應用程式每次在為該應用程式視窗向SurfaceFlinger服務請求配置設定圖形緩沖區之前,是否有能力去判斷之前為該應用程式視窗緩存的圖形緩沖區的有效性。如果沒有的話,那麼Layer類的成員函數doTransaction就需要将為該應用程式視窗緩存的圖形緩沖區設定為無效,以便以後Android應用程式可以請求SurfaceFlinger服務配置設定新的、大小正确的圖形緩沖區。從前面的分析還可以知道,當Android應用程式沒有能力去判斷之前為一個應用程式視窗所緩存的圖形緩沖區的有效性時,那麼之前在請求配置設定這些圖形緩沖區時,傳遞給Layer類的成員函數requestBuffer的參數reqWidth和reqHeight的值就會等于0,這時候Layer類的成員函數requestBuffer就會将Layer類的成員變量mFixedSize的值設定為false。
接下來,我們就分别根據Layer類的成員變量mFixedSize是等于true還是false來分析Layer類的成員函數doTransaction處理一個應用程式視窗大小發生變化的過程。
當Layer類的成員變量mFixedSize的值等于true時,Layer類的成員函數doTransaction的處理很簡單,它隻是調用另外一個成員函數setBufferSize來将新的應用程式視窗大小記錄下來,即儲存在Layer類的成員變量mWidth和mHeight中。
當Layer類的成員變量mFixedSize的值等于false時,由于Android應用程式沒有能力去判斷之前為一個應用程式視窗所緩存的圖形緩沖區的有效性,是以,Layer類的成員函數doTransaction除了會調用外一個成員函數setBufferSize來将新的應用程式視窗大小記錄下來之外,還會通過一個SharedBufferServer對象的成員函數reallocateAll來将為目前正在處理的應用程式視窗所緩存的圖形緩沖區設定為無效。在前面Android應用程式請求SurfaceFlinger服務建立Surface的過程分析一文中,我們已經分析過SharedBufferServer類的作用了,它是用來維護Android應用程式與SurfaceFlinger服務之間的共享UI中繼資料的,通過它可以将對應的圖形緩沖區設定為無效。
當Layer類的成員變量mFixedSize的值等于false時,Layer類的成員函數doTransaction還會提前将成員變量mCurrentState所描述的一個State對象的成員變量requested_w和requested_h的值儲存到成員變量mDrawingState所描述的一個State對象的成員變量requested_w和requested_h中去,這是為了避免後面調用父類LayerBase的成員函數doTransaction時,會傳回一個Layer::eVisibleRegion位不等于0的标志值給前面的Step 2,而這将會導緻SurfaceFlinger服務馬上重新計算各個應用程式視窗的可見區域。現在不傳回一個Layer::eVisibleRegion位不等于0的标志值給前面的Step 2,就會等到下次渲染目前正在處理的應用程式視窗時再重新計算各個應用程式視窗的可見區域。
此外,當Layer類的成員變量mFixedSize的值等于false時,Layer類的成員函數doTransaction還會檢查系統顯示屏是否正處于當機的狀态,這是通過調用Layer類的成員變量mFlinger所指向的SurfaceFlinger服務的成員函數hasFreezeRequest來實作的。如果系統顯示屏處于當機的狀态中,并且目前正在處理的應用程式視窗處于可見狀态,即變量front所描述的State對象的成員變量flags的ISurfaceComposer::eLayerHidden位等于0,那麼Layer類的成員函數doTransaction還會請求獲得一個用來當機系統顯示屏的類型為FreezeLock的鎖,并且将這個鎖儲存在Layer類的成員變量mFreezeLock中。這樣就可以等到下次渲染目前正在處理的應用程式視窗時,再來重新整理系統的UI。
State類的另外一個成員變量sequence用來描述一個應用程式視窗的其它屬性是否發生過變化,例如,X、Y和Z軸位置、透明度等除了大小之外的屬性。每當這些屬性發生了變化,那麼State類的成員變量sequence的值就會比原來增加1。Layer類的成員函數doTransaction再接下來就通過比較變量temp和front所描述的State對象的成員變量sequence的值是否相等來判斷目前正在處理的應用程式視窗的其它屬性是否發生變化。如果發生過變化,并且目前正在處理的應用程式視窗處于不可見狀态或者處于完全透明的狀态,那麼Layer類的成員函數doTransaction就會釋放之前所持有的用來當機系統顯示屏的一個類型為FreezeLock的鎖,這是通過調用Layer類的成員變量mFreezeLock所指向的一個FreezeLock對象的成員函數clear來實作的,這樣就可以避免阻止SurfaceFlinger服務更新系統UI。
最後,Layer類的成員函數doTransaction調用父類LayerBase的成員函數doTransaction來繼續處理應用程式視窗的屬性變化。
Step 4. LayerBase.doTransaction
uint32_t LayerBase::doTransaction(uint32_t flags)
{
const Layer::State& front(drawingState());
const Layer::State& temp(currentState());
if ((front.requested_w != temp.requested_w) ||
(front.requested_h != temp.requested_h)) {
// resize the layer, set the physical size to the requested size
Layer::State& editTemp(currentState());
editTemp.w = temp.requested_w;
editTemp.h = temp.requested_h;
}
if ((front.w != temp.w) || (front.h != temp.h)) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
}
if (temp.sequence != front.sequence) {
// invalidate and recompute the visible regions if needed
flags |= eVisibleRegion;
this->contentDirty = true;
// we may use linear filtering, if the matrix scales us
const uint8_t type = temp.transform.getType();
mNeedsFiltering = (!temp.transform.preserveRects() ||
(type >= Transform::SCALE));
}
// Commit the transaction
commitTransaction();
return flags;
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/LayerBase.cpp中。
與前面的Step 3一樣,LayerBase類的成員函數doTransaction首先通過成員函數drawingState和currentState來獲得用來描述目前正在處理的應用程式視窗的目前渲染狀态的一個State對象front和下一次渲染狀态的一個State對象temp,接下來就可以通過這兩個State對象的成員變量requested_w和requested_h來判斷目前正在處理的應用程式視窗的大小是否發生了變化。如果發生了變化,那麼就将新的應用程式視窗大小值儲存在用來描述下一次渲染狀态的State對象editTemp的成員變量w和h中。
State類的成員變量requested_w、requested_h和w、h的差別在于,前兩者用來描述應用程式視窗的大小,後兩者雖然也是用來描述應用程式視窗的大小,不過它們的作用是用來判斷是否需要重新計算系統中的各個應用程式視窗的可見區域的。一旦用來描述應用程式視窗的目前渲染狀态的State對象front和下一次渲染狀态的State對象temp的成員變量w和h不相等,那麼就說明需要系統中的各個應用程式視窗的可見區域,這是通過将将傳回值flags的Layer::eVisibleRegion位設定為1來實作的。
LayerBase類的成員函數doTransaction接下來還會繼續判斷State對象front和temp的成員變量sequence的值是否相等。如果不相等,那麼就說明目前正在處理的應用程式視窗的其它屬性,例如位置和透明度等發生了變化。在這種情況下,SurfaceFlinger服務也是需要重新計算系統中的各個應用程式視窗的可見區域的。是以,這時候 LayerBase類的成員函數doTransaction就會将它的傳回值flags的Layer::eVisibleRegion位設定為1,并且将Layer類的成員變量contentDirty的值設定為true,表示目前正在處理的應用程式視窗的内容是髒的,需要重新渲染。
LayerBase類的成員函數doTransaction最後還會繼續調用另外一個成員函數commitTransaction來結束對目前正在處理的應用程式視窗的屬性變化處理,以及将傳回值flags傳回給前面的Step 2,即SurfaceFlinger類的成員函數handleTransactionLocked。SurfaceFlinger類的成員函數handleTransactionLocked一旦發現這個傳回值的Layer::eVisibleRegion位的值為1,那麼就會将SurfaceFlinger類的成員變量mVisibleRegionsDirty的值設定為true,以便接下來可以重新計算系統中的各個應用程式視窗的可見區域。
接下來,我們就繼續分析LayerBase類的成員函數commitTransaction的實作。
Step 5. LayerBase.commitTransaction
void LayerBase::commitTransaction() {
mDrawingState = mCurrentState;
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/LayerBase.cpp中。
由于此時目前正在處理的應用程式視窗的屬性變化已經處理完成了,是以,LayerBase類的成員函數commitTransaction就将用來描述下一次渲染狀态的成員變量mCurrentState所描述的一個State對象儲存在另外一個用來描述目前渲染狀态的成員變量mDrawingState中去。
這一步執行完成之後,傳回到前面的Step 2中,即SurfaceFlinger類的成員函數handleTransactionLocked中,這時候系統顯示屏的屬性變化以及各個應用程式的屬性變化就都已經處理完成了,SurfaceFlinger類的成員函數handleTransactionLocked最後就會調用另外一個成員函數commitTransaction來結束整個屬性變化處理過程。
Step 6. SurfaceFlinger.commitTransaction
void SurfaceFlinger::commitTransaction()
{
mDrawingState = mCurrentState;
mResizeTransationPending = false;
mTransactionCV.broadcast();
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
前面提到,SurfaceFlinger類的成員變量mDrawingState用來描述SufaceFlinger服務的目前狀态,而成員變量mCurrentState用來描述SufaceFlinger服務的下一個狀态,由于這時候SufaceFlinger服務的狀态變化已經處理完成了,是以,SurfaceFlinger類的commitTransaction就将成員變量mCurrentState所指向的一個State對象儲存在成員變量mDrawingState中,接着又将成員變量mResizeTransationPending的值設定為false,表示SufaceFlinger服務已經處理過系統顯示屏或者應用程式視窗的大小變化了,最後調用成員變量mTransactionCV所描述的一個條件變量的成員函數broadcast來通知其它線程,例如Binder線程,SufaceFlinger服務已經處理完成一次屬性變化了。
至此,我們就分析完成系統顯示屏以及應用程式視窗的屬性變化的處理過程,接下來我們繼續分析各個應用程式視窗是如何設定它們目前所要渲染的圖形緩沖區的,即SurfaceFlinger類的成員函數handlePageFlip的實作。
2. handlePageFlip
SurfaceFlinger類的成員函數handlePageFlip是用來讓各個應用程式視窗設定它們目前所要渲染的圖形緩沖區的,在這個過程中,SurfaceFlinger服務還會計算各個應用程式視窗的可見區域,如圖3所示。
圖3 應用程式視窗設定目前所要渲染的圖形緩沖區的過程
這個過程可以分為7個步驟,接下來我們就詳細分析每一個步驟。
Step 1. SurfaceFlinger.handlePageFlip
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
LayerVector& currentLayers = const_cast<LayerVector&>(
mDrawingState.layersSortedByZ);
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
const Region screenRegion(hw.bounds());
if (visibleRegions) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
/*
* rebuild the visible layer list
*/
mVisibleLayersSortedByZ.clear();
const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
size_t count = currentLayers.size();
mVisibleLayersSortedByZ.setCapacity(count);
for (size_t i=0 ; i<count ; i++) {
if (!currentLayers[i]->visibleRegionScreen.isEmpty())
mVisibleLayersSortedByZ.add(currentLayers[i]);
}
#ifdef USE_COMPOSITION_BYPASS
sp<LayerBase> bypassLayer;
const size_t numVisibleLayers = mVisibleLayersSortedByZ.size();
if (numVisibleLayers == 1) {
const sp<LayerBase>& candidate(mVisibleLayersSortedByZ[0]);
const Region& visibleRegion(candidate->visibleRegionScreen);
const Region reminder(screenRegion.subtract(visibleRegion));
if (reminder.isEmpty()) {
// fullscreen candidate!
bypassLayer = candidate;
}
}
setBypassLayer(bypassLayer);
#endif
mWormholeRegion = screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty = false;
}
unlockPageFlip(currentLayers);
mDirtyRegion.andSelf(screenRegion);
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
函數首先将SurfaceFlinger類的成員變量mVisibleRegionsDirty的值儲存在變量visibleRegions中。從前面第1部分的内容可以知道,當SurfaceFlinger服務在處理系統顯示屏以及各個應用程式視窗的屬性變化時,如果發現需要重新計算各個應用程式視窗的可見區域,那麼就會将SurfaceFlinger類的成員變量mVisibleRegionsDirty的值設定為true。
函數接下來通過SurfaceFlinger類的成員變量mDrawingState所描述的一個State對象的成員變量layersSortedByZ來獲得系統目前所有的應用程式視窗,并且儲存在一個類型為LayerVector的向量currentLayers中。有了系統目前所有的應用程式視窗之後,就可以通過調用SurfaceFlinger類的成員函數lockPageFlip來讓它們設定自己目前所要渲染
的圖形緩沖區。在後面的Step 2中,我們再詳細分析SurfaceFlinger類的成員函數lockPageFlip的實作。
系統中的各個應用程式視窗在設定自己目前所要渲染的圖形緩沖區的過程中,有可能會改變自己的大小。在這種情況下,它們就會通過SurfaceFlinger類的成員函數lockPageFlip來傳回一個true值來給SurfaceFlinger類的成員函數handlePageFlip,這時候得到的變量visibleRegions的值就會等于true,表示需要重新計算各個應用程式視窗的可見區域。
綜合上述情況,無論變量visibleRegions的值是由于什麼原因等于true,都說明SurfaceFlinger服務需要重新計算各個應用程式視窗的可見區域,這個計算的工作是通過調用SurfaceFlinger類的另外一個成員函數computeVisibleRegions來完成的。在後面的Step 5中,我們再詳細分析SurfaceFlinger類的成員函數computeVisibleRegions的實作。
SurfaceFlinger類的成員函數computeVisibleRegions在計算完成各個應用程式視窗的可見區域之後,會得到一個全局不透明區域,儲存在輸出參數opaqueRegion中。這個全局不透明區域就是接下來需要渲染的,一般情況下,它的大小就應該等于顯示屏的大小,即變量screenRegion所描述的區域。在異常情況下,可能會導緻顯示屏區域screenRegion大于全局不透明區域opaqueRegion,這時候前者減去後者就可以得到一些稱為“蟲洞”的區域,它們儲存在SurfaceFlinger類的成員變量mWormholeRegion中。由于這些蟲洞區域不會被各個應用程式視窗覆寫,是以,SurfaceFlinger服務需要對它們進行特殊處理,即以一種特殊的方式來渲染它們。在後面的第4部分内容中,我們就會看到SurfaceFlinger服務是通過調用SurfaceFlinger類的成員函數drawWormhole來渲染這些蟲洞的。
SurfaceFlinger類的成員函數computeVisibleRegions在計算完成各個應用程式視窗的可見區域之後,各個應用程式視窗的可見區域就會儲存在用來描述它們的一個LayerBase對象的成員變量visibleRegionScreen中,是以,SurfaceFlinger類的成員函數handlePageFlip就會通過這個成員變量來排除掉那些可見區域為空的應用程式視窗,并且将所有可見區域不為空的應用程式視窗按照它們的Z軸大小儲存在SurfaceFlinger類的成員變量mVisibleLayersSortedByZ所描述的一個向量中。
經過前面的操作之後,SurfaceFlinger類的成員函數handlePageFlip就可以将成員變量mVisibleRegionsDirty的值設定為false了,因為這時候系統中各個應用程式視窗的可見區域都已經重新計算過了。
SurfaceFlinger類的成員函數handlePageFlip最後就調用另外一個成員函數unlockPageFlip來讓各個應用程式視窗執行一些善後的工作,例如,讓各個應用程式視窗檢查自己的可見區域是否等于空,如果等于空的話,那麼就需要将它們之前所獲得的用來當機顯示屏的鎖釋放掉,以避免阻止SurfaceFlinger服務渲染系統UI。在後面的Step 6中,我們再詳細分析SurfaceFlinger類的成員函數unlockPageFlip的實作。
SurfaceFlinger類的成員函數handlePageFlip最後還需要做的另外一件事情是将設定系統的髒區域,這個髒區域儲存在SurfaceFlinger類的成員變量mDirtyRegion中,它同樣是作為一個輸出參數來傳遞給SurfaceFlinger類的成員函數computeVisibleRegions的,以便後者在計算各個應用程式視窗的可見區域時,可以将獲得的系統髒區域儲存在它裡面。我們同樣是在後面的Step 5中分析SurfaceFlinger類的成員函數computeVisibleRegions的實作時,再看看系統的髒區域是如何計算的。
有一種特殊情況,即SurfaceFlinger類的成員函數handlePageFlip在重新計算完成各個應用程式視窗的可見區域後,如果滿足以下三個條件:
1. SurfaceFlinger服務在編譯時指定了宏USE_COMPOSITION_BYPASS;
2. 目前要渲染的應用程式視窗隻有一個,即SurfaceFlinger類的成員變量mVisibleLayersSortedByZ所描述的一個向量的大小等于1;
3. 目前要渲染的一個唯一的應用程式視窗的可見區域的大小不為空。
那麼SurfaceFlinger服務就會直接将這個唯一需要渲染的應用程式視窗的圖形緩沖區渲染到硬體幀緩沖區中去,以跳過後面的合成各個應用程式視窗的圖形緩沖區的操作。在這種情況下,這個唯一需要渲染的應用程式視窗會通過SurfaceFlinger類的成員函數setBypassLayer記錄起來。
SurfaceFlinger類的成員函數setBypassLayer的實作如下所示:
void SurfaceFlinger::setBypassLayer(const sp<LayerBase>& layer)
{
// if this layer is already the bypass layer, do nothing
sp<Layer> cur(mBypassLayer.promote());
if (mBypassLayer == layer) {
if (cur != NULL) {
cur->updateBuffersOrientation();
}
return;
}
// clear the current bypass layer
mBypassLayer.clear();
if (cur != 0) {
cur->setBypass(false);
cur.clear();
}
// set new bypass layer
if (layer != 0) {
if (layer->setBypass(true)) {
mBypassLayer = static_cast<Layer*>(layer.get());
}
}
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
從這裡就可以看出,在指定了編譯宏USE_COMPOSITION_BYPASS的情況下,SurfaceFlinger服務唯一需要渲染的應用程式視窗就儲存在SurfaceFlinger類的一個類型為Layer的成員變量mBypassLayer中,并且會調用用來這個Layer對象的成員函數setBypass來将它裡面的一個成員變量mBypassState的值設定為true。在後面的第3部分内容中,我們再詳細分析這個唯一需要渲染的應用程式視窗的圖形緩沖區是如何直接渲染到硬體幀緩沖區中去的。
接下來,我們就繼續分析SurfaceFlinger類的成員函數lockPageFlip、computeVisibleRegions和unlockPageFlip的實作,以便可以分别了解各個應用程式視窗是如何設定自己目前需要渲染的圖形緩沖區的、SurfaceFlinger服務是如何計算各個應用程式視窗的可見區域的,以及各個應用程式視窗的可見區域計算完成之後的善後工作是什麼。
Step 2. SurfaceFlinger.lockPageFlip
bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
{
bool recomputeVisibleRegions = false;
size_t count = currentLayers.size();
sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
const sp<LayerBase>& layer(layers[i]);
layer->lockPageFlip(recomputeVisibleRegions);
}
return recomputeVisibleRegions;
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
在前面Android應用程式與SurfaceFlinger服務的關系概述和學習計劃的一系列文章中,我們假設在SurfaceFlinger服務這一側,每一個應用程式視窗都是使用一個Layer對象來描述,這些Layer對象都是從LayerBase繼承下來的,是以它們可以儲存在一個類型為LayerBase的向量中。
從前面的調用過程可以知道,參數currentLayers裡面儲存的一系列LayerBase對象正是用來描述系統目前的各個應用程式視窗的,SurfaceFlinger類的成員函數lockPageFlip依次調用這些LayerBase對象的成員函數lockPageFlip來讓它們設定目前需要渲染的圖形緩沖區。由于前面我們假設這些LayerBase對象的實際類型為Layer,是以,前面調用的實際上就是Layer類的成員函數lockPageFlip。接下來,我們就繼續分析Layer類的成員函數lockPageFlip的實作。
Step 3. Layer.lockPageFlip
這個函數定義在檔案frameworks/base/services/surfaceflinger/Layer.cpp中。在前面Android應用程式請求SurfaceFlinger服務渲染Surface的過程分析一文中,我們已經分析過這個函數的實作了,不過當時分析得不是太完整,現在我們再重新來閱讀一下。這個函數比較長,我們分段來閱讀:
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
ClientRef::Access sharedClient(mUserClientRef);
SharedBufferServer* lcblk(sharedClient.get());
if (!lcblk) {
// client died
recomputeVisibleRegions = true;
return;
}
ssize_t buf = lcblk->retireAndLock();
if (buf == NOT_ENOUGH_DATA) {
// NOTE: This is not an error, it simply means there is nothing to
// retire. The buffer is locked because we will use it
// for composition later in the loop
return;
}
if (buf < NO_ERROR) {
LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
mPostedDirtyRegion.clear();
return;
}
// we retired a buffer, which becomes the new front buffer
if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) {
LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
mPostedDirtyRegion.clear();
return;
}
這段代碼首先通過Layer類的成員變量mUserClientRef來獲得一個SharedBufferServer對象lcblk。這個SharedBufferServer對象lcblk是用來描述Android應用程式與SurfaceFlinger服務之間的共享UI中繼資料,通過調用它的成員函數retireAndLock,Layer類的成員函數lockPageFlip就可以知道目前需要渲染的圖形緩沖區的編号。有了這個編号之後,Layer類的成員函數lockPageFlip就可以在成員變量mBufferManager所描述的一個BufferManager中找到一個對應的圖形緩沖區。
這段代碼接着将目前需要渲染的圖形緩沖區的編号儲存在Layer類的成員變量mBufferManager所描述的一個BufferManager中,這是通過調用這個BufferManager的成員函數setActiveBufferIndex來實作的。在接下來的Step 4中,我們就可以通過這個BufferManager的成員函數getActiveBufferIndex來重新獲得這個編号,以便可以找到對應的圖形緩沖區。
我們接着往下閱讀代碼:
sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
if (newFrontBuffer != NULL) {
// get the dirty region
// compute the posted region
const Region dirty(lcblk->getDirtyRegion(buf));
mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
// update the layer size and release freeze-lock
const Layer::State& front(drawingState());
if (newFrontBuffer->getWidth() == front.requested_w &&
newFrontBuffer->getHeight() == front.requested_h)
{
if ((front.w != front.requested_w) ||
(front.h != front.requested_h))
{
// Here we pretend the transaction happened by updating the
// current and drawing states. Drawing state is only accessed
// in this thread, no need to have it locked
Layer::State& editDraw(mDrawingState);
editDraw.w = editDraw.requested_w;
editDraw.h = editDraw.requested_h;
// We also need to update the current state so that we don't
// end-up doing too much work during the next transaction.
// NOTE: We actually don't need hold the transaction lock here
// because State::w and State::h are only accessed from
// this thread
Layer::State& editTemp(currentState());
editTemp.w = editDraw.w;
editTemp.h = editDraw.h;
// recompute visible region
recomputeVisibleRegions = true;
}
// we now have the correct size, unfreeze the screen
mFreezeLock.clear();
}
// get the crop region
setBufferCrop( lcblk->getCrop(buf) );
// get the transformation
setBufferTransform( lcblk->getTransform(buf) );
} else {
// this should not happen unless we ran out of memory while
// allocating the buffer. we're hoping that things will get back
// to normal the next time the app tries to draw into this buffer.
// meanwhile, pretend the screen didn't update.
mPostedDirtyRegion.clear();
}
這段代碼首先是用來設定目前正在處理的應用程式視窗的髒區域、紋理坐标和旋轉方向的。
一個應用程式視窗目前的髒區域、紋理坐标,以及旋轉方向是儲存在一塊匿名共享記憶體中的,用來在Android應用程式和SurfaceFlinger服務之間共享UI中繼資料。這塊匿名共享記憶體就是使用前面所獲得的SharedBufferServer對象lcblk來描述的,即我們可以通過調用SharedBufferServer對象lcblk的成員函數getDirtyRegion、getCrop以及getTransform來獲得目前正在處理的應用程式視窗的髒區域、紋理坐标以及旋轉方向。其中,髒區域儲存在Layer類的成員變量mPostedDirtyRegion中,紋理坐标通過調用父類LayerBase的成員函數setBufferCrop儲存在其成員變量mBufferCrop中,而旋轉方向通過調用父類LayerBase的成員函數setBufferTransform儲存在其成員變量mBufferTransform中。
從前面的執行過程可以知道,變量buf儲存的是目前需要渲染的圖形緩沖區的編号。當我們以這個編号為參數來調用Layer類的成員函數getBuffer時,就可以得到一個對應的圖形緩沖區。這個圖形緩沖區使用一個GraphicBuffer對象來描述,并且儲存在變量newFrontBuffer中。
目前需要渲染的圖形緩沖區newFrontBuffer的大小可以通過調用它的成員函數getBounds來獲得。我們把目前需要渲染的圖形緩沖區的大小與目前正在處理的應用程式視窗的髒區域執行一個相交操作之後,才可以得到最終需要渲染的髒區域。這個最終需要渲染的髒區域就儲存在前面提到的Layer類的成員變量mPostedDirtyRegion中。
這段代碼接下來還會檢查是否需要修改目前正在處理的應用程式視窗的大小。本來修改應用程式視窗的大小是在處理應用程式視窗的屬性變化時進行的,如前面第1部分的内容所示。但是在前面第1部分的内容的Step 3中提到,有一種特殊情況,即Android應用程式在請求SurfaceFlinger服務配置設定圖形緩沖區時,傳遞過來的圖形緩沖區的寬度和高度值等于0。在這種情況下,Layer類的成員函數doTransaction會暫時忽略掉應用程式視窗目前所發生的大小變化,而是等到該應用程式視窗大小發生了變化後所申請配置設定的新圖形緩沖區需要渲染時,再來修改它的大小。
從前面第1部分的内容的Step 3可以知道,調用Layer類的成員函數drawingState所獲得的一個State對象是用來描述一個應用程式視窗的目前渲染狀态的,而這個State對象的成員變量requested_w和requested_h描述的就正好是該應用程式視窗的最新寬度和高度值。如果一個應用程式視窗的最新寬度和高度值正好等于它目前所要渲染的圖形緩沖區newFrontBuffer的寬度和高度值,那麼就說明它目前所要渲染的圖形緩沖區newFrontBuffer就正好是在它的大小發生變化之後所申請配置設定的,是以,這時候Layer類的成員函數lockPageFlip就需要繼續檢查它之前是否有被忽略掉的大小變化未被處理。如果有的話,現在就是時候對它的大小變化進行處理了。
從前面第1部分的内容的Step 4可以知道,如果用來描述一個應用程式視窗的目前渲染狀态的一個State對象的兩個成員變量w和h,與另外兩個成員變量requested_w和requested_h不相等的話,那麼就說明該應用程式視窗有被忽略掉的大小變化未被處理,是以,在這種情況下,Layer類的成員函數lockPageFlip就會分别将該State對象的成員變量requested_w和requested_h的值分别儲存在成員變量w和h中,同時還會儲存用來描述該應用程式視窗的下一次渲染狀态的一個State對象的成員變量w和h中,以表示前面被忽略掉的視窗大小變化已經得到處理了。
由于每當有應用程式視窗的大小發生變化之後,SurfaceFlinger服務都需要重新計算各個應用程式視窗的可見區域,是以,Layer類的成員函數lockPageFlip在處理了目前正在處理的應用程式視窗上一次被忽略掉的大小變化之後,需要将輸出參數recomputeVisibleRegions的值設定為true,以便可以用來通知SurfaceFlinger服務更新各個應用程式視窗的可見區域,如前面的Step 1所示。
此外,Layer類的成員函數lockPageFlip在處理了目前正在處理的應用程式視窗上一次被忽略掉的大小變化之後,如果之前擷取過用來當機系統顯示屏的鎖,那麼現在也是時間釋放這個鎖了,以避免阻止SurfaceFlinger服務接下來重新渲染系統的UI。這個用來當機系統顯示屏的鎖是儲存在Layer類的成員變量mFreezeLock中的,通過調用它的成員函數clear就可以釋放它所描述的用來當機系統顯示屏的鎖。
我們接着往下閱讀代碼:
if (lcblk->getQueuedCount()) {
// signal an event if we have more buffers waiting
mFlinger->signalEvent();
}
/* a buffer was posted, so we need to call reloadTexture(), which
* will update our internal data structures (eg: EGLImageKHR or
* texture names). we need to do this even if mPostedDirtyRegion is
* empty -- it's orthogonal to the fact that a new buffer was posted,
* for instance, a degenerate case could be that the user did an empty
* update but repainted the buffer with appropriate content (after a
* resize for instance).
*/
reloadTexture( mPostedDirtyRegion );
}
這段代碼首先檢查目前正在處理的應用程式視窗是否還有其它圖形緩沖區在等待被渲染。如果有的話,那麼就會通過Layer類的成員變量mFlinger的成員函數signalEvent來向SurfaceFlinger服務的UI渲染線程的消息隊列發送另外一個類型為MessageQueue::INVALIDATE的消息,以便SurfaceFlinger服務的UI渲染線程在重新整理了目前的系統UI之後,可以馬上再次重新整理系統的UI。
執行完成以上操作之後,Layer類的成員函數lockPageFlip就調用另外一個成員函數reloadTexture來為目前正在處理的應用程式視窗加載新的紋理,以便接下來可以将這個新的紋理渲染合成和渲染到硬體幀緩沖區中去。
接下來, 我們就繼續分析Layer類的成員函數reloadTexture是如何獲得應用程式視窗即将要合成和渲染的紋理的。
Step 4. Layer.reloadTexture
void Layer::reloadTexture(const Region& dirty)
{
sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
if (buffer == NULL) {
// this situation can happen if we ran out of memory for instance.
// not much we can do. continue to use whatever texture was bound
// to this context.
return;
}
if (mGLExtensions.haveDirectTexture()) {
EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) {
// not sure what we can do here...
goto slowpath;
}
} else {
slowpath:
GGLSurface t;
if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) {
status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
LOGE_IF(res, "error %d (%s) locking buffer %p",
res, strerror(res), buffer.get());
if (res == NO_ERROR) {
mBufferManager.loadTexture(dirty, t);
buffer->unlock();
}
} else {
// we can't do anything
}
}
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/Layer.cpp中。
函數首先調用Layer類的成員變量mBufferManager所描述的一個BufferManager對象的成員函數getActiveBuffer來獲得目前正在處理的應用程式視窗接下來需要渲染的圖形緩沖區,并且儲存在變量buffer中。
函數接着調用Layer類的成員變量mGLExtensions所描述的一個GLExtensions對象的成員函數haveDirectTexture來判斷系統是否支援在硬體上直接建立紋理對象。如果支援的話,那麼就調用Layer類的成員變量mBufferManager所描述的一個BufferManager對象的成員函數initEglImage來根據圖形緩沖區buffer的内容在硬體上直接建立一個紋理對象。如果這個紋理對象建立成功的話,那麼就會儲存在Layer類的成員變量mBufferData所描述的一個BufferData中。
如果系統不支援在硬體上直接建立紋理對象,或者前面在硬體上直接建立紋理對象失敗,那麼函數就會退而求之,轉換為用軟體的方式來根據圖形緩沖區buffer的内容來建立一個紋理對象,這是通過調用Layer類的成員變量mBufferManager所描述的一個BufferManager對象的成員函數loadTexture來實作的。建立出來的這個紋理對象就儲存在Layer類的成員變量mFailoverTexture中。不過,能夠使用軟體的方式來建立紋理對象的一個前提是圖形緩沖區buffer的内容可以通過軟體的方式來讀取,即它的成員變量usage的GRALLOC_USAGE_SW_READ_MASK位等于1。在這種情況下,我們就可以調用圖形緩沖區buffer的成員函數lock來獲得它的内容通路位址,并且儲存在GGLSurface對象t,以便接下來可以通過這個GGLSurface對象t來建立一個紋理對象。
Layer類的成員函數reloadTexture首選在硬體上建立紋理對象,如果這種方式不可行,那麼再使用軟體方式來建立紋理對象。這種技術在計算機領域中就稱為失效備援技術,即Failover技術。
這一步執行完成之後,傳回到前面的Step 1中,即SurfaceFlinger類的成員函數handlePageFlip中,接下來就會繼續調用SurfaceFlinger類的另外一個成員函數computeVisibleRegions來計算各個應用程式視窗的可見區域。
Step 5. SurfaceFlinger.computeVisibleRegions
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,主要用來計算各個應用程式視窗的可見區域。在分析這個函數的實作之前,我們首先解釋一些與應用程式視窗相關的概念:可見區域(Visible Region)、透明區域(Transparent Region)、半透明區域(Translucent Region)、完全不透明區域(Opaque Region)和被覆寫區域(Covered Region)。
假設一個應用程式視窗的寬度和高度分别為w和h,如圖4所示:
圖4 應用程式視窗的可見區域
那麼我們就可以将由(0,0)、(0, w)、(0, h)和(w,h)四個點組成的區域稱為應用程式視窗的可見區域。
接下來,我們可以在一個應用程式視窗的可見區域挖一個洞出來,如圖5所示:
圖5 應用程式視窗的透明區域
這時候應用程式視窗真正的可見區域就需要減去中間被挖出來的洞。這個被挖出來的洞就稱為應用程式視窗的透明可見區域。
如果應用程式視窗的可見區域的Alpha通道大于0并且小255,那麼我們就認為應用程式視窗的可見區域是半透明的。有兩種極端情況,即當應用程式視窗的可見區域的Alpha通道等于0或者255的時候。當等于0的時候,我們就認為應用程式視窗的可見區域是透明的,就如圖5所示的洞一樣,而當等于255的時候,我們就認為應用程式視窗的可見區域是完全不透明的。
上面我們讨論的應用程式視窗的可見區域是基于單個應用程式視窗而言的,當多個應用程式視窗疊加在一起的時候,在讨論一個應用程式視窗的可見區域的時候,就需要考慮位于它上面的其它應用程式視窗的可見區域的影響了。注意,一個應用程式視窗的可見區域隻受位于它上面的其它應用程式視窗影響,而不會受到位于它下面的其它的應用程式視窗影響,是以,我們是按照從上到下的順序來計算系統中各個應用程式視窗的可見區域的。
為了友善描述,我們假設位于一個應用程式視窗上面的所有應用程式視窗組成了一個整體的可見區域(Above Covered Layers),并且這個可見區域與我們所要讨論的應用程式視窗相交,即它們疊加在一起,如圖6所示:
圖6 應用程式視窗的被覆寫區域
由藍色矩形組成的區域即為上層所有應用程式視窗所組成的一個整體可見區域,這個整體可見區域與下面綠色矩形組成的一個應用程式視窗相交的部分,即由虛線所圍成的區域,就是下面的一個應用程式視窗的被覆寫區域。
一個應用程式視窗的被覆寫區域有可能是半透明的,也有可能是完全不透明的,但是不可能是透明的,如圖7所示:
圖7 應用程式視窗的被覆寫完全不透明區域
在原來由虛線圍成的區域中,深藍色的那部分區域就是完全不透明的(Above Opaque Layers),這時候由綠色矩形組成的應用程式視窗的可見區域除了要減去中間的洞(透明區域)之外,還要減去被覆寫的完全不透明區域,如下圖8所示:
圖8 應用程式視窗的最終可見區域
從上面的讨論我們就可以清楚地知道,為了計算一個應用程式視窗的最終可見區域,我們需要知道:
1. 應用程式視窗的左上角位置,以及寬度和高度,以便可以獲得應用程式視窗的原始可見區域。
2. 應用程式視窗的透明區域。
3. 應用程式視窗的被覆寫完全不透明區域。
用第1步到的原始可見區域減去第2步的透明區域和第3步的被覆寫完全不透明區域,就可以得到一個應用程式視窗的最終可見區域。
為了獲得第3步的被覆寫完全不透明區域,我們在計算一個應用程式視窗的最終可見區域的過程中,還需要将此前得到的應用程式視窗的完全不透明區域組合起來,形成一個覆寫完全不透明區域(Above Opaque Layers),是以,我們還需要知道:
4. 應用程式視窗的完全不透明區域。
此外,由于一個應用程式視窗的被覆寫半透明區域是需要與上層的應用程式視窗可見區域執行混合計算的,是以,我們在計算系統中各個應用程式視窗的可見區域的過程中,還需要将所有上層的應用程式視窗可見區域組合起來形成一個覆寫區域(Above Covered Layers)。
有了這些背景知識之後,接下來我們就可以分析SurfaceFlinger類的成員函數computeVisibleRegions的實作了。由于SurfaceFlinger類的成員函數computeVisibleRegions的實作比較長,我們分段來閱讀:
void SurfaceFlinger::computeVisibleRegions(
LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
{
const GraphicPlane& plane(graphicPlane(0));
const Transform& planeTransform(plane.transform());
const DisplayHardware& hw(plane.displayHardware());
const Region screenRegion(hw.bounds());
Region aboveOpaqueLayers;
Region aboveCoveredLayers;
Region dirty;
bool secureFrameBuffer = false;
這段代碼首先計算得到螢幕區域,儲存在變量screenRegion中,接着定義了另外兩個區域aboveOpaqueLayers,分别用來描述上層覆寫完全不透明區域(Above Opaque Layers)和上層覆寫區域(Above Covered Layers),最後定義了一個布爾變量secureFrameBuffer,用來描述系統中是否存在界面受到安全保護的應用程式視窗。
界面受到安全保護的應用程式視窗的内容是不可以在程序間傳輸的,這個屬性主要是應用在螢幕截圖中。例如,如果系統中存在一個界面受到安全保護的應用程式視窗,那麼我們就不可以請求SurfaceFlinger服務執行截屏功能,因為SurfaceFlinger服務截取下來的螢幕會被傳輸給請求的程序使用。
我們接着往下閱讀代碼:
size_t i = currentLayers.size();
while (i--) {
const sp<LayerBase>& layer = currentLayers[i];
layer->validateVisibility(planeTransform);
這段代碼是一個while循環的前面幾行。系統中所有需要計算可見區域的應用程式視窗都儲存在參數currentLayers所描述的一個向量中。這段代碼的while循環就是用來逐個地這些應用程式視窗的可見區域的。注意,這個while是先計算是按照從上到下的順序來計算系統中各個應用程式視窗的可見區域的。
在計算一個應用程式視窗layer的可見區域之前,我們首先要驗證它的可見性,這是通過調用它的成員函數validateVisibility來實作的,即調用LayerBase類的成員函數validateVisibility來實作的。
LayerBase類的成員函數validateVisibility的實作如下所示:
void LayerBase::validateVisibility(const Transform& planeTransform)
{
const Layer::State& s(drawingState());
const Transform tr(planeTransform * s.transform);
const bool transformed = tr.transformed();
uint32_t w = s.w;
uint32_t h = s.h;
tr.transform(mVertices[0], 0, 0);
tr.transform(mVertices[1], 0, h);
tr.transform(mVertices[2], w, h);
tr.transform(mVertices[3], w, 0);
if (UNLIKELY(transformed)) {
// NOTE: here we could also punt if we have too many rectangles
// in the transparent region
if (tr.preserveRects()) {
// transform the transparent region
transparentRegionScreen = tr.transform(s.transparentRegion);
} else {
// transformation too complex, can't do the transparent region
// optimization.
transparentRegionScreen.clear();
}
} else {
transparentRegionScreen = s.transparentRegion;
}
// cache a few things...
mOrientation = tr.getOrientation();
mTransformedBounds = tr.makeBounds(w, h);
mLeft = tr.tx();
mTop = tr.ty();
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/LayerBase.cpp中。
參數planeTransform用來描述系統顯示屏旋轉方向,它是一個變換矩陣,而用來描述目前正在處理的應用程式視窗的目前渲染狀态的一個State對象s的成員變量transform指向的也是一個變換矩陣,用來描述目前正在處理的應用程式視窗的位置、旋轉方向和縮放因子等。将這兩者相乘,就可以得到目前正在處理的應用程式視窗相對于系統顯示屏的一個變換矩陣tr。
函數首先計算目前正在處理的應用程式視窗的四個角在顯示屏中的位置,并且分别儲存在LayerBase的成員變量mVertices所描述的一個數組中,這是通過調用變換矩陣tr的成員函數transform來實作的。
函數接着判斷目前正在處理的應用程式視窗是否被旋轉過或者被縮放過。如果是的話,那麼前面調用變換矩陣tr的成員函數transformed的傳回值就會等于true,即變量transformed的等于true。在這種情況下,函數就要相應地對目前正在處理的應用程式視窗的透明區域進行旋轉或者縮放。但是有一種特殊情況,即當目前正在處理的應用程式視窗被旋轉和縮放得不規則時,這時候對應用程式視窗的透明區域進行旋轉或者縮放就會很複雜,于是函數就幹脆将它的透明區域忽略掉。判斷目前正在處理的應用程式視窗是否被旋轉和縮放得不規則是通過調用變換矩陣tr的成員函數preserveRects來實作的,當它的傳回值等于true的時候,就說明目前正在處理的應用程式視窗是否被旋轉和縮放之後還是規則,否則就是不規則的。
目前正在處理的應用程式視窗的透明區域儲存在State對象s的成員變量transparentRegion中,按照上述原理,函數按照以下規則來對它進行處理:
1. 當變量transformed的等于false時,說明目前正在處理的應用程式視窗的透明區域就不需要進行旋轉或者縮放,這時候就可以将這個透明區域儲存在LayerBase類的成員變量transparentRegionScreen中。
2. 當變量transformed的等于true,并且變換矩陣tr的成員函數preserveRects的傳回值也等于true時,那麼就說明目前正在處理的應用程式視窗的透明區域需要進行旋轉或者縮放,這時候通過調用變換矩陣tr的成員函數transform來實作的。 最終得到的透明區域同樣是儲存在LayerBase類的成員變量transparentRegionScreen中。
3. 當變量transformed的等于true,并且變換矩陣tr的成員函數preserveRects的傳回值等于false時,那麼就說明需要忽略掉目前正在處理的應用程式視窗的透明區域,這是通過LayerBase類的成員變量transparentRegionScreen所描述的一個Region對象的成員函數clear來實作的。
最後,函數就計算目前正在處理的應用程式視窗的方向,左上角位置,以及包含了目前正在處理的應用程式視窗的一個矩形區域,這些值分别儲存在在LayerBase類的成員變量mOrientation,mLeft和mTop,以及mTransformedBounds中。
傳回到SurfaceFlinger類的成員函數computeVisibleRegions中,我們繼續往下閱讀代碼:
// start with the whole surface at its current location
const Layer::State& s(layer->drawingState());
/*
* opaqueRegion: area of a surface that is fully opaque.
*/
Region opaqueRegion;
/*
* visibleRegion: area of a surface that is visible on screen
* and not fully transparent. This is essentially the layer's
* footprint minus the opaque regions above it.
* Areas covered by a translucent surface are considered visible.
*/
Region visibleRegion;
/*
* coveredRegion: area of a surface that is covered by all
* visible regions above it (which includes the translucent areas).
*/
Region coveredRegion;
這段代碼首先獲得用來描述目前正在處理的應用程式視窗的目前渲染狀态的一個State對象s,接着再定義了三個Region對象opaqueRegion、visibleRegion和coveredRegion,分别用來描述目前正在處理的應用程式視窗的完全不透明區域、可見區域和被覆寫區域。這三個區域的含義和作用如前所述。
我們繼續往下閱讀代碼:
// handle hidden surfaces by setting the visible region to empty
if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
const bool translucent = layer->needsBlending();
const Rect bounds(layer->visibleBounds());
visibleRegion.set(bounds);
visibleRegion.andSelf(screenRegion);
if (!visibleRegion.isEmpty()) {
// Remove the transparent area from the visible region
if (translucent) {
visibleRegion.subtractSelf(layer->transparentRegionScreen);
}
// compute the opaque region
const int32_t layerOrientation = layer->getOrientation();
if (s.alpha==255 && !translucent &&
((layerOrientation & Transform::ROT_INVALID) == false)) {
// the opaque region is the layer's footprint
opaqueRegion = visibleRegion;
}
}
}
這段代碼用來計算目前正在處理的應用程式視窗的可見區域和完全不透明區域。隻有在目前正在處理的應用程式視窗處于可見狀态,并且它不是完全透明時,才需要計算這兩個區域。當State對象s的成員變量flags的ISurfaceComposer::eLayerHidden位等于0時,就說明目前正在處理的應用程式視窗是處于可見狀态的,而當它的另外一個成員變量alpha的值不等于0的時候,就說明目前正在處理的應用程式視窗不是完全透明的。
我們首先來看目前正在處理的應用程式視窗的可見區域的計算過程。
LayerBase對象layer的成員函數visibleBounds傳回的是包圍目前正在處理的應用程式視窗的一個矩形,即LayerBase類的成員變量mTransformedBounds,這是在前面調用LayerBase類的成員函數validateVisibility時計算得到的。函數首先将這個矩形與螢幕區域screenRegion執行一個與操作,以便可以得到目前正在處理的應用程式視窗的初始可見區域visibleRegion。
當得到目前正在處理的應用程式視窗的初始可見區域visibleRegion不為空時,函數接着判斷該視窗是否是半透明的,即它是否要與其它的視窗執行混合操作。如果是半透明的話,那麼前面調用LayerBase對象layer的成員函數needsBlending的傳回值translucent就會等于true。在這種情況下,我們就需要将應用程式視窗的初始可見區域visibleRegion減去它的透明區域,即LayerBase對象layer的成員變量transparentRegionScreen所描述的區域。這樣我們進一步得到目前正在處理的應用程式視窗的可見區域visibleRegion。
我們接着來看目前正在處理的應用程式視窗的完全不透明區域的計算過程。
當目前正在處理的應用程式視窗是完全不透明,并且旋轉方向也是規則時,那麼它的完全不透明區域opaqueRegion就等于前面計算所得到的可見區域visibleRegion。當目前正在處理的應用程式視窗的Alpha通道等于255,即當State對象s的成員變量alpha的值等于255,并且變量translucent的值等于false時,就說明它是完全不透明的,而當目前正在處理的應用程式視窗的旋轉方向layerOrientation的Transform::ROT_INVALID位等于0的時候,就說明它的旋轉方向是規則的。
我們繼續往下閱讀代碼:
// Clip the covered region to the visible region
coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
// Update aboveCoveredLayers for next (lower) layer
aboveCoveredLayers.orSelf(visibleRegion);
// subtract the opaque region covered by the layers above us
visibleRegion.subtractSelf(aboveOpaqueLayers);
這段代碼用來計算目前正在處理的應用程式視窗的被覆寫區域,以及再進一步計算它的可見區域,主要考慮是否被上層的不透明區域覆寫了。
變量aboveCoveredLayers用來描述目前正在處理的應用程式視窗的所有上層應用程式視窗所組成的可見區域,将這個區域與目前正在處理的應用程式視窗的可見區域visibleRegion相交,就可以得到目前正在處理的應用程式視窗的被覆寫區域coveredRegion,而将這個區域與目前正在處理的應用程式視窗的可見區域visibleRegion相或一下,就可以得到下一個應用程式視窗的所有上層應用程式視窗所組成的可見區域aboveCoveredLayers。
變量aboveOpaqueLayers用來描述目前正在處理的應用程式視窗的所有上層應用程式視窗所組成的完全不透明區域,這個區域從目前正在處理的應用程式視窗的可見區域visibleRegion減去後,就可以得到目前正在處理的應用程式視窗的最終可見區域visibleRegion。
我們繼續往下閱讀代碼:
// compute this layer's dirty region
if (layer->contentDirty) {
// we need to invalidate the whole region
dirty = visibleRegion;
// as well, as the old visible region
dirty.orSelf(layer->visibleRegionScreen);
layer->contentDirty = false;
} else {
/* compute the exposed region:
* the exposed region consists of two components:
* 1) what's VISIBLE now and was COVERED before
* 2) what's EXPOSED now less what was EXPOSED before
*
* note that (1) is conservative, we start with the whole
* visible region but only keep what used to be covered by
* something -- which mean it may have been exposed.
*
* (2) handles areas that were not covered by anything but got
* exposed because of a resize.
*/
const Region newExposed = visibleRegion - coveredRegion;
const Region oldVisibleRegion = layer->visibleRegionScreen;
const Region oldCoveredRegion = layer->coveredRegionScreen;
const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
}
dirty.subtractSelf(aboveOpaqueLayers);
// accumulate to the screen dirty region
dirtyRegion.orSelf(dirty);
這段代碼用來計算螢幕的髒區域。我們首先解釋一下螢幕的髒區域是如何計算的。将所有應用程式視窗的髒區域都組合起來,就可以得到螢幕的髒區域,這個髒區域就是需要重新執行渲染操作的。是以,為了得到螢幕的髒區域,我們要知道目前正在處理的應用程式視窗的髒區域,以及之前已經處理了的應用程式視窗髒區域組合。前者使用變量dirty來描述,而後者使用輸出參數dirtyRegion來描述。從前面的調用過程可以知道,輸出參數dirtyRegion指向的就正好是SurfaceFlinger類的成員變量mDirtyRegion,是以,當這一步執行完成之後,SurfaceFlinger類的成員變量mDirtyRegion就代表了SurfaceFlinger服務所要渲染的髒區域。
我們首先來看目前正在處理的應用程式視窗的髒區域dirty是如何計算的。我們分兩種情況來考慮。
首先考慮目前正在處理的應用程式視窗上一次的狀态還未來得及處理的情況,即它目前的内容是髒的。在這種情況下,LayerBase對象layer的成員變量contentDirty的值就會等于true。這時候我們就需要将該應用程式視窗的上一次可見區域,以及目前的可見區域合并起來,形成一個大的髒區域,這樣就可以将兩次渲染操作合并成一次來執行。目前正在處理的應用程式視窗的上一次可見區域儲存在LayerBase對象layer的成員變量visibleRegionScreen中,而它前的可見區域儲存在變量visibleRegion中。将這兩者相或一下,就可以得到目前正在處理的應用程式視窗的髒區域dirty。
接着考慮目前正在處理的應用程式視窗上一次的狀态已經處理了的情況,即它目前的内容不是髒的,這意味着它所要顯示的内容沒有發生變化。在這種情況下,就不需要重新渲染所有的可見區域。那麼那些區域是需要重新渲染的呢?第一部分區域是之前是被覆寫的,現在不被覆寫了,第二部分是由于視窗大小變化而引發的新增不被覆寫區域。接下來,我們就來看看這兩部分區域是如何計算的。
将一個應用程式視窗的目前可見區域減去被覆寫區域,就可以它的目前不被覆寫的區域newExposed,按照同樣的方法,我們可以也可以得到它的上一次不被覆寫的區域oldExposed。注意,一個應用程式視窗的上一次可見區域和被覆寫區域分别儲存與它相對應的一個LayerBase對象的成員變量visibleRegionScreen和coveredRegionScreen中。這樣,将一個應用程式視窗的目前不被覆寫的區域newExposed減去它的上一次不被覆寫的區域oldExposed,就可以得到新增的不被覆寫區域,即可以得到第二部分需要重新渲染的區域。另一方面,将一個應用程式視窗的目前可見區域visibleRegion與它的上一次被覆寫區域oldCoveredRegion相交,就可以得到之前是被覆寫的而現在不被覆寫了的區域,即可以得到第一部分需要重新渲染的區域。将第一部分和第二部分需要重新渲染的區域組合起來,就可以得到目前正在處理的應用程式視窗的髒區域dirty。
得到了目前正在處理的應用程式視窗的髒區域dirty,接下來的事情就好辦了。首先從該髒區域dirty減去上層的完全不透明區域,因為後者的渲染不需要目前應用程式視窗來參與,接着最将得到的新的髒區域dirty累加到輸出參數dirtyRegion中去,這樣就可以得到目前為止,SurfaceFlinger服務需要渲染的髒區域。
我們接着往下閱讀代碼:
// Update aboveOpaqueLayers for next (lower) layer
aboveOpaqueLayers.orSelf(opaqueRegion);
// Store the visible region is screen space
layer->setVisibleRegion(visibleRegion);
layer->setCoveredRegion(coveredRegion);
// If a secure layer is partially visible, lock-down the screen!
if (layer->isSecure() && !visibleRegion.isEmpty()) {
secureFrameBuffer = true;
}
}
這段代碼是前面的while循環的幾行結束代碼,主要用來做三件事情。
第一件事情是計算到目前為止所得到的上層應用程式視窗的完全不透明區域,這是通過組合目前正在處理的應用程式視窗的完全不透明區域與位于它上面的的所有應用程式視窗的完全不透明區域aboveOpaqueLayers來得到的,并且最終結果儲存在變量aboveOpaqueLayers中。
第二件事情是調用LayerBase對象layer的成員函數setVisibleRegion和setCoveredRegion來儲存目前正在處理的應用程式視窗的可見區域和被覆寫區域。
第三件事情是判斷目前正在處理的應用程式視窗的内容是否受安全保護的。如果是的話,并且它的可見區域不為空,那麼就需要将變量secureFrameBuffer的值設定為true,以表示目前SurfaceFlinger服務不可以執行截屏功能。
我們接着往下閱讀最後一段代碼:
// invalidate the areas where a layer was removed
dirtyRegion.orSelf(mDirtyRegionRemovedLayer);
mDirtyRegionRemovedLayer.clear();
mSecureFrameBuffer = secureFrameBuffer;
opaqueRegion = aboveOpaqueLayers;
}
由于前面我們得到SurfaceFlinger服務需要重新渲染的髒區域dirtyRegion隻考慮了那些新增或者本來已經存在的應用程式視窗的,而沒有考慮那些已經被删除了的應用程式視窗。那些已經被删除了的應用程式視窗所占據的區域儲存在SurfaceFlinger類的成員變量mDirtyRegionRemovedLayer中,是以,将它從輸出參數dirtyRegion減去之後得到的才是SurfaceFlinger服務最終需要重新渲染的髒區域。
此外,函數還将變量secureFrameBuffer的值儲存在urfaceFlinger類的成員變量mSecureFrameBuffer中,以便SurfaceFlinger服務可以知道自己可以執行截屏功能。
最後,函數還将前面所有的應用程式視窗組成的完全不透明區域aboveOpaqueLayers儲存在輸出參數opaqueRegion,以便可以傳回給調用者使用。
這一步執行完成之後,傳回到前面的Step 1中,即SurfaceFlinger類的成員函數handlePageFlip中,接下來就會繼續調用SurfaceFlinger類的另外一個成員函數unlockPageFlip來讓各個應用程式視窗執行一些清理工作。
接下來,我們就繼續分析SurfaceFlinger類的成員函數unlockPageFlip的實作。
Step 6. SurfaceFlinger.unlockPageFlip
void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
{
const GraphicPlane& plane(graphicPlane(0));
const Transform& planeTransform(plane.transform());
size_t count = currentLayers.size();
sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
const sp<LayerBase>& layer(layers[i]);
layer->unlockPageFlip(planeTransform, mDirtyRegion);
}
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
前面提到,我們假設在SurfaceFlinger服務這一側,每一個應用程式視窗都是使用一個Layer對象來描述,這些Layer對象都是從LayerBase繼承下來的,是以它們可以儲存在一個類型為LayerBase的向量中。
從前面的調用過程可以知道,參數currentLayers裡面儲存的一系列LayerBase對象正是用來描述系統目前的各個應用程式視窗的,SurfaceFlinger類的成員函數unlockPageFlip依次調用這些LayerBase對象的成員函數unlockPageFlip來讓它們有機會執行一些清理工作。由于我們假設這些LayerBase對象的實際類型為Layer,是以,前面調用的實際上就是Layer類的成員函數unlockPageFlip。接下來,我們就繼續分析Layer類的成員函數unlockPageFlip的實作。
Step 7. Layer.unlockPageFlip
void Layer::unlockPageFlip(
const Transform& planeTransform, Region& outDirtyRegion)
{
Region dirtyRegion(mPostedDirtyRegion);
if (!dirtyRegion.isEmpty()) {
mPostedDirtyRegion.clear();
// The dirty region is given in the layer's coordinate space
// transform the dirty region by the surface's transformation
// and the global transformation.
const Layer::State& s(drawingState());
const Transform tr(planeTransform * s.transform);
dirtyRegion = tr.transform(dirtyRegion);
// At this point, the dirty region is in screen space.
// Make sure it's constrained by the visible region (which
// is in screen space as well).
dirtyRegion.andSelf(visibleRegionScreen);
outDirtyRegion.orSelf(dirtyRegion);
}
if (visibleRegionScreen.isEmpty()) {
// an invisible layer should not hold a freeze-lock
// (because it may never be updated and therefore never release it)
mFreezeLock.clear();
}
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/Layer.cpp中。
參數planeTransform用來描述系統顯示屏的旋轉方向,它是一個變換矩陣,而另外一個參數outDirtyRegion是一個輸出參數,指向了SurfaceFlinger類的成員函數mDirtyRegion,即它描述的是SurfaceFlinger服務需要渲染的髒區域。
Layer類的成員變量mPostedDirtyRegion用來描述目前正在處理的應用程式視窗的髒區域,即可能需要渲染的區域。從前面的Step 5可以知道,Layer類的成員變量visibleRegionScreen是從父類LayerBase繼續下來的,用來描述目前正在處理的應用程式視窗的可見區域。這兩個區域相關的那部分區域才是目前正在處理的應用程式視窗需要重新渲染的區域,因為一個區域如果是髒的,但是它同時也是不可見的,那麼我們是不需要去渲染的。注意,Layer類的成員變量visibleRegionScreen的所描述的區域是相對于顯示屏的,而Layer類的成員變量mPostedDirtyRegion所描述的區域是相對于目前正在處理的應用程式視窗的,是以,需要将它轉換成相對于顯示屏的區域之後,才能将它與前者執行一個相交操作,進而得到目前正在處理的應用程式視窗真正需要渲染的髒區域dirtyRegion。有了這個髒區域dirtyRegion之後,就可以将它組合到輸出參數outDirtyRegion中去,以便可以得到SurfaceFlinger服務需要渲染的總髒區域。
當Layer類的成員變量visibleRegionScreen所描述的區域為空時,就說明目前正在處理的應用程式視窗是不需要參與本次的渲染操作的,是以,這時候就要判斷目前正在處理的應用程式視窗是否擁有一個用來當機系統顯示屏的鎖。如果有的話,那麼就要将這個鎖釋放掉,避免阻止SurfaceFlinger服務渲染其它應用程式的UI。釋放目前正在處理的應用程式視窗所擁有的一個用來當機系統顯示屏的鎖是通過調用Layer類的成員變量mFreezeLock所描述的一個FreezeLock對象的成員函數clear來完成的。
至此,我們就分析完成各個應用程式視窗是如何設定它們目前所要渲染的圖形緩沖區以及計算它們的可見區域的了,接下來我們繼續分析當目前需要渲染的應用程式視窗隻有一個,并且SurfaceFlinger服務在編譯的時候指定了USE_COMPOSITION_BYPASS宏時,這個唯一的應用程式視窗是如何被渲染的,即分析SurfaceFlinger類的成員函數handleBypassLayer的實作。
3. handleBypassLayer
SurfaceFlinger類的成員函數handleBypassLayer跳過應用程式視窗的圖形緩沖區合成階段,它直接将系統中唯一的一個應用程式視窗的圖形緩沖區渲染到硬體幀緩沖區中去,它的執行過程如圖9所示:
圖9 SurfaceFlinger服務直接渲染應用程式視窗的圖形緩沖區的過程
這個過程可以分為3個步驟,接下來我們就詳細分析每一個步驟。
Step 1. SurfaceFlinger.handleBypassLayer
bool SurfaceFlinger::handleBypassLayer()
{
sp<Layer> bypassLayer(mBypassLayer.promote());
if (bypassLayer != 0) {
sp<GraphicBuffer> buffer(bypassLayer->getBypassBuffer());
if (buffer!=0 && (buffer->usage & GRALLOC_USAGE_HW_FB)) {
const DisplayHardware& hw(graphicPlane(0).displayHardware());
hw.postBypassBuffer(buffer->handle);
return true;
}
}
return false;
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
從前面第2部分的内容的Step 1可以知道,SurfaceFlinger類的成員變量mBypassLayer指向了系統中唯一需要渲染的應用程式視窗,當它的值不等于0的時候,函數接下來就會檢查它目前需要渲染的圖形緩沖區buffer是否是直接在硬體幀緩沖區中配置設定的,即圖形緩沖區buffer的成員變量usage的GRALLOC_USAGE_HW_FB位是否等于1。如果是直接在硬體幀緩沖區中配置設定的話,那麼函數最後就會先獲得用來描述系統主顯示屏的一個DisplayHardware對象hw的成員函數postBypassBuffer來直接渲染圖形緩沖區buffer。
在前面第2部分的内容的Step 1中提到,用來描述系統中唯一需要渲染的應用程式視窗的一個Layer對象的成員變量mBypassState的值會被設定為true。這樣做的目的是為了讓SurfaceFlinger服務以後在為這個應用程式視窗配置設定圖形緩沖區時,都直接在硬體幀緩沖區中配置設定,這一點可以參考前面Android應用程式請求SurfaceFlinger服務渲染Surface的過程分析一文,這樣SurfaceFlinger服務以後就可以直接将該圖形緩沖區渲染到硬體幀緩沖區上。與那些在匿名共享記憶體中配置設定的圖形緩沖區的渲染過程相比,直接在硬體幀緩沖區中配置設定的圖形緩沖區的渲染過程要高效得多,因從就可以提高系統UI的顯示性能。
接下來,我們就繼續分析DisplayHardware類的成員函數postBypassBuffer的實作,以便可以了解系統中唯一需要渲染的應用程式視窗的圖形緩沖區是如何被直接渲染到硬體幀緩沖區中去的。
Step 2. DisplayHardware.postBypassBuffer
status_t DisplayHardware::postBypassBuffer(const native_handle_t* handle) const
{
framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice();
return fbDev->post(fbDev, handle);
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。
從前面Android系統Surface機制的SurfaceFlinger服務對幀緩沖區(Frame Buffer)的管理分析一文可以知道,DisplayHardware類的成員變量mNativeWindow指向的是一個FramebufferNativeWindow對象,調用這個FramebufferNativeWindow對象的成員函數getDevice就可以獲得它裡面的一個framebuffer_device_t對象fbDev。這個framebuffer_device_t對象fbDev是在HAL層的子產品Gralloc中打開的,通過調用它的成員函數post就可以将指定的圖形緩沖區渲染到硬體幀緩沖區中去。
接下來,我們就繼續分析framebuffer_device_t類的成員函數post的實作,以便可以了解SurfaceFlinger是如何将一個圖形緩沖區渲染到硬體幀緩沖區中去的。
Step 3. framebuffer_device_t.post
從前面Android幀緩沖區(Frame Buffer)硬體抽象層(HAL)子產品Gralloc的實作原理分析一文可以知道,framebuffer_device_t類的成員函數post指向了HAL層的Gralloc子產品的函數fb_post中,後者定義在檔案hardware/libhardware/modules/gralloc/framebuffer.cpp。
HAL層的Gralloc子產品的函數fb_post的作用就是将一個指定的圖形緩沖區的内容渲染到硬體幀緩沖區中去。它會分兩種情況來考慮。第一種情況是指定的圖形緩沖區是直接在硬體幀緩沖區中配置設定的,這時候該函數就使用IO控制指令FBIOPUT_VSCREENINFO來直接在硬體幀緩沖區中渲染該圖形緩沖區的内容。第二種情況是指定的圖形緩沖區是直接在匿名共享記憶體中配置設定,這時候該函數就會調用函數memcpy來将圖形緩沖區的内容從匿名共享記憶體拷貝硬體幀緩沖區中去。在我們這個場景中,指定要渲染的圖形緩沖區是直接在硬體幀緩沖區中配置設定的,是以,它最終就會通過IO控制指令FBIOPUT_VSCREENINFO渲染到硬體幀緩沖區中去。這個過程可以參考Android幀緩沖區(Frame Buffer)硬體抽象層(HAL)子產品Gralloc的實作原理分析一文,這裡不再詳述。
至此,我們就分析完成系統中唯一一個需要渲染的應用程式視窗的圖形緩沖區的渲染過程了,接下來我們繼續分析系統中存在多個需要渲染的應用程式視窗時,SurfaceFlinger服務是如何渲染它們的圖形緩沖區的,即分析SurfaceFlinger類的成員函數handleRepaint的實作。
4. handleRepaint
SurfaceFlinger類的成員函數handleRepaint是用來合成系統中各個應用程式視窗的圖形緩沖區的,以便可以将它們的内容一起渲染到硬體幀緩沖區中去,它的執行過程如圖10所示:
圖10 SurfaceFlinger服務合成應用程式視窗的圖形緩沖區的過程
這個過程可以分為5個步驟,接下來我們就詳細分析每一個步驟。
Step 1. SurfaceFlinger.handleRepaint
void SurfaceFlinger::handleRepaint()
{
// compute the invalid region
mInvalidRegion.orSelf(mDirtyRegion);
if (mInvalidRegion.isEmpty()) {
// nothing to do
return;
}
if (UNLIKELY(mDebugRegion)) {
debugFlashRegions();
}
// set the frame buffer
const DisplayHardware& hw(graphicPlane(0).displayHardware());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
uint32_t flags = hw.getFlags();
if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
(flags & DisplayHardware::BUFFER_PRESERVED))
{
// we can redraw only what's dirty, but since SWAP_RECTANGLE only
// takes a rectangle, we must make sure to update that whole
// rectangle in that case
if (flags & DisplayHardware::SWAP_RECTANGLE) {
// TODO: we really should be able to pass a region to
// SWAP_RECTANGLE so that we don't have to redraw all this.
mDirtyRegion.set(mInvalidRegion.bounds());
} else {
// in the BUFFER_PRESERVED case, obviously, we can update only
// what's needed and nothing more.
// NOTE: this is NOT a common case, as preserving the backbuffer
// is costly and usually involves copying the whole update back.
}
} else {
if (flags & DisplayHardware::PARTIAL_UPDATES) {
// We need to redraw the rectangle that will be updated
// (pushed to the framebuffer).
// This is needed because PARTIAL_UPDATES only takes one
// rectangle instead of a region (see DisplayHardware::flip())
mDirtyRegion.set(mInvalidRegion.bounds());
} else {
// we need to redraw everything (the whole screen)
mDirtyRegion.set(hw.bounds());
mInvalidRegion = mDirtyRegion;
}
}
// compose all surfaces
composeSurfaces(mDirtyRegion);
// clear the dirty regions
mDirtyRegion.clear();
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
SurfaceFlinger類的成員變量mInvalidRegion用來描述SurfaceFlinger服務需要渲染到硬體幀緩沖區中去的髒區域的。前面提到,SurfaceFlinger類的成員變量mDirtyRegion也是用來描述SurfaceFlinger服務需要渲染的髒區域的,不過,它的作用是用來合成系統中各個應用程式視窗的圖形緩沖區的,也就是說,當系統中各個應用程式視窗的圖形緩沖區被合成之後,這個成員變量所描述的區域就會被清空,而成員變量mInvalidRegion會一直等到它的内容被渲染到硬體幀緩沖區中去之後,才會被清空。這樣就可能會出現這種情況:上一次合成的圖形緩沖區還未被渲染到硬體幀緩沖區中去,SurfaceFlinger服務又會執行新一輪的系統中各個應用程式視窗的圖形緩沖區的合并操作。在這種情況下,SurfaceFlinger服務就需要将前面兩次需要渲染到硬體幀緩沖區中去的區域合并在一起,以便可以正确地反映被重新整理的UI。是以,函數在開頭的地方,就會先SurfaceFlinger類的成員變量mDirtyRegion所描述的區域組合到成員變量mInvalidRegion所描述的區域中去。
函數接下來調用用來描述系統主顯示屏的一個DisplayHardware對象hw的成員函數getFlags來獲得系統所支援的渲染方式,并且儲存在一個uint32_t變量flags中。接下來,我們就分四種情況來讨論系統所支援的渲染方式:
1. 變量flags的DisplayHardware::PARTIAL_UPDATES位等于1。在這種情況下,系統在硬體上直接支援部分區域更新功能,不過,這個部分被更新的區域必須要是一個矩形區域。
2. 變量flags的DisplayHardware::SWAP_RECTANGLE位等于1。在這種情況下,系統在軟體上支援部分區域更新功能,同樣,這個部分被更新的區域必須要是一個矩形區域。
3. 變量flags的DisplayHardware::BUFFER_PRESERVED位等于1。在這種情況下,系統支援不規則的部分區域更新功能。所謂不規則,就是這個被更新的區域不必是一個矩形區域。
4. 變量flags的值等于0。在這種情況下,系統不支援部分更新區域,這時候就需要更新整個螢幕的内容。
在第1種和第2種情況中,由于被更新的區域都必須是一個矩形區域,是以,函數就需要将SurfaceFlinger類的成員變量mDirtyRegion所描述的一個區域設定為包含了所有髒區域的一個最小矩形區域。在第4種情況中,由于需要更新的是整個螢幕的内容,是以,函數就需要将SurfaceFlinger類的成員變量mDirtyRegion所描述的一個區域設定為等于螢幕大小的一個矩形區域。在第3種情況中,就不需要調用被更新的區域。對于第1種、第2種和第3種渲染方式的更多描述,可以參考前面Android系統Surface機制的SurfaceFlinger服務對幀緩沖區(Frame Buffer)的管理分析一文。
得到了最終要合成的髒區域mDirtyRegion之後,SurfaceFlinger類的成員函數handleRepaint最後就調用另外一個成員函數composeSurfaces來合成系統中各個應用程式視窗的圖形緩沖區,并且在合成操作完成之後,将髒區域mDirtyRegion設定為一個空區域。
接下來,我們就繼續分析SurfaceFlinger類的成員函數composeSurfaces的實作。
Step 2. SurfaceFlinger.composeSurfaces
void SurfaceFlinger::composeSurfaces(const Region& dirty)
{
if (UNLIKELY(!mWormholeRegion.isEmpty())) {
// should never happen unless the window manager has a bug
// draw something...
drawWormhole();
}
const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(layers[i]);
const Region clip(dirty.intersect(layer->visibleRegionScreen));
if (!clip.isEmpty()) {
layer->draw(clip);
}
}
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
參數dirty用來描述即将要被渲染的髒區域,它是從前面的第1步傳進來的,即為SurfaceFlinger類的成員變量mDirtyRegion所描述的區域。
SurfaceFlinger類的成員變量mVisibleLayersSortedByZ儲存了系統目前所有的可見應用程式視窗,這些應用程式視窗是前面第2部分内容的Step 1中計算得到的,而且都是需要渲染的。 SurfaceFlinger類的成員函數composeSurfaces依次檢查這些應用程式視窗的可見區域是否與即将要渲染的的髒區域有交集。如果有的話,即變量clip所描述的區域不為空,那麼接下來SurfaceFlinger類的成員函數composeSurfaces就會分别調用與這些應用程式視窗對應的一個LayerBase對象的成員函數draw來将它們需要渲染的圖形緩沖區合成到系統的主顯示屏來。
接下來,我們就繼續分析LayerBase類的成員函數draw的實作,以便了解SurfaceFlinger服務合成各個應用程式視窗的圖形緩沖區的過程。
Step 3. LayerBase.draw
void LayerBase::draw(const Region& clip) const
{
// reset GL state
glEnable(GL_SCISSOR_TEST);
onDraw(clip);
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/LayerBase.cpp中。
LayerBase類的成員函數draw的實作很簡單,它隻是調用了另外一個成員函數onDraw來通知各個應用程式視窗重新繪制參數clip所描述的一個區域。LayerBase類的成員函數onDraw是一個虛函數,這是由其子類來重寫的。前面我們假設系統中的所有應用程式視窗都是使用一個Layer對象來描述的,而Layer類是從LayerBase類繼承下來的,并且重寫了其成員函數onDraw。是以,接下來我們就繼續分析Layer類的成員函數onDraw的實作。
Step 4. Layer.onDraw
這個函數定義在檔案frameworks/base/services/surfaceflinger/Layer.cpp中,用來繪制一個應用程式視窗的指定區域,我們分段來閱讀:
void Layer::onDraw(const Region& clip) const
{
Texture tex(mBufferManager.getActiveTexture());
if (tex.name == -1LU) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
// SurfaceView because the WindowManager can't know when the client
// has drawn the first time.
// If there is nothing under us, we paint the screen in black, otherwise
// we just skip this update.
// figure out if there is something below us
Region under;
const SurfaceFlinger::LayerVector& drawingLayers(mFlinger->mDrawingState.layersSortedByZ);
const size_t count = drawingLayers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(drawingLayers[i]);
if (layer.get() == static_cast<LayerBase const*>(this))
break;
under.orSelf(layer->visibleRegionScreen);
}
// if not everything below us is covered, we plug the holes!
Region holes(clip.subtract(under));
if (!holes.isEmpty()) {
clearWithOpenGL(holes, 0, 0, 0, 1);
}
return;
}
這段代碼主要是用來處應用程式視窗的紋理尚未建立好的情況。
在紋理未建立好的情況下,一個應用程式視窗是不應該被渲染的。這時候函數首先将位于目前正在處理的應用程式視窗下面的所有其它應用程式視窗的可見區域組合起來,并且儲存在變量under所描述的區域中。由于這時候目前正在處理的應用程式視窗不會被繪制,是以,如果變量under所描述的區域小于參數clip所描述的區域,即變量holes所描述的區域不為空,那麼SurfaceFlinger服務所要求緩制的區域clip就會留下一個洞。這個洞會被繪制成黑色,這是通過調用函數clearWithOpenGL來實作的。繪制完成之後,函數就可以直接傳回了。
在前面第2部分的Step 4中提到,Layer類的成員變量mBufferManager描述的是一個BufferManager對象,通過調用它的成員函數initEglImage或者loadTexture就可以根據一個應用程式視窗的目前需要渲染的圖形緩沖區來建立出一個紋理對象。這個紋理對象就儲存在BufferManager類的成員變量mFailoverTexture或者另外一個成員變量mBufferData所描述的一個BufferData數組中,取決于系統是否支援在硬體上直接建立紋理對象。這個紋理對象是使用一個Texture對象來描述的,并且可以通過調用Layer類的成員變量mBufferManager描述的是一個BufferManager對象的成員函數getActiveTexture來獲得。如果獲得的Texture對象的名稱name等于-1,那麼就說明目前正在處理的應用程式視窗尚未建立好需要渲染的紋理。
我們繼續往下閱讀代碼:
#ifdef USE_COMPOSITION_BYPASS
sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
if ((buffer != NULL) && (buffer->transform)) {
// Here we have a "bypass" buffer, but we need to composite it
// most likely because it's not fullscreen anymore.
// Since the buffer may have a transformation applied by the client
// we need to inverse this transformation here.
// calculate the inverse of the buffer transform
const uint32_t mask = HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
const uint32_t bufferTransformInverse = buffer->transform ^ mask;
// To accomplish the inverse transform, we use "mBufferTransform"
// which is not used by Layer.cpp
const_cast<Layer*>(this)->mBufferTransform = bufferTransformInverse;
drawWithOpenGL(clip, tex);
// reset to "no transfrom"
const_cast<Layer*>(this)->mBufferTransform = 0;
return;
}
#endif
這段代碼用來檢查目前正在處理的應用程式視窗的圖形緩沖區是否是一個可以跳過合成階段的圖形緩沖區。本來這種圖形緩沖區是可以直接渲染到硬體幀緩沖區中去的,但是由于它不是全屏顯示的,是以就需要與其它應用程式視窗的圖形緩沖區進行合并操作。如果這個圖形緩沖區之前曾經被旋轉過,例如,被水準翻轉或者垂直翻轉過,那麼在對它進行合并之前,還需要将它的旋轉方向恢複回來。
當用來描述一個圖形緩沖區的一個GraphicBuffer對象的成員變量transform的值不等于0時,那麼就說明這個圖形緩沖區是被旋轉過的,這時候函數就會對這個成員變量的值的HAL_TRANSFORM_FLIP_V位或者HAL_TRANSFORM_FLIP_H位進行取反,目的就是為了恢複它之前的旋轉方向。反轉後得到的方向就儲存在Layer類的成員變量mBufferTransform中。Layer類的成員變量mBufferTransform是從父類LayerBase繼承下來的,接下來在調用從父類LayerBase繼承下來的成員函數drawWithOpenGL來繪制目前正在處理的應用程式視窗時,就需要使用到它來設定紋理的旋轉方向。在後面的Step 5中,我們再詳細分析LayerBase類的成員函數drawWithOpenGL的實作。
我們繼續往下閱讀最後一行代碼:
drawWithOpenGL(clip, tex);
}
如果目前正在處理的應用程式視窗的圖形緩沖區沒有被旋轉過,或者這個圖形緩沖區本來就需要進行合并的,那麼Layer類的成員函數onDraw最後就會調用從父類LayerBase繼承下來的成員函數drawWithOpenGL來将這個圖形緩沖區的内容繪制在系統的主顯示屏的指定區域上來。這個圖形緩沖區的内容是使用紋理象tex來描述的,而指定的主顯示屏區域是由參數clip來描述的。
接下來,我們就繼續分析LayerBase類的成員函數drawWithOpenGL的實作,以便可以了解一個應用程式視窗的繪制過程,即它的圖形緩沖區被合成到系統主顯示屏的過程。
Step 5. LayerBase.drawWithOpenGL
這個函數定義在檔案frameworks/base/services/surfaceflinger/LayerBase.cpp中,它通過OpenGL提供的接口來繪制一個應用程式視窗的指定區域,我們分段來閱讀:
void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const uint32_t fbHeight = hw.getHeight();
const State& s(drawingState());
// bind our texture
TextureManager::activateTexture(texture, needsFiltering());
uint32_t width = texture.width;
uint32_t height = texture.height;
GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
if (UNLIKELY(s.alpha < 0xFF)) {
const GLfloat alpha = s.alpha * (1.0f/255.0f);
if (mPremultipliedAlpha) {
glColor4f(alpha, alpha, alpha, alpha);
} else {
glColor4f(1, 1, 1, alpha);
}
glEnable(GL_BLEND);
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
} else {
glColor4f(1, 1, 1, 1);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
if (needsBlending()) {
glEnable(GL_BLEND);
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
}
}
參數clip用來描述要繪制的區域,而參數texture用來描述要繪制的紋理。
這段代碼首先得到主顯示屏的高度fbHeight、要繪制的紋理texture的寬度width和高度height,以及用來描述目前正在處理的應用程式視窗狀态的一個State對象s,接下來就是調用函數再設定紋理texture的繪制模式,即是否需要以混合模式來繪制,這是通過調用函數glEnable(GL_BLEND)來實作的。在需要混合模式來繪制紋理texture的情況下,還需要調用函數glBlendFunc來指定混合函數。
在以下兩種情況下,紋理texture需要以混合模式來繪制:
1. 目前正在處理的應用程式視窗的Alpha通道的值小于0xFF,即State對象s的成員變量alpha的值小于0xFF,這表明該視窗的背景是半透明的。
2. 目前正在處理的應用程式視窗的像素格式是半透明的,這是通過調用LayerBase類的成員函數needsBlending來判斷的。Layer類重寫了父類LayerBase的成員函數needsBlending。當一個Layer對象所描述的應用程式視窗的像素格式是半透明的時候,它就會将它的成員變量mNeedsBlending的值設定為true。這樣,我們就可以調用Layer類的成員函數needsBlending來檢查它的成員變量mNeedsBlending是否為true來判斷一個應用程式視窗的像素格式是否是半透明的。這一點可以參考前面Android應用程式請求SurfaceFlinger服務建立Surface的過程分析一文的Step 6。
我們繼續往下閱讀代碼:
/*
* compute texture coordinates
* here, we handle NPOT, cropping and buffer transformations
*/
GLfloat cl, ct, cr, cb;
if (!mBufferCrop.isEmpty()) {
// source is cropped
const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width;
const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height;
cl = mBufferCrop.left * us;
ct = mBufferCrop.top * vs;
cr = mBufferCrop.right * us;
cb = mBufferCrop.bottom * vs;
} else {
cl = 0;
ct = 0;
cr = (texture.NPOTAdjust ? texture.wScale : 1.0f);
cb = (texture.NPOTAdjust ? texture.hScale : 1.0f);
}
這段代碼用來計算紋理坐标,并且儲存在變量cl、ct、cr和cb中,用來描述紋理texture的四個角的坐标。
從前面Android應用程式請求SurfaceFlinger服務渲染Surface的過程分析一文可以知道,如果Android應用程式設定了一個視窗的紋理坐标,那麼SurfaceFlinger服務就會通過調用LayerBase類的成員函數setBufferCrop來儲存在成員變量mBufferCrop中。另一方面,如果Android應用程式沒有指定一個視窗的紋理坐标,那麼這個視窗的紋理坐标的預設值就使用要繪制的紋理的四個角的坐标來描述。注意,在計算紋理坐标的時候,還要考慮紋理的大小,以及紋理本身所設定的縮放因子,以便可以正确地将紋理繪制在應用程式視窗中。
我們繼續往下閱讀代碼:
/*
* For the buffer transformation, we apply the rotation last.
* Since we're transforming the texture-coordinates, we need
* to apply the inverse of the buffer transformation:
* inverse( FLIP_V -> FLIP_H -> ROT_90 )
* <=> inverse( ROT_90 * FLIP_H * FLIP_V )
* = inverse(FLIP_V) * inverse(FLIP_H) * inverse(ROT_90)
* = FLIP_V * FLIP_H * ROT_270
* <=> ROT_270 -> FLIP_H -> FLIP_V
*
* The rotation is performed first, in the texture coordinate space.
*
*/
struct TexCoords {
GLfloat u;
GLfloat v;
};
enum {
// name of the corners in the texture map
LB = 0, // left-bottom
LT = 1, // left-top
RT = 2, // right-top
RB = 3 // right-bottom
};
// vertices in screen space
int vLT = LB;
int vLB = LT;
int vRB = RT;
int vRT = RB;
// the texture's source is rotated
uint32_t transform = mBufferTransform;
if (transform & HAL_TRANSFORM_ROT_90) {
vLT = RB;
vLB = LB;
vRB = LT;
vRT = RT;
}
if (transform & HAL_TRANSFORM_FLIP_V) {
swap(vLT, vLB);
swap(vRT, vRB);
}
if (transform & HAL_TRANSFORM_FLIP_H) {
swap(vLT, vRT);
swap(vLB, vRB);
}
TexCoords texCoords[4];
texCoords[vLT].u = cl;
texCoords[vLT].v = ct;
texCoords[vLB].u = cl;
texCoords[vLB].v = cb;
texCoords[vRB].u = cr;
texCoords[vRB].v = cb;
texCoords[vRT].u = cr;
texCoords[vRT].v = ct;
這段代碼主要根據目前正在處理的應用程式視窗的旋轉方向來調整前面所計算得到的紋理坐标。
從前面Android應用程式請求SurfaceFlinger服務渲染Surface的過程分析一文可以知道,如果Android應用程式設定了一個視窗的旋轉方向時,那麼SurfaceFlinger服務就會通過調用LayerBase類的成員函數setBufferTransform來儲存在成員變量mBufferTransform中,是以,這段代碼就可以根據這個成員變量的值來相應地調用前面所計算得到的紋理坐标。
我們繼續往下閱讀代碼:
if (needsDithering()) {
glEnable(GL_DITHER);
} else {
glDisable(GL_DITHER);
}
這段代碼用來檢查是否需要以抖動的方式來繪制紋理,如果需要的話,就調用函數glEnable(GL_DITHER)來啟動抖動功能,否則的話,就調用函數glDisable(GL_DITHER)來關閉抖動功能。
從前面Android應用程式請求SurfaceFlinger服務渲染Surface的過程分析一文可以知道,如果一個Android應用程式視窗的像素格式的紅色通道的位數大于系統主顯示屏的像素格式的紅色通道的位數時,SurfaceFlinger服務就會将用來描述該Android應用程式視窗的一個Layer對象的成員變量mNeedsDithering的值設定為true。Layer類重寫了父類LayerBase的成員函數needsDithering,它通過檢查其成員變量mNeedsDithering的值是否等于true來告訴這段代碼用來檢查是否需要以抖動的方式來繪制紋理。
我們繼續往下閱讀最後一段代碼:
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, mVertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
Region::const_iterator it = clip.begin();
Region::const_iterator const end = clip.end();
while (it != end) {
const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
這段代碼首先調用函數glEnableClientState(GL_TEXTURE_COORD_ARRAY)來指定使用頂點數組的方式繪制目前正在處理的應用程式視窗,接着又分别調用函數glVertexPointer和glTexCoordPointer來指定要繪制的頂點數組以及紋理坐标。設定好要繪制的頂點數組以及紋理坐标之後,最後就要設定裁剪區域,以便可以調用函數glDrawArrays來繪制前面指定的頂點數組以及紋理。
前面提到,參數clip用來描述要繪制的區域,而要繪制的區域即為目前需要指定的裁剪區域。由于參數clip所描述的區域是可以由一系列的矩形區域來組成的,是以,這段代碼就通過一個while循環來周遊裡面的每一個矩形區域,并且調用函數glScissor來分别将它們設定為裁剪區域。
至此,我們就分析完成系統中各個應用程式視窗的圖形緩沖區的合成過程了,這個過程其實就是分别根據各個應用程式視窗的圖形緩沖區來建立一個紋理對象,并且結合各個應用程式視窗的UI中繼資料來将該紋理對象繪制在系統主顯示屏的指定區域中,而這個指定的區域即為系統的髒區域。
接下來,我們就繼續分析系統中各個應用程式視窗将自己的圖形緩沖區合成到系統的主顯示屏之後,SurfaceFlinger服務是如何将系統的主顯示屏渲染到硬體幀緩沖區中去的,即分析SurfaceFlinger類的成員函數postFramebuffer的實作。
5. postFramebuffer
SurfaceFlinger類的成員函數postFramebuffer用來将系統的主顯示屏的内容渲染到硬體幀緩沖區中去,它的執行過程如圖11所示:
圖11 SurfaceFlinger服務渲染系統主顯示屏的内容到硬體幀緩沖區的過程
這個過程可以劃分為4步驟,接下來我們就詳細分析每一個步驟。
Step 1. SurfaceFlinger.postFramebuffer
void SurfaceFlinger::postFramebuffer()
{
if (!mInvalidRegion.isEmpty()) {
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const nsecs_t now = systemTime();
mDebugInSwapBuffers = now;
hw.flip(mInvalidRegion);
mLastSwapBufferTime = systemTime() - now;
mDebugInSwapBuffers = 0;
mInvalidRegion.clear();
}
}
這個函數定義在檔案frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中。
在前面第4部分内容的Step 1中提到,SurfaceFlinger類的成員變量mInvalidRegion用來描述系統主顯示屏的髒區域,即SurfaceFlinger服務目前需要渲染的區域。函數首先得到用來描述系統主顯示屏的一個DisplayHardware對象hw,接着再調用這個DisplayHardware對象hw的成員函數flip來渲染這個髒區域。
接下來,我們就繼續分析DisplayHardware類的成員函數flip的實作。
Step 2. DisplayHardware.flip
這個函數定義在檔案frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中,在前面Android系統Surface機制的SurfaceFlinger服務對幀緩沖區(Frame Buffer)的管理分析一文中,我們已經分析過它的實作了。這個函數會檢查系統是否支援部分更新功能。如果支援的話,那麼就先設定要更新的區域,否則的話,就直接調用函數eglSwapBuffers來将前面已經合成好的了圖形緩沖區渲染到硬體幀緩沖區去。
從前面Android系統Surface機制的SurfaceFlinger服務對幀緩沖區(Frame Buffer)的管理分析一文可以知道,調用函數eglSwapBuffers在渲染圖形緩沖區的時候,會導緻FramebufferNativeWindow類的成員函數queueBuffer被調用,後者會通過HAL層的Gralloc子產品來執行渲染硬體幀緩沖區的操作。
Step 3. FramebufferNativeWindow.queueBuffer
這個函數定義在檔案frameworks/base/libs/ui/FramebufferNativeWindow.cpp中,同樣,在前面Android系統Surface機制的SurfaceFlinger服務對幀緩沖區(Frame Buffer)的管理分析一文中,我們已經分析過它的實作了。這個函數主要就是通過HAL層的Gralloc子產品中的framebuffer_device_t裝置的成員函數post來執行渲染硬體幀緩沖區的操作。
Step 4. framebuffer_device_t.post
這個函數指向定義在HAL層的Gralloc子產品中的函數fb_post,後者定義在檔案hardware/libhardware/modules/gralloc/framebuffer.cpp,在前面Android幀緩沖區(Frame Buffer)硬體抽象層(HAL)子產品Gralloc的實作原理分析一文中,我們已經分析過這個函數的實作了。由于要渲染的圖形緩沖區是用于渲染系統主顯示屏的,是以,它是直接在硬體幀緩沖區上配置設定的,這時候函數fb_post就會通過IO控制指令FBIOPUT_VSCREENINFO來通知位核心空間的fb驅動來将系統主顯示屏的UI繪制出來。
至此,SurfaceFlinger服務渲染系統主顯示屏的内容到硬體幀緩沖區的過程就分析完成了,整個SurfaceFlinger服務渲染應用程式UI的過程也分析完成了。
這樣,我們就通過Android應用程式與SurfaceFlinger服務的關系概述和學習計劃和Android系統Surface機制的SurfaceFlinger服務簡要介紹和學習計劃這兩個系列的文章系統地分析了Android系統的SurfaceFlinger服務的實作,為後面我們後面進一步分析Android系統的UI架構打下堅實的基礎!
老羅的新浪微網誌:http://weibo.com/shengyangluo,歡迎關注!