iOS-離屏渲染詳解

README:

引言: 一款優秀的app,流暢很關鍵,用戶使用60的fps的app,跟使用30的fps的app感受是完全不一樣的.類似于 半糖 這種優秀的應用肯定花了大把精力去優化界面.網上關于優化的界面的文章一搜一大把.本文并不是講界面優化的,優化的話推薦下面幾篇文章;

YYKit作者: "iOS 保持界面流暢的技巧" (我相信認真看一定有收獲!)

離屏渲染的優化 (這篇文章很強)

jim: 淺談iOS中的視圖優化

有些東西研究起來挺費勁的.但是想要更多的收獲,還真就必須得研究.知識零零散散,時間久了再次用起來難免會生疏,又得重新找資料,看文章.很麻煩.索性就整理個比較全面的知識點供以后復習.


  • 屏幕渲染

  • OpenGL中,GPU屏幕渲染有兩種方式.

  1. On-Screen Rendering (當前屏幕渲染)

    指的是GPU的渲染操作是在當前用于顯示的屏幕緩沖區進行.

  2. Off-Screen Rendering (離屏渲染)

    指的是在GPU在當前屏幕緩沖區以外開辟一個緩沖區進行渲染操作.

幾個名詞 "GPU" " 緩沖區" .

不知道GPU的就自行百度吧 (- - 寶寶說不清). 說下緩沖區.要明白緩沖區,首先就得要知道圖像顯示出來的原理,本文的第一篇博客里面介紹的很詳細.
顯示器 顯示出來的圖像是經過 CRT電子槍一行一行的掃描.(可以是橫向的也可以是縱向 ,具體CRT電子槍又是什么,百度文庫介紹的很詳細.),掃描出來就呈現了一幀畫面,隨后電子槍又會回到初始位置循環掃描,為了讓顯示器的顯示跟視頻控制器同步,當電子槍新掃描一行的時候.準備掃描的時候,會發送一個 水平同步信號(HSync信號),而當一幀畫面繪制完成后,電子槍回復到原位,準備畫下一幀前,顯示器會發出一個垂直同步信號(vertical synchronization簡稱 VSync),顯示器一般是固定刷新頻率的,這個刷新的頻率其實就是VSync信號產生的頻率. 然后CPU計算好frame等屬性,就將計算好的內容提交給GPU去渲染,GPU渲染完成之后就會放入幀緩沖區,然后視頻控制器會按照VSync信號逐行讀取幀緩沖區的數據,經過可能的數模轉換傳遞給顯示器.就顯示出來了.
原理圖就不放了,過一遍概念.

離屏渲染的代價很高,想要進行離屏渲染,首選要創建一個新的緩沖區,屏幕渲染會有一個上下文環境的一個概念,離屏渲染的整個過程需要切換上下文環境,先從 當前屏幕切換到離屏,等結束后,又要將上下文環境切換回來.這也是為什么會消耗性能的原因了.
。由于垂直同步的機制,如果在一個 VSync 時間內,CPU 或者 GPU 沒有完成內容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏會保留之前的內容不變。這就是界面卡頓的原因。


那有個問題: 為什么離屏渲染這么耗性能,為什么有這套機制呢?

當使用圓角,陰影,遮罩的時候,圖層屬性的混合體被指定為在未預合成之前(下一個VSync信號開始前)不能直接在屏幕中繪制,所以就需要屏幕外渲染。 你可以這么理解. 老板叫我短時間間內做一個 app.我一個人能做,但是時間太短,所以我得讓我朋友一起來幫著我做.(性能消耗: 也就是耗 你跟你朋友之間溝通的這些成本,多浪費啊).但是沒辦法 誰讓你做不完呢.
這么一講會不會比較明白點.


  • 哪些操作會觸發 離屏渲染?

官方公開的的資料里關于離屏渲染的信息最早是在 2011年的 WWDC, 在多個 session 里都提到了盡量避免會觸發離屏渲染的效果,包括:mask, shadow, group opacity, edge antialiasing。

  • shouldRasterize(光柵化)

  • masks(遮罩)

  • shadows(陰影)

  • edge antialiasing(抗鋸齒)

  • group opacity(不透明)

  • 復雜形狀設置圓角等

  • 漸變

  • Text(UILabel, CATextLayer, Core Text, etc)...


介紹一些屬性.

  • shouldRasterize(光柵化): 將圖轉化為一個個柵格組成的圖象。 光柵化特點:每個元素對應幀緩沖區中的一像素。

