如何改善图形性能

iOS 的 App 现在已经变得功能越来越强大,同时也越来越复杂,在满足设计和产品的需求的同时,性能问题也越来越重要。

Instruments Instruments 是我们在做性能调试时经常用到的工具,在对图像性能进行调试的时候,我们会用到 Core Animation 组件。Core AnimationDebug Options 有诸多选项可以帮助我们调试。

Screen Shot 2017-07-28 at 11.36.44 A

以下根据其中的几个选项,在描述原因的同时,也会涉略一些解决的办法:

  • Color Blended Layer
  • Color Copied Images
  • Color Misaligned Images
  • Color Offscreen-Rendered Yellow
  • Color Hits Green and Misses Red

Color Blended Layer

Apple 的官方文档Color Blended Layer 有如下的解释:

Shows blended view layers. Multiple view layers that are drawn on top of each other with blending enabled are highlighted in red. Reducing the amount of red in your app when this option is selected can dramatically improve your app’s performance. Blended view layers often cause slow table scrolling.

什么是 Blending

Blending 主要指的是混合像素颜色的计算。例如,我们有两个 view - viewA 和 viewB。

1
2
3
4
viewA.backgroundColor = .red
viewA.alpha = 0.5
viewB.backgroundColor = .blue
viewB.alpha = 0.5

temp1

两个 view 都有 0.5 的透明度,他们重叠的部分就需要额外的计算:

  • 只有红色视图的部分,需要通过红色视图和背景的白色试图来混合计算;
  • 只有蓝色视图的部分,需要通过蓝色视图和背景的白色试图来混合计算;
  • 红色视图和蓝色试图重叠的部分,需要通过红色视图、蓝色试图和背景的白色试图来混合计算;

由此可见,如何有比较多的互相重叠的图片,在混合计算的这一块需要耗费比较多的性能。

会造成 Blending 的原因有

  • UIView/CALayer 的 alpha 小于 1.0;
  • UIImageView 的 image 含有透明通道;

如何优化

尽量使用不透明图层,图片中去掉不必要的透明。

在 UILabel 中文本是中文的时候,即使设置了背景色,也还是会出现 blending 的情况,layer.maskToBounds = true。

Instruments 中的表现

  • 绿色:表示无像素混合
  • 红色:表示有像素混合

Color Copied Images

苹果的 GPU 只解析 32bit 的颜色格式,如果一张图片的颜色格式不是 32bit,CPU 会先进行颜色格式转换,再让 GPU 渲染。CPU 这样额外的消耗,很可能引起滚动试图的卡顿。

如何优化

  • 让设计师导出符合格式要求的图片;
  • 如果一定要处理这样的图片,先在后台线程处理转换成 32bit,再使用。

Instruments 中的表现

  • GPU 不支持的色彩格式的图片会标记为青色。

Color Misaligned Images

这里的 Misaligned 其实有两个意思:① 视图的 frame 像素不对齐,即不能换算成整数像素值;② UIImageView 显示的图片像素大小与 UIImageView.frame.size 不对齐,即图片发生了缩放。

如何优化

  • 借助 ceilf()floorf()CGRectIntegral() 等将小数点后数据除去。其中在使用 floorf() 需要注意,向下取整的时候,会不会导致视图显示不完整;
  • 将需要显示的图片,缩放到与 UIImageView 对应的大小,然后再显示。具体如何缩放可参考 NSHipster 上的一篇文章

Instruments 中的表现

  • 洋红色:frame 像素不对齐;
  • 黄色:图片像素大小与 frame.size 不对齐(图片缩放造成);

Color Offscreen-Rendered

Offscreen Rendering 指的是在图像绘制到当前屏幕前,需要先进行一次离屏渲染,之后才会绘制到当前屏幕。Offscreen Rendering 时需要另外 alloc 一块内存来进行渲染,渲染完毕后再绘制到当前屏幕,而且对于显卡来说,onscreenoffscreen 的上下文切换是非常昂贵的,所以一般导致图形性能问题大部分都出在了 Offscreen Rendering

Offscreen Rendering 主要讲的是通过 GPU 执行的 offscreen,事实上还有的 Offscreen Rendering 是通过 CPU 来执行的(例如使用 Core Graphics, drawRect:)。其它类似 cornerRadios, masks, shadows 等触发的 offscreen 是基于 GPU 的。可参考 Andy Matuschak 的回复

会造成 Offscreen Rendering 的原因有:

  • layer.cornerRadiuslayer.masksToBounds 设置为 true
  • layer.mask
  • layer.allowsGroupOpacity 设置为 true 同时 layer.opacity 小于 1.0
  • layer.edgeAntialiasingMask, layer.allowsEdgeAntialiasing
  • layer.shouldRasterize 设置为 true

如何优化

  1. 裁剪图片为圆

    1
    2
    3
    4
    5
    6
    7
    8
    // 使用 cornerRadius
    CALayer *imageViewLayer = cell.imageView.layer;
    imageViewLayer.cornerRadius = imageHeight / 2.0;
    imageViewLayer.masksToBounds = YES;

    // 混合图层:利用一张中间为透明圆形的图片来进行遮盖,虽然会引起 blending,但性能仍然高于 offerScreen

    // 重绘圆角
  1. 使用 shadowPath 替代 shadowOffset 来进行阴影绘制;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*
    CALayer *imageViewLayer = cell.imageView.layer;
    imageViewLayer.shadowColor = [UIColor blackColor].CGColor;
    imageViewLayer.shadowOpacity = 1.0;
    imageViewLayer.shadowRadius = 2.0;
    imageViewLayer.shadowOffset = CGSizeMake(1.0, 1.0);
    */
    // 改为使用 shadowPath
    imageViewLayer.shadowPath = CGPathCreateWithRect(imageRect, NULL);
  2. Mask

    Mask 效果无法取消离屏渲染,使用混合图层的方法来模拟 mask 效果,性能各方面都是和无效果持平。

  3. EdgeAntialiasing

    开启 edge antialiasing(旋转视图并且设置layer.allowsEdgeAntialiasing = true) 在 iOS 8 和 iOS 9 上并不会触发离屏渲染,对性能也没有什么影响,也许到现在这个功能已经被优化了。

  4. GroupOpacity

    GroupOpacity 开启离屏渲染的条件是:layer.opacity != 1.0 并且有子 layer 或者背景图。触发离屏渲染,对性能无明显影响。

  5. shouldRasterize`

    开启 Rasterization 后,GPU 只合成一次内容,然后复用合成的结果;合成的内容超过 100ms 没有使用会从缓存里移除,在更新内容时还会产生更多的离屏渲染。对于内容不发生变化的视图,原本拖后腿的离屏渲染就成为了助力;如果视图内容是动态变化的,使用这个方案有可能让性能变得更糟。

Instruments 中的表现

  • 黄色:产生了离屏渲染

Color Hits Green and Misses Red

开启 Rasterization 后,GPU 只合成一次内容,然后复用合成的结果,这是一种比较理想的结果。红色越多,性能越差。因为栅格化生成缓存的过程是有开销的,如果缓存能被大量命中和有效使用,则总体上会降低开销,反之则意味着要频繁生成新的缓存,这会让性能问题雪上加霜。

如果是在第一次打开显示为红色是正常的,因为一开始时没有可复用的内容的,都需要重建。

如何优化

如果出现了大量的红色,则说明你的视图是会动态改变的,Rasterization 并不适合为这样的视图来优化性能,所以关掉 Rasterization,采用其他的方式来优化性能是比较好的选择。

Instruments 中的表现

  • 绿色:缓存被复用
  • 红色:缓存未被复用,存在重复创建

Ref.
0%