GPU由來:CPU的任務繁多,做邏輯計算,還要做內存管理、顯示操作,因此在實際運算(浮點運算)的時候性能會大打折扣,在沒有GPU的時代,不能顯示復雜的圖形,其運算速度遠跟不上今天復雜三維游戲的要求。即使CUP的工作頻率超過2GHZ或更高,對它繪制圖形提高也不大。這時GPU的設計就出來了。
CPU和GPU架構:
黃色的Control: 控制器,用于協調控制整個CPU的運行,包括取出指令、控制其他模塊的運行等;
綠色的ALU(Arithmetic Logic Unit): 算術邏輯單元,用于進行數學、邏輯運算;
橙色的Cache: 緩存,用于存儲信息。
橙色的DRAM: RAM,用于存儲信息。
從結構圖可以看出,CPU的控制器較為復雜,而ALU數量較少。因此CPU擅長各種復雜的邏輯運算,但不擅長數學尤其浮點運算。
CPU: 中央處理器,它集成了運算,緩沖,控制等單元,包括繪圖功能.CPU將對象處理為多維圖形,紋理(Bitmaps、Drawables等都是一起打包到統一的紋理).
GPU:一個類似于CPU的專門用來處理Graphics的處理器, 作用用來幫助加快格柵化操作,當然,也有相應的緩存數據(例如緩存已經光柵化過的bitmap等)機制。
OpenGL ES:是手持嵌入式設備的3DAPI,跨平臺的、功能完善的2D和3D圖形應用程序接口API,有一套固定渲染管線流程. 附相關OpenGL渲染流程資料
DisplayList: 在Android把XML布局文件轉換成GPU能夠識別并繪制的對象。這個操作是在DisplayList的幫助下完成的。DisplayList持有所有將要交給GPU繪制到屏幕上的數據信息。
垂直同步VSYNC:讓顯卡的運算和顯示器刷新率一致以穩定輸出的畫面質量。它告知GPU在載入新幀之前,要等待屏幕繪制完成前一幀。
Refresh Rate:屏幕一秒內刷新屏幕的次數,由硬件決定,例如60Hz.
Frame Rate:GPU一秒繪制操作的幀數,單位是30fps,
XML加載過程:
柵格化:
將向量圖形格式表示的圖像轉換成位圖以用于顯示器,即把button、textview等組件拆分到不同的像素上進行顯示。這是一個很費時的操作,GPU的引入就是為了加快柵格化的操作。
60HZ刷新頻率由來
12fps: 由于人類的眼睛的特殊生理結構,如果所看到的畫面幀率高于每秒10-12幀的時候,就會認為是連慣的
24fps:有聲電影的拍攝及播放幀率均為每秒24幀,對一般人而言已算可接受
30fps:早期的高動態電子游戲,幀率少于每秒30幀的話就會顯得不連貫,這是因為沒有動態模糊使流暢度降低
60fps:在與手機交換過程中,如果觸摸及反饋 60幀以下人是能感覺出來的。60幀以上不能察覺變化。當低于60幀時感覺畫面卡頓和遲滯現象
Android系統每隔16ms發出VSYNC信號(1000ms/60=16.66ms),觸發對UI進行渲染,如果每次渲染都成功這樣能夠達到流暢畫面所需要的60fps,為了能夠實現60fps,這意味著計算渲染的大多數操作都必須在16ms內完成。
Android系統的繪制原理
Android中每個界面都是大大小小的View組成,對于應用里的每個view都會經過老三部:measure,layout,draw.然后就由主線程傳給CPU進行計算紋理,再通過OpenGL ES接口調用GPU進行柵格化處理,再通過跨進程通信機制把需要顯示的數據傳到SurfaceFlinger,通過它將柵格化的信息交給硬件合成器合成后輸出到顯示屏.
開啟硬件加速:
如果沒有開啟硬件加速,系統則會用軟件加速的形式來渲染,即CPU計算紋理后通過skia進行柵格化,再通過跨進程通信機制把需要顯示的數據傳到SurfaceFlinger,通過它將柵格化的信息交給硬件合成器合成后輸出到顯示屏.
PS:Androd3.0開始支持硬件加速,到Android4.0默認開啟硬件加速.
繪制任務由應用發起,最終通過系統層繪制到硬件屏幕上,也就是說應用進程繪制后,通過跨進程通信機制把需要顯示的數據傳到系統層,由系統層中的SurfaceFlinger服務繪制到屏幕上;
-
應用層
一個Android應用程序窗口里面包含了很多UI元素,這些UI元素是以樹形結構來組織的,即它們存在著父子關系,其中,子UI元素位于父UI元素里面,如下圖:
image
在繪制一個Android應用程序窗口的UI之前,我們首先要確定它里面的各個子UI元素在父UI元素里面的大小以及位置。確定各個子UI元素在父UI元素里面的大小以及位置的過程又稱為測量過程和布局過程。Android每個View繪制的三個核心步驟:通過Measure和Layout確定當前需要繪制View的大小和位置,通過繪制到surface,在Android系統繪制源碼是在ViewRootImpl的performTraversals方法開始的,通過該方法會執行Measure和layout和draw方法;Measure和layout都是遞歸獲取View的大小和位置,都是以深度為優先級,可以看出層級越深元素越多,耗時也就越長;
測量、布局沒有太多要說的,這里要著重說一下繪制。Android目前有兩種繪制模型:基于軟件的繪制模型和硬件加速的繪制模型(從Android 3.0開始全面支持)。
基于軟件的繪制模型下,CPU主導繪圖,視圖按照兩個步驟繪制
(1)讓View層次結構失效;
(2) 繪制View層次結構;
當應用程序需要更新它的部分UI時,都會調用內容發生改變的View對象的invalidate()方法。無效(invalidation)消息請求會在View對象層次結構中傳遞,以便計算出需要重繪的屏幕區域(臟區)。然后,Android系統會在View層次結構中繪制所有的跟臟區相交的區域。不幸的是,這種方法有兩個缺點:
(1)繪制了不需要重繪的視圖(與臟區域相交的區域);
(2)掩蓋了一些應用的bug(由于會重繪與臟區域相交的區域);
注意:在View對象的屬性發生變化時,如背景色或TextView對象中的文本等,Android系統會自動的調用該View對象的invalidate()方法。
基于硬件加速的繪制模式下,GPU主導繪圖,繪制按照三個步驟繪制:
(1)讓View層次結構失效
(2)記錄、更新顯示列表
(3)繪制顯示列表
這種模式下,Android系統依然會使用invalidate()方法和draw()方法來請求屏幕更新和展現View對象。但Android系統并不是立即執行繪制命令,而是首先把這些View的繪制函數作為繪制指令記錄一個顯示列表中,然后再讀取顯示列表中的繪制指令調用OpenGL相關函數完成實際繪制。另一個優化是,Android系統只需要針對由invalidate()方法調用所標記的View對象的臟區進行記錄和更新顯示列表。沒有失效的View對象則能重放先前顯示列表記錄的繪制指令來進行簡單的重繪工作。使用顯示列表的目的是,把視圖的各種繪制函數翻譯成繪制指令保存起來,對于沒有發生改變的視圖把原先保存的操作指令重新讀取出來重放一次就可以了,提高了視圖的顯示速度。而對于需要重繪的View,則更新顯示列表,以便下次重用,然后再調用OpenGL完成繪制。硬件加速提高了Android系統顯示和刷新的速度,但它也不是萬能的,它有三個缺陷:
耗電:GPU功耗比CPU高;
兼容問題:某些接口或者函數不支持硬件加速;
內存大:使用OpenGL接口至少需要8MB內存;
所以是否使用硬件加速需要考慮接口是否支持,通過結合產品形態,如TV版本不需要考慮功耗問題,TV屏幕大,使用硬件加速實現更好的顯示效果; -
系統層:
真正把需要顯示器的數據渲染到屏幕上,是通過系統級進程匯總的SurfaceFlinger服務來實現的,SurfaceFlinger主要工作:
(1)響應客戶端事件,創建Layer,與客戶端Surface建立連接;
(2)接收客戶端數據及屬性,修改Layer屬性,如尺寸、顏色、透明度等;
(3)將創建的layer內容刷新到屏幕上;
(4)維持layer序列,并對Layer最終輸出做出裁剪計算;
既然是兩個不同進程,那么肯定需要跨進程通信機制來實現數據傳輸,Android顯示系統使用匿名共享內存:SharedClient,每個應用和SurfaceFlinger之間都會創建一個SharedClient,如下圖所示,在每個SharedClient中,最多會創建31個SharedBufferStack,每個Surface對應一個SharedBufferStack,也就是一個Window;一個SharedClient對應一個Android應用程序,而一個Android應用程序可能包含多個窗口,即Surface,也就是說SharedClient包含的是SharedBufferStack集合,因為最多可以創建31個SharedBufferStack,也就意味著一個Android應用最多可以還包含31個窗口,同時每個SharedBufferStack中又包含兩個(低于4.1版本)或者三個(4.1及以上版本)緩沖區,即顯示刷新機制中雙緩沖和三重緩沖機制;
image
最后總結起來顯示整體流程分為三個模塊:應用層繪制到緩存區,SurfaceFlinger把緩存區數據渲染到屏幕,由于是兩個不同的進程,所以Android使用匿名共享內存SharedClient緩存需要顯示的數據來達到目的;SurfaceFlinger把緩存區的數據渲染到屏幕主要是驅動層的事情。
梳理如下:- 每個Surface對應的BufferQueue內部都有兩個Graphic Buffer ,一個用于繪制一個用于顯示.我們會把內容先繪制到離屏緩 沖區(OffScreen Buffer),在需要顯示時,才把離屏緩沖區的內容通過SwapBuffer驅動復制到Front Graphic Buffer中. Android4.1加入了Tripple Buffer。
- 這樣SurfaceFlinge就拿到了某個Surface最終要顯示的內容,但是同一時間我們可能會有多個Surface.這里面可能是不同 應用的Surface,也可能是同一個應用里面類似SurefaceView和TextureView,它們都會有自己單獨的Surface.
- 這時SurfaceFlinger把所有Surface要顯示的內容統一交給Hareware Composer,它會根據位置、Z-Order順序等信息合 成為最終屏幕需要顯示的內容,而這個內容會交給系統的幀緩沖區Frame Buffer來顯示(Frame Buffer是非常底層的,可以 理解為屏幕顯示的抽象).
Android是如何將view繪制到屏幕?
大致流程如下:
(1)首先應用主線里里的每個view都會經過老三部:measure,layout,draw.然后TextView,Button等等控件通過CPU計算轉換為內存中的polygons(多邊圖形)和texture(紋理)。
(2)其次,CPU通過OpenGL的接口將紋理數據傳遞給GPU渲染處理,由于圖形API不允許CPU直接與GPU通信,而是通過中間圖形驅動層來連接兩部分,驅動層維護了一個隊列,CPU把display list添加到隊列中,GPU從這個隊列去除數據進行繪制,最終在屏幕上顯示出來;在這個過程中,每個View都有一個DisplayList,由DisplayList這個結構負責保存繪制用到的所有信息,在Displaylist無需重新創建或改變的情況下,GPU可以直接使用這里的數據進行渲染.當View中的某些可見組件,那么之前的DisplayList就無法繼續使用了,我們需要回頭重新創建一個DisplayList并且重新執行渲染指令并更新到屏幕上。而不是像軟件繪制那樣需要向上遞歸。這樣可以大大減少繪圖的操作數量,因而提高了渲染效率.
(3)最后,GPU對圖形數據進行渲染,通過匿名共享內存:SharedClient把需要顯示的數據傳到SurfaceFlinger;一個SharedClient對應一個Android應用程序,一個SharedClient可以創建31個SharedBufferStack;每個SharedBufferStack對應一個Surface,也就是一個Window;每個SharedBufferStack包含的BufferQueue內部都有三個Graphic Buffer,兩個用于繪制一個用于顯示.我們會把內容先繪制到一個后置緩沖區(OffScreen Buffer或者Back Buffer),在另外一個繪制下一幀;在需要顯示時,才把離屏緩沖區的內容通過SwapBuffer驅動復制到Front Graphic Buffer中, 通過它將柵格化的信息交給SurfaceFlinger,SurfaceFlinger通過創建維護Layer再交給Hareware Composer,它會根據Layer中的位置、Z-Order順序等信息合成為最終屏幕需要顯示的內容,而這個內容會交給系統的幀緩沖區Frame Buffer來顯示(Frame Buffer是非常底層的,可以 理解為屏幕顯示的抽象).
(4)垂直同步VSYNC 60fps: 讓顯卡的運算和顯示器刷新率一致以穩定輸出的畫面質量。它告知GPU在載入新幀之前,要等待屏幕繪制完成前一幀。如果下一幀周期到是屏幕前一幀沒有繪制結束,后置緩沖區不能清空,多出來的一個后置緩沖區就可以用來繪制。
名詞解釋:
DisplayList:DisplayList持有所有將要交給GPU繪制到屏幕上的數據信息。顯示刷新機制
Android系統一直在不斷的優化、更新,但直到4.0版本發布,有關UI顯示不流暢的問題仍未得到根本解決;從Android4.1版本開始,Android對顯示系統進行了重構,引入了三個核心元素:VSYNC, Tripple Buffer和Choreographer。VSYNC是Vertical Synchronized的縮寫,是一種定時中斷;Tripple Buffer是顯示數據的緩沖區;Choreographer起調度作用,將繪制工作統一到VSYNC的某個時間點上,使應用的繪制工作有序進行。
名詞解釋:
雙緩沖:顯示內容的數據內存,雙緩沖意味著要使用兩個緩沖區(SharedBufferStack中),其中一個稱為Front Buffer,另外一個稱為Back Buffer。UI總是先在Back Buffer中繪制,然后再和Front Buffer交換,渲染到顯示設備中。
三緩沖:即Triple Buffer。一個前置緩沖區和兩個后置緩沖區,利用CPU/GPU的空閑時間準備數據,用于彌補在VSYNC+雙緩沖配合使用的缺陷(一前一后);
VSYNC:當雙緩沖的介紹了解到,只有當另外一個buffer準備好之后,才能去刷新,這就需要CPU以主動查詢方式來保證數據是否準備好,因為這種機制效率很低,引入VSYNC,一旦受到VSYNC定時中斷,CPU就開始處理各幀數據;沒有VSYNC信號,CPU不知道何時去處理UI繪制,引入VSYNC核心目的就是解決刷新不同步的問題;
Choreographer:收到VSYNC信號時,調用用戶設置的回調函數,一種有三種類型的回調:
CALLBACK_INPUT:優先級最高,與輸入事件有關;
CALLBACK_ANIMATON:第二優先級,與動畫有關;
CALLBACK_TRAVERSAL:最低優先級,與UI控件繪制有關;
View的onclick、onDraw等等都是從Choreographer.doFrame開始執行的;關于Choreographer可以參考Android Choreographer 源碼分析,講的特別好;那雙緩沖機制又是什么呢?
雙緩沖機制簡單說就是不同的View或者Activity它們都會共用一個Window,
也就是共用同一個Surface對象. 而每個Surface都會有一個BufferQueue緩存隊列,但是這個隊列會由SurfaceFlinger管理,通過匿名共享內存機制與App應用
層交互.那三級緩沖機制又是什么呢?
簡單來說,三緩沖機制就是在雙緩沖機制基礎上增加了一個Graphic Buffer緩沖區,這樣可以最大限度的利用空閑時間.
如果只有兩個Graphic Buffer緩存區A和B, CPU/GPU又繪制過程過?,超過了一個VSYNC信號周期,因為緩沖區B中的數據還沒有準備完成,所以只能繼續展示A緩沖區 的內容,這樣緩沖區A和B都分別被顯示設備和GPU占用,CPU無法準備下一幀的數據.RenderNode和RenderThread
在Android 5.0引入了兩個比較大的改變.一個是引入了RenderNode的概念,它對DisplayList及一些View顯示屬性 做了進一步封裝.另一個是引入了RenderThread,所有的GL命令執行都放到這個線程上,渲染線程在RenderNode中存有渲 染幀的所有信息,可以做一些屬性動畫,這樣即便主線程有耗時操作的時候也可以保證動畫流暢.HWUI
hwui主要是android用于2d硬件繪圖而加入的一個模塊,在hwui之前,android主要是用skia來進行軟件繪制,后由于繪制性能等問題,現在android的繪圖幾乎都是使用了hwui硬件加速繪圖。hwui主要則是使用opengles來進行gpu硬件繪圖,提升整個系統的繪制性能,主要有以下方式:直接渲染,顯示列表渲染,延時渲染列表,分別代表的類為:OpenGLRenderer,DisplayListRenderer,DeferredDisplayList。
原文鏈接:http://www.lxweimin.com/p/abfaea892611Vulkan
Vulkan為Khronos Group推出的下一代跨平臺圖形開發接口,用于替代歷史悠久的OpenGL。Android從7.0(Nougat)開始加入了對其的支持。Vulkan與OpenGL相比,接口更底層,從而使開發者能更直接地控制GPU。由于更好的并行支持,及更小的開銷,性能上也有一定的提升。另外層式架構可以幫助減少調試和測試的時間。但是,代價是實現相同的功能更復雜了。原本用OpenGL寫個最簡單的demo百來行,用vulkan祼寫的話沒千把行下不來。因此實際使用中需要有utility層來簡化接口調用。Android對vulkan的支持主要是提供接口讓開發者可以用vulkan開發圖形相關應用,尤其是像游戲這樣的3D渲染場景。如果有支持vulkan的Android移動設備,開發者就可以利用NDK進行基于vulkan的開發
原文鏈接:https://blog.csdn.net/jinzhuojun/article/details/52430543
卡頓原理分析:
????當這一幀畫面渲染時間超過16ms的時候,垂直同步機制會讓顯示器硬件等待這一次GPU完成柵格化渲染操作,這樣會讓這一幀畫面,多停留16ms,甚至更多,這樣就造成了畫面看起來停頓。
16毫秒的時間主要被兩件事情所占用
第一件:將UI對象轉換為一系列多邊形和紋理
第二件事情:CPU傳遞處理數據到GPU。
所以很明顯,我們要縮短這兩部分的時間,也就是說需要盡量減少對象轉換的次數,以及上傳數據的次數(布局 自定義)
CPU 減少xml轉換成對象的時間
GPU 減少重復繪制的時間
什么是過度繪制:
????GPU的繪制過程,就跟刷墻一樣,一層層的進行,16ms刷一次,這樣就會造成,圖層覆蓋的現象,即無用的突出還是被繪制在底層,造成不必要的浪費。
GPU過度繪制幾種情況:
????1.自定義控件中 onDraw方法做了過多重復繪制
????2.布局層次太深,重疊性太強。用戶看不到的區域GPU也會渲染,導致耗時增加。
過度繪制工具使用鏈接
Android系統優化操作:
????CPU轉移到GPU是一件很麻煩的事情,所幸的是OpenGL ES可以把那些需要渲染的紋理Hold在GPU Memory里面,在下次需要渲染的時候直接進行操作。所以如果你更新了GPU所hold住的紋理內容,那么之前保持的狀態就丟失了。
????在Android里面那些由主題所提供的資源,例如Bitmaps,Drawables都是一起打包到統一的Texture紋理當中,然后再傳遞到GPU里面,這意味著每次你需要使用這些資源的時候,都是直接從紋理里面進行獲取渲染的。當然隨著UI組件的越來越豐富,有了更多演變的形態。例如顯示圖片的時候,需要先經過CPU的計算加載到內存中,然后傳遞給GPU進行渲染。文字的顯示比較復雜,需要先經過CPU換算成紋理,然后交給GPU進行渲染,返回到CPU繪制單個字符的時候,再重新引用經過GPU渲染的內容。動畫則存在一個更加復雜的操作流程。
為了能夠使得App流暢,我們需要在每幀16ms以內處理完所有的CPU與GPU的計算,繪制,渲染等等操作
以上參考了以下博客文章
http://www.lxweimin.com/p/a978a6250f9e
http://zzmblog.cn/html/sb/arc/157105315943.html
https://blog.csdn.net/ccj659/article/details/53219288