前言
我們平常iOS開發中,很少有機會能夠深入的了解繪制和渲染詳細的底層過程,在UI顯示方面,我們大多時候都只知其然而不知其所以然,然后在遇到一些UI方面的性能問題,內存問題,這部分知識就尤其重要了,我們今天就來探究一下iOS繪制渲染的整個過程。
相關框架
這張截圖是蘋果官方文檔中,關于視覺相關的一個框架結構圖。
- UIKit
我們可以看到,最上層的框架就是我們平時開發最常用到的UIKit(Mac開發是AppKit)UIkit為我們封裝了大量的視圖,窗口,視圖結構等豐富的UI元素,以及事件處理結構,文本圖像支持等等,我們可以直接使用UIKit來完成我們平時開發中絕大多數視圖的相關實現,非常簡單方便。
- CoreAnimation(QuartzCore)
在接下來的底層基礎設施中,最上層的框架為CoreAnimation,提到CoreAnimation,我們可能會想到它是動畫相關功能的庫,這其實是對它非常大的一個誤解,實際上,動畫功能只是CoreAnimation的一部分,它的原名叫做LayerKit,這樣一說,你就能知道,它正真的核心是什么了吧,CoreAnimation組合了不同的可視視圖,形成一個個獨立的圖層,存儲在圖層樹中,而UIKit的UIView視覺顯示上完全依賴這個圖層,UIView封裝了CALayer并添加了一些CALayer并不具備的功能,例如交互事件等。
- CoreGraphics
CoreGraphics是底層繪圖框架,我們平時可以直接調用它的API做一些自定義視圖,它使用了Quartz2D引擎,calyer是CoreGraphics封裝出來的圖層類,calayer知道如何利用CoreGraphics繪制自己。
- Metal
Metal是蘋果最新推出的底層渲染編程接口,是針對iphone和ipad中的GPU高度優化的編程框架,是iOS平臺中最接近底層硬件的圖形框架。
視圖和動畫渲染過程
渲染過程我們可以分成6個階段:
APP內部4個階段:
1. 布局:為圖層準備層級關系,位置,顏色等等。
2. 創建BackingStore Image(寄宿圖片): 這個階段如果需要的話,創建layer的backing image,可能是通過layer的Contents傳入的,也可能是drawRect、drawLayer:inContext:方法畫出來的。
3. 準備以及解壓:這個階段,Core Animation框架準備各種渲染需要的數據,解壓需要顯示的圖片。
4. 提交:這個階段Core Animation打包layer的所有信息,以及動畫參數,通過IPC(進程內通訊)傳遞給Render Server。(此步驟遞歸,如果layerTree比較復雜,則開銷較大)。
APP外部2個階段:
到達render Server后會轉化為render Tree,進行下面的步驟:
5. 渲染前準備和計算:所有layer的屬性,如果是動畫,計算中間值,準備渲染。
6. 渲染:渲染到屏幕上。
這6個階段中,前5個都是由CPU處理的,最后一個階段才由GPU處理;我們能控制的只有前面兩個階段,剩下的工作由系統框架幫我們處理;如果是動畫,最后兩個步驟會一直重復,知道動畫結束。
我們知道iOS設備的屏幕刷新率是60fps,如果沒有在1/60s內結束這6個步驟就會掉幀視覺感受就是在卡。如下圖:
所以,我們在動畫性能調優方面的策略就是讓CPU和GPU都可以在這1/60秒內做完他們應該做的事情,如果有卡頓,我們便要找出來是CPU負擔太重,還是GPU負擔太重,找到原因后相應的給他們減負。
繪制渲染相關優化
耗費CPU的性能的項目
- 布局計算:當視圖層級復雜,frame變化,需要復雜計算,會加重CPU的計算負擔。例如:動態計算tableviewCell的高度,使用autolayout也會非常耗費計算CPU的性能。
- 圖片的解壓和轉換:渲染前會解壓壓縮的圖片,而且如果圖片顏色格式不是32位的,那么CPU會進行顏色格式轉換,所以,我們最好直接提供32位顏色格式的圖片。
- 繪制需求:直接使用CaLayer進行繪制的話,當我們重寫UIView的drawRect方法,或者是drawLayer方法,那么系統會創建layer的寄宿圖,這種方式繪制會使得內存暴增。盡量避免,建議使用CAShapeLayer進行繪制,CAShapeLayer使用了硬件加速,渲染速度更快,內存使用非常穩定,不會被圖層邊界切割,也不會像素化。
- 隱藏的繪制:UILable的文字都是畫到寄宿圖片上的,如果改變frame,會重新繪制。
- 多層級視圖:如果一個layer被另一個layer完全遮蓋,GPU不會渲染被遮蓋的視圖,但是計算是否完全被遮蓋,很消耗CPU資源
耗費GPU的性能的項目
- 圖層混合overdraw:一個像素點被多次使用顏色填充,如果太多的話,肯定會影響GPU性能。如果一個視圖是不透明的我們應該設置opaque為yes,主動告訴GPU。
- 離屏渲染Off-Screen Rendering:某些屬性的設置,會出發離屏渲染,而離屏渲染會讓GPU在當前屏幕緩沖區以外再開拓一個新的緩沖區,進行渲染操作,創建完新的緩沖區會進行上下文切換,切到新緩沖區,渲染完再切回來,把新緩沖區的結果帶回來顯示,這一切操作,都比較耗費GPU的資源。
關于優化的問題,我們日后再詳細分析。