iOS圖層效能優化

時光易逝發表於2017-12-27

來自譯文 iOS核心動畫高階技巧


[toc]

下面是閱讀iOS核心動畫高階技巧關於圖層優化的一些記錄。

光柵化

可以解決重疊透明圖層的混合失靈問題,也作為繪製複雜圖層樹結構的優化方法。 開啟方法:

layer.shouldRasterize = YES;
layer.rasterizationScale = [UIScreen mainScreen].scale
複製程式碼

啟用 shouldRasterize屬性會將圖層繪製到一個螢幕之外的影象。然後這個影象將會被快取起來並繪製到實際圖層的contents和子圖層。如果有很多的子圖層或者有複雜的效果應用,這樣做就會比重繪所有事務的所有幀划得來得多。但是光柵化原始影象需要時間,而且還會消耗額外的記憶體。當我們使用得當時,光柵化可以提供很大的效能優勢,但是一定要避免作用在內容不斷變動的圖層上,否則它快取方面的好處就會消失,而且會讓效能變的更糟。 如何檢測 檢測你是否正確地使用了光柵化方式,用Instrument > Core Animation > Color Hits Gree and Misses Red選項,檢視是否已光柵化影象被頻繁地重新整理(這樣就說明圖層並不是光柵化的好選擇,或則你無意間觸發了不必要的改變導致了重繪行為)。

離屏渲染

當圖層屬性的混合體被指定為在未預合成之前不能直接在螢幕中繪製時,螢幕外渲染就被喚起了。螢幕外渲染並不意味著軟體繪製,但是它意味著圖層必須在被顯示之前在一個螢幕外上下文中被渲染(不論CPU還是GPU)。圖層的以下屬性將會 觸發螢幕外繪製:

  • 圓角(當和 maskToBounds 一起使用時)
  • 圖層蒙板
  • 陰影

螢幕外渲染和我們啟用光柵化時相似,除了它並沒有像光柵化圖層那麼消耗大,子圖層並沒有被影響到,而且結果也沒有被快取,所以不會有長期的記憶體佔用。但是,如果太多圖層在螢幕外渲染依然會影響到效能。 有時候我們可以把那些需要螢幕外繪製的圖層開啟光柵化以作為一個優化方式,前提是這些圖層並不會被頻繁地重繪。 對於那些需要動畫而且要在螢幕外渲染的圖層來說,你可以用 CAShapeLayercontentsCenter 或者 shadowPath 來獲得同樣的表現而且較少地影響到效能。

CAShapeLayer

cornerRadiusmaskToBounds 獨立作用的時候都不會有太大的效能問題, 但是當他倆結合在一起,就觸發了螢幕外渲染。有時候你想顯示圓角並沿著圖層裁 切子圖層的時候,你可能會發現你並不需要沿著圓角裁切,這個情況下 用 CAShapeLayer 就可以避免這個問題了。 你想要的只是圓角且沿著矩形邊界裁切,同時還不希望引起效能問題。其實你可 以用現成的UIBezierPath的構造器+ bezierPathWithRoundedRect: cornerRadius: 這樣做並不會比直接用 cornerRadius更快,但是它避免了效能問題。

CAShapeLayer *blueLayer = [CAShapeLayer layer]; blueLayer.frame = CGRectMake(50, 50, 100, 100); blueLayer.fillColor = [UIColor blueColor].CGColor; blueLayer.path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 0, 100, 100) cornerRadius:20].CGPath;
 
//add it to our view
[self.layerView.layer addSublayer:blueLayer];
複製程式碼

可伸縮圖片

另一個建立圓角矩形的方法就是用一個圓形內容圖片是提到的contentsCenter屬性去建立一個可伸縮圖片。理論上來說,這 個應該比用 要快,因為一個可拉伸圖片只需要18個三角形(一個圖片是由一個3*3網格渲染而成),然而,許多都需要渲染成一個順滑的曲線。在 實際應用上,二者並沒有太大的區別。

contentsCenter 其實是一個CGRect,它定義了一個固定的邊框和一個在圖層上可拉伸的區域。改變contentsCenter的值並不會影響到寄宿圖的顯示,除非這個圖層的大小改變了,你才看得到效果。

CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50, 50, 100, 100);
blueLayer.contentsCenter = CGRectMake(0.5, 0.5, 0.0, 0.0);
blueLayer.contentsScale = [UIScreen mainScreen].scale;
blueLayer.contents = (__bridge id)[UIImage imageNamed:@"Circle"];
//add it to our view
[self.layerView.layer addSublayer:blueLayer];
複製程式碼

使用可伸縮圖片的優勢在於它可以繪製成任意邊框效果而不需要額外的效能消耗。可伸縮圖片甚至還可以顯示出矩形陰影的效果。

shadowPath

如果圖層是一個簡單幾何圖形如矩形或 者圓角矩形(假設不包含任何透明部分或者子圖層),建立出一個對應形狀的陰影 路徑就比較容易,而且Core Animation繪製這個陰影也相當簡單,避免了螢幕外的 圖層部分的預排版需求。這對效能來說很有幫助。 如果你的圖層是一個更復雜的圖形,生成正確的陰影路徑可能就比較難了,這樣子的話你可以考慮用繪圖軟體預先生成一個陰影背景圖。

混合和過度繪製

