iOS 性能优化思路:界面离屏渲染、图层混色

3,433 阅读4分钟

手机性能优化的重点,就是界面渲染。一般,计算任务都交给服务端。

界面渲染慢,就不好了。


常见问题,就是离屏渲染。 这里用 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