關于離屏渲染,面試中經(jīng)常發(fā)現(xiàn)面試者知道因為圓角、陰影等造成,但是什么是離屏渲染,為什么產(chǎn)生,會有什么影響,其實,都不是非常了解,這里我們通過文章底部的兩篇參考文章,把這幾個點簡單梳理一下,讓我自己能從自己的角度去按順序理解離屏渲染,也讓大家有個明確的理解,可以先去看看原文。
一、什么是離屏渲染
先聊聊常規(guī)渲染是什么樣的
我們先來看看常規(guī)渲染和離屏渲染的流程,如下圖:


如上圖,GPU 屏幕渲染有兩種方式:
- On-Screen Rendering,意為當前屏幕渲染,指的是 GPU 的渲染操作是在當前用于顯示的屏幕緩沖區(qū)中進行。
- Off-Screen Rendering,意為離屏渲染,指的是 GPU 在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作。
常規(guī)渲染流程中的GPU 渲染機制為:CPU 計算好顯示內(nèi)容提交到 GPU,GPU 渲染完成后將渲染結(jié)果放入幀緩沖區(qū),隨后視頻控制器會按照 VSync 信號逐行讀取幀緩沖區(qū)的數(shù)據(jù),經(jīng)過可能的數(shù)模轉(zhuǎn)換傳遞給顯示器顯示。
再說說離屏渲染是什么
我們可以看到上圖中有一個離屏緩沖區(qū),離屏渲染主要有這些用處:
1、為了能夠直接復用一些常用的圖層數(shù)據(jù),將其緩存在離屏緩存中。
2、為了將一些常規(guī)渲染流程無法完成的特殊效果如圓角、陰影和遮罩、高斯模糊、半透明圖層混合等正常的渲染流程采用油畫算法由遠及近的渲染圖層進行處理并緩存,當該圖層顯示到屏幕上后,幀緩沖區(qū)再來刪除這一圖層的數(shù)據(jù),過程參照下圖:

常規(guī)渲染流程將這張圖顯示到屏幕上可以分為兩步:
1、先繪制黃色背景圖層,顯示到屏幕上后,刪除幀緩沖區(qū)中黃色圖層的數(shù)據(jù)。
2、再渲染藍色圖層,顯示藍色圖層到屏幕后,刪除幀緩沖區(qū)中藍色圖層數(shù)據(jù)。
但是如果給圖層設置了特殊效果則有可能需要觸發(fā)離屏渲染,以圓角為例,此時是不走常規(guī)渲染的,為什么,如下圖所示:

