什么是離屏渲染
首先我們來(lái)看一下渲染架構(gòu):
CPU 計(jì)算好顯示內(nèi)容提交到 GPU,如果要在顯示屏上顯示內(nèi)容,我們至少需要一塊與屏幕像素?cái)?shù)據(jù)量一樣大的幀緩沖區(qū)(frame buffer)萍聊,作為像素?cái)?shù)據(jù)存儲(chǔ)區(qū)域,而這也是GPU存儲(chǔ)渲染結(jié)果的地方黔漂。GPU 渲染完成后將渲染結(jié)果放入frame buffer蹦骑,隨后視頻控制器會(huì)按照 VSync 信號(hào)逐行讀取frame buffer的數(shù)據(jù),經(jīng)過(guò)可能的數(shù)模轉(zhuǎn)換傳遞給顯示器顯示区岗。顯示完成后略板,frame buffer中的數(shù)據(jù)直接丟棄。
如果有時(shí)因?yàn)槊媾R一些限制慈缔,無(wú)法把渲染結(jié)果直接寫(xiě)入frame buffer叮称,而是先暫存在另外的內(nèi)存區(qū)域,之后再寫(xiě)入frame buffer藐鹤,那么這個(gè)過(guò)程被稱(chēng)之為離屏渲染瓤檐。
這里要注意,被iOS認(rèn)定的離屏渲染是GPU產(chǎn)生的娱节,如果重寫(xiě)了drawRect方法挠蛉,并且使用任何Core Graphics 的技術(shù)進(jìn)行了繪制操作,就涉及到CPU渲染肄满。這種CPU渲染可以成為軟件渲染谴古,不會(huì)被iOS系統(tǒng)認(rèn)定為離屏渲染质涛。
離屏渲染帶來(lái)的影響
1)額外的內(nèi)存
離屏渲染需要開(kāi)放一塊內(nèi)存存放渲染數(shù)據(jù),這部分內(nèi)存最大為屏幕展示的2.5倍
2)上下文切換
離屏渲染的整個(gè)過(guò)程需要切換上下文環(huán)境掰担,先從當(dāng)前屏幕切換到離屏汇陆,等結(jié)束后,又要將上下文環(huán)境切換回來(lái)恩敌。雖然在iOS中瞬测,設(shè)備主存和GPU的顯存共享物理內(nèi)存,這樣可以省去一些數(shù)據(jù)傳輸開(kāi)銷(xiāo)纠炮,但是還是要占用系統(tǒng)資源月趟。
離屏渲染的原因
先說(shuō)下我的理解:
離屏渲染是由于渲染層之間存在依賴(lài)引發(fā)的。
在上面的渲染流水線(xiàn)示意圖中我們可以看到恢口,主要的渲染操作都是由CoreAnimation的Render Server模塊孝宗,通過(guò)調(diào)用顯卡驅(qū)動(dòng)所提供的OpenGL/Metal接口來(lái)執(zhí)行的。通常對(duì)于每一層layer耕肩,Render Server會(huì)遵循“畫(huà)家算法”因妇,按次序輸出到frame buffer,后一層 After Layer 會(huì)覆蓋前一層 Before Layer猿诸,就能得到最終的顯示結(jié)果婚被。
但是,如果此時(shí)After Layer的渲染數(shù)據(jù)是半透明的梳虽,需要混合Before Layer的數(shù)據(jù)址芯,按照正常的渲染流程,此時(shí)frame buffer中Before Layer已經(jīng)被丟棄了窜觉,根本無(wú)法進(jìn)行混合谷炸。為了保留Before Layer,iOS開(kāi)辟了離屏渲染緩沖區(qū)禀挫,對(duì)Before Layer進(jìn)行存儲(chǔ)旬陡。
圓角 與 masksToBounds
首先我們要了解 CALayer的層次結(jié)構(gòu):CALayer由背景色backgroundColor、內(nèi)容contents语婴、邊緣borderWidth&borderColor構(gòu)成
cornerRadius的文檔中明確說(shuō)明:
cornerRadius的設(shè)置只對(duì) CALayer 的背景色層和邊緣層起作用
當(dāng)我們只配置cornerRadius時(shí)描孟,背景色層和邊緣層生成圓角,內(nèi)容層的渲染數(shù)據(jù)是直接覆蓋背景色層砰左,邊緣層的數(shù)據(jù)也是同樣画拾,這時(shí)候不會(huì)觸發(fā)離屏渲染
當(dāng)我們配置 masksToBounds的時(shí)候,這個(gè)時(shí)候菜职,內(nèi)容層也要被裁剪圓角青抛,而裁剪方式依賴(lài)邊緣層。還記得我們說(shuō)過(guò)的酬核,離屏渲染是由于渲染層之間存在依賴(lài)引發(fā)的∶哿恚現(xiàn)在內(nèi)容層與邊緣層就相互依賴(lài)了适室,從而導(dǎo)致了離屏渲染。
常見(jiàn)的觸發(fā)離屏渲染情況
- 需要進(jìn)行裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
在View上設(shè)置相同形狀的Mask View举瑰,要背裁剪掉的部分設(shè)置成背景色
- 設(shè)置了組透明度為 YES捣辆,并且透明度不為 1 的 layer (layer.allowsGroupOpacity/ layer.opacity)
這個(gè)只能盡量不要使用
- 添加了投影的 layer (layer.shadow*)
使用陰影路徑計(jì)算
繪制了文字的 layer (UILabel, CATextLayer, Core Text 等)
盡量使用Label代替采用了光柵化的 layer (layer.shouldRasterize)
這是主動(dòng)進(jìn)行離屏渲染,提高渲染效率.