GPU每一幀可以繪製的畫素有一個最大限制(就是所謂的fillrate),這個情況下可以輕易地繪製整個螢幕的所有畫素。但是如果由於重疊圖層的關係需要不停地重繪同一區域的話,掉幀就可能發生了。 GPU會放棄繪製那些完全被其他圖層遮擋的畫素,但是要計算出一個圖層是否被遮擋也是相當複雜並且會消耗處理器資源。同樣,合併不同圖層的透明重疊畫素(即混合)消耗的資源也是相當客觀的。所以為了加速處理程式,不到必須時刻不要使用透明圖層。任何情況下,你應該這樣做:

  • 給檢視的 屬性設定一個固定的,不透明的顏色
  • 設定opaque屬性為YES

這樣做減少了混合行為(因為編譯器知道在圖層之後的東西都不會對最終的畫素顏色產生影響)並且計算得到了加速,避免了過度繪製行為因為Core Animation可以捨棄所有被完全遮蓋住的圖層,而不用每個畫素都去計算一遍。 如果用到了影象,儘量避免透明除非非常必要。 最後,明智地使用 shouldRasterize 屬性,可以將一個固定的圖層體系摺疊成 單張圖片,這樣就不需要每一幀重新合成了,也就不會有因為子圖層之間的混合和 過度繪製的效能問題了。

#減少圖層數量

初始化圖層,處理圖層,打包通過IPC發給渲染引擎,轉化成OpenGL幾何圖形,這些是一個圖層的大致資源開銷。事實上,一次效能夠在螢幕上顯示的最大圖層數量也是有限的。 確切的限制數量取決於iOS裝置,圖層型別,圖層內容和屬性等。但是總得說來 可以容納上百或上千個。 在對圖層做任何優化之前,你需要確定你不是在建立一些不可見的圖層,圖層在以下幾種情況下回事不可見的:

  • 圖層在螢幕邊界之外,或是在父圖層邊界之外。
  • 完全在一個不透明圖層之後。
  • 完全透明

不可見的圖層儘量延遲建立(需要時在建立)。

物件回收

處理巨大數量的相似檢視或圖層時還有一個技巧就是回收他們。物件回收在iOS 頗為常見; UITableViewUICollectionView 都有用到還有其他很多例子。 物件回收的基礎原則就是你需要建立一個相似物件池。當一個物件的指定例項結束了使命,你把它新增到物件池中。每次當你需要一個例項時,你就從池中取出一個。當且僅當池中為空時再建立一個新的。 這樣做的好處在於避免了不斷建立和釋放物件(相當消耗資源,因為涉及到記憶體的分配和銷燬)而且也不必給相似例項重複賦值。


# Instrument>Core Animation使用介紹 >**Instrument** > **Core Animation** 工具也提供了一系列核取方塊選項來幫助除錯渲染瓶頸:
  • Color Blended Layers - 這個選項基於渲染程度對螢幕中的混合區域進行綠到 紅的高亮(也就是多個半透明圖層的疊加)。由於重繪的原因,混合對GPU性 能會有影響,同時也是滑動或者動畫幀率下降的罪魁禍首之一。
  • Color Hits Green and Misses Red - 當使用 shouldRasterizep 屬性的時候, 耗時的圖層繪製會被快取,然後當做一個簡單的扁平圖片呈現。當快取再生的時候這個選項就用紅色對柵格化圖層進行了高亮。如果快取頻繁再生的話,就意味著柵格化可能會有負面的效能影響了。
  • Color Copied Images - 有時候寄宿圖片的生成意味著Core Animation被強制生成一些圖片,然後傳送到渲染伺服器,而不是簡單的指向原始指標。這個選 項把這些圖片渲染成藍色。複製圖片對記憶體和CPU使用來說都是一項非常昂貴的操作,所以應該儘可能的避免。
  • Color Immediately - 通常Core Animation Instruments以每毫秒10次的頻率更 新圖層除錯顏色。對某些效果來說,這顯然太慢了。這個選項就可以用來設定每幀都更新(可能會影響到渲染效能,而且會導致幀率測量不準,所以不要一直都設定它)。
  • Color Misaligned Images - 這裡會高亮那些被縮放或者拉伸以及沒有正確對齊到畫素邊界的圖片(也就是非整型座標)。這些中的大多數通常都會導致圖片的不正常縮放,如果把一張大圖當縮圖顯示,或者不正確地模糊影象,那麼這個選項將會幫你識別出問題所在。
  • Color Offscreen-Rendered Yellow - 這裡會把那些需要離屏渲染的圖層高亮 成黃色。這些圖層很可能需要用 shadowPath 或者 shouldRasterize 來優化。
  • Color OpenGL Fast Path Blue - 這個選項會對任何直接使用OpenGL繪製的 圖層進行高亮。如果僅僅使用UIKit或者Core Animation的API,那麼不會有任何效果。如果使用 GLKView 或者 CAEAGLLayer,那如果不顯示藍色塊的話就意味著你正在強制CPU渲染額外的紋理,而不是繪製到螢幕。
  • Flash Updated Regions - 這個選項會對重繪的內容高亮成黃色(也就是任何在軟體層面使用Core Graphics繪製的圖層)。這種繪圖的速度很慢。如果頻繁發生這種情況的話,這意味著有一個隱藏的bug或者說通過增加快取或者使 用替代方案會有提升效能的空間。

相關文章