我們想要是如右圖所示的效果,設置圓角后包括子視圖也進行圓角裁剪。
但是按照正常流程顯示完黃色圖層后,在渲染藍色圖層進行圓角設置時(超出時按圓角裁剪,未超出則不需要裁剪),已經(jīng)找不到黃色圖層的數(shù)據(jù)。因此,需要增加離屏緩沖區(qū),將后續(xù)要用到的圖層數(shù)據(jù)先緩存起來,在后續(xù)用到時進行渲染顯示。
當使用圓角,陰影,遮罩的時候,圖層屬性的混合體被指定為在未預合成之前(下一個
VSync 信號開始前)不能直接在屏幕中繪制,所以就需要屏幕外渲染被喚起。屏幕外渲染并不意味著軟件繪制,但是它意味著圖層必須在被顯示之前在一個屏幕外上下文中被渲染(不論 CPU 還是 GPU)。
以上,就解答了第一個問題,什么是離屏渲染。
二、離屏渲染會產(chǎn)生什么問題
1、如上圖所示相比于正常的渲染流程,離屏渲染需要額外創(chuàng)建一個離屏緩沖區(qū),需要 多耗費一些空間;
2、觸發(fā)離屏渲染后,需要先從 Frame Buffer 切換到 Off-Screen Buffer ,渲染完畢后再切換回 Frame Buffer,這一過程需是比較 耗費性能 的,因為要來回切換上下文;
3、數(shù)據(jù)由 Off-Screen Buffer 取出,再存入 Frame Buffer 也需要 耗費時間 ,這樣增加了 掉幀 的可能性;由于垂直同步的機制,如果在一個 VSync 時間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏會保留之前的內(nèi)容不變。這就是界面卡頓的原因。
4、 離屏緩沖區(qū)存在 空間限制 ,即屏幕像素的 2.5倍,當大于這一值時便不會觸發(fā)離屏渲染。(有待擴展相關知識面,當需要離屏渲染時又超出離屏渲染的空間限制后,對應超出部分會產(chǎn)生什么問題?)
三、如何手動觸發(fā)離屏渲染及離屏渲染使用建議
如上文所述,實現(xiàn)一些特殊效果例如圓角、陰影和遮罩、抗鋸齒、高斯模糊、半透明圖層混合等會觸發(fā)離屏渲染。
重點說說光柵化,當設置view.layer.shouldRasterize 為 true時,也會觸發(fā)離屏渲染。
光柵化概念:將圖轉(zhuǎn)化為一個個柵格組成的圖象。光柵化特點:每個元素對應幀緩沖區(qū)中的一像素。shouldRasterize = YES 在其他屬性觸發(fā)離屏渲染的同時,會將光柵化后的內(nèi)容緩存起來,如果對應的 layer 及其 sublayers 沒有發(fā)生改變,在下一幀的時候可以直接復用。shouldRasterize = YES 這將隱式的創(chuàng)建一個位圖,各種陰影遮罩等效果也會保存到位圖中并緩存起來,從而減少渲染的頻度。相當于光柵化是把 GPU的操作轉(zhuǎn)到 CPU 上了,生成位圖緩存,直接讀取復用。
當你使用光柵化時,你可以開啟 Color Hits Green and Misses Red 來檢查該場景下光柵化操作是否是一個好的選擇。綠色表示緩存被復用,紅色表示緩存在被重復創(chuàng)建。如果光柵化的層變紅得太頻繁那么光柵化對優(yōu)化可能沒有多少用處。位圖緩存從內(nèi)存中刪除又重新創(chuàng)建得太過頻繁,紅色表明緩存重建得太遲??梢葬槍π缘倪x擇某個較小而較深的層結(jié)構(gòu)進行光柵化,來嘗試減少渲染時間。對于經(jīng)常變動的內(nèi)容,這個時候不要開啟,否則會造成性能的浪費。例如經(jīng)常打交道的 TableViewCell,因為 TableViewCell 的重繪是很頻繁的(因為 Cell 的復用),如果 Cell 的內(nèi)容不斷變化,則 Cell 需要不斷重繪,如果此時設置了 cell.layer 可光柵化,則會造成大量的離屏渲染,降低圖形性能。
因此對于是否開啟 shouldRasterize 有以下建議:
- 如果緩存的圖像在之后用不到或很少用到( 100ms內(nèi)用不到 ),則不需要開啟
shouldRasterize - 如果緩存的圖像會經(jīng)常發(fā)生變動,比如本身處于動畫中,或者像
tabeleView的cell的上圖片可能經(jīng)常改變,則不要開啟shouldRasterize - 緩存的圖像過大,超過屏幕像素的 2.5 倍,不會觸發(fā)離屏渲染,所以開啟
shouldRasterize也沒有效果.
四、總結(jié)
1、iOS圖形渲染流程分為 正常渲染流程 和 離屏渲染流程 ;
2、離屏渲染是在幀緩沖區(qū)之外開辟了一個臨時的緩沖區(qū),用于保存一些暫時沒有用到的數(shù)據(jù),之后會從離屏緩沖區(qū)取出,渲染后再放入幀緩沖區(qū);
3、離屏渲染會有一定的性能問題,但是我們依然會有使用到的地方;
4、離屏緩沖區(qū)最大為 屏幕像素的2.5倍 ,超出不會觸發(fā)離屏渲染;
5、設置圓角不一定會觸發(fā)離屏渲染,但是如果有 多個圖層 ,則會觸發(fā)離屏渲染。
五、調(diào)試
Instruments 監(jiān)測離屏渲染:
-
Color Offscreen-Rendered Yellow,開啟后會把那些需要離屏渲染的圖層高亮成黃色,這就意味著黃色圖層可能存在性能問題。 -
Color Hits Green and Misses Red,如果shouldRasterize被設置成YES,對應的渲染結(jié)果會被緩存,如果圖層是綠色,就表示這些緩存被復用;如果是紅色就表示緩存會被重復創(chuàng)建,這就表示該處存在性能問題了。
六、優(yōu)化記錄
iOS 版本上的優(yōu)化:
-
iOS 9.0之前UIimageView、UIButton設置圓角都會觸發(fā)離屏渲染。 -
iOS 9.0之后UIButton設置圓角會觸發(fā)離屏渲染,而UIImageView里png圖片設置圓角不會觸發(fā)離屏渲染了,如果設置其他陰影效果之類的還是會觸發(fā)離屏渲染的。
原創(chuàng)參考文章:
面試題型—iOS離屏渲染探索
iOS面試題:聊一聊iOS 中的離屏渲染?