屏幕顯示圖像的原理:從過去的CRT到現在的液晶顯示器,成像的原理是一樣的。
屏幕成像
在屏幕成像的過程中,CPU和GPU起着至關重要的過程。
- CPU(Central Processing Unit,中央處理器)負責對象的創建和銷燬、對象屬性的調整、佈局計算、文本的計算和排版、圖片的格式轉換和解碼、圖像的繪製(Core Graphics)等
- GPU(Graphics Processing Unit,圖形處理器)負責渲染工作
- iOS雙緩衝機制,有前幀緩存、後幀緩存
幀緩衝區的定義
幀緩衝存儲器(Frame Buffer):簡稱幀緩存或[顯存],它是屏幕所顯示畫面的一個直接映象,又稱爲位映射圖(Bit Map)或光柵。幀緩存的每一[存儲單元]對應屏幕上的一個像素,整個幀緩存對應一幀圖像。
畫面撕裂的現象
GPU的渲染完成一個新的圖像幀時,此時屏幕顯示器只完成了上一幀的部分圖像時,此時GPU刷新幀緩衝區時,這時就會出現畫面撕裂的現象了。爲了解決這個問題,GPU 通常有一個機制叫做垂直同步(簡寫也是 V-Sync),當開啓垂直同步後,GPU 會等待顯示器的 VSync 信號發出後,才進行新的一幀渲染和緩衝區更新。這樣能解決畫面撕裂現象,也增加了畫面流暢度,但需要消費更多的計算資源,也會帶來部分延遲
屏幕卡頓
卡頓造成的原因通常是CPU和GPU導致的掉幀引起的,
當視圖創建,佈局計算、圖片解碼、文本繪製等。隨後 CPU 會將計算好的內容提交到 GPU進行合成、渲染。隨後 GPU 會把渲染結果提交到幀緩衝區去,等待VSync 信號到來時顯示到屏幕上。如果此時下一個VSync 信號到來時,CPU或GPU都沒有完成相應的工作時,則那一幀將會丟失,則就是我們看到屏幕卡頓的原因。
如圖:
什麼是離屏渲染
在OpenGL中,GPU有2種渲染方式:
- On-Screen Rendering:當前屏幕渲染,在當前用於顯示的屏幕緩衝區進行渲染操作
- Off-Screen Rendering:離屏渲染,在當前屏幕緩衝區以外新開闢一個緩衝區進行渲染操作
爲什麼離屏渲染消耗性能原因
- 需要創建新的緩衝區
- 離屏渲染的整個過程,需要多次切換上下文環境,先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束以後,將離屏緩衝區的渲染結果顯示到屏幕上,又需要將上下文環境從離屏切換到當前屏幕
優化
減少離屏渲染
- 光柵化,layer.shouldRasterize = YES
- 遮罩,layer.mask
- 圓角,同時設置layer.masksToBounds = YES、layer.cornerRadius大於0
- 考慮通過CoreGraphics繪製裁剪圓角,或者叫美工提供圓角圖片
陰影,layer.shadowXXX - 如果設置了layer.shadowPath就不會產生離屏渲染
- CAShapeLayer 和UIBezierPath繪圓角,不經節省內存還可以減少CPU消耗
CAShapeLayer
CAShapeLayer是一個通過矢量圖形而不是bitmap(位圖)來揮之的圖層子類。
你指定諸如顏色和線寬等屬性,用CAPath來定義想繪製的圖形,最後CAShapeLayer就自動渲染出來了。
優點
- 渲染快速。CAShapeLayer使用了硬件加速,繪製同一個圖形會比用Core Graphics快很多。
- 高效使用內存。一個CAShapeLayer不需要像CALayer一樣創建一個寄宿圖,所以無論有多大,都不會佔用大多的內存。
- 不會被圖層邊界裁剪掉。
- 不會出現像素化。當你給CAShapeLayer做3D變換時,它不像一個有寄宿圖普通圖層一樣變得像素化。
不用寫在drawRect裏面
///UIBezierPath創建
let maskPath = UIBezierPath.init(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize.init(width: corderSize, height: corderSize))
bounds = CGRect.init(origin: CGPoint.zero, size: imageSize)
///CAShapeLayer創建
let maskLayer = CAShapeLayer.init()
maskLayer.path = maskPath.cgPath;
//maskLayer.frame = bounds
layer.mask = maskLayer
減少視圖的層級
特別是在TableviewCell中,當層級越多GPU渲染需要時間就更多,越容易出現掉幀現象
CALayer代替部分UIView
layer給view提供了基礎設施,使得繪製內容和呈現更高效動畫更容易、更低耗,並且layer不參與view的事件處理、不參與響應鏈。因此,當我們僅僅只是顯示一些文本或者某個線條亦或者圖片,我們可以用Layer對應的子類進行替換例如:CATextLayer代替UILabel。
當我們加載特別大的圖片的時候,我們可以考慮用:CATiledLayer,CATiledLayer爲載入大圖造成的性能問題提供了一個解決方案:將大圖分解成小片然後將它們單獨的載入。