1.什么是離屏渲染
1.1渲染方式一:當前屏幕渲染
On-Screen Rendering意為當前屏幕渲染裂允,指的是GPU的渲染操作是在當前用于顯示的屏幕緩沖區(qū)中進行
APP將要渲染的信息提交給CPU挪鹏,CPU通過一定的處理后提交給GPU征绎。GPU不停的將內容渲染完成放到幀緩沖區(qū)中(FrameBuffer),最后顯示到屏幕上额港。
1.2渲染方式二:屏幕渲染
Off-Screen Rendering 離屏渲染: GPU在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作
GPU把渲染好的的內容存放到離屏渲染緩沖區(qū)中伤极,在離屏渲染緩沖區(qū)(OffscreenBuffer)中進一步做一些處理后鲫咽,再提交到幀緩沖區(qū)(FrameBuffer)中
總結來說就是:
- 圖像/圖形渲染的流程:GPU進?渲染->幀緩存區(qū)? ->視頻控制器->讀取幀緩存區(qū)信息(位圖) -> 數(shù)模轉化(數(shù)字信號處->模 擬型號) ->(逐?掃描)顯示。
- 當幀緩沖區(qū)的數(shù)據(jù)不能直接被視頻控制器掃描顯示的時候溃斋,我們需要開辟一些離屏緩存區(qū)來存放一些中間狀態(tài)的數(shù)據(jù)界拦,等待全部的圖層都渲染到離屏緩存區(qū)之后,分別從各個離屏緩存區(qū)取出數(shù)據(jù)梗劫,分別做相應的操作(裁剪等)之后享甸,組合存入幀緩存區(qū),再等待屏幕控制器的讀取和屏幕刷新
下面通過一個簡單案例說明:
上圖是實現(xiàn)一個毛玻璃效果梳侨,每次處理一個圖層的層級都會先存到一個離屏緩沖區(qū)蛉威,等到所有層級都渲染完然后在交給幀緩存區(qū)組合,最后顯示到屏幕上走哺。
2.離屏渲染的檢測
模擬器打開color offscreen-rendered,開啟后會把那些觸發(fā)離屏渲染的圖層高亮成黃色
3.離屏渲染的利弊
劣勢
- 創(chuàng)建新緩沖區(qū)蚯嫌,要想進行離屏渲染,首先要創(chuàng)建一個新的緩沖區(qū)
- 離屏渲染的整個過程丙躏,需要多次切換上下文環(huán)境:先是從當前屏幕(On-Screen)切換到離屏(Off-Screen)择示,等到離屏渲染結束以后,將離屏緩沖區(qū)的渲染結果顯示到屏幕上有需要將上下文環(huán)境從離屏切換到當前屏幕晒旅。而上下文環(huán)境的切換是要付出很大代價的
- 離屏渲染需要額外的存儲空間栅盲,存儲空間大小的上限是2.5倍的屏幕像素大小,一旦超過废恋,則無法使用離屏渲染
- 容易掉幀:一旦因為離屏渲染導致最終存入幀緩存區(qū)的時候谈秫,已經(jīng)超過了16.67ms,則會出現(xiàn)掉幀的情況
優(yōu)勢
- 用戶需要特殊的渲染效果:使用額外的離屏緩沖區(qū)(offscreen butter)保存中間狀態(tài)鱼鼓,最后疊加拟烫、處理后繪制在屏幕上,這樣就不得不使用離屏渲染
- 效率優(yōu)勢:需要多次使用的效果蚓哩,提前渲染存入離屏緩沖區(qū)构灸,然后復用來提高效率
4.離屏渲染的幾種情況
使用了 mask 的 layer (layer.mask)
mask是應用在layer和其所有子layer的組合之上的,而且可能帶有透明度,只有到整個layer樹畫完之后,再統(tǒng)一加上mask喜颁,最后和底下其他layer的像素進行組合需要進行裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
layer包含有三層:border稠氮,content,background
如果只是設置clipsToBounds或者不會觸發(fā)離屏渲染半开,clipsToBounds之后作用在backgroundColor和border上隔披,content上不會有圓角,只有同時設置了clipsToBounds和masksToBounds,并且contents有內容或者內容的背景不是透明的才會觸發(fā)離屏渲染寂拆,并實現(xiàn)content圓角裁減
注:view.clipsToBounds對應layer.cornerRadius設置了組透明度為 YES奢米,并且透明度不為 1 的 layer (layer.allowsGroupOpacity/ layer.opacity)
CALayer的allowsGroupOpacity屬性,UIView 的alpha屬性等同于 CALayer opacity屬性纠永。GroupOpacity=YES鬓长,子layer 在視覺上的透明度的上限是其父 layer 的opacity。
當父視圖的layer.opacity != 1.0時尝江,會開啟離屏渲染涉波,opacity并不是分別應用在每一層之上,而是只有到整個layer樹畫完之后炭序,再統(tǒng)一加上opacity啤覆,最后和底下其他layer的像素進行組合
當父視圖的layer.opacity == 1.0時,父視圖不用管子視圖惭聂,只需顯示當前視圖即可窗声。
為了讓子視圖與父視圖保持同樣的透明度,從 iOS 7 以后默認全局開啟了這個功能辜纲。我們可以設置layer的opacity值為YES笨觅,減少復雜圖層合成添加了投影的 layer (layer.shadow)
layer本身是一塊矩形區(qū)域,陰影會作用在所有子layer所組成的形狀上侨歉,只能等全部子layer畫完才能得到陰影屋摇,當陰影的圖形本身(layer和其子layer)都還沒有被組合到一起是不能確定陰影的形狀和渲染,我們只能另外申請一塊內存幽邓,把圖形本身先畫好炮温,再根據(jù)渲染結果的形狀,添加陰影到frame buffer牵舵,最后把內容畫上去柒啤。
如果我們能夠預先告訴CoreAnimation(通過shadowPath屬性)陰影的幾何形狀,那么陰影可以先被獨立渲染出來畸颅,不需要依賴layer圖形本身担巩,就不需要離屏渲染了,因此可以通過設置shadowPath來優(yōu)化性能采用了光柵化的 layer (layer.shouldRasterize)
如果layer的shouldRasterize被設置成YES没炒,在觸發(fā)離屏繪制的同時涛癌,會將光柵化后的內容緩存起來,如果對應的layer及其sublayers沒有發(fā)生改變,在下一幀的時候可以直接復用拳话。這將在很大程度上提升渲染性能
使用光柵化時先匪,可以開啟“Color Hits Green and Misses Red”來檢查該場景下光柵化操作是否是一個好的選擇。綠色表示緩存被復用弃衍,紅色表示緩存在被重復創(chuàng)建呀非。
使用注意:
如果layer不能被復用,則不必打開光柵化
如果layer不是靜態(tài)的镜盯,需要被頻繁修改岸裙,如處于動畫中,那開啟離屏渲染反而會影響效率
離屏渲染緩存內容有時間限制速缆,緩存內容100ms內沒有被使用降允,那么它就會丟棄,無法進行復用了
離屏渲染緩存空間有限激涤,不能超過2.5倍屏幕像素大小繪制了文字的 layer (UILabel, CATextLayer, Core Text 等)
使用高斯模糊(毛玻璃)效果
iOS的控制屏幕顯示推送通知頁面或者UIVisualEffectViewedge antialiasing(抗鋸齒)
設置 allowsEdgeAntialiasing 屬性為YES(默認為NO)