扒一扒安卓的渲染原理

Gtw發表於2020-04-28

導語:
在測試流暢度的過程中,必不可免的要與FPS,Jank等指標接觸,但為了加深理解,今天來簡單扒一扒安卓的渲染原理;
PerfDog使用Jank作為來代表遊戲流暢度的指標,詳情可以看
APP&遊戲需要關注Jank卡頓嗎?

一.CPU與GPU結構

現在大部分移動端都會配有CPU(中央處理器)和GPU(圖形處理器),有的現在還有一塊NPU用於處理智慧運算。來簡單看一下他們的結構;
在這裡插入圖片描述
綠色的是計算單元(ALU),
橙紅色的是儲存單元,
橙黃色的是控制單元。
CPU需要很強的通用性來處理各種不同的資料型別,同時又要進行復雜的數學和邏輯運算,所以使得CPU的內部結構異常複雜;
CPU被Cache佔據了大量空間,還有很多複雜的控制邏輯和諸多優化電路,其實計算能力只是CPU很小的一部分,在早期的時候,CPU除了做邏輯計算外,還負責記憶體管理、圖形顯示等操作因此在實際運算的時候效能會大打折扣,而且還不能顯示覆雜的圖形,完全不能滿足現在3D遊戲的要求;所以GPU應運而生。
GPU面對的則是型別高度統一的、相互無依賴的大規模資料和不需要被打斷的純淨的計算環境,所以結構也大不相同。
GPU採用了數量眾多的計算單元和超長的流水線,但只有非常簡單的控制邏輯並省去了Cache,GPU將計算機系統所需要的顯示資訊進行轉換驅動,並向顯示器提供行掃描訊號,控制顯示器的正確顯示,主要負責圖形顯示部分的工作。

二.Android系統繪圖機制

在這裡插入圖片描述
現在的安卓終端通常在一個典型顯示系統中首先由CPU發出影像繪製指令要讓GPU去畫一個樣式,但CPU不能直接和GPU通訊,也要遵守相應的規則,就和現在我們幹什麼事都要走個流程一樣的嘛,不能亂套;所以CPU要先向OpenGL ES傳送一些指令,表達要畫一個樣式,Opengl ES是一組介面API,**通過這些API可以操作驅動,讓GPU達到各種各樣的操作;GPU接收到這些命令,開始柵格化處理,把樣式顯示到螢幕中;

現在我們把應用加到顯示流程裡面來
在這裡插入圖片描述
在Android應用層通過LayoutInflater把佈局XML檔案對映成物件載入到記憶體中,此時這個UI物件含有大小,位置啦等等資訊。然後CPU從記憶體中取出這個UI物件,再經過運算處理成多維的向量圖形,然後交給GPU去柵格化成點陣圖,顯示到螢幕上;
簡單介紹一下向量圖和點陣圖
向量圖:由一個函式來描述,這個函式描述了此圖如何生成
點陣圖:由畫素點矩陣來描述

Android系統每隔16ms就重新繪製一次Activity,所以要求應用必須在16ms內完成螢幕重新整理的全部邏輯操作,這樣才能達到每秒60幀(60FPS),然而這個每秒幀數的引數由手機硬體所決定,現在大多數手機螢幕重新整理率是60赫茲(是每秒中的週期性變動重複次數的計量),如果超過了16ms就會出現所謂的丟幀(1000ms/60=16.66ms)

三.一幀影像完整渲染過程

在這裡插入圖片描述
在Android應用程式視窗裡麵包含了很多檢視(View)元素,這些元素是以樹形結構來組織,最終構成所謂檢視樹的結構;
在繪製一個Android應用程式視窗的UI之前,要確定它裡面的各個子View元素在父元素裡面的大小以及位置。確定各個子View元素在父View元素裡面的大小以及位置的過程又稱為測量過程佈局過程。Android應用程式視窗的UI渲染過程可以分為
Measure(測量)、Layout(佈局)和Draw(繪製)
三個階段(由ViewRootImpl類的performTraversals()方法發起)

測量——遞迴(深度優先)確定所有檢視的大小(高、寬)
佈局——遞迴(深度優先)確定所有檢視的位置
繪製——在畫布canvas上繪製應用程式視窗所有的檢視

經過多次繪製後,這一幀內要顯示的所有view都已經被繪製完畢,注意繪製View層次結構這些操作是在圖形緩衝區中繪製完成的;
此時就要把這個圖形緩衝區被交給SurfaceFlinger服務

SurfaceFlinger服務概述:
在這裡插入圖片描述
SurfaceFlinger服務和其他系統服務一樣是在Android系統的System程式裡被啟動並執行在其中的,主要負責統一管理裝置中Android系統的幀緩衝區Frame Buffer,簡單理解為螢幕所顯示出來的所有圖形效果都是由它統一管理的),在SurfaceFlinger服務啟動的過程中會自動建立兩個執行緒:其中一個執行緒用於監控控制檯事件,另外一個執行緒則用於渲染系統的UI
Android應用程式為了能夠將自己的UI繪製在系統的幀緩衝區上,就需要將UI資料傳遞SurfaceFlinger服務並告知自己具體的UI資料(例如要繪製UI的區域、位置等資訊),
Android應用程式與SurfaceFlinger服務是執行在不同的程式中,所以相互間通過Binder機制進行通訊,
大致可以分為3步:
1.首先是建立一個到SurfaceFlinger服務的連線,
2通過這個連線來建立一個Surface,
3.請求SurfaceFlinger服務渲染該Surface(在Android應用的每個視窗對應一個畫布(Canvas),也可以理解為Android應用程式的一個視窗)

在APP層我們對於這部分的無法進行任何的優化,這是ROOM做的工作。

簡單來說就是當Android應用層在圖形緩衝區中繪製好View層次結構後,應用層通過Binder機制與SurfaceFlinger通訊並藉助一塊匿名共享記憶體會把這個圖形緩衝區會被交給SurfaceFlinger服務。因為單純的匿名共享記憶體在傳遞多個視窗資料時缺乏有效的管理,所以匿名共享記憶體就被抽象為一個更上流的資料結構SharedClient,在每個SharedClient中,最多有31個SharedBufferStack,每個SharedBufferStack都對應一個Surface即一個視窗。

幀快取有個地址,是在記憶體裡。我們通過不停的向frame buffer中寫入資料, 顯示控制器就自動的從frame buffer中取資料並顯示出來。全部的圖形都共享記憶體中同一個幀快取。

四.VSync機制

為了減少卡頓,Android 4.1(JB)中已經開始引入VSync(垂直同步)機制
簡單來說就是CPU/GPU會接收vsync訊號,Android系統每隔16ms發出Vsync訊號,觸發對UI 進行渲染(即每16ms顯示一幀)
在16ms內需要完成兩項任務:將UI 物件轉換為一系列多邊形和紋理(柵格化)和CPU傳遞處理資料到GPU。
但即使引入垂直同步機制也不是非常完美,如果某些原因導致CPU和GPU渲染某一幀畫面的時間超過16ms時,Vsync垂直同步機制會讓硬體顯示器等待,直到GPU完成柵格化操作,這就直接導致這一幀畫面多停留了16ms甚至更長時間,讓使用者看起來畫面停頓。

相關文章