美文网首页
003-iOS离屏渲染产生的原因

003-iOS离屏渲染产生的原因

作者: 沉默Coder | 来源:发表于2020-07-07 13:58 被阅读0次

    离屏渲染产生的原因

    在讨论离屏渲染之前我们先要搞清楚正常的渲染流程是怎样的

    非离屏渲染流程:
    非离屏渲染

    我们可以看到在非离屏渲染的场景下,需要渲染的数据是直接提交到GPU的帧缓冲区,等待屏幕的垂直同步信号来完成页面的显示,但是在离屏渲染的场景下又是怎样的呢?请看下图


    离屏渲染

    我们可以看到在离屏渲染的场景下,要完成页面的展示,中间多了一个过程,需要渲染的数据首先提交到了离屏缓冲区,然后再提交到帧缓冲区的,为什么要增加一个OffsceenBuffer呢?这个OffsceenBuffer又有什么作用呢?

    为什么要增加OffsceenBuffer?

    如果触发了离屏渲染,那么在显示这个页面的每一帧都会去做离屏渲染的处理,比如切圆角,显示阴影等,如果每一帧都要重新去处理,那么对于CPU以及GPU都带来很大的负担。所以引入了离屏缓冲区,将处理好的图层直接丢到离屏缓冲区,下次渲染的时候直接拿出来显示到屏幕上

    离屏缓存区也是有限制的

    • 时间限制:缓存的内容超过100ms没有被使用,内容将会被丢弃
    • 空间限制:超过屏幕像素大小的2.5倍,将不能再存储新的数据,离屏渲染会失效

    这样做虽然解决了多次处理数据的问题,但是还是会带来另外的性能问题,由于离屏缓冲区和帧缓冲区属于两个不同的缓冲区,如果一个页面触发了多个离屏渲染,那么GPU在显示每一帧的时候都要在离屏缓冲区和帧缓冲区之间切换,在快速滚动的场景下就会出现严重的掉帧。那么怎么解决这个问题呢?其实答案就是要减少离屏渲染,那些场景会触发离屏渲染呢?

    触发离屏渲染的场景

    离屏渲染的检测

    在模拟器运行时我们在这里开启离屏渲染的检测:


    离屏渲染检测开关

    开启后,如果有离屏渲染,则会显示成黄色,如下图:


    离屏渲染
    1. cornerRadius + masksToBounds

    这两个属性我们都不陌生,这是我们经常使用的切圆角的方式,将cornerRadius > 0同时masksToBounds = YES时,就可能会触发离屏渲染,注意,是可能,我们来看下图中的例子


    例1

    先不要着急,我们再来看另外一个例子:


    例2

    为什么同样都是设置cornerRadius + masksToBounds,例1和例2的结果却不相同呢?相信细心的同学已经发现了,例2多了一个绿色的view,例2中的图层层级是这样的:


    例2图层

    所以我们能够得出:

    如果只有一个图层,就算同时设置 cornerRadius + masksToBounds 也并不会触发离屏渲染,只有多个图层发生叠加的时候才会触发离屏渲染

    为什么多个图层叠加会触发离屏渲染呢 ?

    画家算法: 当GPU在渲染图层的时候会根据图层的远近去由远及近完成渲染,这里说的远近就是叠加图层时由内到外,当一帧数据处理好后将被提交到帧缓冲区,但是如果当前图层是多个图层叠加后的结果,那么将不能被直接提交到帧缓冲区(如果提交了,下一个垂直同步信号到来的时候会被直接显示在界面上),需要存储起来,等待这个图层的所有子图层都被处理完通过叠加后,再提交到帧缓冲区用于显示

    如果我们只设置cornerRadius,会不会触发离屏渲染呢?

    cornerRadius
    例3

    这个就要从CALayer的结构说起了


    CALayer结构

    CALayer大致分为三层

    1. backgroundColor : 背景颜色层
    2. content :内容显示层,这一层主要用于显示我们设置的内容
    3. border :当我们设置边框的时候,就是这一层在工作

    cornerRadius只会设置backgroundColor和border的圆角,并不会对content设置圆角。除非同时设置了masksToBounds为YES(对应view中的clipsToBounds),所以如果只设置cornerRadius,在图层叠加时并不会触发离屏渲染

    2.mask

    Mask 效果与混合图层的效果非常相似,只是使用同一个遮罩图像时,mask 与混合图层的效果是相反的。Mask无法取消离屏渲染


    例4

    3. GroupOpacity

    GroupOpacity

    GroupOpacity 是指 CALayer 的allowsGroupOpacity属性,UIView 的alpha属性等同于 CALayer opacity属性。开启 GroupOpacity 后,子 layer 在视觉上的透明度的上限是其父 layer 的opacity。

    从 iOS 7 以后默认全局开启了这个功能,这样做是为了让子视图与其容器视图保持同样的透明度。

    GroupOpacity 开启离屏渲染的条件是:layer.opacity != 1.0并且有子 layer 或者背景图。

    例5

    4. shadow

    阴影直接合成在视图的下面,视图结构里并没有多出一个视图。在没有指定阴影路径时,阴影是沿着视图的非透明部分扩展的,而且 CALayer 的三个视觉元素至少有一个存在时才会有阴影。
    使用阴影必须保证 layer 的masksToBounds = false,因此阴影与系统圆角不兼容。但是注意,只是在视觉上看不到,对性能的影响依然。


    例6

    如果我们在设置阴影时,给阴影指定了路径,就不会触发离屏渲染了
    例如,这样设置阴影:

        self.view1.layer.shadowColor = [UIColor greenColor].CGColor;
        self.view1.layer.shadowOffset = CGSizeMake(5, 5);
        self.view1.layer.shadowOpacity = 0.5;
        self.view1.layer.shadowRadius = 3;
        UIBezierPath * path = [UIBezierPath bezierPathWithRect:self.view1.bounds];
        self.view1.layer.shadowPath = path.CGPath;
    
    例7

    相关文章

      网友评论

          本文标题:003-iOS离屏渲染产生的原因

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