三结蟋、離屏渲染脯倚,你真的知道了嗎?

一嵌屎、首先讓我們什么是離屏渲染

離屏渲染就是在當前屏幕緩沖區(qū)以外挠将,新開辟一個緩沖區(qū)進行操作胳岂。

離屏渲染出發(fā)的場景有以下:
  • 圓角 (maskToBounds并用才會觸發(fā),contents內(nèi)有內(nèi)容)
  • 圖層蒙版
  • 陰影
  • 光柵化
為什么要有離屏渲染?

大家高中物理應(yīng)該學過顯示器是如何顯示圖像的:需要顯示的圖像經(jīng)過CRT電子槍以極快的速度一行一行的掃描舔稀,掃描出來就呈現(xiàn)了一幀畫面乳丰,隨后電子槍又會回到初始位置循環(huán)掃描,形成了我們看到的圖片或視頻内贮。

為了讓顯示器的顯示跟視頻控制器同步产园,當電子槍新掃描一行的時候,準備掃描的時發(fā)送一個水平同步信號(HSync信號)夜郁,顯示器的刷新頻率就是HSync信號產(chǎn)生的頻率什燕。然后CPU計算好frame等屬性,將計算好的內(nèi)容交給GPU去渲染竞端,GPU渲染好之后就會放入幀緩沖區(qū)屎即。然后視頻控制器會按照HSync信號逐行讀取幀緩沖區(qū)的數(shù)據(jù),經(jīng)過可能的數(shù)模轉(zhuǎn)換傳遞給顯示器事富,就顯示出來了技俐。具體的大家自行查找資料或詢問相關(guān)專業(yè)人士,這里只參考網(wǎng)上資料做一個簡單的描述统台。

離屏渲染的代價很高雕擂,想要進行離屏渲染,首選要創(chuàng)建一個新的緩沖區(qū)贱勃,屏幕渲染會有一個上下文環(huán)境的一個概念井赌,離屏渲染的整個過程需要切換上下文環(huán)境,先從當前屏幕切換到離屏贵扰,等結(jié)束后仇穗,又要將上下文環(huán)境切換回來。這也是為什么會消耗性能的原因了戚绕。

由于垂直同步的機制仪缸,如果在一個 HSync 時間內(nèi),CPU 或者 GPU 沒有完成內(nèi)容提交列肢,則那一幀就會被丟棄,等待下一次機會再顯示宾茂,而這時顯示屏會保留之前的內(nèi)容不變瓷马。這就是界面卡頓的原因。

為什么要避免離屏渲染跨晴?

CPU GPU 在繪制渲染視圖時做了大量的工作欧聘。離屏渲染發(fā)生在 GPU 層面上,會創(chuàng)建新的渲染緩沖區(qū)端盆,會觸發(fā) OpenGL 的多通道渲染管線怀骤,圖形上下文的切換會造成額外的開銷费封,增加 GPU 工作量。如果 CPU GPU 累計耗時 16.67 毫秒還沒有完成蒋伦,就會造成卡頓掉幀弓摘。

圓角屬性、蒙層遮罩 都會觸發(fā)離屏渲染痕届。指定了以上屬性韧献,標記了它在新的圖形上下文中,在未愈合之前研叫,不可以用于顯示的時候就出發(fā)了離屏渲染锤窑。

二、屏幕上最終顯示的數(shù)據(jù)有兩種加載流程

  • On-Screen Rendering:當前屏幕渲染嚷炉,在當前用于顯示的屏幕緩沖區(qū)進行渲染操作
  • Off-Screen Rendering:離屏渲染渊啰,在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作
2251862-9f6a6db1c7de28c7.png

2.1 首先來講講正常渲染流程On-Screen Rendering

APP中的數(shù)據(jù)經(jīng)過CPU計算和GPU渲染后,將結(jié)果存放在幀緩沖區(qū)申屹,利用視頻控制器從幀緩沖區(qū)中取出绘证,并顯示到屏幕上。

  • 在GPU的渲染流程中独柑,顯示到屏幕上的圖像是遵循大畫家算法按照由遠及近的順序迈窟,依次將結(jié)果存儲到幀緩沖區(qū)
  • 視屏控制器從幀緩沖區(qū)中讀取一幀數(shù)據(jù),將其顯示到屏幕上后忌栅,會立即丟棄這幀數(shù)據(jù)车酣,不會做任何保留,這樣做的目的是可以節(jié)省空間索绪,且在屏幕上是各自顯示各自的湖员,互相不影響。
