1. 程式人生 > >關於離屏渲染

關於離屏渲染

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

YYKit作者:“iOS保持介面流暢的技巧”(我相信認真看一定有收穫!)

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

jim:淺談iOS中的檢視優化

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

螢幕渲染
OpenGL的中,GPU螢幕渲染有兩種方式。
螢幕渲染(當前螢幕渲染)

指的是GPU的渲染操作是在當前用於顯示的螢幕緩衝區進行。

離屏渲染(離屏渲染)

指的是在GPU在當前螢幕緩衝區以外開闢一個緩衝區進行渲染操作。

幾個名詞“GPU”“緩衝區”。

不知道GPU的就行百度吧( - - 寶寶說不清)。說下緩衝區。要明白緩衝區,首先就得要知道影象顯示出來的原理,本文的第一篇部落格裡面介紹的很詳細。
顯示器顯示出來的影象是經過CRT電子槍一行一行的掃描。(可以是橫向的也可以是縱向,具體CRT電子槍又是什麼,百度文庫介紹的很詳細。),掃描出來就呈現了一幀畫面,隨後電子槍又會回到初始位置迴圈掃描,為了讓顯示器的顯示跟視訊控制器同步,當電子槍新掃描一行的時候。準備掃描的時候,會發送一個水平同步訊號(HSync訊號),顯示器一般是固定重新整理頻率的,這個重新整理的頻率其實就是行同步訊號產生的頻率。然後CPU計算好幀等屬性,就將計算好的內容提交給GPU去渲染,GPU渲染完成之後就會放入幀緩衝區,然後視訊控制器會按照行同步訊號逐行讀取幀緩衝區的資料,經過可能的數模轉換傳遞給顯示器。就顯示出來了。
原理圖就不放了,過一遍概念。

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

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

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

哪些操作會觸發離屏渲染?
官方公開的資料裡關於離屏渲染的資訊最早是在2011年的WWDC上,在多個會話裡都提到了儘量避免會觸發離屏渲染的效果,包括:mask,shadow,group opacity,edge antialiasing。

shouldRasterize(光柵化)

面具(遮罩)

陰影(陰影)

邊緣抗鋸齒(anti-鋸齒)

group opacity(不透明)

複雜形狀設定圓角等

漸變

文字(UILabel,CATextLayer,核心文字等)…

介紹一些屬性。

應光柵化:將圖轉化為一個個柵格組成的影象。光柵化特點:每個元素對應幀緩衝區中的一畫素。
shouldRasterize = YES在其它屬性觸發離屏渲染的同時,會將光柵化後的內容快取起來,如果對應的layer或者sublayers沒有發生改變,在下一幀的時候可以直接複用,從而減少渲染的頻率。
當使用光柵化是,可以開啟“Color Hits Green and Misses Red”來檢查該場景下是否適合選擇光柵化,綠色表示快取被複用,紅色表示快取在被重複建立。對於經常變動的內容,不要開啟,否則會造成效能的浪費。

如果cell裡面的內容不斷變化(cell的複用),如果設定了cell.layer.shouldRaseterize = YES則會降低圖形效能,造成離屏渲染。

掩模(遮罩)。
掩模是層的一個屬性。

/一個其alpha通道被用作掩碼的圖層來選擇
*圖層的背景和合成圖層的結果
*過濾背景的內容。預設為零。當用作
*蒙板圖層的“compositingFilter”和“backgroundFilters”
*屬性被忽略。將遮罩設定為新圖層時,
*新圖層必須有一個零超層,否則的行為是
*未定義。巢狀蒙版(帶有自己的蒙版的蒙版圖層)是
不受支援。 /

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

提示:用掩模可以做一些轉場動畫這裡就不介紹了。

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

彩色混合層
用吉姆的話來介紹它就是

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

陰影(陰影)。
略過….(自己可以隨便嘗試一下)
再介紹一下edge antialiasing(抗鋸齒)這個吧。因為之前自己也沒接觸過,很多人估計也是沒接觸過吧。

177286BA-E7BE-4CFE-B992-AF18B30FE1DE.png
翻譯:

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

