1.圖層合成指綜合各個視窗的繪制内容,送往lcd顯示的過程。從原理上可分為線上合成與離線合成兩種方式。
2.在android的surfaceflinger代碼流程中,圖層合成方式分3d合成(opengl)和硬體合成兩大類。
3.圖形系統采用垂直同步vsync機制,由lcd上報vsync,觸發圖層合成。
以android原生版本的launcher為例,這個場景下有四個圖層,狀态欄、導航欄由systemui繪制,桌面由桌面服務提供,圖示由launcher應用繪制,圖層合成就是把這四個圖層按既定的顯示區域,展現到顯示屏上。

這幅圖描述的是帶虛拟導航欄的情況,導航欄在最下,狀态欄(顯示電池容量、時間的那個)在最上,中間大塊的區域由牆紙和圖示層混合而得,其中牆紙隻取一部分。
這幅是小米平闆的一個截屏,由于沒有虛拟導航欄,它隻有三個圖層。圖示層中空白部分的alpha值為0,其餘部分為255,這樣在與牆紙混合時,便可形成遮檔效果。
三個圖層是怎麼看出來的呢,
輸 adb shell dumpsys surfaceflinger :
截取出這段資訊,這段資訊是surfaceflinger告知硬體合成器如何進行合成的。最後一個framebuffertarget是目标層,不算進去,參與合成的圖層是三個,分别是
com.android.systemui.imagewallpaper,
com.miui.home/com.miui.home.launcher.launcher,
statusbar
至于其他各個參數是什麼意思到後面再講。
先将所有圖層畫到一個最終層(framebuffer)上,再将framebuffer送到lcd顯示。由于合成framebuffer與送lcd顯示一般是異步的(線下生成framebuffer,需要時線上的lcd去取),是以叫離線合成。
不使用framebuffer,在lcd需要顯示某一行的像素時,用顯示控制器将所有圖層與該行相關的資料取出,合成一行像素送過去。隻有一個圖層時,又叫overlay技術。
由于省去合成framebuffer時讀圖層,寫framebuffer的步驟,大幅降低了記憶體傳輸量,減少了功耗,但這個需要硬體支援。
大部分情況下,線上合成比起離線合成有很明顯的優勢,大幅降低了記憶體帶寬的消耗。不過對于多屏顯示,靜态場景(僅限lcd不帶緩存的情況),離線合成會有優勢,做下簡單的計算不難推得。
surfaceflinger是android裡面用于提供圖層合成的服務,負責給應用層提供視窗,并按指定位置合成所有圖層到螢幕。
surfaceflinger的代碼位于
frameworks/native/services/surfaceflinger
目錄下。
見 frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
典型的 binder 服務端寫法
surfaceflinger采用commander設計模式,surfaceflinger主線程接受消息,形成消息隊列,逐個處理消息隊列中的資訊。
是以直接從onmessagereceived看起
handlemessagerefresh處理合成任務:
往下根據裝置情況,會走3d合成或硬體合成。
所謂3d合成,其實是使用opengl标準,用gpu把圖層畫到統一的framebuffer上,然後送顯。毫無疑問這是離線合成的一種。既然是按opengl标準的,我們來帶着如下問題閱讀:
1、opengl渲染的framebuffer是如何送到lcd的?
2、為了使用opengl繪制圖像,必須把該圖像内容作為紋理上傳到gpu記憶體,然後使用opengl綁定紋理渲染。那麼,每個圖層是怎麼樣變成紋理的?glteximage2d傳輸上去?
3、每個圖層的繪制區域是如何計算的,圖層間存在重疊區域時,如何混合顔色?
4、使用opengl es的哪套标準(1.1 /2.0 /3.0)?
egl标準下,opengl環境建立的一般流程如下圖所示:
這部分工作在surfaceflinger::init函數完成,也即服務初起之時:
注意到,建立的視窗是framebuffersurface,在3d渲染完成後,會由eglswapbuffers觸發queuebuffer,進而觸發framebuffersurface中的onframeavailable方法:
把graphicbuffer上傳為紋理,再渲染是非常cost的,是以android用的方式是共享:
1、應用層繪制指令完成,queuebuffer回去。
2、通過binder機制,觸發layer的onframeavailable回調,給surfaceflinger消息處理線程發一個layer更新的消息。
3、在收到layer更新的消息後,surfaceflinger更新所有的dirty layer,把graphicbuffer映射為opengl的texture
中間代碼流程很複雜,我們隻看下實際建立的一段:
android在此新增一個renderengine類,用來屏蔽opengl es1.0、1.1和2.0的用法差異。基本用法和opengl是一樣的,沒有簡化太多。
在建立renderengine的時候,會根據gpu所支援的opengl es 版本号,優先選擇 2.0,沒有2.0可用時使用1.1/1.0。
在 docomposition->dodisplaycomposition->docomposesurfaces中,會讓每個layer畫到每個顯示屏上:
layer::draw->layer::ondraw->layer::drawwithopengl:
opengl的頂點坐标(位置坐标與紋理坐标)都在 mmesh 之中,其計算方式可以仔細看看,較為繁瑣,這裡不講。
硬體合成将在下篇講述