2251862-f372c2ca8eed57ec.png

2.2 離屏渲染流程Off-Screen Rendering

當App需要進行額外的渲染和合并時瑞驱,例如按鈕設(shè)置圓角娘摔,我們是需要對UIButton這個控件中的所有圖層都進行圓角+裁剪,然后再將合并后的結(jié)果存入幀緩存區(qū)唤反,再從幀緩存中取出交由屏幕顯示凳寺,這時,在正常的渲染流程中彤侍,我們是無法做到對所有圖層進行圓角裁剪的肠缨,因為它是用一個丟一個。所以我們需要提前將處理好的結(jié)果放入離屏緩沖區(qū)盏阶,最后將幾個圖層進行疊加合并晒奕,存放到站緩沖區(qū),最后屏幕上就是我們想實現(xiàn)的效果。

2251862-74cbcd310d5c26a4.png

說白了脑慧,離屏緩存區(qū)就是一個臨時的緩沖區(qū)魄眉,用來存放在后續(xù)操作使用,但目前并不使用的數(shù)據(jù)闷袒。

  • 離屏渲染再給我們帶來方便的同時坑律,也帶來了嚴重的性能問題。由于離屏渲染中的離屏緩沖區(qū)霜运,是額外開辟的一個存儲空間脾歇,當它將數(shù)據(jù)轉(zhuǎn)存到Frame Buffer時,也是需要耗費時間的淘捡,所以在轉(zhuǎn)存的過程中藕各,仍有掉幀的可能。
  • 離屏緩沖區(qū)的空間并不是無限大的焦除, 它是又上限的激况,最大只能是屏幕的2.5倍
2.2.1 離屏渲染消耗性能的原因

需要創(chuàng)建新的緩沖區(qū)
離屏渲染的整個過程,需要多次切換上下文環(huán)境膘魄,先是從當前屏幕(On-Screen)切換到離屏(Off-Screen)乌逐;等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上创葡,又需要將上下文環(huán)境從離屏切換到當前屏幕

2.2.2 哪些操作會觸發(fā)離屏渲染浙踢?
  • 光柵化,layer.shouldRasterize = YES
  • 遮罩灿渴,layer.mask
  • 圓角洛波,同時設(shè)置 layer.masksToBounds = YES、layer.cornerRadius大于0
  • 考慮通過 CoreGraphics 繪制裁剪圓角骚露,或者叫美工提供圓角圖片
  • 陰影蹬挤,layer.shadowXXX,如果設(shè)置了 layer.shadowPath 就不會產(chǎn)生離屏渲染
2.2.3 那為什么我們明知有性能問題時棘幸,還是要使用離屏渲染呢焰扳?
  • 可以處理一些特殊的效果,這種效果并不能一次就完成误续,需要使用離屏緩沖區(qū)來保存中間狀態(tài)吨悍,不得不使用離屏渲染,這種情況下的離屏渲染是系統(tǒng)自動觸發(fā)的蹋嵌,例如經(jīng)常使用的圓角育瓜、陰影、高斯模糊欣尼、光柵化等
  • 可以提升渲染的效率,如果一個效果是多次實現(xiàn)的,可以提前渲染愕鼓,保存到離屏緩沖區(qū)钙态,以達到復用的目的。這種情況是需要開發(fā)者手動觸發(fā)的菇晃。
離屏渲染的另一個原因:光柵化

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.

當我們開啟光柵化時册倒,會將layer渲染成位圖保存在緩存中,這樣在下次使用時磺送,就可以直接復用驻子,提高效率。
針對光柵化的使用估灿,有以下幾個建議:

  • 如果layer不能被復用崇呵,則沒有必要開啟光柵化
  • 如果layer不是靜態(tài),需要被頻繁修改(例如動畫過程中)馅袁,此時開啟光柵化反而影響效率
  • 離屏渲染緩存內(nèi)容有時間限制域慷,如果100ms內(nèi)沒有被使用,那么就會丟棄汗销,無法進行復用
  • 離屏渲染的緩存空間有限犹褒,是屏幕的2.5倍,超過2.5倍屏幕像素大小的話也會失效弛针,無法實現(xiàn)復用

三叠骑、圓角中離屏渲染的觸發(fā)時機

在講圓角之前,首先說明下CALayer的構(gòu)成削茁,如圖所示宙枷,它是backgrodColorcontents付材、borderWidth&borderColor構(gòu)成的朦拖。跟我們即將解釋的圓角觸發(fā)離屏渲染息息相關(guān)。

