UI圖像顯示原理 & 卡頓掉幀

一.圖像顯示原理

關(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渲染管線

  1. 頂點著色
  2. 圖元裝配
  3. 光柵化
  4. 片段著色
  5. 片段處理

二. 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把下面操作放到子線程:

  1. 對象的創(chuàng)建, 調(diào)整, 銷毀
  2. 預排班(布局計算, 文本計算)
  3. 預渲染(文本等異步繪制, 圖片編解碼等)
  • 方案二
GPU優(yōu)化
  1. 文理渲染
  • 1.1 避免離屏渲染
  • 1.2 依托于CPU異步繪制機制減輕GPU壓力
  1. 視圖合成
  • 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異步繪制過程.

五 . 離屏渲染

  1. 在屏渲染
  • 指的是GPU的渲染操作是在當前用于顯示的屏幕緩沖區(qū)進行.
  1. 離屏渲染
    指的是GPU在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作.
    也就是當我們設置某些UI視圖層屬性, 如果指令為:在未被合成前不能直接顯示的時候, 典型的比如設置:
  • 圓角屬性&&maskTobounds = YES(僅有一條是不會觸發(fā)的),
  • 圖層蒙版,
  • 陰影,
  • 光柵化,
    就會觸發(fā)離屏渲染
  1. 為什么要避免離屏渲染
  • 離屏渲染是發(fā)生在GPU層面, 使得GPU觸發(fā)了OpenGL的多通道渲染管線, 產(chǎn)生了額外的開銷, 增加了GPU工作量, 可能使得CPU+GPU的工作時間超出了16.7ms的總耗時, 可能會導致UI卡頓和掉幀

總結(jié)

  1. 系統(tǒng)的UI傳遞機制是怎樣的?
  1. 使UITableView滾動流暢的方案和思路?
    子線程加載數(shù)據(jù),主線程刷新,cell復用,cell視圖層級簡化,盡量少觸發(fā)離屛渲染;
  • 方案一

CPU把下面操作放到子線程:

  1. 對象的創(chuàng)建, 調(diào)整, 銷毀
  2. 預排班(布局計算, 文本計算)
  3. 預渲染(文本等異步繪制, 圖片編解碼等)
  • 方案二
GPU優(yōu)化
  1. 文理渲染
  • 1.1 避免離屏渲染
  • 1.2 依托于CPU異步繪制機制減輕GPU壓力
  1. 視圖合成
  • 2.1 如果多個View層層疊加, GPU就要做每個視圖的合成, 合成每一個像素點的像素值, 需要大量的計算
  • 2.2如果減輕視圖的復雜性, 就可以減輕GPU的壓力, 也可以采用CPU異步繪制, 使得提交的位圖本身就是一個層級很少的視圖, 也可以減輕GPU的壓力
  1. 什么是離屏渲染?
    指的是GPU在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作

  2. UIView和CALayer之間的關(guān)系是怎樣的?

  • UIView是專門負責事件傳遞和視圖響應的, CALayer是專一負責視圖顯示的;單一職責設計原則(六大設計原則之一)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容