UI視圖-圖像顯現(xiàn)原理&滑動優(yōu)化

圖像顯現(xiàn)原理

CPU和GPU兩個硬件是通過總線連接起來的,在CPU輸出的結(jié)果是一個位圖,經(jīng)由總線,在合適的時機(jī),上傳給GPU,GPU拿到位圖后會做相應(yīng)位圖的圖層渲染,包括紋理合成,之后把結(jié)果放到幀緩沖區(qū)Frame Buffer中,由視頻控制器根據(jù)VSync信號,在指定時間之前,去提取幀緩沖區(qū)中屏幕顯示內(nèi)容,最終顯示到顯示器上


UIView的顯示是CALayer負(fù)責(zé),CALayer有個contents屬性,就是最終要繪制到屏幕上的位圖,假如繪制的是label,內(nèi)容是hello world,那么contents里放置的就是hello world文字的位圖,系統(tǒng)會在合適的時機(jī)回調(diào)給我們drawRect方法,我們可以在屏幕顯示的基礎(chǔ)之上,去繪制一些自定義想要繪制的內(nèi)容,繪制好的位圖,最終會經(jīng)由Core Animation這個框架,提交給GPU部分的OpenGL渲染管線,進(jìn)行位圖的最終渲染,包括紋理合成,然后顯示到屏幕上面

CPU工作

CPU需要完成UI的布局,包括顯示繪制,之后做一些準(zhǔn)備工作,然后把相應(yīng)的位圖提交到GPU上面
UI布局和文本計算包括控件Frame的設(shè)置,對于控件的文字或者size的計算
Display就是繪制過程,drawRect方法發(fā)生在這一步驟
PrePare準(zhǔn)備階段:假如有UIImageView,那么設(shè)置它的image的時候,圖片是不能直接顯示到屏幕上去的,需要對圖片進(jìn)行解碼
Commit:對最終的輸出結(jié)果位圖進(jìn)行提交

GPU渲染管線過程

指的是OpenGL的渲染管線
首先頂點著色,指的是對位圖進(jìn)行處理
做完上面五步之后,會把最終的像素點提交到對應(yīng)的幀緩沖區(qū)中,由視頻控制器根據(jù)Vsync信號,在指定時間之前,去提取幀緩沖區(qū)中屏幕顯示內(nèi)容,最終顯示到顯示器上

UI卡頓,掉幀的原因

上面是VSync垂直信號
我們一般說的頁面滑動的流暢性是60fps,指的是每一秒會有60幀的畫面更新,那么人眼看到的就是流暢的效果,基于此,相當(dāng)于每隔16.7毫秒(60分之1)就要產(chǎn)生一幀畫面,在16.7ms之內(nèi),需要CPU和GPU共同協(xié)同完成最終的一幀的數(shù)據(jù),
灰方塊表示CPU花費(fèi)一定的時間進(jìn)行文本的布局,UI計算,視圖的繪制,圖片解碼等
然后把位圖提交給GPU,GPU進(jìn)行圖層的合成,紋理渲染,準(zhǔn)備好要顯示的畫面,那么在下一次VSync信號到來時,就可以顯示準(zhǔn)備好的這一幀畫面了
假如CPU做工作時時長特別長的時候,留給GPU的時間就比較少了,等GPU將工作做完,總時長就超出16.7了,在下一幀VSync信號到來的時候,沒有準(zhǔn)備好當(dāng)下這一幀的畫面,就產(chǎn)生了掉幀,那么我們?nèi)庋劭吹降男Ч褪腔瑒拥目D

所以,掉幀的原因是:在規(guī)定的16.7ms內(nèi),在下一幀VSync信號到來之前,CPU和GPU并沒有完成下一幀畫面的合成,于是導(dǎo)致了卡頓掉幀

基于這個掉幀原因,我們可以分析如何提高Tableview等控件的滑動流暢性優(yōu)化的方案

滑動優(yōu)化方案
CPU:文本的布局,UI計算,視圖的繪制,圖片解碼等

減輕CPU的工作時長和壓力
在子線程中進(jìn)行對象的創(chuàng)建,調(diào)整和銷毀
在子線程中預(yù)排版(布局計算,文本計算)
讓主線程有更多的時間去響應(yīng)用戶的交互
預(yù)渲染(文本等異步繪制,圖片編解碼等)

GPU:(圖層的合成,紋理渲染等)

紋理渲染方面的優(yōu)化:
假如我們觸發(fā)了離屏渲染,就會產(chǎn)生一些layer的圓角,masksToBoundsdang的設(shè)置,陰影蒙層,這些都會觸發(fā)GPU層面的離屏渲染,這種情況下,GPU做離屏渲染的工作量就會很大.優(yōu)化就是盡量避免離屏渲染,還可以依托CPU的異步繪制機(jī)制來減輕GPU的壓力

