轉載,代碼稍微有點過時。
Android系統Surface機制的SurfaceFlinger服務渲染應用程序UI的過程分析
在前面的一系列文章中,我們學習了Android應用程序與SurfaceFlinger服務的關係,以及SurfaceFlinger服務的啓動過程、初始化硬件幀緩衝區的過程、線程模型。SurfaceFlinger服務所做的一切都是爲了給Android應用程序提服務的,即爲Android應用程序渲染它們的UI。在本文中,我們就詳細分析SurfaceFlinger服務渲染Android應用程序UI的過程。
從前面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架構打下堅實的基礎!