美文网首页
离屏渲染

离屏渲染

作者: 得_道 | 来源:发表于2020-10-14 14:35 被阅读0次

    概念:(Off-Screen Rendering ):如果有时因为一些限制,无法把渲染结果直接写入frame buffer,而是先暂存在另外的内存区域,之后再写入frame buffer,那么这个过程被称之为离屏渲染。也就是GPU需要在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。

    声明一点,单纯的设置 layer.cornerRadius 是不会造成任何性能问题的,只有配合 layer.masksToBounds 使用时才会造成离屏渲染(根据试验在 iOS 10 and later 的系统这样设置大部分 UIKit 控件也不会再触发离屏渲染)。

    如果是 iOS 10 & Later,可以放心的使用系统方法,除了带图片的 UIButton 其他的控件直接使用不会造成离屏渲染。

    离屏渲染分析

    测试工具

    在出现图像性能问题,滑动,动画不够流畅之后,我们首先要做的就是定位出问题的所在。而这个过程并不是只靠经验和穷举法探索,我们应该用有脉络,有顺序的科学的手段进行探索。

    首先,我们要有一个定位问题的模式。我们可以按照这样的顺序来逐步定位,发现问题。

    1. 定位帧率,为了给用户流畅的感受,我们需要保持帧率在60帧左右。当遇到问题后,我们首先检查一下帧率是否保持在60帧。

    2. 定位瓶颈,究竟是CPU还是GPU。我们希望占用率越少越好,一是为了流畅性,二也节省了电力。

    3. 检查有没有做无必要的CPU渲染,例如有些地方我们重写了drawRect,而其实是我们不需要也不应该的。我们希望GPU负责更多的工作。

    4. 检查有没有过多的offscreen渲染,这会耗费GPU的资源,像前面已经分析的到的。offscreen 渲染会导致GPU需要不断地onScreen和offscreen进行上下文切换。我们希望有更少的offscreen渲染。

    5. 检查我们有无过多的Blending,GPU渲染一个不透明的图层更省资源。

    6. 检查图片的格式是否为常用格式,大小是否正常。如果一个图片格式不被GPU所支持,则只能通过CPU来渲染。一般我们在iOS开发中都应该用PNG格式,之前阅读过的一些资料也有指出苹果特意为PNG格式做了渲染和压缩算法上的优化。

    7. 检查是否有耗费资源多的View或效果。我们需要合理有节制的使用。像之前提到的UIBlurEffect就是一个例子。

    8. 最后,我们需要检查在我们View层级中是否有不正确的地方。例如有时我们不断的添加或移除View,有时就会在不经意间导致bug的发生。像我之前就遇到过不断添加View的一个低级错误。我们希望在View层级中只包含了我们想要的东西。

    OK,当我们有了一套模式之后,就可以使用苹果为我们提供的优秀测试工具来进行测试了。

    对于图形性能问题的地位。一般我们有下列测试工具:

    Instruments里的:

    • Core Animation instrument
    • OpenGL ES Driver instrument

    模拟器中的:

    Color debug options View debugging

    还有Xcode的:

    View debugging

    然后我们来根据上面定位问题的模式来选择相应测试工具:

    1. 定位帧率

    2. 定位瓶颈

    3. 检查有无必要的CPU渲染

      以上三点我们可以使用CoreAnimation instrument来测试。

      CoreAnimation instrument包含了两个模块。第一幅图展示了检测帧率。第二幅图展示了检测CPU调用。我们能够通过它们来进行上述三个问题的检测。注意到第二幅图左下角,那是CPU 的call stack.我们就是在这里检测我们有没有做无必要的drawRect,有没有在主线程做太多事务导致阻塞了UI更新。

      关于GPU的瓶颈问题,我们可以通过OpenGL ES Driver instrument来获得更详细的信息。例如GPU的占用率。可以看到下图左下角有显示Device utilization。

      [图片上传失败...(image-a789f6-1602656916765)]

    4. 检查有无过多offscreen渲染

    5. 检查有无过多Blending

    6. 检查有无不正确图片格式,图片是否被放缩,像素是否对齐。

    7. 检查有无使用复杂的图形效果。

      以上这四点我们同样使用CoreAnimation instrument来测试。

      我们可以看到上图右下角的Debug options有多个选项。我们通过勾选这些选项来触发Color Debug。下面逐个对这些选项进行分析。

      • Color Blended layers

        如图,勾选这个选项后,blended layer 就会被显示为红色,而不透明的layer则是绿色。我们希望越少红色区域越好。

      • Color Hits Green and Misses Red

        这个选项主要是检测我们有无滥用或正确使用layer的shouldRasterize属性.成功被缓存的layer会标注为绿色,没有成功缓存的会标注为红色。

        在测试的过程中,第一次加载时,开启光栅化的layer会显示为红色,这是很正常的,因为还没有缓存成功。但是如果在接下来的测试,例如我们来回滚动TableView时,我们仍然发现有许多红色区域,那就需要谨慎对待了。因为像我们前面讨论过的,这会引起offscreen rendering。

        检查一下是否有滥用该属性,因为系统规定的缓存大小是屏幕大小的2.5倍,如果使用过度,超出了缓存大小,会引起offscreen rendering。检测layer是否内容不断更新,内容的更新会导致缓存失效和大量的offscreen rendering.

      • Color copied images

        这个选项主要检查我们有无使用不正确图片格式,若是GPU不支持的色彩格式的图片则会标记为青色,则只能由CPU来进行处理。我们不希望在滚动视图的时候,CPU实时来进行处理,因为有可能会阻塞主线程。

      • Color misaligned images

        这个选项检查了图片是否被放缩,像素是否对齐。被放缩的图片会被标记为黄色,像素不对齐则会标注为紫色。

      • Color offscreen-rendered yellow

        这个选项将需要offscreen渲染的的layer标记为黄色。

        以上图为例子,NavigationBar和ToolBar被标记为黄色。因为它们需要模糊背后的内容,这需要offscreen渲染。但是这是我们需要的。而图片也是被标记为黄色,那是因为阴影的缘故。我前面已经提到了这一点,如果此时我们用shadowPath来替代的话,就能够避免offscreen渲染带来的巨大开销。

      • Color OpenGL fast path blue

        这个选项勾选后,由OpenGL compositor进行绘制的图层会标记为蓝色。这是一个好的结果。

      • Flash updated regions

        会标记屏幕上被快速更新的部分为黄色,我们希望只是更新的部分被标记完黄色。

      好啦,终于完整介绍完这些调试选项了,我们总结一下。

      我们需要重点注意的是
      1.Color Blended layers
      2.Color Hits Green and Misses Red
      3.Color offscreen-rendered yellow这三个选项。
      因为这三个部分对性能的影响最大。

    8. 检查View层级是否正确。

      我们可以在上图清楚地看到View的层级关系。可以检查View的层级是否正确。

      小提示(应用运行后,在这里打开):从左往右第七个图标

    问题

    离屏渲染是在哪一步发生的吗?
    GPU渲染之前

    离屏渲染产生的原因是什么呢?
    在某些场景下“画家算法”虽然可以逐层输出,但是无法在某一层渲染完成后,在回过头来擦除/修改某一部分,因为这一层之前的layer像素数据已经被永久覆盖了。这就意味着对于每一层的layer要么能够通过单次遍历就能完成渲染,要么就只能令开辟一块内存作为临时中转区来完成复杂的修改/裁剪等操作。

    哪些触发离屏渲染的情况?

    1. shadow(阴影)
      因为shadow必须知道其上层 Layer 的形状才能渲染,而上层 Layer 又必须等其下层 Layer 渲染完成才能渲染,所以就不得不新建一个 Offscreen Buffer 参与渲染。

    解决办法就是设置 Layer 的.shadowPath,这样可以使 GPU 在渲染阴影 Layer 时,不用知道其上层 Layer 形状就可以完成阴影路径的渲染。

    1. cornerRadius + clipsToBounds(圆角与裁切圆角以外内容)
      在 Layer 被渲染时,其上层 Layer 还没有被渲染,所以无法完成裁切圆角以外内容,所以也需要新建一个 Offscreen Buffer 参与渲染。

    解决办法是使用UIBezierPath与CAShapeLayer设置圆角。

    1. group opacity(群组透明)、mask(遮罩)、UIBlurEffect(高斯模糊)与allowsEdgeAntialiasing(抗锯齿)
      在所有 Layer 的渲染完成后,再应用透明度,与分别为每层 Layer 应用透明度再渲染得到的结果是不同的,与透明度相关以及抗锯齿的渲染都无可避免的需要一个 Offscreen Buffer 参与渲染。

    2. shouldRasterize(栅格化)
      shouldRasterize相当于主动进行离屏渲染,目的是为了将渲染结果保存在一起,使下一帧可以直接复用,避免重复渲染。

    设置圆角一定会触发离屏渲染吗?
    不一定

    离屏渲染既然会影响性能我们为什么还要使用呢?优化方案又有那些?
    劣势
    离屏渲染增大了系统的负担,会形象App性能。主要表现在以下几个方面:

    离屏渲染需要额外的存储空间,渲染空间大小的上限是2.5倍的屏幕像素大小,超过无法使用离屏渲染
    容易掉帧:一旦因为离屏渲染导致最终存入帧缓存区的时候,已经超过了16.67ms,则会出现掉帧的情况,造成卡顿

    优势
    虽然离屏渲染会需要多开辟出新的临时缓存区来存储中间状态,但是对于多次出现在屏幕上的数据,可以提前渲染好,从而进行复用,这样CPU/GPU就不用做一些重复的计算。
    特殊产品需求,为实现一些特殊动效果,需要多图层以及离屏缓存区保存中间状态,这种情况下就不得不使用离屏渲染。比如产品需要实现高斯模糊,无论自定义高斯模糊还是调用系统API都会触发离屏渲染。

    文章

    https://github.com/100mango/zen/blob/master/WWDC%E5%BF%83%E5%BE%97%EF%BC%9AAdvanced%20Graphics%20and%20Animations%20for%20iOS%20Apps/Advanced%20Graphics%20and%20Animations%20for%20iOS%20Apps.md

    相关文章

      网友评论

          本文标题:离屏渲染

          本文链接:https://www.haomeiwen.com/subject/xuavpktx.html