離屏渲染是在iOS開發(fā)過程中脫離不了的話題骨杂,那么什么是離屏渲染以及哪些情況會導(dǎo)致離屏渲染呢?以及離譜渲染有哪些優(yōu)勢和劣勢芍殖?
首先看下面一段代碼疾嗅,那種模式會觸發(fā)離屏渲染呢外厂?
? ? ? ?//按鈕存在背景圖片,并且設(shè)置圓角
? ? ? ? let?btn1 =UIButton.init(type: .custom)
? ? ? ? btn1.frame=CGRect.init(x:100, y:100, width:44, height:44)
? ? ? ? btn1.layer.cornerRadius=5.0
? ? ? ? self.view.addSubview(btn1)
? ? ? ? btn1.setImage(UIImage.init(named:"tabbar_discover_hl"), for: .normal)
? ? ? ? btn1.clipsToBounds=true
? ? ? ? //按鈕不存在背景圖片代承,并且設(shè)置圓角
? ? ? ? let?btn2 =UIButton(type: .custom)
? ? ? ? btn2.frame=CGRect.init(x:100, y:180, width:44, height:44)
? ? ? ? btn2.layer.cornerRadius=5.0
? ? ? ? btn2.backgroundColor = .blue
? ? ? ? self.view.addSubview(btn2)
? ? ? ? btn2.clipsToBounds=true
? ? ? ? //圖片設(shè)置了背景色+背景圖片
? ? ? ? let?imgv1 =UIImageView(frame:CGRect.init(x:100, y:260, width:44, height:44))
? ? ? ? imgv1.image=UIImage.init(named:"tabbar_friends_hl")
? ? ? ? imgv1.backgroundColor = .blue
? ? ? ? imgv1.layer.cornerRadius=5.0
? ? ? ? imgv1.layer.masksToBounds=true
? ? ? ? self.view.addSubview(imgv1)
? ? ? ? //圖片只設(shè)置了背景色
? ? ? ? let?imgv2 =UIImageView(frame:CGRect.init(x:100, y:340, width:44, height:44))
? ? ? ? imgv2.image=UIImage.init(named:"tabbar_mine_hl")
? ? ? ? imgv2.layer.cornerRadius=5.0
? ? ? ? imgv2.layer.masksToBounds=true
? ? ? ? self.view.addSubview(imgv2)
從上圖可知汁蝶,1、3中寫法觸發(fā)了離屏渲染论悴,2掖棉、4兩種寫法未觸發(fā)離屏渲染;
APP渲染流程
從圖中可以看到(圖片來源:WWDC)膀估,Application以及RenderServer部分是運(yùn)行在CPU上的幔亥,Application處理好數(shù)據(jù)后,提交到Render Server察纯,然后Core Animation在會將具體操作轉(zhuǎn)換成GPU的Draw calls(OpenGL/Metal)帕棉;也就是CPU+GPU共同完成渲染工作
什么是離屏渲染:
如果要在顯示屏上顯示內(nèi)容,我們至少需要一塊與屏幕像素數(shù)據(jù)量一樣大的frame buffer饼记,作為像素數(shù)據(jù)存儲區(qū)域香伴,而這也是GPU存儲渲染結(jié)果的地方。如果有時因為面臨一些限制具则,無法把渲染結(jié)果直接寫入frame buffer即纲,而是先暫存在另外的內(nèi)存區(qū)域,之后再寫入frame buffer博肋,那么這個過程被稱之為離屏渲染(offscreen buffer)
通過下面兩幅圖結(jié)合上面的概念可以清晰離屏渲染的原理:
那么離屏渲染是如何工作的呢拔稳?通過圓角觸發(fā)離屏渲染來進(jìn)行分析
從上圖可知,給UIButton設(shè)置背景圖片并且添加圓角涉及3部分內(nèi)容
1锹雏、backgroundColor
2巴比、contents
3、borderColor/borderWidth
當(dāng)每一部分layer會獨(dú)立繪制礁遵,然后保存到offscreenBuffer中轻绞,當(dāng)全部layer繪制后,對這3部分整體進(jìn)行添加圓角,所以需要先存入到offscreenBuffer中佣耐,否則繪制完會自動銷毀政勃,當(dāng)設(shè)置圓角時無法獲取到之前的layer;
GPU離屏渲染兼砖,“畫家畫法”
通過渲染流水線示意圖中我們可以看到奸远,主要的渲染操作都是由CoreAnimation的Render Server模塊既棺,通過調(diào)用顯卡驅(qū)動所提供的OpenGL/Metal接口來執(zhí)行的。通常對于每一層layer懒叛,Render Server會遵循“畫家算法”丸冕,按次序輸出到frame buffer,后一層覆蓋前一層薛窥,就能得到最終的顯示結(jié)果(值得一提的是胖烛,與一般桌面架構(gòu)不同,在iOS中诅迷,設(shè)備主存和GPU的顯存共享物理內(nèi)存佩番,這樣可以省去一些數(shù)據(jù)傳輸開銷)
然而有些場景并沒有那么簡單。作為“畫家”的GPU雖然可以一層一層往畫布上進(jìn)行輸出罢杉,但是無法在某一層渲染完成之后趟畏,再回過頭來擦除/改變其中的某個部分——因為在這一層之前的若干層layer像素數(shù)據(jù),已經(jīng)在渲染中被永久覆蓋了屑那。這就意味著,對于每一層layer艘款,要么能找到一種通過單次遍歷就能完成渲染的算法持际,要么就不得不另開一塊內(nèi)存,借助這個臨時中轉(zhuǎn)區(qū)域來完成一些更復(fù)雜的哗咆、多次的修改/剪裁操作
通過上面的理解年碘,可以發(fā)現(xiàn)澈歉,App需要進(jìn)行額外的渲染和合并,必須使用Offscreen Buffer屿衅,然后進(jìn)行組合放入到Frame Buffer中埃难,然后現(xiàn)在到屏幕上
離屏渲染性能問題:
1,需要額外的存儲空間涤久,
2涡尘,將結(jié)果從Offscreen buffer轉(zhuǎn)存到Frame Buffer中也是需要時間的
Offscreen Buffer的空間也是有限制的(限制大小是屏幕像素大小的2.5倍)
既然離屏渲染容易帶來性能問題,為什么還要用呢响迂?
1.非常多的特殊效果考抄,并不能一次用一個圖層就能畫出來,所以需要使用額外的offscreen Buffer來保持中間狀態(tài)(不得不使用),比如:圓角蔗彤,陰影
2.能帶來效率的優(yōu)勢:既然效果會多次出現(xiàn)在屏幕上川梅,可提前渲染好疯兼,保存在offscreen Buffer中,從而達(dá)到復(fù)用的結(jié)果? 比如:光珊化shouldRasterize
常見離屏渲染:
所以我們在項目開發(fā)的過程中挑势,根據(jù)實際情況進(jìn)行相關(guān)的優(yōu)化镇防,突出其優(yōu)勢避免其劣勢
特別說明:關(guān)于光珊化并不是所有的光珊化都會帶來性能問題,以下是關(guān)于光珊化的使用建議