理解UIView的绘制
界面的繪制和渲染
UIView是如何到顯示的屏幕上的。
這件事要從RunLoop開始,RunLoop是一個(gè)60fps的回調(diào),也就是說每16.7ms繪制一次屏幕,也就是我們需要在這個(gè)時(shí)間內(nèi)完成view的緩沖區(qū)創(chuàng)建,view內(nèi)容的繪制這些是CPU的工作;然后把緩沖區(qū)交給GPU渲染,這里包括了多個(gè)View的拼接(Compositing),紋理的渲染(Texture)等等,最后Display到屏幕上。但是如果你在16.7ms內(nèi)做的事情太多,導(dǎo)致CPU,GPU無法在指定時(shí)間內(nèi)完成指定的工作,那么就會(huì)出現(xiàn)卡頓現(xiàn)象,也就是丟幀。
60fps是Apple給出的最佳幀率,但是實(shí)際中我們?nèi)绻鼙WC幀率可以穩(wěn)定到30fps就能保證不會(huì)有卡頓的現(xiàn)象,60fps更多用在游戲上。所以如果你的應(yīng)用能夠保證33.4ms繪制一次屏幕,基本上就不會(huì)卡了。
總的來說,UIView從Draw到Render的過程有如下幾步:
-
每一個(gè)UIView都有一個(gè)layer,每一個(gè)layer都有個(gè)content,這個(gè)content指向的是一塊緩存,叫做backing store。
-
UIView的繪制和渲染是兩個(gè)過程,當(dāng)UIView被繪制時(shí),CPU執(zhí)行drawRect,通過context將數(shù)據(jù)寫入backing store。
-
當(dāng)backing store寫完后,通過render server交給GPU去渲染,將backing store中的bitmap數(shù)據(jù)顯示在屏幕上。
下圖就是從CPU到GPU的過程
pic_5.jpeg其實(shí)說到底CPU就是做繪制的操作把內(nèi)容放到緩存里,GPU負(fù)責(zé)從緩存里讀取數(shù)據(jù)然后渲染到屏幕上。
就如同下圖的所示
pic_4.jpeg整個(gè)過程也就是一件事:CPU將準(zhǔn)備好的bitmap放到RAM里,GPU去搬這快內(nèi)存到VRAM中處理。
而這個(gè)過程GPU所能承受的極限大概在16.7ms完成一幀的處理,所以最開始提到的60fps其實(shí)就是GPU能處理的最高頻率。
因此,GPU的挑戰(zhàn)有兩個(gè):
-
將數(shù)據(jù)從RAM搬到VRAM中
-
將Texture渲染到屏幕上
這兩個(gè)中瓶頸基本在第二點(diǎn)上。渲染Texture基本要處理這么幾個(gè)問題:
合成(Compositing):
Compositing是指將多個(gè)紋理拼到一起的過程,對(duì)應(yīng)UIKit,是指處理多個(gè)view合到一起的情況(drawRect只有當(dāng)addsubview情況下才會(huì)觸發(fā))
[self.view?addsubview:subview]如果view之間沒有疊加,那么GPU只需要做普通渲染即可。 如果多個(gè)view之間有疊加部分,GPU需要做blending。
尺寸(Size):
這個(gè)問題,主要是處理image帶來的,假如內(nèi)存里有一張400x400的圖片,要放到100x100的imageview里,如果不做任何處理,直接丟進(jìn)去,問題就大了,這意味著,GPU需要對(duì)大圖進(jìn)行縮放到小的區(qū)域顯示,需要做像素點(diǎn)的sampling,這種smapling的代價(jià)很高,又需要兼顧pixel alignment。計(jì)算量會(huì)飆升。
離屏渲染(Offscreen Rendering And Mask):
我們來看一下關(guān)于iOS中圖形繪制框架的大致結(jié)構(gòu)
pic_3.jpegUIKit是iOS中用來管理用戶圖形交互的框架,但是UIKit本身構(gòu)建在CoreAnimation框架之上,CoreAnimation分成了兩部分OpenGL ES和Core Graphics,OpenGL ES是直接調(diào)用底層的GPU進(jìn)行渲染;Core Graphics是一個(gè)基于CPU的繪制引擎;
我們平時(shí)所說的硬件加速其實(shí)都是指OpenGL,Core Animation/UIKit基于GPU之上對(duì)計(jì)算機(jī)圖形合成以及繪制的實(shí)現(xiàn),由于CPU是渲染能力要低于GPU,所以當(dāng)采用CPU繪制時(shí)動(dòng)畫時(shí)會(huì)有明顯的卡頓。
但是其中的有些繪制會(huì)產(chǎn)生離屏渲染,額外增加GPU以及CPU的繪制渲染。
OpenGL中,GPU屏幕渲染有以下兩種方式:
-
On-Screen Rendering即當(dāng)前屏幕渲染,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行。
-
Off-Screen Rendering即離屏渲染,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。
離屏渲染的代價(jià)主要包括兩方面內(nèi)容:
-
創(chuàng)建新的緩沖區(qū)
-
上下文的切換,離屏渲染的整個(gè)過程,需要多次切換上下文環(huán)境:先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上有需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕。而上下文環(huán)境的切換是要付出很大代價(jià)的。
為什么需要離屏渲染?
目的在于當(dāng)使用圓角,陰影,遮罩的時(shí)候,圖層屬性的混合體被指定為在未預(yù)合成之前不能直接在屏幕中繪制,即當(dāng)主屏的還沒有繪制好的時(shí)候,所以就需要屏幕外渲染,最后當(dāng)主屏已經(jīng)繪制完成的時(shí)候,再將離屏的內(nèi)容轉(zhuǎn)移至主屏上。
離屏渲染的觸發(fā)方式:
-
shouldRasterize(光柵化)
-
masks(遮罩)
-
shadows(陰影)
-
edge antialiasing(抗鋸齒)
-
group opacity(不透明)
上述的一些屬性設(shè)置都會(huì)產(chǎn)生離屏渲染的問題,大大降低GPU的渲染性能。
CPU渲染:
以上所說的都是離屏渲染發(fā)生在OpenGL SE也就是GPU中,但是CPU也會(huì)發(fā)生特殊的渲染,我們的CPU渲染,也就是我們使用Core Graphics的時(shí)候,但是要注意的一點(diǎn)的是只有在我們重寫了drawRect方法,并且使用任何Core Graphics的技術(shù)進(jìn)行了繪制操作,就涉及到了CPU渲染。整個(gè)渲染過程由CPU在App內(nèi) 同步地 完成,渲染得到的bitmap最后再交由GPU用于顯示。
理論上CPU渲染應(yīng)該不算是標(biāo)準(zhǔn)意義上的離屏渲染,但是由于CPU自身做渲染的性能也不好,所以這種方式也是需要盡量避免的。
分析
所以對(duì)于當(dāng)屏渲染,離屏渲染和CPU渲染的來說,當(dāng)屏渲染永遠(yuǎn)是最好的選擇,但是考慮到GPU的浮點(diǎn)運(yùn)算能力要比CPU強(qiáng),但是由于離屏渲染需要重新開辟緩沖區(qū)以及屏幕的上下文切換,所以在離屏渲染和CPU渲染的性能比較上需要根據(jù)實(shí)際情況作出選擇。
?
UIView和CALayer
關(guān)于UIView和CALayer的關(guān)系這里大致講主要的幾點(diǎn):
-
每個(gè)UIView都包含一個(gè)CALayer在背后提供內(nèi)容的繪制和顯示(一個(gè)layer可能包含多個(gè)子layer),并且UIView的bound,frame都由內(nèi)部的Layer所提供。兩者都有樹狀層級(jí)結(jié)構(gòu),layer內(nèi)部有SubLayers,View內(nèi)部有SubViews.但是Layer比View多了個(gè)AnchorPoint,AnchorPoint相比Postion的區(qū)別在于position點(diǎn)是相對(duì)suerLayer的,anchorPoint點(diǎn)是相對(duì)layer的,兩者都是中心點(diǎn)的位置,只是相對(duì)參照物不同罷了。
-
在View顯示的時(shí)候,UIView做為Layer的CALayerDelegate,View的顯示內(nèi)容由內(nèi)部的CALayer的display。
-
View可以接受并處理事件,而Layer不可以,因?yàn)閁IKit使用UIResponder作為響應(yīng)對(duì)象。
-
layer內(nèi)部維護(hù)著三分layer tree,分別是presentLayer Tree(動(dòng)畫樹),modeLayer Tree(模型樹), Render Tree(渲染樹),在做 iOS動(dòng)畫的時(shí)候,我們修改動(dòng)畫的屬性,在動(dòng)畫的其實(shí)是Layer的presentLayer的屬性值,而最終展示在界面上的其實(shí)是提供View的modelLayer。
http://www.cocoachina.com/ios/20160929/17673.html
總結(jié)
以上是生活随笔為你收集整理的理解UIView的绘制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac下Git安装及配置
- 下一篇: 分布式存储与传统SAN、NAS的优、劣对