Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析
文章轉載至CSDN社區羅升陽的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/7932268
在前面一篇文章中,我們分析了Android應用程序請求SurfaceFlinger服務創建Surface的過程。有了Surface之后,Android應用程序就可以在上面繪制自己的UI了,接著再請求SurfaceFlinger服務將這個已經繪制好了UI的Surface渲染到設備顯示屏上去。在本文中,我們就將詳細分析Android應用程序請求SurfaceFlinger服務渲染Surface的過程。
?? ? ? ?Android應用程序在請求SurfaceFlinger服務渲染一個Surface之前,首先要將該Surface作為當前活動的繪圖上下文,以便可以使用OpengGL庫或者其它庫的API來在上面繪制UI,我們以Android系統的開機動畫應用程序bootanim為例,來說明這個問題。
?? ? ? ?從前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文可以知道,Android系統的開機動畫應用程序bootanim是在BootAnimation類的成員函數readyToRun中請求 SurfaceFlinger服務創建Surface的。這個Surface創建完成之后,就會被設置為當前活動的繪圖上下文,如下所示:
[cpp] view plaincopy?? ? ? BootAnimation類的成員函數readyToRun首先調用eglGetDisplay和eglInitialize函數來獲得和初始化 OpengGL庫的默認顯示屏,接著再調用EGLUtils::selectConfigForNativeWindow函數來獲得前面所創建的一個 Surface(由sp<Surface>指針s來描述)的配置信息。有了這些信息之后,接下來就分別調用 eglCreateWindowSurface和eglCreateContext函數來創建一個適用于OpenGL庫使用的繪圖表面surface以及 繪圖上下文context,最后就可以調用eglMakeCurrent函數來將繪圖表面surface和繪圖上下文context設置為當前活動的繪圖 表面和繪圖上下文,這就相當于是將前面請求SurfaceFlinger服務創建的一個Surface設置為當前活動的緩圖上下文了。
?? ? ? 完成了上述操作之后,Android系統的開機動畫應用程序bootanim就可以繼續使用OpengGL庫的其它API來在當前活動的Surface上繪制UI了,不過,通過前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文的學習,我們知道,此時SurfaceFlinger服務為Android應用程序創建的Surface只有UI元數據緩沖區,而沒有UI數據緩沖區, 即還沒有圖形緩沖區,換句來說,就是還沒有可以用來繪制UI的載體。那么,這些用來繪制UI的圖形緩沖區是什么時候創建的呢?
?? ? ? 從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃一文可以知道,每一個Surface都有一個對應的UI元數據緩沖區堆棧,這個UI元數據緩沖區堆棧是使用一個SharedBufferStack來描述的,如圖1所示。
?
圖1 SharedBufferStack的結構示意圖
?
?? ? ? 從圖1就可以看出,每一個UI元數據緩沖區都可能對應有一個UI數據緩沖區,這個UI數據緩沖區又可以稱為圖形緩沖區,它使用一個 GraphicBuffer對象來描述。注意,一個UI元數據緩沖區只有第一次被使用時,Android應用程序才會為它創建一個圖形緩沖區,因此,我們 才說每一個UI元數據緩沖區都可能對應有一個UI數據緩沖區。例如,在圖1中,目前只使到了編號為1和2的UI元數據緩沖區,因此,只有它們才有對應的圖 形緩沖區,而編號為3、4和5的UI元數據緩沖區沒有。
?? ? ? Android應用程序渲染一個Surface的過程大致如下所示:
?? ? ? 1. 從UI元數據緩沖區堆棧中得到一個空閑的UI元數據緩沖區;
?? ? ? 2. 請求SurfaceFlinger服務為這個空閑的UI元數據緩沖區分配一個圖形緩沖區;
?? ? ? 3. 在圖形緩沖區上面繪制好UI之后,即填充好UI數據之后,就將前面得到的空閑UI元數據緩沖區添加到UI元數據緩沖區堆棧中的待渲染隊列中去;
?? ? ? 4. 請求SurfaceFlinger服務渲染前面已經準備好了圖形緩沖區的Surface;
?? ? ? 5. SurfaceFlinger服務從即將要渲染的Surface的UI元數據緩沖區堆棧的待渲染隊列中找到待渲染的UI元數據緩沖區;
?? ? ? 6.?SurfaceFlinger服務得到了待渲染的UI元數據緩沖區之后,接著再找到在前面第2步為它所分配的圖形緩沖區,最后就可以將這個圖形緩沖區渲染到設備顯示屏上去。
?? ? ? 這個過程的第1步、第3步和第5步涉到UI元數據緩沖區堆棧的一些出入棧操作,為了方便后面描述Android應用程序請求SurfaceFlinger服務渲染Surface的過程,我們首先介紹一下UI元數據緩沖區堆棧的一些出入棧操作。
?? ? ? 在前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文中,我們分析了用來描述UI元數據緩沖區堆棧的SharedBufferServer和SharedBufferClient類的父類 SharedBufferBase,它有一個成員函數waitForCondition,用來等待一個條件得到滿足,它定義在文件 frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所 示:
[cpp] view plaincopy?? ? ?SharedBufferBase類的成員函數waitForCondition只有一個參數condition,它的類型為 ConditionBase,用來描述一個需要等待滿足的條件。ConditionBase類是一個抽象類,我們需要以它來為父類,來實現一個自定義的條 件,并且重寫操作符號()和成員函數name。接下來,我們分析SharedBufferBase類的成員函數waitForCondition的實現, 接著再分析ConditionBase類的一個子類的實現。
?? ? ?SharedBufferBase類的成員函數waitForCondition實現在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
[cpp] view plaincopy?? ? ?SharedBufferBase類的成員變量mSharedStack指向了一個SharedBufferStack對象,即一個UI元數據緩沖區堆 棧,另外一個成員變量mSharedClient指向了當前應用程序進程的一個SharedClient單例。
?? ? ?SharedClient類有一個類型為Condition的成員變量cv,用來描述一個條件變量,同時,SharedClient類還有一個類型為 Mutex的成員變量lock,用來描述一個互斥鎖。通過調用一個Condition對象的成員函數waitRelative,就可以在指定的時間內等待 一個互斥鎖變為可用。
?? ? ?SharedBufferBase類的成員函數waitForCondition中的while循環的作用是循環等待一個UI元數據緩沖區堆棧滿足某一 個條件,這個條件是通過參數condition來描述的。當調用參數condition所描述的一個CondtitionBase對象的重載操作符號() 的返回值等于true的時候,就表示所要等待的條件得到滿足了,這時候函數就會停止執行中間的while循環語句。另一方面,當調用參數 condition所描述的一個CondtitionBase對象的重載操作符號()的返回值等于flase的時候,就表示所要等待的條件還沒有得到滿 足,這時候函數就會繼續執中間的while循環,直到所要等待的條件得到滿足為止。等待的操作是通過調用下面這個語句來完成的:
[cpp] view plaincopy?? ? ?即調用當前應用程序進程的SharedClient單例client的成員變量cv所描述的一個條件變量的成員函數waitRelative來完成,并 且指定要等待的互斥鎖為當前應用程序進程的SharedClient單例client的成員變量lock所描述的一個互斥鎖,以及指定等待的時間為 TIMEOUT,即1秒。如果在1秒內,當前應用程序進程的SharedClient單例client的成員變量lock所描述的一個互斥鎖還是不可用, 那么上述等待操作就會超時,然后導致重新執行外層的while循環,否則的話,等待操作就完成了。
?? ? 在SharedBufferClient類中,定義了一個ConditionBase子類DequeueCondition,用來描述一個UI元數據緩沖 區堆棧是否有空閑的緩沖區可以出棧,它定義在文件frameworks/base/include/private/surfaceflinger /SharedBufferStack.h中,如下所示:
[cpp] view plaincopy?? ? ??一個UI元數據緩沖區堆棧是否有空閑的緩沖區可以出棧是由DequeueCondition類的重載操作符號()來決定的,它實現在文件 frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所 示:
[cpp] view plaincopy?? ? ??DequeueCondition類的成員變量stack是從父類ConditionBase繼承下來的,它指向了一個SharedBufferStack對象,即用來描述一個UI元數據緩沖區堆棧。從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃一 文可以知道,當一個SharedBufferStack對象的成員變量available的值大于0的時候,就說明它所描述的UI元數據緩沖區堆棧有空閑 的緩沖區可以使用,因此,這時候DequeueCondition類的重載操作符號()的返回值就等于true,表示一個UI元數據緩沖區堆棧有空閑的緩 沖區可以出棧。
?? ? ??SharedBufferBase類還有一個成員函數updateCondition,用來操作一個UI元數據緩沖區堆棧,例如,執行一個UI元數據 緩沖區的出入棧操作。這個成員函數定義和實現在文件rameworks/base/include/private/surfaceflinger /SharedBufferStack.h中,如下所示:
[cpp] view plaincopy?? ? ? ?SharedBufferBase類的成員函數updateCondition是一個模板函數,它過調用參數T的重載操作符號()來實現一個具體的UI元數據緩沖區堆棧操作。這個參數T必須要從基類UpdateBase繼承下來,并且重載操作符號()。
?? ? ? ?SharedBufferBase類的成員函數updateCondition執行完成一個UI元數據緩沖區堆棧操作之后,還會調用當前應用進程的 SharedClient單例client的成員變量cv所描述的一個條件變量的成員函數broadcast,用來喚醒那些在當前應用進程的 SharedClient單例client的成員變量lock所描述的一個互斥鎖上等待的其它線程,以便它們可以繼續執行自己的操作,這 樣,SharedBufferBase類的成員函數updateCondition就可以和前面介紹的成員函數waitCondition對應起來。
?? ? ? 接下來,我們就分別分析UpdateBase的三個子類QueueUpdate、DequeueUpdate和RetireUpdate。 QueueUpdate和DequeueUpdate兩個子類是Android應用程序這一側使用的,前者用來向一個UI元數據緩沖區堆棧的待渲染隊列增 加一個緩沖區,而后者用來從一個UI元數據緩沖區堆棧出棧一個空閑的緩沖區。RetireUpdate類是在SurfaceFlinger服務這一側使用 的,用來從一個UI元數據緩沖區堆棧的待渲染隊列出棧一個緩沖區,以便可以將與它所對應的圖形緩沖區渲染到設備顯示屏去。
?? ? ??QueueUpdate類定義在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
[cpp] view plaincopy?? ? ? ?它的重載操作符號()實現在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
[cpp] view plaincopy?? ? ? ?QueueUpdate類的成員變量stack是從父類UpdateBase繼承下來的,它指向了一個SharedBufferStack對象,用來描 述當前要操作的UI元數據緩沖區堆棧。從前面的圖1可以知道,當我們將一個SharedBufferStack對象的成員變量queued的值增加1的時 候,就表示這個SharedBufferStack對象所描述的UI元數據緩沖區堆棧的待渲染隊列的大小增加了1。不過,在執行這個操作之前,我們還需要 將用來這個待渲染隊列頭queue_head往前移動一個位置。后面在分析Surface的渲染過程時,我們再詳細分析。
?? ? ? ?DequeueUpdate類定義在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
[cpp] view plaincopy?? ? ? ?它的重載操作符號()實現在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
[cpp] view plaincopy?? ? ? DequeueUpdate類的成員變量stack是從父類UpdateBase繼承下來的,它指向了一個SharedBufferStack對象,用來 描述當前要操作的UI元數據緩沖區堆棧。從前面的圖1可以知道,當我們將一個SharedBufferStack對象的成員變量available的值減 少1的時候,就表示這個SharedBufferStack對象所描述的UI元數據緩沖區堆棧的空閑緩沖區的大小就減少了1。不過,在執行這個操作之前, 我們還需要將用來這個UI元數據緩沖區堆棧尾tail往前移動一個位置。后面在分析Surface的渲染過程時,我們再詳細分析。
?? ? ? RetireUpdate類定義在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
[cpp] view plaincopy?? ? ? ?RetireUpdate類的成員變量numBuffers用來描述一個UI元數據緩沖區堆棧的大小,它的重載操作符號()實現在文件 frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所 示:
[cpp] view plaincopy?? ? ? 在前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃一 文中提到,在圖1所描述的UI元數據緩沖區堆棧中,位于(head, queue_head]里面的緩沖區組成了一個待渲染隊列,而SurfaceFlinger服務就是按照head到queue_head的順序來渲染這個 隊列中的緩沖區的。理解了這一點之后,RetireUpdate類的重載操作符號()的實現就好理解了。
?? ? ? 首先,函數使用一個do...while循環來將queued的值減少1,即將待渲染隊列的大小減少1。當然,如果這個待渲染隊列的大小本來就等于0,那 么函數就什么也不做就返回了。接著,函數將待渲染隊列的頭部head向前移一個位置。移動后的得到的位置所對應的緩沖區就是接下來要渲染的,因此,函數最 后要將它返回給調用者。函數在將要渲染的緩沖區的位置返回給調用者之前,還會將當前正在操作的UI元數據緩沖區的空閑緩沖區的個數available增加 1。
?? ? ? 至此,DequeueCondition、QueueUpdate、DequeueUpdate和RetireUpdate這四個輔助類就介紹完成了,接 下來,我們就可以繼續分析Android應用程序請求SurfaceFlinger服務渲染Surface的過程了。在分析的過程中,我們還會繼續看到這 四個輔助類的使用方法。
?? ? ? 在前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文的Step 16中,我們將在Android應用程序這一側所創建的一個Surface的父類ANativeWindow的OpenGL回調函數 dequeueBuffer和queueBuffer分別設置為Surface類的靜態成員函數dequeueBuffer和queueBuffer。 OpenGL在繪圖之前,就首先會調用Surface類的靜態成員函數dequeueBuffer來獲得一個空閑的UI元數據緩沖區,接著請求 SurfaceFlinger服務為這個UI元數據緩沖區分配一個圖形緩沖區。有了圖形緩沖區之后,OpengGL庫就可以往里面填入UI數據。在往圖形 緩沖區填入UI數據的同時,OpenGL庫也會往前面獲得的UI元數據緩沖區填入當前正在操作的Surface的裁剪區域、紋理坐標和旋轉方向等信息。再 接下來,OpenGL庫就會調用Surface類的靜態成員函數queueBuffer來將前面已經填好了數據的UI元數據緩沖區添加到當前正在操作的 Surface的UI元數緩沖區堆棧的待渲染隊列中。最后,Android應用程序就會請求SurfaceFlinger服務將當前正在操作的 Surface的UI數據渲染到設備顯示屏去。
?? ? ?接下來,我們就首先分析Surface類的靜態成員函數dequeueBuffer的實現,接著再分析Surface類的靜態成員函數queueBuffer的實現,最后分析SurfaceFlinger服務渲染Surface的圖形緩沖區的過程。
?? ? ?Surface類的靜態成員函數dequeueBuffer獲得空閑UI元數據緩沖區,以及請求SurfaceFlinger服務為這個空閑UI元數據緩沖區分配圖形緩沖區的過程如圖2所示:
圖2 分配空閑UI元數據緩沖區及其圖形緩沖區的過程
?? ? ?這個過程一共分為12個步驟,接下來我們就詳細分析每一個步驟。
?? ? ?Step 1. Surface.dequeueBuffer
[cpp] view plaincopy ?? ? ? 這個函數定義在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ? 參數window雖然是一個ANativeWindow指針,但是它實際上指向的是一個Surface對象,因此,函數首先調用另外一個靜態成員函數 getSelf來將它轉換為一個Surface對象self,接著再調用這個Surface對象self的成員函數dequeueBuffer來分配一個 空閑UI元數據緩沖區和一個圖形緩沖區,其中,分配的圖形緩沖區就保存在輸出參數buffer中。
?? ? ? Surface類的非靜態成員函數dequeueBuffer的實現如下所示:
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ? 函數首先調用Surface類的成員變量mSharedBufferClient所指向的一個SharedBufferClient對象的成員函數 dequeue來從UI元數據緩沖區堆棧中獲得一個空閑的緩沖區。獲得的空閑緩沖區使用一個編號來描述,這個編號保存在變量bufIdx中。后面我們再分 析SharedBufferClient類的成員函數dequeue的實現。
?? ? ?獲最一個空閑UI元數據緩沖區之后,函數接下來判斷該緩沖區的編號是否大于Surface類的成員變量mBuffers所描述的一個 GraphicBuffer向量的大小。如果大于,那么就需要擴充這個向量的大小,以便后面可以用來保存與該緩沖區對應的一個 GraphicBuffer,即一個圖形緩沖區。
?? ? ?函數再接下來調用Surface類的另外一個成員函數needNewBuffer來判斷之前是否已經為編號為bufIdx的UI元數據緩沖區分配過圖形緩沖區了,它的實現如下所示:
[cpp] view plaincopy?? ? ? ?這個函數定義在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ? ?由于UI元數據緩沖區堆棧中的緩沖區是循環使用的。當一個UI元數據緩沖區第一次被使用的時候,應用程序就會請求SurfaceFlinger服務為它 分配一個圖形緩沖區。這個圖形緩沖區使用完成之后,就會被應用程序緩存起來,以便后面可以繼續使用。但是這個圖形緩沖區可能會被得無效,例如,與它對應的 Surface的大小和用途等信息發生改變之后,該圖形緩沖區就會變得無效了,因為它在分配的時候,是按照既定的大小和用途來分配的。
?? ? ? 這個函數首先調用Surface類的成員變量mSharedBufferClient所指向的一個SharedBufferClient對象的成員函數 needBuffer來驗證編號為bufIdx的UI元數據緩沖區所對應的圖形緩沖區信息是否發生了變化。如果發生了變化,那么變量 needNewBuffer的值就會等于true,表示要重新為編號為bufIdx的UI元數據緩沖區分配新的圖形緩沖區。
?? ? ??SharedBufferClient類的成員函數needBuffer的實現如下所示:
[cpp] view plaincopy?? ? ??這個函數定義在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
?? ? ??SharedBufferClient類的成員變量mSharedStack的類型為SharedBufferStack,它是從父類 SharedBufferBase繼承下來的,用來描述一個UI元數據緩沖區堆棧。SharedBufferStack類的成員變量 reallocMask是一個掩碼,如果它的某一位的值等于1,那么這一位所描述的一個UI元數據緩沖區所對應的圖形緩沖區就是無效的。這一般是由 SurfaceFlinger服務來設備的。當SurfaceFlinger服務發現一個Surface的元信息發生變化時,就會通過一個 SharedBufferServer對象來設置這個Surface的UI元數據緩沖區堆棧的成員變量reallocMask的相應位等于1,以便應用程 序在使用到該位所描述的UI元數據緩沖區時,請求分配一個新的圖形緩沖區。例如,假設SharedBufferStack類的成員變量 reallocMask的值等于01000000 00000000 00000000 00000000,那么就表示編號為1的UI元數據緩沖區對應的圖形緩沖區需要重新分配。
?? ? ?回到Surface類的成員函數needNewBuffer中,接下來該函數繼續驗證編號為bufIdx對應的UI元數據緩沖區在成員變量 mBuffers中所對應的圖形緩沖區是否還有效,即圖形緩沖區mBuffers[bufIdx]是否還有效,這是通過調用Surface類的成員變量 mBufferInfo所描述的一個BufferInfo對象的成員函數validateBuffer來驗證的。如果沒有效,那么變量 validBuffer的值就會等于false,表示要重新為編號為bufIdx的UI元數據緩沖區分配新的圖形緩沖區。
?? ? ?BufferInfo類的成員函數validateBuffer的實現如下所示:
[cpp] view plaincopy?? ? ?這個函數定義在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ?BufferInfo類的成員變量mDirty用來描述一個Surface的元數據是否發了變化,例如,它的大小、像素格式等是發生了變化。如果發生了變化,那么它的值就會不等于0。
?? ? ?參數buffer是一個類型為GraphicBuffer的強指針,如果它的值等于null,那么就說明它所描述的圖形緩沖是無效的。
?? ? ?如果參數buffer所指向的圖形緩沖區是有效的,但是它的用途發生了變化,即它的用途與它所對應的Surface的用途已經不一致了。
?? ? ?上述三種情況都說明需要為編號為bufIdx的UI元數據緩沖分配新的圖形緩沖區,因此,這個函數的返回值就會等于false。
?? ? ?回到Surface類的成員函數needNewBuffer中,接下來該函數通過變量needNewBuffer和變量validBuffer的值就可 以知道是否需要為編號為bufIdx的UI元數據緩沖分配新的圖形緩沖區了。假設應用程序是第一次使用編號為bufIdx的UI元數據緩沖,那么變量 validBuffer的值就一定會等于false,因此,Surface類的成員函數needNewBuffer的返回值就會等于true,表示要為編 號為bufIdx的UI元數據緩沖分配新的圖形緩沖區。該函數在返回之前,還會通過Surface類的成員變量mBufferInfo所描述的一個 BufferInfo對象來得到當前正在繪制的Surface的寬度、高度、像素格式以及用途,分別保存在輸出參數pWidth、pHeight、 pFormat和pUsage,以便應用程序接下來可以使用這些信息來請求SurfaceFlinger服務分配一個新的圖形緩沖區。
?? ? ? 回到Surface類的非靜態成員函數dequeueBuffer中,該函數接下來就會調用Surface類的另外一個成員函數 getBufferLocked來請求SurfaceFlinger服務為編號為bufIdx的UI元數據緩沖區分配一個圖形緩沖區。分配完成之后,這個 圖形緩沖區就會保存在mBuffers[bufIdx]中。后面我們就詳細分析Surface類的成員函數getBufferLocked的實現。
?? ? ? Surface類的非靜態成員函數dequeueBuffer獲得了編號為bufIdx的圖形緩沖區之后,接下來就會得到這個圖形緩沖區的寬度和高度,并 且保存Surface類的成員變量mWidth和mHeight中,以便可以表示當前下在繪制的Surface的寬度和高度。同時,這個圖形緩沖區的寬度 和高度還會被更新到用來描述當前正在繪制的Surface的裁剪區域去,因為SurfaceFlinger服務在渲染該Surface時,需要用到這個信 息。當前正在繪制的Surface的裁剪區域是由Surface類的成員變量mDirtyRegion來描述的,只要調用它的成員函數set,就可以重新 設置它的寬度和高度。
?? ? ?最后,Surface類的非靜態成員函數dequeueBuffer就將得到的圖形緩沖區的地址保存輸出參數buffer中,以便OpenGL庫可以在 上面填入UI數據。另一方面,如果分配圖形緩沖區失敗,那么Surface類的非靜態成員函數dequeueBuffer會將前面得到的一個UI元數據緩 沖區返回給成員變量mSharedBufferClient所描述的一個UI元數據緩沖區堆棧去,這是通過調用成員變量 mSharedBufferClient的成員函數undoDequeue來實現的。
?? ? ?接下來,我們就繼續分析SharedBufferClient類的成員函數dequeue的實現,以便了解它是如何從UI元數據緩沖區堆棧中獲得一個空閑的緩沖區的。
?? ? ?Step 2.?SharedBufferClient.dequeue
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
?? ? ? 從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃一 文可以知道,SharedBufferClient類的成員變量tail指向了一個UI元數據緩沖區堆棧的空閑緩沖區列表的尾部。當這個UI元數據緩沖區 堆棧的可用空閑緩沖區的數量available的值大于0的時候,應用程序就可以從它的空閑緩沖區列表的尾部分配一個繪沖區出來使用。
?? ? ? 函數首先創建了一個DequeueCondition對象condition,然后再調用SharedBufferClient從 SharedBufferBase類繼承下來的成員函數waitForCondition來判斷當前正在使用的UI元數據緩沖區堆棧是否有空閑的緩沖區可 以分配。如果沒有,那么當前線程就會一直等待,直到可以得到一個空閑緩沖區為止。
?? ? ? 函數接著創建了一個DequeueUpdate對象update,然后再調用SharedBufferClient從SharedBufferBase類 繼承下來的成員函數updateCondition來減少當前正在使用的UI元數據緩沖區堆棧的空閑緩沖區的數量,因為接下來要將空閑緩沖區列表尾部的緩 沖區分配出來使用。
?? ? ? 函數最后就通過SharedBufferClient類的成員變量tail來獲得了一個編號為dequeued的空閑UI元數據緩沖區,并且將這個編號返 回給調用者。不過,在返回之前,函數還會將SharedBufferClient類的成員變量tail向前移一個位置,以便它可以指向下一個可以用來分配 的空閑UI元數據緩沖區。由于UI元數據緩沖區堆棧是循環使用的,因此,當SharedBufferClient類的成員變量tail向前移一個位置,即 加1之后,它的值大于等于UI元數據緩沖區堆棧的大小mNumBuffers時,就需要繞回到堆棧的開頭去。
?? ? ? 這一步執行完成之后,就返回到Step 1中,即Surface類的成員函數dequeueBuffer中,這時候應用程序就為當前正在繪制的Surface獲得了一個空閑UI元數據緩沖區,接 下來就會繼續調用Surface類的成員函數getBufferLocked來為該空閑UI元數據緩沖區分配圖形緩沖區。
?? ? ? Step 3. ?Surface.getBufferLocked
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ? 從前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文可以知道,??Surface類的成員變量mSurface指向了一個類型為BpSurface的Binder代理對象,這個Binder代理對象引用 了運行在SurfaceFlinger服務一側的一個類型為SurfaceLayer的Binder本地對象。函數首先將這個成員變量保存在變量s中,后 面會通過它來向SurfaceFlinger服務為編號為index的空閑UI元數據緩沖區分配一個圖形緩沖區。
?? ? ? 在請求SurfaceFlinger服務為編號為index的空閑UI元數據緩沖區分配圖形緩沖區之前,函數還會檢查在Surface類的成員變量 mBuffers中是否存在一個與編號為index的空閑UI元數據緩沖區對應的圖形緩沖區。如果存在的話,就需要將這個圖形緩沖區從應用程序進程的地址 空間注銷掉,因為這個圖形緩沖區已經變成無效了。Surface類的成員函數getBufferMapper的返回值是一個 GraphicBufferMapper對象,通過調用這個GraphicBufferMapper對象的成員函數unregisterBuffer就可 以注銷一個指定的圖形緩沖區。GraphicBufferMapper類的成員函數unregisterBuffer最終也是通過HAL層中的 Gralloc模塊提供的接口gralloc_unregister_buffer來注銷一個指定的圖形緩沖區,這一點可以參考前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文。
?? ? ? ?函數接下來就請求變量s所指向的一個BpSurface對象的成員函數requestBuffer請求SurfaceFlinger服務為編號為 index的空閑UI元數據緩沖區分配一個圖形緩沖區,這個緩沖區保存在變量buffer中。應用程序得到圖形緩沖區buffer之后,還需要將它注冊到 本進程的地址空間之后,才能使用,這是通過調用GraphicBufferMapper類的成員函數registerBuffer來實現的,后面我們再詳 細分析這個注冊的過程。
?? ? ? 接下來,函數就將圖形緩沖區buffer保存在一個GraphicBuffer引用currentBuffer中。由于currentBuffer引用的 是Surface類的成員變量mBuffers的第index個圖形緩沖區,因此,前面相當于將圖形緩沖區buffer保存在Surface類的成員變量 mBuffers的第index個位置中,以便以后可以重復利用。最后,函數還調用GraphicBuffer引用currentBuffer的成員函數 setIndex來將前面分配到的圖形緩沖區的編號設置為index,這樣就可以將它與編號為index的UI元數據緩沖區關聯起來。
?? ? ?由于變量s引用的是一個類型為SurfaceLayer的Binder本地對象,因此,接下來我們就繼續分析SurfaceLayer類的成員函數 requestBuffer的實現,以便可以了解SurfaceFlinger服務是如何為應用程序的一個Surface分配一個圖形緩沖區的。
?? ? ? Step 4. SurfaceLayer.requestBuffer
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/services/surfaceflinger/Layer.cpp中。
?? ? ? 函數首先調用SurfaceLayer類的成員函數getOwner來獲得當前正在處理的一個SurfaceLayer對象的宿主Layer對象,接著再調用這個Layer對象的成員函數requestBuffer來執行分配圖形緩沖區的操作。從前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文可以知道,SurfaceFlinger服務在為Android應用程序創建一個Surface的時候,會相應地創建一個Layer對象和一個SurfaceLayer對象來描述這個Surface。
?? ? ? 接下來,我們就繼續分析Layer類的成員函數requestBuffer的實現。
?? ? ? Step 5. Layer.requestBuffer
?? ? ? 這個函數定義在frameworks/base/services/surfaceflinger/Layer.cpp文件中,我們分段來閱讀:
[cpp] view plaincopy?? ? ? 我們首先明確一下各個函數參數的含義。參數index用來描述一個UI元數據緩沖區的編號,參數reqWidth、reqHeight、 reqFormat和usage分別表示要分配的圖形緩沖區的寬度、高度、像素格式和用途。函數首先檢查各個參數的合法性,即參數reqWidth、 reqHeight和reqFormat不能為負數,并且參數reqWidth和reqHeight不能同時等于0。
?? ? ??從前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文可以知道,Layer類的成員變量mUserClientRef指向了一個ClientRef對象,通過這個ClientRef對象可以獲得一個 SharedBufferServer對象lcblk。得到的SharedBufferServer對象lcblk就是用來描述正在請求 SurfaceFlinger服務分配圖形緩沖區的Surface的UI元數據緩沖區堆棧的,接下來我們就會看到它的用法。
?? ? ? 我們繼續往下看:
[cpp] view plaincopy?? ? ? ?這一段代碼主要就是用來判斷要分配圖形緩沖區的Surface的寬度、高度和像素格式是否發生變化。當請求分配的圖形緩沖區的寬度、高度和像素格式與這 個圖形緩沖區所描述的Surface原來的寬度、高度和像素格式不一樣時,SurfaceFlinger服務就會認為這個Surface的元信息發生了變 化,這時候函數就會將請求分配的圖形緩沖區的寬度、高度和像素格式設置為當前Surface的寬度、高度和像素格式,并且調用前面所獲得的一個 SharedBufferServer對象lcblk的成員函數reallocateAllExcept來將之前已經分配給當前Surface的圖形緩沖 區設置為無效,因為之前已經分配給當前Surface的圖形緩沖區已經不適合于當前Surface使用了。
?? ? ? 在這一段代碼中,還有一個需要注意的地方,即Layer類的成員變量mBypassState。這個成員變量表示當前正在處理的一個Layer對象所描述 的一個Surface在SurfaceFlinger服務渲染UI時,是否需要參與合成。當它的值等于true的時候,就表示不需要參與合成,否則就要參 考合成。一般當一個Layer對象所描述的Surface的圖形緩沖區是直接在硬件幀緩沖區fb上分配時,對應的Surface就不需要參與 SurfaceFlinger服務的合成操作。
?? ? ? 我們繼續向下看:
[cpp] view plaincopy?? ? ? 這段代碼用來判斷是否需要直接在硬件幀緩沖區fb上分配一個圖形緩沖區。
?? ? ? 當滿足以下四個條件時,一個圖形緩沖區就可以在硬件幀緩沖區fb分配:
?? ? ? 1. SurfaceFlinger服務在編譯時,定義了USE_COMPOSITION_BYPASS宏;
?? ? ? 2. 當前正在處理的Layer對象的成員變量mSecure的值等于false,即這個Layer對象的所描述的一個Surface是非進程間傳輸安全的,這種類型的Surface一般用來保存屏幕UI數據或者用來傳輸遠程桌面UI數據;
?? ? ? 3.?當前正在處理的Layer對象的成員變量mBypassState的值等于true,即這個Layer對象的所描述的一個Surface是不需要參與到SurfaceFlinger服務渲染合成操作的;
?? ? ? 4. 請求分配的圖形緩沖區的有效用途effectiveUsage的第GRALLOC_USAGE_HW_RENDER位等于1,即請求分配的圖形緩沖區要在直接在硬件幀緩沖區上渲染。
?? ? ? 當滿足上述四個條件,函數就會創建一個類型為GRALLOC_USAGE_HW_FB的圖形緩沖區buffer,并且將它返回給應用程序。如果創建失敗, 函數會調用前面所獲得的一個SharedBufferServer對象lcblk的成員函數reallocateAll來將之前已經分配給當前 Surface的圖形緩沖區都設置為無效,因為這些圖形緩沖區有可能不是在硬件幀緩沖區fb上分配的。
?? ? ??請求分配的圖形緩沖區的有效用途effectiveUsage是通過調用Surface類的成員函數getEffectiveUsage來獲得的,如下所示:
[cpp] view plaincopy?? ? ? 參數usage用來描述請求分配的圖形緩沖區的原始用途。
?? ? ? 如果當前正在處理的Layer對象所描述的一個Surface是可以在進程間安全傳輸的,那么函數就會將參數usage的值修改為 (GraphicBuffer::USAGE_SW_READ_OFTEN |?GraphicBuffer::USAGE_SW_WRITE_OFTEN),目的是防止該Surface的圖形緩沖區直接在硬件幀緩沖區上分配。
?? ? ??如果當前正在處理的Layer對象所描述的一個Surface是不可以在進程間安全傳輸的,那么函數除了會保留參數usage的原值之外,還會將它的 第GraphicBuffer::USAGE_HW_TEXTURE位設置為1,用來表示分配的圖形緩沖區可以用來作為OpenGL庫的紋理緩沖區。
?? ? ? 最后,函數就將修改后的參數usage的值返回給調用者。
?? ? ? 回到Layer類的成員函數requestBuffer中。對于一般應用程序創建的Surface來說,它們都是不可以在進程間安全傳輸的,即與它對應的 Layer對象的成員變量mSecure的值等于false,因此,這時候Layer類的成員函數requestBuffer得到即將要分配的圖形緩沖區 的有效用途effectiveUsage的GraphicBuffer::USAGE_HW_TEXTURE位就被設置為1。我們記住這個值,以便接下來 可以了解圖形緩沖區的分配過程。
?? ? ?我們假設SurfaceFlinger服務在編譯時,沒有定義USE_COMPOSITION_BYPASS宏,或者當前正在處理的Layer對象所描 述的一個Surface是需要由SurfaceFlinger服務執行渲染合成操作的,即前面第1個或者第3個條件不滿足,于是,我們就繼續向下分析 Layer類的成員函數requestBuffer的實現:
[cpp] view plaincopy?? ? ? 這段代碼首先使用參數w、h、f和effectiveUsage來創建了一個GraphicBuffer對象buffer,用來描述即將要分配的圖形緩沖 區,接著再調用Layer類的成員變量mBufferManager所描述的一個BufferManager對象的成員函數attachBuffer來將 GraphicBuffer對象buffer的編號設置為index,以便可以表示這個GraphicBuffer對象buffer是為編號為index 的UI元數據緩沖區創建的。
?? ? ? 最后,Layer類的成員函數requestBuffer就將分配好的圖形緩沖區,即GraphicBuffer對象buffer返回給應用程序。
?? ? ? 接下來,我們首先分析一個GraphicBuffer對象的創建過程,即GraphicBuffer類的構造函數的實現,以便可以了解它所描述的圖形緩沖 區是如何分配的,接著再分析BufferManager類的成員函數attachBuffer的實現,以便可以了解SurfaceFlinger服務是如 何將一個UI元數據緩沖區與一個圖形緩沖區關聯起來的。
?? ? ?Step 6. new GraphicBuffer
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/libs/ui/GraphicBuffer.cpp文件中。
?? ? ? GraphicBuffer類的構造函數最重要的是調用另外一個成員函數initSize來初始化即將要分配的圖形緩沖區。
?? ? ? Step 7. GraphicBuffer.initSize
[cpp] view plaincopy?? ? ??這個函數定義在文件frameworks/base/libs/ui/GraphicBuffer.cpp文件中。
?? ? ? 函數首先獲得一個GraphicBufferAllocator對象,然后再調用這個GraphicBufferAllocator對象的成員函數 alloc來分配一塊指定大小、像素格式以及用用途的圖形緩沖區。分配好的圖形緩沖的句柄值最終就保存在GraphicBuffer類的成員變量 handle中。
?? ? ? GraphicBufferAllocator類是用來分配圖形緩沖區的,接下來我們就繼續分析它的成員函數alloc的實現。
?? ? ? Step 8.?GraphicBufferAllocator.alloc
[cpp] view plaincopy?? ? ? ?這個函數定義在文件frameworks/base/libs/ui/GraphicBufferAllocator.cpp中。
?? ? ? ?參數usage是從前面的Step 5中傳進來的,前面我們假設它的第GraphicBuffer::USAGE_HW_TEXTURE位的值等于1。
?? ? ? ?GraphicBuffer::USAGE_HW_TEXTURE是一個枚舉值,定義在文件frameworks/base/include/ui/GraphicBuffer.h中,如下所示:
[cpp] view plaincopy?? ? ? 它的值等于GRALLOC_USAGE_HW_TEXTURE。
?? ? ??GRALLOC_USAGE_HW_TEXTURE和GRALLOC_USAGE_HW_MASK也是兩個枚舉值,它們定義在文件hardware/libhardware/include/hardware/gralloc.h中,如下所示:
[cpp] view plaincopy ?? ? ? 從GRALLOC_USAGE_HW_TEXTURE和GRALLOC_USAGE_HW_MASK這兩個枚舉值的定義可以知 道,GraphicBufferAllocator類的成員函數alloc最終會調用其成員變量mAllocDev的成員函數alloc來分配一個圖形緩 沖區。
?? ? ??GraphicBufferAllocator類的成員變量mAllocDev指向了一個alloc_device_t結構體,用來描述HAL層的 Gralloc模塊中的一個gralloc設備,這個gralloc設備是在GraphicBufferAllocator類的構造函數中創建的,如下所 示:
[cpp] view plaincopy?? ? ? GraphicBufferAllocator類的構造函數首先調用函數hw_get_module來加載ID值等于 GRALLOC_HARDWARE_MODULE_ID的HAL模塊,即加載HAL層中的Gralloc模塊,目的是為了接下來調用函數 gralloc_open來打開里面的gralloc設備,并且將這個打開的gralloc設備保存在GraphicBufferAllocator類的 成員變量mAllocDev中,這一點可以參考前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文。
?? ? ? 接下來,我們就繼續分析alloc_device_t結構體的成員函數alloc的實現。
?? ? ? Step 9.?alloc_device_t.alloc
?? ? ? 從前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文可以知道,Gralloc模塊中的gralloc設備的成員函數alloc被設置為Gralloc模塊中的函數gralloc_alloc。
?? ? ? Gralloc模塊模塊中的函數gralloc_alloc有一個參數usage,當它的GRALLOC_USAGE_HW_FB位等于1的時候,函數 gralloc_alloc就會直接在硬件幀緩沖區上分配一個圖形緩沖區,否則的話,就會在匿名共享內存中分配一個圖形緩沖區。從前面的調用過程可以知 道,這個參數的值最開始是從前面的Step 1傳過來的,即是從應用程序進程這一側傳遞過來的。
?? ? ? 由于整個系統在硬件上就只有一個幀緩沖區,它是由SurfaceFlinger服務來統一管理的,即只有SurfaceFlinger服務使用的圖形緩沖 區才可以在上面分配,否則的話,隨便一個應用程序進程都可以在上面分配圖形緩沖區來使用,這個幀緩沖區的管理就亂套了。應用程序進程使用的圖形緩沖區一般 都是在匿名共享內存里面分配的,這個圖形緩區填好數據之后,就會再交給SurfaceFlinger服務來合成到硬件幀緩沖區上去渲染。因此,從前面 Step 1傳過來給函數gralloc_alloc的參數usage的GRALLOC_USAGE_HW_FB位會被設置為0,以便可以在匿名共享內存中分配一個 圖形緩沖區。這個分配的過程可以參考前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文,這里就不再復述了。
?? ? ? 這一步執行完成之后,應用程序所請求的圖形緩沖區就分配完成了,回到前面的Step 5中,即Layer類的成員函數requestBuffer中,接下來就會調用BufferManager類的成員函數attachBuffer來設置這 個圖形緩沖區的編號,以便它可以與一個UI元數據緩沖區關聯起來的。關聯好之后,應用程序在請求SurfaceFlinger服務渲染一個Surface 時,只需要指定一個UI元數據緩沖區的編號,SurfaceFlinger服務就可以根據這個編號來找到對應的圖形緩沖區,進而把這個圖形緩沖區的內容渲 染到硬件幀緩沖區上去,即渲染到設備顯示屏上去。
?? ? ?接下來,我們就繼續分BufferManager類的成員函數attachBuffer的實現。
?? ? ?Step 10.?BufferManager.attachBuffer
[cpp] view plaincopy?? ? ??這個函數定義在frameworks/base/services/surfaceflinger/Layer.cpp文件中。
?? ? ? 從前面的調用過程可以知道,參數index用來描述一個UI元數據緩沖區的編號,而參數buffer用來描述一個圖形緩沖區。
?? ? ??BufferManager類的成員變量mBufferData是一個類型為BufferData的數組,這個數組就是用來關聯當前正在處理的一個 Layer對象所描述的一個Surface的UI元數據緩沖區和圖形緩沖區的。例如,編號為i的UI元數據緩沖區在數組mBufferData的第i個位 置有一個對應的BufferData結構體,而這個BufferData結構體的成員變量buffer就指向為編號為i的UI元數據緩沖區所分配的一個圖 形緩沖區。
?? ? ? 理解了這一點之后,我們就不難理解BufferManager類的成員函數attachBuffer的實現了,它就是將編號為index的UI元數據緩沖區與參數buffer所描述的圖形緩沖區關聯起來。
?? ? ? 這一步執行完成之后,回到前面的Step 5中,即Layer類的成員函數requestBuffer中,接下來就會將分配好的圖形緩沖區返回給應用程序,即返回到前面的Step 3中去,這時候應用程序就繼續調用GraphicBufferMapper類的成員函數registerBuffer來將獲得的圖形緩沖區注冊到當前進程 的地址空間去。
?? ? ? Step 11.?GraphicBufferMapper.registerBuffer
[cpp] view plaincopy?? ? ? ?這個函數定義在文件frameworks/base/libs/ui/GraphicBufferMapper.cpp中。
?? ? ? ?參數handle的類型為buffer_handle_t,它描述的便是前面在匿名共享內存中分配的一個圖形緩沖區。
?? ? ? ?從前面的Step 8可以知道,當GraphicBufferAllocator類的成員函數alloc的參數usage的第 GraphicBuffer::USAGE_HW_TEXTURE位的值等于1的時候,SurfaceFlinger服務就會通過HAL層的 Gralloc模塊來分配一個圖形緩沖區,否則的話,就會在sw_gralloc_handle_t模塊中分配一個圖形緩沖區。 sw_gralloc_handle_t模塊中分配的圖形緩沖區是使用一個sw_gralloc_handle_t對象來描述的,而從前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一文可以知道,Gralloc模塊分配的圖形緩沖區是使用一個private_handle_t對象來描述的。
?? ? ??sw_gralloc_handle_t類的靜態成員函數validate就是用來驗證參數handle所描述的一個圖形緩沖區是否是在sw_gralloc_handle_t模塊中分配的。如果是的話,那么它的返回值就會等于0,否則的話,就會小于0。
?? ? ? 由于參數handle所描述的一個圖形緩沖區是在Gralloc模塊中分配的,因此,這個函數接下來就會調用GraphicBufferMapper類的成員變量mAllocMod的成員函數registerBuffer來執行注冊圖形緩沖區的操作。
?? ? ?GraphicBufferMapper類的成員變量mAllocMod指向了HAL層中的Gralloc模塊,它是在GraphicBufferMapper類的構造函數中初始化的,如下所示:
[cpp] view plaincopy?? ? ? GraphicBufferMapper類的構造函數首先調用函數hw_get_module來加載ID值等于GRALLOC_HARDWARE_MODULE_ID的模塊,就可以將Gralloc模塊加載到應用程序進程中來。從前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一 文可以知道,Gralloc模塊是使用一個gralloc_module_t結構體來描述的,因此,GraphicBufferMapper類的構造函數 最終就可以將加載得到的模塊module轉換為一個gralloc_module_t結構體,并且保存在GraphicBufferMapper類的成員 變量mAllocMod中。
?? ? ? 接下來,我們就繼續分析gralloc_module_t結構體的成員函數registerBuffer的實現。
?? ? ? Step 12.?gralloc_module_t.registerBuffer
?? ? ? 從前面Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析一 文可以知道,Gralloc模塊的成員函數registerBuffer被設置為Gralloc模塊中的函數 gralloc_register_buffer。Gralloc模塊模塊中的函數gralloc_register_buffer主要就是將一塊指定的 圖形緩沖區映射到當前進程的地址空間來。在我們這個情景中,就是映射到應用程序進程的地址空間來。
?? ? ? 這一步執行完成之后,應用程序請求SurfaceFlinger服務為一個空閑的UI元數據緩沖區分配一個圖形緩沖區的過程就分析完成了。從分析的過程可 以知道,這個圖形緩沖區指向的是一塊匿名共享內存,最初是在SurfaceFlinger服務這一側分配的,然后再返回給應用程序進程這一側,并且會被映 射到應用程序進程的地址空間來。這樣,SurfaceFlinger服務和應用程序就可以在各自的地址空間中訪問這個圖形緩沖區,其中,應用程序訪問這個 圖形緩沖區的目的是往里面寫入UI數據,而SurfaceFlinger服務訪問這個圖形緩沖區的目的是將里面的UI數據讀取出來渲染。
?? ? ? 接下來,我們繼續再分析Surface類的靜態成員函數queueBuffer的實現,以便可以了解應用程序是如何將一塊已經填好了數據的UI元數據緩沖區添加到當前正在繪制的Surface的UI元數緩沖區堆棧的待渲染隊列中去,這個過程如圖3所示:
圖3 準備就緒的UI元數據緩沖區進入待渲染隊列的過程?? ? ?這個過程一共分為4個步驟,接下來我們就詳細分析每一個步驟。
?? ? ?Step 1. Surface.queueBuffer
[cpp] view plaincopy ?? ? ?這個函數定義文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ?參數window雖然是一個ANativeWindow指針,但是它實際上指向的是一個Surface對象,因此,函數首先調用另外一個靜態成員函數 getSelf來將它轉換為一個Surface對象self,接著再調用這個Surface對象self的成員函數queueBuffer將一個UI元數 據緩沖區加入到待渲染隊列中去,其中,要加入到待渲染隊列中去的UI元數據緩沖區就是使用參數buffer來描述的。
?? ? ? Surface類的非靜態成員函數queueBuffer的實現如下所示:
[cpp] view plaincopy?? ? ??這個函數定義文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ? Surface類的成員變量mSwapRectangle用來指定要繪制的Surface的區域。如果它的值是正確的,那么函數就會將它的值設置到 Surface類的另外一個成員變量mDirtyRegion中去,以便接下來可以用來作為裁剪區域傳遞給SurfaceFlinger服務。
?? ? ? 如前所述,參數buffer描述的是一個圖形緩沖區,它的實際類型為GraphicBuffer,因此,函數就可以調用GraphicBuffer的靜態 成員函數getSelf來將它轉換為一個GraphicBuffer對象,接著函數還調用Sufrace類的成員函數getBufferIndex來獲得 參數buffer所描述的一個圖形緩沖區的編號bufIdx。這個編號是用來關聯一個UI元數據緩沖區。在前面分析空閑UI元數據緩沖區及其圖形緩沖區的 分配過程的Step 3中提到,應用程序請求SurfaceFlinger服務為一個UI元數據緩沖區分配了一個圖形緩沖區時,就會將這個UI元數據緩沖區的編號設置到這個圖 形緩沖區里面去。
?? ? ? Surface類的成員變量mSharedBufferClient指向了一個SharedBufferClient對象,用來描述當前正在繪制的 Surface的UI元數據緩沖區堆棧,接下來函數就會調用它的成員函數queue來將前面獲得的一個編號為bufIdx的UI元數據緩沖區加入到它的待 渲染隊列中去。不過,在加入這個編號為bufIdx的UI元數據緩沖區之前,函數還會分別調用成員變量mSharedBufferClient的成員函數 setTransform、setCrop和setDirtyRegion來設置它的旋轉方向、紋理坐標和裁剪區域等信息。這些信息分別保存在 Surface類的成員變量mNextBufferTransform、mNextBufferCrop和mDirtyRegion中。注 意,Surface類的成員變量mNextBufferTransform和mNextBufferCrop是由OpenGL庫通過調用Surface類 的成員函數perform來設置。從前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文可以知道,應用程序在請求SurfaceFlinger服務創建一個繪圖表面的時候,會將用來描述這個繪圖表面的一個Surface對象的成員函數 perform設置為OpenGL庫在畫圖的過程需要調用到的一個回調接口peform,這樣做的目的就是為了可以設置繪圖表面的元信息。
?? ? ? 將編號為bufIdx的UI元數據緩沖區加入到正在繪制的Surface的UI元數據緩沖區堆棧的待渲染隊列之后,函數最后就會調用Surface類的成 員變量mClient所描述的一個SurfaceClient對象的成員函數signalServer來通知SurfaceFlinger服務來相應的圖 形緩沖區渲染到設備顯示屏上去。
?? ? ? 接下來,我們首先分析SharedBufferClient類的成員函數queue的實現,以便可以了解一個UI元數緩沖區是如何進入到一個UI元數據緩 沖區堆棧的待渲染隊列中去的,接著分析SurfaceClient類的成員函數signalServer的實現,以便可以了解應用程序是如何請求 SurfaceFlinger服務渲染一個Surface的圖形緩沖區的。
?? ? ? Step 2.?SharedBufferClient.queue
[cpp] view plaincopy?? ? ? ?這個函數定義在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
?? ? ? ?SharedBufferClient類的成員變量mSharedStack是從父類SharedBufferBase繼承下來的,它的成員變量 index是一個類型為int8_t的數組,而這個數組就是用來描述一個UI元數據緩沖區堆棧的,這一點可以參考前面Android應用程序與SurfaceFlinger服務之間的共享UI元數據(SharedClient)的創建過程分析一文。
?? ? ? 從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃一文可以知道,SharedBufferClient類的成員變量queue_head指向了一個UI元數據緩沖區堆棧的待渲染隊列的尾部,所有需要加入到這個待渲染隊列的UI元數據緩沖都保存在queue_head的下一個位置上。
?? ? ? 參數buf描述的是要加入到當前正在繪制的Surface的UI元數據緩沖區堆棧的待渲染隊列的緩沖區的編號,在將這個緩沖區加入到待渲染隊列之后,還需 要將這個待渲染隊列的大小增加1,以便SurfaceFlinger服務可以知道一個Surface當前有多少個圖形緩沖區是正在等待被渲染的。從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃一 文可以知道,一個UI元數據緩沖區堆棧的待渲染隊列的大小保存在一個SharedBufferStack對象的成員變量queued中,而將這個待渲染隊 列的大小增加1的操作是通過調用用SharedBufferClient類從SharedBufferBase類繼承下來的成員函數 updateCondition來實現的。
?? ? ? 函數首先創建了一個QueueUpdate對象update,然后再以這個QueueUpdate對象update為參數,來調用 SharedBufferClient從SharedBufferBase類繼承下來的成員函數updateCondition,就可以將當前正在處理的 一個UI元數據緩沖區堆棧的待渲染隊列的大小增加1了。
?? ? ? 這一步執行完成之后,就返回到Step 1中,即Surface類的成員函數queueBuffer中,這時候與需要渲染的圖形緩沖區所對應的UI元數據緩沖區就加入到當前正在繪制的 Surface的UI元數據緩沖區的待渲染隊列中去了,接下來,應用程序就會調用SurfaceClient類的成員函數signalServer來請求 SurfaceFlinger服務渲染這個Surface。
?? ? ? Step 3.?SurfaceClient.signalServer
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
?? ? ? 從前面Android應用程序與SurfaceFlinger服務之間的共享UI元數據(SharedClient)的創建過程分析一 文可以知道,SurfaceClient類的成員變量mComposerService指向了一個實現了ISurfaceComposer接口的 Binder代理對象,而這個Binder代理對象引用了系統中的SurfaceFlinger服務,因此,SurfaceClient類的成員函數 signalServer實際上就是通過成員變量mComposerService的成員函數signal來通知SurfaceFlinger服務執行渲 染Surface的操作。
?? ? ?這一步執行完成之后,就會導致SurfaceFlinger類的成員函數signal被調用,接下來我們繼續分析這個成員函數的實現。
?? ? ?Step 4.?SurfaceFlinger.signal
[cpp] view plaincopy?? ? ?這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?? ? ?SurfaceFlinger服務是運行在System進程中的一個單獨的線程中的。當SurfaceFlinger服務什么也不需要做的時候,它就會 在這個線程中睡眠。由于現在有應用程序請求SurfaceFlinger服務執行渲染Surface的操作了,因此,就需要將這個線程喚醒起來了。
?? ? ?喚醒SurfaceFlinger服務所運行在的線程的操作是通過調用SurfaceFlinger類的成員函數signalEvent來實現的。當這 個線程被喚醒之后,它就會繼續執行SurfaceFlinger類的成員函數threadLoop來執行渲染Surface的操作。在后面的文章中,我們 再詳細分析SurfaceFlinger服務的實現,到時候就可以知道SurfaceFlinger服務的運行原理了。
?? ? ?接下來,我們就從SurfaceFlinger類的成員函數threadLoop開始,分析SurfaceFlinger服務渲染Surface的圖形 緩沖區的過程。這里我們并不打算詳細分析這個過程,而是粗略地分析,目的是為了理清一下思路,為后面從正面分析SurfaceFlinger服務的實現打 下基礎。這個過程如圖4所示。
圖 4 SurfaceFlinger服務渲染Surface的過程
?? ? ? 這個過程一共分為6個步驟,接下來我們就詳細分析每一個步驟。
?? ? ? Step 1. SurfaceFlinger.threadLoop
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?? ? ? 在SurfaceFlinger服務中,每一個Surface,或者說每一個Layer,都有自己的一系列圖形緩沖區。而對于每一個Surface來說, 它們各自需要渲染的圖形緩沖區都保存在內部的一個UI元數據緩沖區堆棧的待渲染隊列中。當SurfaceFlinger服務需要渲染系統的UI時,首先就 會將各個Surface的UI元數據緩沖區堆棧的待渲染隊列的緩沖區逐個取出來,并且找到對應的圖形緩沖區,接著再將這些圖形緩沖區合成在一起渲染到硬件 幀緩沖區fb上,最后我們就可以在設備顯示屏上看到系統的UI了。
?? ? ?了解了這個背景知識之后,接下來我們就簡要描述SurfaceFlinger類的成員函數threadLoop的工作過程:
?? ? ?1. 在SurfaceFlinger類的成員函數waitForEvent中等待。一旦SurfaceFlinger類的成員函數signalEvent被調用,那么SurfaceFlinger服務所運行的線程就會被喚醒。
?? ? ?2. 調用SurfaceFlinger類的成員函數handlePageFlip將各個Surface的UI元數據緩沖區堆棧的待渲染隊列頭部的緩沖區取出來,并且找到對應的圖形緩沖區。這些圖緩沖區就是接下來要被合成和渲染的。
?? ? ?3. 調用SurfaceFlinger類的成員函數handleRepaint來合成第2步得到的圖形緩沖區。
?? ? ?4. SurfaceFlinger服務使用一個DisplayHardware對象hw來描述系統當前所使用的顯示屏,這個DisplayHardware對 象hw實際就是用來封裝對硬件幀緩沖區fb的訪問的,第3步就是將各個圖形緩沖區的內容合成到這個DisplayHardware對象hw中去。合成完成 之后,就會調用這個DisplayHardware對象hw的成員函數compositionComplete來通知HAL層Gralloc模塊中的fb 設備。這一步是可選的,即HAL層Gralloc模塊中的fb設備可以忽略掉這個合成完成通知。
?? ? ?5. 調用SurfaceFlinger類的成員函數postFramebuffer來將產須合成好的圖形緩沖區渲染到硬件幀緩沖區fb上去。
?? ? ?這里我們只關注第2步的實現,以后分析SurfaceFlinger服務的實現時,再分析其它步驟的實現。
?? ? ?Step 2.?SurfaceFlinger.handlePageFlip
[cpp] view plaincopy?? ? ? ?這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?? ? ? ?SurfaceFlinger服務將系統中的各個Surface按照其Z軸大小排列在SurfaceFlinger類的成員變量 mDrawingState內部的一個layersSortedByZ列表中。函數首先將這個Surface列表layersSortedByZ取出來, 并且調用SurfaceFlinger類的另外一個成員函數lockPageFlip來將各個Surface當前需要渲染的圖形緩沖區取出來。
?? ? ? 如果SurfaceFlinger服務確實需要執行渲染圖形緩沖區的操作,那么SurfaceFlinger類的成員函數lockPageFlip的返回 值就會等于true。在這種情況下,函數接下來主要就是調用SurfaceFlinger類的成員函數computeVisibleRegions來計算 需要渲染的各個Surface的可見區域,以便后面可以正確地將它們的UI繪制出來。
?? ? ? 接下來,我們只關注SurfaceFlinger類的成員函數lockPageFlip的實現,以便可以了解SurfaceFlinger服務是如何將各個Surface當前需要渲染的圖形緩沖區取出來的。
?? ? ? Step 3.?SurfaceFlinger.lockPageFlip
[cpp] view plaincopy?? ? ??這個函數定義在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
?? ? ? 保存在Surface列表currentLayers中的Surface有可能是一個Layer對象,也有可能一個是LayerBlur對象或者一個 LayerDim對象等。這里我們只關心類型為Layer的Surface的圖形緩沖區是如何取出來渲染的,這是通過調用Layer類的成員函數 lockPageFlip來實現的。
?? ? ? Step 4. Layer.lockPageFlip
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/services/surfaceflinger/Layer.cpp中。
?? ? ??從前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一 文可以知道,Layer類的成員變量mUserClientRef指向了一個ClientRef對象,通過這個ClientRef對象可以獲得一個 SharedBufferServer對象lcblk。有了SharedBufferServer對象lcblk之后,函數就可以調用它的成員函數 retireAndLock來從它所描述的UI元數據緩沖區堆棧的待渲染隊列中取出一個緩沖區來,以便接下來可以找到對應的圖形緩沖區來渲染。
?? ? ? 注意,調用SharedBufferServer對象lcblk的成員函數retireAndLock得到的是一個UI元數據緩沖區的編號,這個編號保存 在變量buf中。函數接下來就會調用Layer類的成員變量mBufferManager所描述的一個BufferManager對象的成員函數 setActiveBufferIndex來將這個編號為buf的圖形緩沖區設置當前激活的圖形緩沖區,即接下來要被SurfaceFlinger服務渲 染的圖形緩沖區。前面在分析空閑UI元數據緩沖區及其圖形緩沖區的分配過程的Step 10時提到,每一個分配的圖形緩沖區都關聯有一個編號,而這個編號正好就是一個對應的UI元數據緩沖區的編號。因此,知道了一個UI元數據緩沖區的編號之 后,就可以找到對應的圖形緩沖區。
?? ? ? 函數接下來還會通過SharedBufferServer對象lcblk的成員函數getDirtyRegion、getCrop和 getTransform來獲得當前激活的圖形緩沖區的元信息,即裁剪區域、紋理坐標和旋轉方向。這些元信息是在前面分析UI元數據緩沖區進入待渲染隊列 的過程的Step 1中提供的,因此,這里就可以將它們獲取回來。后面在渲染當前激活的圖形緩沖區時,就需要使用到這些元信息。
?? ? ? 最后,函數調用SharedBufferServer對象lcblk的成員函數getQueueCount來檢查待渲染待隊列中的緩沖區的個數是否大于 0。如果大于0,那么就說明還有圖形緩沖區在等待被渲染。在這種情況下,函數就就會繼續調用Layer類的成員變量mFlinger的成員函數 signalEvent來通知SurfaceFlinger服務在執行完成當前這次的Surface渲染操作之后,接著要馬上進行下一次的Surface 渲染操作。
?? ? ? 接下來,我們首先分析SharedBufferServer類的成員函數retireAndLock的實現,以便可以了解SurfaceFlinger服 務是如何從一個Surface的UI元數據緩沖區堆棧的待渲染隊列中取出一個緩沖區編號的,接著再分析BufferManager類的成員函數 setActiveBufferIndex的實現,以便可以了解一個圖形緩沖區是如何被設置為一個Surface的當前激活的圖形緩沖區的。
?? ? ? Step 5.?SharedBufferServer.retireAndLock
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
?? ? ? 函數首先創建了一個RetireUpdate對象update,然后再調用SharedBufferServer從SharedBufferBase類繼 承下來的成員函數updateCondition來獲得當前正在使用的UI元數據緩沖區堆棧的待渲染隊列頭部的緩沖區在堆棧中的位置值buf。有了這個位 置值buf之后,函數再接下來就可以從用來描述當前正在使用的UI元數據緩沖區堆棧的一個index數組中獲得一個對應的緩沖區的編號。這個編號即為待渲 染隊列頭部的UI元數據緩沖區的編號,因此,函數最后就可以將它返回給調用者。
?? ? ? 這一步執行完成之后,就返回到Step 4中,即Layer類的成員函數lockPageFlip中,這時候SurfaceFlinger服務就可以就為當前正在處理的Surface設置當前激 活的圖形緩沖區了,這是通過調用BufferManager類的成員函數setActiveBufferIndex來實現的。
?? ? ? Step 6.?BufferManager.setActiveBufferIndex
[cpp] view plaincopy?? ? ? 這個函數定義在文件frameworks/base/services/surfaceflinger/Layer.cpp中。
?? ? ??BufferManager類的成員變量mActiveBuffer用來描述一個Surface的當前激活的圖形緩沖區的編號。由于參數index正 好就是描述一個Surface的當前激活的圖形緩沖區的編號,因此,函數就可以將它保存在BufferManager類的成員變量 mActiveBuffer中。
?? ? ? 前面在分析空閑UI元數據緩沖區及其圖形緩沖區的分配過程的Step 10時提到,BufferManager類有一個類型BufferData數組的成員變量mBufferData,它里面保存了 SurfaceFlinger服務為一個Surface所分配的圖形緩沖區,這樣,后面SurfaceFlinger服務在渲染一個Surface時,就 可以通過與它所關聯的一個BufferManager對象的成員變量mActiveBuffer來在另外一個成員變量mBufferData中找到當前激 活的圖形緩沖區。
?? ? ? 這一步執行完成之后,SurfaceFlinger服務渲染Surface的圖形緩沖區的過程就分析完成了。這個過程雖然只是一個粗略的過程,但是已經足 夠我們理解Android應用程序請求SurfaceFlinger服務渲染Surface的過程了,同時也為后面我們從正面分析 SurfaceFlinger服務的實現原理理清了思路,以及打下了良好的基礎。
?? ? ? 至此,Android應用程序和SurfaceFlinger服務的關系我們就學習完成了。要重新學習,請參考前面Android應用程序與SurfaceFlinger服務的關系概述和學習計劃一文。接下來,我們還會繼續分析SurfaceFlinger服務的實現原理,敬請關注!
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!
轉載于:https://www.cnblogs.com/Free-Thinker/p/4142603.html
總結
以上是生活随笔為你收集整理的Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android-----Activity
- 下一篇: MySql项目中使用的小窍门