0068582C-92E8-4A80-AAD8-C89E22668964.png
放一個plist常見的key value表info.plist配置表(樓主真的好…)

說了這麼多。那抗鋸齒又是啥????

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

CALayer *圖層= [CALayer圖層]; 
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]; ```

正常新增層在檢視上是這樣的。

![模擬器不旋轉巴紐(http://upload-images.jianshu.io/upload_images/1315383-c2502546a0ca0a32.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

下一步。
CGFloat角度= M_PI / 30.0;
[layer setTransform:CATransform3DRotate(layer.transform,angle,0.0,0.0,1.0)];“`
在模擬器表現是這樣的。

模擬器NO.png
如果layer.allowsEdgeAntialiasing = YES;

—在模擬器上是

模擬器YES.png
—在真機上。

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

繪製與畫素邊界不對齊的圖層時使用抗鋸齒。此選項允許在模擬器中進行更復雜的渲染,但可能會對效能產生顯著影響。

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

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

group opacity(不透明)
組不透明巴紐
大概的意思就是這個屬性決定了Core Animation框架下子layers從他們Superlayer。繼承過來的不透明度。
iOS 6之前是預設NO,iOS7以後就預設是YES。文件也是說可以在模擬器上呈現。但是對效能有明顯的影響。
這裡我就不測試了。這個屬性過一遍,重點是下一個屬性。

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

設定層層的圓角大小。經常我們還會設定masksToBounds,
//按正方形來算。長的一半就是半徑。按照這個去設定就是圓角了,長方形的話則按短的那一邊
_imageView.layer.cornerRadius = iamgeView.width / 2;
_imageView.layer.masksToBounds = YES;“`
這樣做對於少量的圖片,這個沒有什麼問題,但是數量比較多的時候,UITableView的滑動可能不是那麼流暢,螢幕的幀數下降,影響使用者體驗。
2.使用層的遮罩遮罩和CAShapLayer
建立圓形的CAShapeLaer物件,設定為View的mask屬性,這樣也可以達到圓角的效果,但是前面提到過了,使用mask屬性會離屏渲染,不僅僅這樣,還曾加了一個CAShapLayer物件。著實不可以取。

3.使用帶圓形的透明圖片。(求個切圖大師 - - )
4. CoreGraphics自定義繪製圓角。

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

提示:CoreGraphic通常是執行緒安全的,所以可以進行非同步繪製,然後在主執行緒上更新。

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

  • 如何檢測我的專案裡面離屏渲染了

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

    開啟的正確方式:
    模擬器的debug - >選取顏色Offscreen-Rendered。

![BB55F33F-97CC-4C28-B3F1-22456A2A7BD8.png(http://upload-images.jianshu.io/upload_images/1315383-752efaac717c3947.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

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

正常:是這樣的
![正常渲染巴紐(http://upload-images.jianshu.io/upload_images/1315383-de2aad921928fa7b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

有問題的圖層:

![除錯巴紐(http://upload-images.jianshu.io/upload_images/1315383-61746e58c6057b02.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240
可以看見我設定了圓角的ImageView的有問題。

###專案開發中怎麼去處理?

丟擲一個問題:需求就是有很多圓角那我們專案中應該怎麼去處理圓角呢?

1.使用[YYWebImage去處理](http://www.jianshu.com/p/60cd5f8bb4cb
2. [iOS中圓角圖片的處理](http://www.jianshu.com/p/82e68984711f

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

有些人說:

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

結論:經過測試

![70915C7C-7523-4008-9A88-B5682407926D.png(http://upload-images.jianshu.io/upload_images/1315383-a58dfe9543375e1d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

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

關於UIImageView,現在測試發現(現版本:iOS10),在效能的範圍之內,給UIImageView設定圓角是不會觸發離屏渲染的,但是同時給UIImageView設定背景色則肯定會觸發。觸發離屏渲染跟png.jpg格式並無關聯(可能採取的壓縮格式不同,這裡不做探討,這裡我給出結果是沒有關係)


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

作者:uncleRX
連結:HTTPS://www.jianshu.com/p/57e2ec17585b
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。