什么是離屏渲染贩挣?
離屏渲染(Off-Screen Rendering)顧名思義,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作没酣。如果要在顯示屏上顯示內(nèi)容王财,我們至少需要一塊與屏幕像素?cái)?shù)據(jù)量一樣大的frame buffer,作為像素?cái)?shù)據(jù)存儲(chǔ)區(qū)域裕便,而這也是GPU存儲(chǔ)渲染結(jié)果的地方绒净。如果有時(shí)因?yàn)槊媾R一些限制,無(wú)法把渲染結(jié)果直接寫入frame buffer偿衰,而是先暫存在另外的內(nèi)存區(qū)域疯溺,之后再寫入frame buffer论颅,那么這個(gè)過程被稱之為離屏渲染哎垦。
渲染結(jié)果是先經(jīng)過離屏buffer,再到frame buffer囱嫩。
常見觸發(fā)離屏渲染?
測(cè)試環(huán)境(Xcode12.5.1漏设、iPhone 12墨闲、iOS 14.5)
設(shè)置cornerRadius并不一定就會(huì)觸發(fā)離屏渲染。
UIImageView為例:
cornerRadius+clipsToBounds或者layer.masksToBounds = YES也不會(huì)觸發(fā)離屏渲染郑口。即使設(shè)置了圖片也不會(huì)觸發(fā)離屏渲染鸳碧。iOS9 之后蘋果做了優(yōu)化,但是當(dāng)imageView添加子視圖,并且子視圖有超出父視圖的部分犬性,設(shè)置clipsToBounds或者layer.masksToBounds為YES瞻离,此時(shí)超出的部分會(huì)觸發(fā)離屏渲染
cornerRadius+clipsToBounds或者layer.masksToBounds+backgroundColor。則會(huì)觸發(fā)離屏渲染乒裆。所以我們?cè)O(shè)置imgView為圓角的時(shí)候套利,盡量不要設(shè)置背景顏色,其他視圖同理例如UIView鹤耍,UILabel,UIButton肉迫。
cornerRadius+clipsToBounds或者layer.masksToBounds +borderColor + borderWidth 會(huì)觸發(fā)離屏渲染,當(dāng)borderWidth為0時(shí)也不會(huì)觸發(fā)離屏渲染稿黄。
根據(jù)油畫算法喊衫,GPU沒辦法一步到位既設(shè)置圓角又設(shè)置背景顏色、或者邊框杆怕,所以會(huì)觸發(fā)離屏渲染族购。
光柵會(huì)觸發(fā)離屏渲染:
shouldRasterize = YES;會(huì)觸發(fā)離屏渲染陵珍。
開啟光柵化會(huì)將圖層渲染為一個(gè)屏幕之外的位圖寝杖,然后將這個(gè)位圖緩存起來。如果圖層比較復(fù)雜撑教,這樣做會(huì)比重繪所有幀劃算朝墩。但是光柵化原始圖像需要時(shí)間,會(huì)消耗額外的內(nèi)存伟姐。
如果使用合理收苏,光柵化可以提供很大的性能優(yōu)勢(shì),但是對(duì)于內(nèi)容不斷變化的圖層來說愤兵,就沒必要緩存鹿霸,因此為了避免額外內(nèi)存消耗,不要使用光柵秆乳。
如何檢測(cè)光柵化是否正確使用:(使用真機(jī)調(diào)試)
Xcode->Debug->View Debugging -> Rendering -> color Hits Green and Misses Red.當(dāng)開啟光柵懦鼠,渲染的結(jié)果會(huì)被緩存钻哩,如果圖層為綠色,就表示有渲染緩存可用肛冶;若為紅色街氢,則無(wú),緩存會(huì)被重新創(chuàng)建睦袖,可能引起性能問題珊肃。?
陰影不一定觸發(fā)離屏渲染:
設(shè)置陰影會(huì)觸發(fā)離屏渲染,因?yàn)殛幱疤幱趌ayer下面馅笙,根據(jù)油畫算法伦乔,陰影會(huì)優(yōu)先渲染,但是此時(shí)陰影的本體(layer與其子layer)還沒有組合在一起董习,因此GPU會(huì)開啟新的內(nèi)存烈和,將本體 畫好,再根據(jù)渲染結(jié)果的形狀皿淋,添加陰影到frame buffer,最后將內(nèi)容畫上去招刹。如果預(yù)先告訴Core Animation陰影的形狀,那么陰影可以獨(dú)立渲染出來不依賴本體沥匈。
所以單純?cè)O(shè)置陰影會(huì)觸發(fā)離屏渲染蔗喂,但是設(shè)置了shaowPath將不會(huì)觸發(fā)。
遮罩mask會(huì)觸發(fā)離屏渲染:
mask也是layer類型,但是是覆蓋在layer和其所有子layer組合之上高帖,根據(jù)渲染原理缰儿,屏幕上的每一個(gè)像素點(diǎn)都是當(dāng)前像素點(diǎn)上多個(gè)layer通過GPU混合顏色計(jì)算出來,因此多加一個(gè)mask會(huì)增加GPU計(jì)算難度散址,導(dǎo)致無(wú)法一次性渲染緩存到frame buffer里面乖阵,因此會(huì)觸發(fā)離屏渲染。
抗鋸齒會(huì)觸發(fā)離屏渲染:
當(dāng)view縮放或者旋轉(zhuǎn)會(huì)造成邊框出現(xiàn)鋸齒预麸,在出現(xiàn)鋸齒是時(shí)瞪浸,打開allowsEdgeAntialiasing = YES時(shí),會(huì)將邊框磨平吏祸,而鋸齒的計(jì)算也會(huì)觸發(fā)離屏渲染对蒲。
不透明可能觸發(fā)離屏渲染:
當(dāng)視圖沒有子視圖時(shí),設(shè)置alpha<1,不會(huì)觸發(fā)離屏渲染,當(dāng)有子視圖時(shí)贡翘,因?yàn)镚PU渲染時(shí)蹈矮,除了要渲染子視圖,還需要渲染父視圖的內(nèi)容鸣驱,沒有辦法一步到位泛鸟,所以會(huì)觸發(fā)。
怎么查看項(xiàng)目中是否觸發(fā)離屏渲染踊东?
打來模擬器->Debug->Color Off-screen Rendered
如果所屬區(qū)域被標(biāo)記為黃色就代表觸發(fā)了離屏渲染
離屏渲染的影響北滥?
GPU的操作是高度流水線化的刚操。本來所有計(jì)算工作都在有條不紊地正在向frame buffer輸出,此時(shí)突然收到指令再芋,需要輸出到另一塊內(nèi)存菊霜,那么流水線中正在進(jìn)行的一切都不得不被丟棄,切換到只能服務(wù)于我們當(dāng)前的“切圓角”操作祝闻。等到完成以后再次清空占卧,再回到向frame buffer輸出的正常流程。
在tableView或者collectionView中联喘,滾動(dòng)的每一幀變化都會(huì)觸發(fā)每個(gè)cell的重新繪制,因此一旦存在離屏渲染辙纬,上面提到的上下文切換就會(huì)每秒發(fā)生60次豁遭,并且很可能每一幀有幾十張的圖片要求這么做,對(duì)于GPU的性能沖擊可想而知(GPU非常擅長(zhǎng)大規(guī)模并行計(jì)算贺拣,但是我想頻繁的上下文切換顯然不在其設(shè)計(jì)考量之中)
離屏渲染消耗性能的原因蓖谢?
1、需要?jiǎng)?chuàng)建新的緩沖區(qū)
2譬涡、離屏渲染的整個(gè)過程闪幽,需要多次切換上下文環(huán)境,先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen)涡匀;等到離屏渲染結(jié)束以后盯腌,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上,又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕
如何優(yōu)化離屏渲染陨瘩?
1腕够、使用cornerRadius設(shè)置了圓角,能不設(shè)置背景顏色或者borderWidth盡量避免設(shè)置
2舌劳、如果不能避免帚湘,能夠使用圓角圖片的盡量使用圓角圖片,不能避免的可以創(chuàng)建弧形layer蓋住對(duì)應(yīng)角甚淡,視覺上制造圓角效果大诸。
3、設(shè)置陰影贯卦,使用shadowPath來規(guī)避離屏渲染
4资柔、特殊復(fù)雜并且復(fù)用度高的View,使用layer mask并打開shouldRasterize來對(duì)渲染結(jié)果進(jìn)行緩存
參考:?
https://zhuanlan.zhihu.com/p/72653360?