iOS離屏渲染的解釋
重開一個環境(內存、資源、上下文)來完成(部分)圖片的繪制
指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作
意為離屏渲染,指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作。
紅色代表GPU
需要做額外的工作來渲染View
,綠色代表GPU
無需做額外的工作來處理bitmap
。
界面渲染過程
RunLoop
有一個60fps
的回調,即每16.7ms
繪制一次屏幕,所以view
的繪制必須在這個時間內完成,view
內容的繪制是CPU
的工作,然後把繪制的內容交給GPU
渲染,包括多個View
的拼接(Compositing
)、紋理的渲染(Texture
)等等,最後顯示在屏幕上。但是,如果無法是16.7ms
30fps
以上,界面卡頓效果不明顯,那麽就需要在33.4ms
內完成View
的繪制,而低於這個幀率,就會產生卡頓的效果,影響體驗。
渲染的過程如下:
UIView
的layer
層有一個content
,指向一塊緩存,即backing store
UIView
繪制時,會調用drawRect
方法,通過context
將數據寫入backing store
- 在
backing store
寫完後,通過render server
交給GPU
去渲染,將backing store
中的bitmap
數據顯示在屏幕上
離屏渲染
在使用圓角、陰影和遮罩等視圖功能的時候,圖層屬性的混合體被指定為在未預合成之前不能直接在屏幕中繪制,所有就需要在屏幕外的上下文中渲染,即離屏渲染。
離屏渲染卡頓原因
離屏渲染之所以會特別消耗性能,是因為要創建一個屏幕外的緩沖區,然後從當屏緩沖區切換到屏幕外的緩沖區,然後再完成渲染;其中,創建緩沖區和切換上下文最消耗性能,而繪制其實不是性能損耗的主要原因。
設置了以下屬性時,就會觸發離屏繪制:
- shouldRasterize(光柵化)
- masks(遮罩)
- shadows(陰影)
- edge antialiasing(抗鋸齒)
- group opacity(不透明)
- 復雜形狀設置圓角等
- 漸變
屏幕渲染類型
CPU
計算好顯示內容提交到GPU
,GPU
渲染完成後將渲染結果放入幀緩沖區,隨後視頻控制器會按照 VSync
信號逐行讀取幀緩沖區的數據,經過可能的數模轉換傳遞給顯示器顯示。
屏幕渲染有如下三種:
GPU
中的屏幕渲染:
1、On-Screen Rendering
意為當前屏幕渲染,指的是GPU
的渲染操作是在當前用於顯示的屏幕緩沖區中進行
2、Off-Screen Rendering
意為離屏渲染,指的是GPU
在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作
3、CPU
中的離屏渲染(特殊離屏渲染,即不在GPU
中的渲染)
如果我們重寫了drawRect
方法,並且使用任何Core Graphics
的技術進行了繪制操作,就涉及到了CPU
渲染
CoreGraphic通常是線程安全的,所以可以進行異步繪制,顯示的時候再放回主線程
https://www.cnblogs.com/fishbay/p/7576176.html
一、概念理解
OpenGL中,GPU屏幕渲染有以下兩種方式:
○ On-Screen Rendering
意為當前屏幕渲染,指的是GPU的渲染操作是在當前用於顯示的屏幕緩沖區中進行。
○ Off-Screen Rendering
意為離屏渲染,指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作。
二、離屏渲染的是是非非
相比於當前屏幕渲染,離屏渲染的代價是很高的,主要體現在兩個方面:
· 創建新緩沖區
要想進行離屏渲染,首先要創建一個新的緩沖區。
· 上下文切換
離屏渲染的整個過程,需要多次切換上下文環境:先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束以後,將離屏緩沖區的渲染結果顯示到屏幕上有需要將上下文環境從離屏切換到當前屏幕。而上下文環境的切換是要付出很大代價的。
三、離屏渲染觸發方式
設置了以下屬性時,都會觸發離屏繪制:
· shouldRasterize(光柵化)
· masks(遮罩)
· shadows(陰影)
· edge antialiasing(抗鋸齒)
· group opacity(不透明)
需要註意的是,如果shouldRasterize被設置成YES,在觸發離屏繪制的同時,會將光柵化後的內容緩存起來,如果對應的layer及其sublayers沒有發生改變,在下一幀的時候可以直接復用。這將在很大程度上提升渲染性能。
而其它屬性如果是開啟的,就不會有緩存,離屏繪制會在每一幀都發生。
四、另一種特殊的“離屏渲染”
按照之前的說法,如果將不在GPU的當前屏幕緩沖區中進行的渲染都稱為離屏渲染,那麽就還有另一種特殊的“離屏渲染”方式:CPU渲染。
如果我們重寫了drawRect方法,並且使用任何Core Graphics的技術進行了繪制操作,就涉及到了CPU渲染。整個渲染過程由CPU在App內同步地完成,渲染得到的bitmap最後再交由GPU用於顯示。
五、Instruments
Instruments的Core Animation工具中有幾個和離屏渲染相關的檢查選項:
· Color Offscreen-Rendered Yellow
開啟後會把那些需要離屏渲染的圖層高亮成黃色,這就意味著黃色圖層可能存在性能問題。
· Color Hits Green and Misses Red
如果shouldRasterize被設置成YES,對應的渲染結果會被緩存,如果圖層是綠色,就表示這些緩存被復用;如果是紅色就表示緩存會被重復創建,這就表示該處存在性能問題了。
六、如何抉擇
現在擺在我們面前得有三個選擇:當前屏幕渲染、離屏渲染、CPU渲染,該用哪個呢?這需要根據具體的使用場景來決定。
· 盡量使用當前屏幕渲染
鑒於離屏渲染、CPU渲染可能帶來的性能問題,一般情況下,我們要盡量使用當前屏幕渲染。
· 離屏渲染 VS CPU渲染
由於GPU的浮點運算能力比CPU強,CPU渲染的效率可能不如離屏渲染;但如果僅僅是實現一個簡單的效果,直接使用CPU渲染的效率又可能比離屏渲染好,畢竟離屏渲染要涉及到緩沖區創建和上下文切換等耗時操作。
總之,具體的選擇應該由性能測試結果來決定。
七、寫在最後
在趙巖同學的點撥下才理解了離屏渲染的概念,在此表示感謝!
如理解有誤還請大家指出。
http://imgtec.eetrend.com/d6-imgtec/blog/2018-08/17019.html
在第一章的1.2節中有提到渲染的流程圖,我們再更深入點,先看看最基本的渲染通道流程:
引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps
註:iOS的GPU渲染機制是Tile-Based的,而Tile-Based GPU也是現在移動設備的主流;
我們再來看看需要Offscreen Render的渲染通道流程:
引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps
一般情況下,OpenGL會將應用提交到Render Server的動畫直接渲染顯示(基本的Tile-Based渲染流程),但對於一些復雜的圖像動畫的渲染並不能直接渲染疊加顯示,而是需要根據Command Buffer分通道進行渲染之後再組合,這一組合過程中,就有些渲染通道是不會直接顯示的;對比基本渲染通道流程和Masking渲染通道流程圖,我們可以看到到Masking渲染需要更多渲染通道和合並的步驟;而這些沒有直接顯示在屏幕的上的通道(如上圖的 Pass 1 和 Pass 2)就是Offscreen Rendering Pass。
Offscreen Render為什麽卡頓,從上圖我們就可以知道,Offscreen Render需要更多的渲染通道,而且不同的渲染通道間切換需要耗費一定的時間,這個時間內GPU會閑置,當通道達到一定數量,對性能也會有較大的影響;
那哪些情況會Offscreen Render呢?
1) drawRect 2) layer.shouldRasterize = true; 3) 有mask或者是陰影(layer.masksToBounds, layer.shadow*); 4)Text(UILabel, CATextLayer, Core Text, etc) ...
註:layer.cornerRadius,layer.borderWidth,layer.borderColor並不會Offscreen Render,因為這些不需要加入Mask。
還有更多與Offscreen Render以及動畫圖形優化相關的知識,請認真觀看WWDC。
參考:
-
WWDC2011 #121 Understanding UIKit Rendering
-
WWDC2014 #419 Advanced Graphics and Animations for iOS Apps
http://oncenote.com/2015/12/08/How-to-build-UI/
iOS離屏渲染的解釋