iOS 效能優化思路:介面離屏渲染、圖層混色

鄧輕舟發表於2018-11-19

手機效能優化的重點,就是介面渲染。一般,計算任務都交給服務端。

介面渲染慢,就不好了。


常見問題,就是離屏渲染。 這裡用 NSShadow 處理掉 CALayer 的陰影屬性帶來的離屏渲染。

常見的離屏渲染程式碼: 繪製陰影,

        var label = UILabel()
        label.layer.shadowColor = UIColor.lightGray.cgColor
        label.layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
        label.layer.shadowOpacity = 1.0
        label.layer.shadowRadius = 5.0
        label.text = "離屏渲染"
複製程式碼

寫完以後,CPU 和 GPU 都沒有充足的資訊繪製陰影效果。

過程是, CPU 會先把文字傳出去,請求 GPU (CPU 把文字傳給 GPU ),建立一個記憶體中的點陣圖上下文(GPU 把文字放進去 ),離屏渲染這就開始了。

這個上下文缺資訊,不在螢幕上, 不是幀緩衝。(渲染出來的圖形上下文,不屬於當前幀)。

image

之前 CPU 把文字處理好了,現在 CPU 處理文字效果(這裡是陰影)。

然後,CPU 拿到渲染好的文字,基於渲染出來的每一個畫素的透明度,計算出陰影的形狀。

最後,CPU 把最新計算出來的文字陰影形狀的資訊,傳給 GPU . GPU 有陰影資訊,有之前圖形上下文的渲染文字,GPU 就渲染好了最終的文字及其陰影,交給幀緩衝 (Frame Buffer), 我們就看到了。

這段程式碼要渲染兩次,出現了離屏渲染。對 GPU 的效能有影響。他需要等待 CPU 來算出陰影的形狀。

因為我們要 60 的幀數 ( FPS ), GPU 準備幀緩衝,渲染出當前幀,只有 17 毫秒的時間。拖累了主執行緒,螢幕重新整理不過來。


對於圖形陰影, 用 layer 的 shadowPath. 對於文字陰影,用 NSShadow .

layer 的四個屬性 shadowColor , shadowOffset ,shadowOpacity ,shadowRadius ,一般效能不好。

對於一個檢視框, 通過 layer 在周邊加陰影。用 layer 的 shadowPath,建立一個 UIBezierPath,

UIBezierPath(rect: CGRect(x: 0, y: 0, width: 50, height: 50))
// size of your label
複製程式碼

與之前不同,圖層不用渲染兩次。現在 GPU 有了足夠的資訊繪製陰影效果, 就不用離屏渲染了。

對於文字陰影,用 NSShadow ,用 layer.path 就比較難。


UI 效能優化主要用的是 Instruments 的 Core Animation 模版。

過去,Core Animation 模版幾個除錯選項非常強大,正常的綠色, 異常的紅色,離屏渲染的黃色。

(光柵化有效,光柵化後快取的內容成功複用。介面會顯示綠色。

光柵化失效的部分,呈紅色。光柵化後快取的內容沒有複用,光柵化隱式建立的點陣圖浪費了。直接重新繪製。 )

現在這些利器都在 Xcode 裡了,可以直接使用。

真機執行直接選擇,

直接 debug

本文 Demo 使用的是 500 px 的 API .

除錯介面的離屏渲染,用的是 Color Offscreen-Rendered Yellow 選項。

除錯目標是,出現大片的綠色。

離屏渲染

這裡可以用 Apple 的 UIKit 框架下的 NSShadow 物件。

            let shadow = NSShadow()
            shadow.shadowColor = UIColor.lightGray
            shadow.shadowOffset = CGSize(width: 0.0, height: 5.0)
            shadow.shadowBlurRadius = 5.0
            if let mutableAttributedString = label.attributedText as? NSMutableAttributedString{
                let range = NSRange(location: 0, length: mutableAttributedString.string.count)
                mutableAttributedString.addAttribute(NSAttributedString.Key.shadow, value: shadow, range: range)
            }

複製程式碼

解決離屏渲染

用背景色處理掉混色。

介面圖層混色,就是多個檢視的位置有重疊,他們又不是透明的。重疊區域的每一個畫素,GPU 需要算出一種新的顏色(混色)。 如果這種效果,不是 UI 設計的,儘量避免。

除錯介面的圖層混色,用的是 Color Blended Layer 選項。

UILabel 建議設定背景色。UILabel 有文字,那就不透明,Label 預設的背景色是透明色,與父檢視的背景色,混雜了。GPU 對相關位置的顏色,需要重新計算。

指定 Lable 背景色前

  
override func awakeFromNib() {
        super.awakeFromNib()
        [userNameLabel, photosLikeLabel, photosDescriptionLabel, photoTimeIntevalSincePostLabel].forEach {
            $0?.backgroundColor = UIColor.white
        }
}

複製程式碼

設定後, 效果明顯

指定 Lable 背景色後

混色,如果不是 UI 指定的效果,建議處理掉。

介面圖層混色,常見的影響因素是 view 的 alpha 屬性。 alpha 小於 1, 一般自帶混色效果。

相關程式碼: github.com/BoxDengJZ/I…

其他知識點介紹: 卡頓(丟幀)。

Instruments 的 Core Animation 模版的時間線,就是 FPS. 顯示隨著時間,App 的幀率波動。

Instruments 的 Core Animation

可以方便的讀取幀數,直觀的瞭解那些介面要改善。

用程式碼檢測卡頓,自然是 CADisplayLink ,網上相關部落格很多。

相關資源:

視訊教程,practical-instruments

Apple, Performance Tips

相關文章