什么是離屏渲染
首先我們要了解GPU的渲染機(jī)制以及屏幕渲染的方式
-
GPU渲染機(jī)制:
CPU通過(guò)解壓計(jì)算好顯示內(nèi)容通過(guò)系統(tǒng)總線(xiàn)到 GPU,GPU 渲染完成后將渲染結(jié)果放入幀緩沖區(qū)锯仪,隨后視頻控制器會(huì)按照VSync
信號(hào)逐行讀取幀緩沖區(qū)的數(shù)據(jù)疯汁,經(jīng)過(guò)可能的數(shù)模轉(zhuǎn)換傳遞給顯示器顯示。
GPU屏幕渲染有以下兩種方式:
-
OnScreen Rendering
表示當(dāng)前屏幕渲染卵酪,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行幌蚊。
正常渲染流程
APP通過(guò)CPU計(jì)算和GPU渲染,然后將他們的結(jié)果放到幀緩存區(qū)frame buffer
中溃卡,在通過(guò)視頻控制器將幀緩存區(qū)中的結(jié)果取出溢豆,顯示到屏幕上面。 -
OffScreen Rendering
表示離屏渲染瘸羡,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開(kāi)辟一個(gè)緩沖區(qū)進(jìn)行渲染操作漩仙。
離屏渲染流程
當(dāng)App需要進(jìn)行額外的渲染和合并時(shí),例如圖片切圓角犹赖,我們需要對(duì)圖片中所有的圖層進(jìn)行圓角剪切队他,然后在將所有圖層的處理后的結(jié)果存入幀緩存區(qū)中,再由視頻控制器從幀緩存區(qū)中取出由屏幕來(lái)顯示峻村;在OnScreen Rendering
中是無(wú)法做到對(duì)所有的圖層進(jìn)行裁剪圓角的麸折,因?yàn)閷?duì)多圖層圖片進(jìn)行分層裁剪時(shí),是用完一個(gè)丟棄一個(gè)的粘昨,從而節(jié)省空間垢啼。這使得我們需要提前將處理好的結(jié)果放入到離屏緩存區(qū)中,等待所有圖層處理完后张肾,再將所有的圖層合并到一起芭析。
特殊的離屏渲染:如果將不在GPU的當(dāng)前屏幕緩沖區(qū)中進(jìn)行的渲染都稱(chēng)為離屏渲染,那么就還有另一種特殊的“離屏渲染”方式: CPU渲染吞瞪。如果我們重寫(xiě)了drawRect
方法馁启,并且使用任何Core Graphics
的技術(shù)進(jìn)行了繪制操作,就涉及到了CPU渲染芍秆。整個(gè)渲染過(guò)程由CPU在App內(nèi) 同步地完成惯疙,渲染得到的bitmap
最后再交由GPU用于顯示。
離屏渲染一些問(wèn)題的總結(jié):
1.離屏緩存區(qū)再給我們帶來(lái)方便同時(shí)浪听,也會(huì)帶來(lái)一些問(wèn)題螟碎,由于離屏渲染是額外開(kāi)辟一個(gè)空間,所以在數(shù)據(jù)的轉(zhuǎn)存到幀緩存區(qū)時(shí)需要時(shí)間迹栓,并且在轉(zhuǎn)存過(guò)程中可能會(huì)出現(xiàn)掉幀的情況掉分。
2.離屏緩存空間并不是無(wú)限大的俭缓,它最大只能是屏幕像素大小的2.5倍。
3.離屏渲染在我們處理一些特殊效果酥郭,這種效果不能一次性完成华坦,需要使用離屏緩存區(qū)來(lái)保持 中間的狀態(tài),也就是 不得不使用的時(shí)候不从,這時(shí)候離屏渲染是系統(tǒng)觸發(fā)的惜姐,比如:光柵化、高斯模糊等椿息。
4.如果一個(gè)效果需要實(shí)現(xiàn)多次歹袁,我們可以提前渲染保存到離屏緩存區(qū),來(lái)達(dá)到復(fù)用的目的寝优,這個(gè)時(shí)候就需要開(kāi)發(fā)者手動(dòng)觸發(fā)条舔。
離屏渲染實(shí)例化剖析
1,如何檢測(cè)項(xiàng)目中哪些圖層觸發(fā)了離屏渲染
- 打開(kāi)模擬器設(shè)置點(diǎn)擊
Debug
按鈕-勾選Color Off-screen Render
模擬器設(shè)置
2乏矾,代碼解析離屏渲染
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//1.按鈕存在背景圖片
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
btn1.frame = CGRectMake(100, 30, 100, 100);
btn1.layer.cornerRadius = 50;
[self.view addSubview:btn1];
[btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
btn1.clipsToBounds = YES;
//2.按鈕不存在背景圖片
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame = CGRectMake(100, 180, 100, 100);
btn2.layer.cornerRadius = 50;
btn2.backgroundColor = [UIColor blueColor];
[self.view addSubview:btn2];
btn2.clipsToBounds = YES;
//3.UIImageView 設(shè)置了圖片+背景色;
UIImageView *img1 = [[UIImageView alloc]init];
img1.frame = CGRectMake(100, 320, 100, 100);
img1.backgroundColor = [UIColor blueColor];
[self.view addSubview:img1];
img1.layer.cornerRadius = 50;
img1.layer.masksToBounds = YES;
img1.image = [UIImage imageNamed:@"btn.png"];
//4.UIImageView 只設(shè)置了圖片,無(wú)背景色;
UIImageView *img2 = [[UIImageView alloc]init];
img2.frame = CGRectMake(100, 480, 100, 100);
[self.view addSubview:img2];
img2.layer.cornerRadius = 50;
img2.layer.masksToBounds = YES;
img2.image = [UIImage imageNamed:@"btn.png"];
}
結(jié)合上面的代碼孟抗,設(shè)置模擬器就可以查看查看離屏渲染的圖層,黃色背景的就是觸發(fā)了離屏渲染3钻心,離屏渲染的原因
-
shouldRasterize
光柵化凄硼,當(dāng)開(kāi)啟光柵化,就會(huì)將layer渲染為位圖并保存在緩存中捷沸,在下次使用就可以直接進(jìn)行復(fù)用摊沉,來(lái)提高效率。
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
光柵化使用建議:
1坯钦,如果layer
不能被復(fù)用,則沒(méi)有必要打開(kāi)光柵化侈玄;
2,如果layer
不是靜態(tài)的吟温,需要被頻繁修改序仙,比如處于動(dòng)畫(huà)之中,那么開(kāi)啟離屏渲染 反而影響效率了鲁豪;
3潘悼,離屏渲染緩存內(nèi)容有時(shí)間限制,緩存內(nèi)容100ms
內(nèi)容如果沒(méi)有被使用爬橡,那么它就會(huì)被丟棄治唤,無(wú)法進(jìn)行復(fù)用了;
4糙申,離屏渲染緩存空間有限宾添,超過(guò)2.5倍
屏幕像素大小的話(huà),也無(wú)法進(jìn)行復(fù)用了; - 圓角觸發(fā)離屏渲染
offscreen Rendering
缕陕,我們首先要清楚CALayer
的組成結(jié)構(gòu)粱锐,包括:backgroundColor、contents扛邑、borderWidth&borderColor
三部分怜浅。offscreen Rendering
設(shè)置layer.cornerRaclius
只會(huì)設(shè)置backgroundColor
的圓角,不會(huì)設(shè)置content
的圓角蔬崩,除非同時(shí)設(shè)置了layer.masksToBounds
為True
(對(duì)應(yīng)view
中的clipsToBounds
屬性);圓角的離屏渲染的原因view.layer.masksToBounds = true;
恶座。
總結(jié):常見(jiàn)觸發(fā)離屏渲染的幾種情況
- 使用了
mask
的layer(layer.mask)
; - 需要進(jìn)行裁剪的layer
(layer.masksToBounds / view.clipsToBounds)
; - 設(shè)置了組透明度為
Yes
沥阳,并且透明度不為1的layer(layer.allowsGroupOpacity / layer.opacity)
奥裸; - 添加了投影的layer
(layer.shadow)
; - 采用了光柵化的layer
(layer.shouldRaterize)
; - 繪制了文字的layer
(UILable,CAtextLayer,Core Text等)
;