shouldRasterize = YES在其它屬性觸發離屏渲染的同時,會將光柵化后的內容緩存起來,如果對應的layer或者 sublayers沒有發生改變,在下一幀的時候可以直接復用,從而減少渲染的頻率.
當使用光柵化是, 可以開啟 "Color Hits Green and Misses Red"來檢查該場景下是否適合選擇光柵化,綠色表示緩存被復用,紅色表示緩存在被重復創建.對于經常變動的內容,不要開啟,否則會造成性能的浪費.

如果cell里面的內容不斷變化(cell的復用),如果設置了cell.layer.shouldRaseterize = YES則會降低圖形性能,造成離屏渲染.

  • mask(遮罩).

mask是layer的一個屬性.

/ A layer whose alpha channel is used as a mask to select between the
 * layer's background and the result of compositing the layer's
 * contents with its filtered background. Defaults to nil. When used as
 * a mask the layer's `compositingFilter' and `backgroundFilters'
 * properties are ignored. When setting the mask to a new layer, the
 * new layer must have a nil superlayer, otherwise the behavior is
 * undefined. Nested masks (mask layers with their own masks) are
 * unsupported. */

@property(nullable, strong) CALayer *mask;

大概的意思. 當透明度改變的時候,這個 mask 就是覆蓋上去的那個陰影.該層的layer的alpha決定了多少層背景跟內容通過并顯示,完全,或者部分不透明的像素允許潛在的內容 通過并顯示.
默認是nil,當配置一個 遮罩的時候,記得設置 遮罩的大小,位置.已確保跟蓋圖層對齊.(這是官方文檔說的 如果不對齊會怎樣.you can try. 我試過是只能顯示對齊的那一部分.)
如果你想給這個屬性賦值,前提是必須沒有 superLayer,如果有superLayer,這個行為則是無效的.(你也可以嘗試一下反的.)

tip:用mask可以做一些轉場動畫.這里就不介紹了.

那說了這么多,他就是生來會觸發離屏渲染的.所以要謹慎 設置透明度. 提到透明度,另外補充一個概念.)

Color Blended Layers
用jim的話來介紹它就是

屏幕上的每個像素點的顏色是由當前像素點上的多層layer(如果存在)共同決定的,GPU會進行計算出混合顏色的RGB值,最終顯示在屏幕上。而這需要讓GPU計算,所以我們要盡量避免設置alpha,這樣GPU會忽略下面所有的layer,節約計算量。再提一下opaque這個屬性,網上普遍認為view.opaque = YES,GPU就不會進行圖層混合計算了。而這個結論是錯誤的,其實view.opaque事實上并沒什么卵用。
如果你真的想達到這個效果,可以用layer.opaque,這個才是正確的做法

  • shadows(陰影.)
    略過.... (自己可以隨便嘗試一下)

再介紹一下edge antialiasing(抗鋸齒) 這個吧. 因為之前自己也沒接觸過,很多人估計也是沒接觸過吧.

image

翻譯:

是否允許執行反鋸齒邊緣。
默認的值是 NO.(不使用抗鋸齒,也有人叫反鋸齒),當 Value 為YES的時候, 在layer的 edgeAntialiasingMask屬性layer依照這個值允許抗鋸齒邊緣,(參照這個值) 可以在info.plist里面開啟這個屬性.

image

放一個 plist 常見的key值表 info.plist配置表 (樓主真的好...)

說了這么多. 那抗鋸齒又是啥????

抗鋸齒的概念 (隨便看看.)
在我們iOS中表現 參考這里


    CALayer *layer = [CALayer layer]; 
    layer.position = CGPointMake(100, 100);
    layer.bounds = CGRectMake(0,0, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    //layer.allowsEdgeAntialiasing = YES;
    [self.view.layer addSublayer:layer]; 

正常添加layer 在view上是這樣的.

image

下一步.

 CGFloat angle = M_PI / 30.0;
[layer setTransform:CATransform3DRotate(layer.transform, angle, 0.0, 0.0, 1.0)];

在模擬器表現是這樣的.

image

如果 layer.allowsEdgeAntialiasing = YES;

--- 在模擬器上是

image

--- 在真機上.

image

可見真機效果跟模擬器還是有差距的,(模擬器邊緣比真機模糊,)官方文檔也有提到這點

Use antialiasing when drawing a layer that is not aligned to pixel boundaries. This option allows for more sophisticated rendering in the simulator but can have a noticeable impact on performance.

這是UIView的抗鋸齒,在模擬器上還是有性能的消耗的.

看看效果就行,具體的不研究太多了,知道怎么避免就行.


  • group opacity(不透明)
image

大概的意思就是 這個屬性決定了Core Animation框架下 子layers從他們Superlayer.繼承過來的不透明度.
iOS 6之前是默認NO,iOS7以后就默認 是YES. 文檔也是說可以在模擬器上呈現.但是對性能有明顯的影響.
這里我就不測試了.這個屬性過一遍,重點是下一個屬性.


  • 復雜形狀設置圓角.

我們在開發中經常會對一些圖片或者按鈕進行圓角處理,需求還是特別多的,設置圓角有多種方法,我列一下常見的方式.

  1. 設置layer層的圓角大小.經常我們還會設置masksToBounds,
//按正方形來算.長的一半就是半徑.按照這個去設置就是圓角了,長方形的話則按短的那一邊
_imageView.layer.cornerRadius = iamgeView.width/2;
_imageView.layer.masksToBounds = YES;

這樣做對于少量的圖片,這個沒有什么問題,但是數量比較多的時候,UITableView滑動可能不是那么流暢,屏幕的幀數下降,影響用戶體驗。

  1. 使用layer的mask遮罩和CAShapLayer
    創建圓形的CAShapeLaer對象,設置為View的mask屬性,這樣也可以達到圓角的效果,但是前面提到過了,使用mask屬性會離屏渲染,不僅這樣,還曾加了一個 CAShapLayer對象.著實不可以取.

  2. 使用帶圓形的透明圖片.(求個切圖大師 - - ).

  3. CoreGraphics自定義繪制圓角.

提到CoreGraphics,還有一種 特殊的"離屏渲染"方式 不得不提,那就是drawRect方法.觸發的方式:
如果我們重寫了 drawRect方法,并且使用CoreGraphics技術去繪制.就設計到了CPU渲染,整個渲染,由CPU在app內同步完成,渲染之后再交給GPU顯示.(這種方式對性能的影響不是很高)

tip:CoreGraphic通常是線程安全的,所以可以進行異步繪制,然后在主線程上更新.

- (void)display {
   dispatch_async(backgroundQueue, ^{
       CGContextRef ctx = CGBitmapContextCreate(...);
       CGImageRef img = CGBitmapContextCreateImage(ctx);
       CFRelease(ctx);
       dispatch_async(mainQueue, ^{
           layer.contents = img;
       });
   });
}

  • 如何檢測我的項目里面離屏渲染了

  • 之前看了一些文章說在intruments里面的 CoreAnimation里面有工具.檢測.(沒找著.求補充)

打開的正確方式:
模擬器的 debug -> 選取 color Offscreen-Rendered.

image

開啟后會把那些需要離屏渲染的圖層高亮成黃色,這就意味著黃色圖層可能存在性能問題。

正常:是這樣的

image

有問題的圖層:

image

可以看見我設置了圓角的imageView有問題.

項目開發中怎么去處理?

拋出一個問題: 需求就是有很多圓角那我們項目中應該怎么去處理圓角呢?

  1. 使用YYWebImage去處理
  2. iOS中圓角圖片的處理

相信看完兩篇文章,多少都會能收獲一點!

有些人說:

iOS 9.0 之后UIButton設置圓角會觸發離屏渲染,而UIImageView里png圖片設置圓角不會觸發離屏渲染了,如果設置其他陰影效果之類的還是會觸發離屏渲染的(這句話不知道誰說的.自己有沒有去嘗試呢???)

結論: 經過測試

image

大家可以看到,
UIButton 的 masksToBounds = YES下發生離屏渲染與 背景圖存不存在有關系, 如果沒有給按鈕設置 btn.image = [UIImage imageName:@"xxxxx"]; 是不會產生離屏渲染的 .

關于 UIImageView,現在測試發現(現版本: iOS10),在性能的范圍之內,給UIImageView設置圓角是不會觸發離屏渲染的,但是同時給UIImageView設置背景色則肯定會觸發.觸發離屏渲染跟 png.jpg格式并無關聯(可能采取的壓縮格式不同,這里不做探討,這里我給出結果是沒有關系)


  • 總結:

  • 對于網上一些文章得出的結論,我覺得大家得理性分析,并不是每個人都是對的,只有經過自己實踐才能得出較好的定論.本文也是,我希望哪里有理解錯或者其它什么錯誤請提出(認真臉.),人無完人,我希望在學習的道路上能碰見更多一起進步的人!

作者:uncleRX
鏈接:http://www.lxweimin.com/p/57e2ec17585b
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容