視圖混合方面的優(yōu)化:
若視圖層級復(fù)雜,GPU就要做每一個視圖的合成,合成每個像素點對應(yīng)的像素值,需要做大量的計算,可以減輕視圖層級復(fù)雜性,就能減輕GPU的壓力,也可以通過CPU層面的異步繪制機(jī)制,來達(dá)到提交的位圖本身就是一個層級少的視圖,這樣也可以減輕GPU的壓力

離屏渲染-GPU層面

當(dāng)前屏幕渲染:
GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行
離屏渲染:
GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進(jìn)行渲染操作

也就是說:當(dāng)我們設(shè)置UI視圖的某些圖層屬性,標(biāo)記為它在未預(yù)合成之前,不能用于當(dāng)前屏幕上面直接顯示的時候,就會觸發(fā)離屏渲染
什么場景下回觸發(fā)離屏渲染
  1. 設(shè)置視圖的圓角屬性(必須同時maskToBounds為YES才會觸發(fā))
  2. 設(shè)置視圖的圖層蒙版
  3. 設(shè)置陰影
  4. 設(shè)置光柵化
為何要避免離屏渲染
  1. 離屏渲染是發(fā)生在GPU層面上的,觸發(fā)了OpenGL的多通道渲染管線,產(chǎn)生了額外的開銷,所以我們要避免離屏渲染
  2. 離屏渲染會創(chuàng)建新的渲染緩沖區(qū),會有內(nèi)存上的開銷,包括對上下文的切換(因為有多通道的渲染管線,所以會需要把多通道的渲染結(jié)果做渲染合成,就涉及到了上下文切換),就會有GPU的額外開銷
  3. 觸發(fā)離屏渲染時,會增加GPU的工作量,很可能導(dǎo)致CPU和GPU的總耗時加起來超過了16.7ms,就會導(dǎo)致UI的卡頓和掉幀,所以需要避免離屏渲染
UIView的繪制原理

當(dāng)我們調(diào)用UIView的setNeedsDisplay后,并沒有立刻發(fā)生當(dāng)前視圖的繪制工作,而是在之后的某一時機(jī)才會進(jìn)行當(dāng)前視圖的繪制
當(dāng)調(diào)用UIView的setNeedsDisplay后,系統(tǒng)會立刻調(diào)用view的layer的同名方法,之后相當(dāng)于在layer上面打上了一個臟標(biāo)記,然后再當(dāng)前runloop將要結(jié)束的時候,才會調(diào)用CALayer的display方法,然后才進(jìn)入到當(dāng)前視圖的真正繪制工作流程中
CALayer的display方法,在內(nèi)部會首先判斷l(xiāng)ayer的delegate是否響應(yīng)displayLayer這個方法,若不響應(yīng),則系統(tǒng)開始繪制流程
若響應(yīng),則開始異步繪制

系統(tǒng)繪制流程

首先CALayer內(nèi)部會創(chuàng)建一個CGContextRef,在drawRect方法中,可以通過堆棧中的上下文取出這個context,然后layer會判斷它是否有代理,若沒有,則調(diào)用CALayer的drawInContext,若有則調(diào)用代理方法,做當(dāng)前視圖的繪制工作,再在合適的時機(jī),基于drawRect回調(diào)方法,
drawRect默認(rèn)操作是什么都不做,而之所以有這個接口,就是為了讓我們在系統(tǒng)繪制之上,可以做些自定義的繪制工作
最后再由CALayer上傳對應(yīng)的context給GPU,

異步繪制流程

基于layer的delegate,如果實現(xiàn)了displayLayer方法,就可以進(jìn)入到異步繪制流程當(dāng)中
在異步繪制過程中

  1. 需要代理去負(fù)責(zé)生成對應(yīng)的bitmap
  2. 同時需要把bitmap作為layer的contents屬性



    假如在某一時機(jī)調(diào)用了setNeedsDiaplay方法后,在當(dāng)前runloop將要結(jié)束的時候,會有系統(tǒng)調(diào)用視圖所對應(yīng)layer的display方法,如果代理實現(xiàn)了displayLayer方法,會調(diào)用這個方法,然后通過子線程的切換,我們會在子線程中去做位圖的繪制,此時主線程可以去做些其他的工作
    子線程的繪制
    1.通過CGBitmapContextCreat方法,來創(chuàng)建一個位圖的上下文
    2.通過CoreGraphic的相關(guān)API,可以做當(dāng)前UI控件的一些繪制工作
    3.之后通過CGBitmapContextCreatImage方法,根據(jù)所繪制的上下文,生成一張CGImage圖片,然后再回到主隊列中,提交這個位圖,設(shè)置給CALayer的contents屬性

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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