一.圖像顯示原理
關(guān)于CPU和GPU都是通過總線連接起來的
在CPU當中往往是一個位圖,再經(jīng)由總線在合適的時機傳輸給GPU, 然后GPU會把位圖進行圖層的渲染,包括文理的合成; 之后把結(jié)果放到Frame Buffer,由視頻控制器根據(jù)Vsync信號,在制定的時間之前去提取FrameBuffer中對應屏幕顯示內(nèi)容,最終顯示到手機屏幕上
CPU全部工作
- Layout(布局):UI布局, 文字size計算等
- Display(繪制):如drawRect
- Prepare(準備)如圖片編解碼
Commint(提交):提交位圖
- 首先,我們創(chuàng)建一個UIView空間,顯示部分交由CALayer負責. CALayer中有一個contents屬性, 就是我們最終繪制到屏幕上的一個位圖, 比如helloworld這個Label, contents里面是一個helloworld文字位圖
- 然后系統(tǒng)會在合適的時機,回調(diào)drawrect方法, 我們在此之上繪制自定義的內(nèi)容, 繪制好的內(nèi)容交由CoreAnimation這個框架提交OpenGL渲染管線, 進行最終的繪圖渲染和文理合成, 然后顯示到屏幕上
GPU渲染管線
- 頂點著色
- 圖元裝配
- 光柵化
- 片段著色
- 片段處理
二. UI卡頓和掉幀
知識背景
- 頁面滑動的流暢性是60fps, 即1s有60幀的畫面更新才可以讓眼睛感覺畫面流暢
- 在規(guī)定的事件內(nèi), 即16.67ms(按照一分鐘60幀計算), 由CPU+GPU協(xié)同產(chǎn)生一幀的數(shù)據(jù)
- CPU在做 UI布局, 文本計算, 繪制, 圖片編解碼等工作占用時間過長, 導致留給GPU的時間非常少, GPU想要把圖層合成和文理渲染準備完畢, 所需要的總時間可能超過16.67ms,
這樣在下一幀VSync信號到來之前, 我們沒有準備好下一幀畫面, 就產(chǎn)生了掉幀現(xiàn)象, 我們?nèi)庋劭吹降男Ч褪强D現(xiàn)象.
三 . 滑動方案優(yōu)化
- 方案一
CPU把下面操作放到子線程:
- 對象的創(chuàng)建, 調(diào)整, 銷毀
- 預排班(布局計算, 文本計算)
- 預渲染(文本等異步繪制, 圖片編解碼等)
- 方案二
GPU優(yōu)化
- 文理渲染
- 1.1 避免離屏渲染
- 1.2 依托于CPU異步繪制機制減輕GPU壓力
- 視圖合成
- 2.1 如果多個View層層疊加, GPU就要做每個視圖的合成, 合成每一個像素點的像素值, 需要大量的計算
- 2.2如果減輕視圖的復雜性, 就可以減輕GPU的壓力, 也可以采用CPU異步繪制, 使得提交的位圖本身就是一個層級很少的視圖, 也可以減輕GPU的壓力
四. UI繪制原理&異步繪制
- UIView調(diào)用setNeedsDislay(實際上是這個View的layer調(diào)用setNeedsDislay方法, 之后在layer上打上一個臟標記), 然后并沒有立即發(fā)生當前視圖的繪制工作, 而是在當前runloop快要結(jié)束的時候調(diào)用CALayer的display方法, 進入到當前視圖的真正繪制工作中.
- 原因要減少繪制次數(shù), 提升性能, 所以要在當前runloop快要結(jié)束時調(diào)用CALayer的display方法
1. 系統(tǒng)繪制流程
- 在CALayer內(nèi)部會創(chuàng)建一個backing Store (我們可以理解為是CGContextRef)
- 然后Layer會判斷是否有代理
- 沒有代理的話會調(diào)用CALayer的drawInContext:方法
有代理的話會調(diào)用代理的drawInContext方法, 然后做當前視圖的繪制工作(發(fā)生在系統(tǒng)當中的). 然后在一個合適的時機回調(diào)一個系統(tǒng)的方法[UIView drawRect], drawRect的實現(xiàn)默認是什么都不做的, 而給我們開這個口子就是允許我們在系統(tǒng)繪制的基礎之上繪制一些其他的工作- 最終無論上面哪一個分支, 都是由CALayer上傳位圖(backing store)給GPU
2. 異步繪制
問: 如何實現(xiàn)異步繪制?
答: 只要layer.delegate實現(xiàn)了displayLayer:方法, 我么就可以進行異步繪制
- 代理負責生成對應的bitmap(位圖)
- 設置該bitmap(位圖)作為layer.contents屬性的值
異步繪制原理
通過子線程切換, 借助Global queue, 在子線程進行位圖的繪制, 此時主線程可以做其他工作; 等子線程繪制完畢, 再回到主線程提交位圖, 設置CAlayer的contents屬性, 完成UI異步繪制過程.
五 . 離屏渲染
- 在屏渲染
- 指的是GPU的渲染操作是在當前用于顯示的屏幕緩沖區(qū)進行.
- 離屏渲染
指的是GPU在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作.
也就是當我們設置某些UI視圖層屬性, 如果指令為:在未被合成前不能直接顯示的時候, 典型的比如設置:
- 圓角屬性&&maskTobounds = YES(僅有一條是不會觸發(fā)的),
- 圖層蒙版,
- 陰影,
- 光柵化,
就會觸發(fā)離屏渲染
- 為什么要避免離屏渲染
- 離屏渲染是發(fā)生在GPU層面, 使得GPU觸發(fā)了OpenGL的多通道渲染管線, 產(chǎn)生了額外的開銷, 增加了GPU工作量, 可能使得CPU+GPU的工作時間超出了16.7ms的總耗時, 可能會導致UI卡頓和掉幀
總結(jié)
- 系統(tǒng)的UI傳遞機制是怎樣的?
- 使UITableView滾動流暢的方案和思路?
子線程加載數(shù)據(jù),主線程刷新,cell復用,cell視圖層級簡化,盡量少觸發(fā)離屛渲染;
- 方案一
CPU把下面操作放到子線程:
- 對象的創(chuàng)建, 調(diào)整, 銷毀
- 預排班(布局計算, 文本計算)
- 預渲染(文本等異步繪制, 圖片編解碼等)
- 方案二
GPU優(yōu)化
- 文理渲染
- 1.1 避免離屏渲染
- 1.2 依托于CPU異步繪制機制減輕GPU壓力
- 視圖合成
- 2.1 如果多個View層層疊加, GPU就要做每個視圖的合成, 合成每一個像素點的像素值, 需要大量的計算
- 2.2如果減輕視圖的復雜性, 就可以減輕GPU的壓力, 也可以采用CPU異步繪制, 使得提交的位圖本身就是一個層級很少的視圖, 也可以減輕GPU的壓力
什么是離屏渲染?
指的是GPU在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作UIView和CALayer之間的關(guān)系是怎樣的?
- UIView是專門負責事件傳遞和視圖響應的, CALayer是專一負責視圖顯示的;單一職責設計原則(六大設計原則之一)





