屏幕上最終顯示的數(shù)據(jù)有兩種加載流程
正常渲染加載流程
離屏渲染加載流程
從圖上看,他們之間的區(qū)別就是離屏渲染比正常渲染多了一個(gè)離屏緩沖區(qū),這個(gè)緩沖區(qū)的作用是什么呢檀何?下面來仔細(xì)說說
首先,說說正常渲染流程
正常渲染流程
APP中的數(shù)據(jù)經(jīng)過CPU計(jì)算和GPU渲染后,將結(jié)果存放在幀緩沖區(qū)愕把,利用視頻控制器從幀緩沖區(qū)中取出拣凹,并顯示到屏幕上。
在GPU的渲染流程中恨豁,顯示到屏幕上的圖像是遵循大畫家算法按照由遠(yuǎn)及近的順序嚣镜,依次將結(jié)果存儲(chǔ)到幀緩沖區(qū)
視屏控制器從幀緩沖區(qū)中讀取一幀數(shù)據(jù),將其顯示到屏幕上后圣絮,會(huì)立即丟棄這幀數(shù)據(jù)祈惶,不會(huì)做任何保留,這樣做的目的是可以節(jié)省空間扮匠,且在屏幕上是各自顯示各自的捧请,互相不影響。
離屏渲染流程
當(dāng)App需要進(jìn)行額外的渲染和合并時(shí)棒搜,例如按鈕設(shè)置圓角疹蛉,我們是需要對(duì)UIButton這個(gè)控件中的所有圖層都進(jìn)行圓角+裁剪,然后再將合并后的結(jié)果存入幀緩存區(qū)力麸,再?gòu)膸彺嬷腥〕鼋挥善聊伙@示可款,這時(shí),在正常的渲染流程中克蚂,我們是無法做到對(duì)所有圖層進(jìn)行圓角裁剪的闺鲸,因?yàn)樗怯靡粋€(gè)丟一個(gè)。所以我們需要提前將處理好的結(jié)果放入離屏緩沖區(qū)埃叭,最后將幾個(gè)圖層進(jìn)行疊加合并摸恍,存放到站緩沖區(qū),最后屏幕上就是我們想實(shí)現(xiàn)的效果赤屋。
離屏緩存區(qū)就是一個(gè)臨時(shí)的緩沖區(qū)立镶,用來存放在后續(xù)操作使用,但目前并不使用的數(shù)據(jù)类早。
離屏渲染再給我們帶來方便的同時(shí)媚媒,也帶來了嚴(yán)重的性能問題。由于離屏渲染中的離屏緩沖區(qū)涩僻,是額外開辟的一個(gè)存儲(chǔ)空間缭召,當(dāng)它將數(shù)據(jù)轉(zhuǎn)存到Frame Buffer時(shí),也是需要耗費(fèi)時(shí)間的逆日,所以在轉(zhuǎn)存的過程中恼琼,仍有掉幀的可能。
離屏緩沖區(qū)的空間并不是無限大的屏富, 它是又上限的晴竞,最大只能是屏幕的2.5倍
那為什么我們明知有性能問題時(shí),還是要使用離屏渲染呢狠半?
可以處理一些特殊的效果噩死,這種效果并不能一次就完成颤难,需要使用離屏緩沖區(qū)來保存中間狀態(tài),不得不使用離屏渲染已维,這種情況下的離屏渲染是系統(tǒng)自動(dòng)觸發(fā)的行嗤,例如經(jīng)常使用的圓角、陰影垛耳、高斯模糊栅屏、光柵化等
可以提升渲染的效率,如果一個(gè)效果是多次實(shí)現(xiàn)的堂鲜,可以提前渲染栈雳,保存到離屏緩沖區(qū),以達(dá)到復(fù)用的目的缔莲。這種情況是需要開發(fā)者手動(dòng)觸發(fā)的哥纫。
離屏渲染的另一個(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.
當(dāng)我們開啟光柵化時(shí),會(huì)將layer渲染成位圖保存在緩存中痴奏,這樣在下次使用時(shí)蛀骇,就可以直接復(fù)用,提高效率读拆。
針對(duì)光柵化的使用擅憔,有以下幾個(gè)建議:
如果layer不能被復(fù)用,則沒有必要開啟光柵化
如果layer不是靜態(tài)檐晕,需要被頻繁修改(例如動(dòng)畫過程中)暑诸,此時(shí)開啟光柵化反而影響效率
離屏渲染緩存內(nèi)容有時(shí)間限制,如果100ms內(nèi)沒有被使用棉姐,那么就會(huì)丟棄屠列,無法進(jìn)行復(fù)用
離屏渲染的緩存空間有限啦逆,是屏幕的2.5倍伞矩,超過2.5倍屏幕像素大小的話也會(huì)失效,無法實(shí)現(xiàn)復(fù)用
圓角中離屏渲染的觸發(fā)時(shí)機(jī)
圓角設(shè)置不生效問題夏志!
在平常寫代碼時(shí)乃坤,比如UIButton設(shè)置圓角,當(dāng)設(shè)置好按鈕的image沟蔑、cornerRadius湿诊、borderWidth、borderColor等屬性后瘦材,運(yùn)行發(fā)現(xiàn)并沒有實(shí)現(xiàn)我們想要的效果
let btn0 = UIButton(type: .custom)
btn0.frame = CGRect(x: 100, y: 60, width: 100, height: 100)
//設(shè)置圓角
btn0.layer.cornerRadius = 50
//設(shè)置border寬度和顏色
btn0.layer.borderWidth = 2
btn0.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(btn0)
//設(shè)置背景圖片
btn0.setImage(UIImage(named: "mouse"), for: .normal)
此時(shí)的效果就是這樣的厅须,可以發(fā)現(xiàn),我們?cè)O(shè)置的按鈕圖片還是方方正正的
針對(duì)上面的這個(gè)問題食棕,我相信99%的人都能信手拈來朗和,知道必須要設(shè)置masksToBounds為 true错沽,才會(huì)得到我們想要的效果。解決的方法很簡(jiǎn)單眶拉,但原理是大部人都沒有去仔細(xì)研究的千埃。
下面是蘋果官方文檔針對(duì)圓角設(shè)置的一些說明:
官方文檔告訴我們,設(shè)置cornerRadius只會(huì)對(duì)CALayer中的backgroundColor 和 boder設(shè)置圓角忆植,不會(huì)設(shè)置contents的圓角放可,如果contents需要設(shè)置圓角,需要同時(shí)將maskToBounds / clipsToBounds設(shè)置為true朝刊。
所以我們可以理解為圓角不生效的根本原因是沒有對(duì)contents設(shè)置圓角耀里,而按鈕設(shè)置的image是放在contents里面的,所以看到的界面上的就是image沒有進(jìn)行圓角裁剪坞古。
下面我們通過幾段代碼來說明 圓角設(shè)置中什么時(shí)候會(huì)離屏渲染觸發(fā)
首先备韧,需要打開模擬器的離屏渲染顏色標(biāo)記
1、按鈕 僅設(shè)置背景顏色+border
let btn01 = UIButton(type: .custom)
btn01.frame = CGRect(x: 100, y: 200, width: 100, height: 100)
//設(shè)置圓角
btn01.layer.cornerRadius = 50
//設(shè)置border寬度和顏色
btn01.layer.borderWidth = 4
btn01.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(btn01)
//設(shè)置背景顏色
btn01.backgroundColor = UIColor.green
在這種情況下痪枫,無論是使用默認(rèn)的maskToBounds / clipsToBounds(false)织堂,還是將其修改為true,都不會(huì)觸發(fā)離屏渲染奶陈,究其根本原因是 contents中沒有需要圓角處理的layer易阳。
情況2:按鈕設(shè)置背景圖片+boder
let btn0 = UIButton(type: .custom)
btn0.frame = CGRect(x: 100, y: 60, width: 100, height: 100)
//設(shè)置圓角
btn0.layer.cornerRadius = 50
//設(shè)置border寬度和顏色
btn0.layer.borderWidth = 2
btn0.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(btn0)
//設(shè)置背景圖片
btn0.setImage(UIImage(named: "mouse"), for: .normal)
使用默認(rèn)的maskToBounds / clipsToBounds(false)
這種情況就是最開始我們講到的圓角設(shè)置不生效的情況,就不再多做說明了
maskToBounds / clipsToBounds 修改為true
<article class="_2rhmJa">
從屏幕的顯示上可以看出吃粒,此時(shí)觸發(fā)了離屏渲染潦俺,是因?yàn)閳A角的設(shè)置是需要對(duì)所有l(wèi)ayer都進(jìn)行裁剪的,而maskToBounds裁剪是應(yīng)用到所有l(wèi)ayer上的徐勃。如果從正常渲染的角度來說事示,一個(gè)個(gè)layer是用完即扔的。而現(xiàn)在我們的圓角設(shè)置需要3個(gè)layer疊加合并的僻肖,所以將先處理好的layer保存在離屏緩沖區(qū)肖爵,等到最后一個(gè)layer處理完,合并進(jìn)行圓角+裁剪臀脏,所以才會(huì)觸發(fā)離屏渲染
總結(jié)
- 當(dāng)只設(shè)置backgroundColor劝堪、border,而contents中沒有子視圖時(shí)揉稚,無論
maskToBounds / clipsToBounds
是true
還是false
秒啦,都不會(huì)觸發(fā)離屏渲染 - 當(dāng)contents中有子視圖時(shí),此時(shí)設(shè)置
cornerRadius
+maskToBounds / clipsToBounds
,就會(huì)觸發(fā)離屏渲染搀玖,但是這種情況在UIImageView中并不適用余境,當(dāng)UIImageView中只設(shè)置圖片+maskToBounds / clipsToBounds
是不會(huì)觸發(fā)離屏渲染,蘋果對(duì)UIImageView優(yōu)化我想也只是將image直接畫在了contents上面這樣不設(shè)置背景色其實(shí)只需要渲染一個(gè)layer,所以不需要用到離屏緩沖區(qū),
,所以不會(huì)產(chǎn)生離屏渲染芳来,如果此時(shí)再加上背景色暴氏,就會(huì)觸發(fā)離屏渲染。
離屏渲染是否觸發(fā)绣张,在于我們是否需要使用離屏緩沖區(qū)答渔。