2251862-9f16cc22ac8e8ee5.png

首先我們先開啟離屏渲染的檢測厌衔,在模擬器打開color offscreen-rendered,開啟后會把那些需要離屏渲染的圖層高亮成黃色璧帝,這就意味著黃色圖層可能存在性能問題。

22813661-3d3d8edcef335637.png

我們寫下如下代碼:

  let btn = UIButton(type: .custom)
        btn.frame = CGRect(x: 100, y: 200, width: 100, height: 100)
        //設(shè)置圓角
        btn.layer.cornerRadius = 50
        //設(shè)置border寬度和顏色
        btn.layer.borderWidth = 2
        btn.layer.borderColor = UIColor.red.cgColor
        self.view.addSubview(btn)
        //設(shè)置背景圖片
        btn.setImage(UIImage(named: "update_header"), for: .normal)

截屏2020-07-08 下午1.35.04.png

此時我們發(fā)現(xiàn)圖片并沒有變成圓形還是一個正方形
針對上面的這個問題富寿,我相信99%的人都能信手拈來睬隶,知道必須要設(shè)置masksToBoundstrue,才會得到我們想要的效果页徐。解決的方法很簡單苏潜,但原理是大部人都沒有去仔細研究的。

其實蘋果的官方文檔有告訴我們,設(shè)置cornerRadius只會對CALayer中的backgroundColorboder設(shè)置圓角觉既,不會設(shè)置contents的圓角,如果contents需要設(shè)置圓角栋艳,需要同時將maskToBounds / clipsToBounds設(shè)置為true飞袋。
所以我們理解為圓角不生效的根本原因是沒有對contents設(shè)置圓角戳气,而按鈕設(shè)置的image是放在contents里面的,所以看到的界面上的就是image沒有進行圓角裁剪巧鸭。

然而當我們加上maskToBounds / clipsToBounds修改為true時瓶您,會出現(xiàn)以下情況,說明此時觸發(fā)了離屏渲染

截屏2020-07-08 下午1.41.04.png

那為啥會觸發(fā)離屏渲染呢纲仍?

是因為圓角的設(shè)置是需要對所有l(wèi)ayer都進行裁剪的呀袱,而maskToBounds裁剪是應(yīng)用到所有l(wèi)ayer上的。如果從正常渲染的角度來說郑叠,一個個layer是用完即扔的夜赵。而現(xiàn)在我們的圓角設(shè)置需要3個layer疊加合并的,所以將先處理好的layer保存在離屏緩沖區(qū)锻拘,等到最后一個layer處理完油吭,合并進行圓角+裁剪,所以才會觸發(fā)離屏渲染

所以我們可以做以下總結(jié)
  • 當只設(shè)置backgroundColor署拟、borde婉宰,而contents中沒有子視圖時,無論maskToBounds / clipsToBoundstrue還是false推穷,都不會觸發(fā)離屏渲染
  • contents中有子視圖時心包,此時設(shè)置 cornerRadius+maskToBounds / clipsToBounds,就會觸發(fā)離屏渲染
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市馒铃,隨后出現(xiàn)的幾起案子蟹腾,更是在濱河造成了極大的恐慌,老刑警劉巖区宇,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娃殖,死亡現(xiàn)場離奇詭異,居然都是意外死亡议谷,警方通過查閱死者的電腦和手機炉爆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卧晓,“玉大人芬首,你說我怎么就攤上這事”岂桑” “怎么了郁稍?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胜宇。 經(jīng)常有香客問我耀怜,道長恢着,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任财破,我火速辦了婚禮然评,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狈究。我一直安慰自己,他們只是感情好盏求,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布抖锥。 她就那樣靜靜地躺著,像睡著了一般碎罚。 火紅的嫁衣襯著肌膚如雪磅废。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天荆烈,我揣著相機與錄音拯勉,去河邊找鬼。 笑死憔购,一個胖子當著我的面吹牛宫峦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播玫鸟,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼导绷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了屎飘?” 一聲冷哼從身側(cè)響起妥曲,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钦购,沒想到半個月后檐盟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡押桃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年葵萎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怨规。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡陌宿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出波丰,到底是詐尸還是另有隱情壳坪,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布掰烟,位于F島的核電站爽蝴,受9級特大地震影響沐批,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝎亚,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一九孩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧发框,春花似錦躺彬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铣减,卻和暖如春她君,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背葫哗。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工缔刹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劣针。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓校镐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捺典。 傳聞我的和親對象是個殘疾皇子灭翔,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354