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