1.GPU屏幕渲染的兩種方式
-On-Screen Rendering 當(dāng)前屏幕渲染:指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行湘纵。
-Off-Screen Rendering 離屏渲染: 指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開(kāi)辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。
2.什么是離屏渲染
相信許多iOS開(kāi)發(fā)者及舍,都聽(tīng)說(shuō)過(guò)離屏渲染贤徒,大部分應(yīng)該是面試的時(shí)候被問(wèn)及芹壕。那么問(wèn)題來(lái)了,什么是離屏渲染接奈,原理又是什么哪
-什么是離屏渲染
先說(shuō)一下畫家算法
畫家算法踢涌,又稱深度排序法。
我們先看看它的算法:
(1)將畫布設(shè)成背景色序宦,
(2)然后頭腦簡(jiǎn)單的畫家首先繪制距離較遠(yuǎn)的場(chǎng)景,然后用繪制距離較近的場(chǎng)景覆蓋較遠(yuǎn)的部分睁壁。畫家算法首先將場(chǎng)景中的多邊形根據(jù)深度進(jìn)行排序,然后按照順序進(jìn)行描繪。這種方法通常會(huì)將不可見(jiàn)的部分覆蓋,這樣就可以解決可見(jiàn)性問(wèn)題互捌∨嗣鳎”
如上圖,要在屏幕上顯示圖3的ImageView疫剃,通常GPU的Render Server會(huì)遵循 “畫家算法” 按秩序先渲染圖1的那一層钉疫,然后渲染圖2的那一層硼讽,最后渲染圖3巢价,渲染好后的每一層都會(huì)存入幀緩存區(qū),然后按照次序繪制到屏幕固阁,當(dāng)繪制完一層壤躲,就會(huì)將該層從幀緩存區(qū)中移除(以節(jié)省空間)
但是有時(shí)候有些特殊情況,例如對(duì)多圖層的圖片進(jìn)行圓角顯示的時(shí)候备燃,一層一層渲染完成碉克,進(jìn)行圓角裁剪,當(dāng)前已經(jīng)渲染完成的圖片幀緩存區(qū)的數(shù)據(jù)渲染完成丟棄或者被覆蓋的時(shí)候 我們沒(méi)有辦法再去對(duì)圖層進(jìn)行裁剪并齐,因此需要?jiǎng)?chuàng)建一個(gè)離屏緩存區(qū)來(lái)存儲(chǔ)渲染完成的數(shù)據(jù)漏麦,等全部圖層渲染到離屏緩存區(qū)之后,我們?cè)偃〕鰜?lái)况褪,進(jìn)行裁剪操作撕贞,之后存在幀緩存區(qū),等待屏幕控制器讀取和操作测垛。
比如 UIImageView 要圓角. 然后還有背景.
此時(shí)它就先把你里面的背景色圖層渲染好,存到離屏緩存區(qū);
然后再把圖片渲染好, 存到離屏渲染緩存去.
最后在把這個(gè)2個(gè)存到離屏緩存區(qū)的圖層進(jìn)行圓角處理
然后再把結(jié)果放到幀緩存區(qū)
最后 顯示
3.離屏渲染的利弊
離屏渲染其實(shí)是加大了系統(tǒng)的負(fù)擔(dān)捏膨,確實(shí)會(huì)造成性能上的損耗
1.離屏渲染需要開(kāi)辟額外的存儲(chǔ)控件,是當(dāng)前屏幕的2.5倍,超過(guò)無(wú)效
2.從離屏緩沖區(qū)拷貝數(shù)據(jù)到幀緩沖區(qū)号涯,上下文切換耗性能
有那么多弊端為什么還要用離屏渲染
多次出現(xiàn)在屏幕當(dāng)中目胡,可以提前渲染好,可以復(fù)用链快,減壓CPU/GPU
還有一些特殊情況誉己,不得不使用離屏渲染(使用額外的離屏緩沖區(qū)(offscreen butter)保存中間狀態(tài),最后疊加域蜗、處理后繪制在屏幕上巫延,這樣就不得不使用離屏渲染)
4.常見(jiàn)的幾種離屏渲染的情況
使用了 mask 的 layer (layer.mask)
需要進(jìn)行裁剪的 layer (layer.masksToBounds /view.clipsToBounds)
設(shè)置了組透明度為 YES,并且透明度不為 1 的layer (layer.allowsGroupOpacity/ layer.opacity)
添加了投影的 layer (layer.shadow*)
采用了光柵化的 layer (layer.shouldRasterize)
繪制了文字的 layer (UILabel, CATextLayer, Core Text 等)
-離屏渲染的另一個(gè)情況(光柵化)
When the value of this property is YES, the layer is rendered as a bitmap in its local coordinate space and then composited to the destination with any other content
如果shouldRasterize被設(shè)置成YES地消,在觸發(fā)離屏繪制的同時(shí)炉峰,會(huì)將光柵化后的內(nèi)容緩存起來(lái),如果對(duì)應(yīng)的layer及其sublayers沒(méi)有發(fā)生改變脉执,在下一幀的時(shí)候可以直接復(fù)用疼阔。這將在很大程度上提升渲染性能
5.layer.cornerRadius和clipsToBounds = YES一定會(huì)觸發(fā)離屏渲染嗎
開(kāi)始代碼測(cè)試前,我們先開(kāi)啟離屏渲染的檢測(cè)半夷,在模擬器打開(kāi)color offscreen-rendered,開(kāi)啟后會(huì)把那些需要離屏渲染的圖層高亮成黃色婆廊。
上代碼
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
btn1.frame = CGRectMake(kScreenWidth/2-50, 120, 100, 100);
btn1.layer.cornerRadius = 50;
[self.view addSubview:btn1];
[btn1 setImage:[UIImage imageNamed:@"test.png"] forState:UIControlStateNormal];
btn1.clipsToBounds = YES;
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame = CGRectMake(kScreenWidth/2-50, 260, 100, 100);
btn2.layer.cornerRadius = 50;
btn2.backgroundColor = [UIColor orangeColor];
[self.view addSubview:btn2];
btn2.clipsToBounds = YES;
UIImageView *image3 = [[UIImageView alloc]initWithFrame:CGRectMake(kScreenWidth/2-50, 400, 100, 100)];
image3.backgroundColor = [UIColor orangeColor];
[self.view addSubview:image3];
image3.image = [UIImage imageNamed:@"test.png"];
image3.layer.cornerRadius = 50;
image3.clipsToBounds = YES;
UIImageView *image4 = [[UIImageView alloc]initWithFrame:CGRectMake(kScreenWidth/2-50, 540, 100, 100)];
[self.view addSubview:image4];
image4.layer.cornerRadius = 50;
image4.clipsToBounds = YES;
image4.image = [UIImage imageNamed:@"test.png"];
run一下看效果
我們創(chuàng)建了兩個(gè)button 兩個(gè)imageview 為什么1和3觸發(fā)了離屏渲染 2和4沒(méi)有觸發(fā)
分析一下
按鈕1和2 的區(qū)別就是一個(gè)是設(shè)置了背景圖片,一個(gè)設(shè)置的是顏色巫橄,可以看出淘邻,離屏渲染觸發(fā)的條件就是存在多組不同的layer需要渲染在一個(gè)視圖上,而GPU無(wú)法一次性全部渲染時(shí)湘换,需要保存離屏緩存區(qū)宾舅,等把這個(gè)2個(gè)存到離屏緩存區(qū)的圖層進(jìn)行圓角處理的時(shí)候才會(huì)觸發(fā)。
imageview3和4的去區(qū)別就是一個(gè)設(shè)置了背景色一個(gè)沒(méi)有彩倚,為啥3觸發(fā)了離屏渲染筹我,因?yàn)?里面包好了背景色,圖片帆离,圓角處理蔬蕊,渲染完背景色,再去渲染圖片哥谷,再去圓角裁剪的時(shí)候岸夯,可能背景色已經(jīng)被幀緩存區(qū)給移除了,所以需要觸發(fā)離屏渲染们妥,image4 直接渲染裁剪就可以顯示
可能有人會(huì)提問(wèn)了猜扮,為啥button1只設(shè)置了圖片但是觸發(fā)了離屏渲染,因?yàn)閁Ibutton有一個(gè)UIImageView屬性王悍,離屏渲染的觸發(fā)兩個(gè)條件破镰,重組和渲染,按鈕里面的imageview屬性先去渲染圖片 再去裁剪imageview 再去裁剪按鈕 ,button的bg是layer鲜漩,只有在重組源譬,重新渲染才會(huì)離屏渲染。
-所以總結(jié)了一下并不是layer.cornerRadius和clipsToBounds = YES一定會(huì)觸發(fā)離屏渲染嗎孕似,