离屏渲染

作者: 吕建雄 | 来源:发表于2020-07-07 09:45 被阅读0次

    离屏渲染是在iOS开发过程中脱离不了的话题,那么什么是离屏渲染以及哪些情况会导致离屏渲染呢?以及离谱渲染有哪些优势和劣势?

    首先看下面一段代码,那种模式会触发离屏渲染呢?

           //按钮存在背景图片,并且设置圆角

            let btn1 =UIButton.init(type: .custom)

            btn1.frame=CGRect.init(x:100, y:100, width:44, height:44)

            btn1.layer.cornerRadius=5.0

            self.view.addSubview(btn1)

            btn1.setImage(UIImage.init(named:"tabbar_discover_hl"), for: .normal)

            btn1.clipsToBounds=true

            //按钮不存在背景图片,并且设置圆角

            let btn2 =UIButton(type: .custom)

            btn2.frame=CGRect.init(x:100, y:180, width:44, height:44)

            btn2.layer.cornerRadius=5.0

            btn2.backgroundColor = .blue

            self.view.addSubview(btn2)

            btn2.clipsToBounds=true

            //图片设置了背景色+背景图片

            let imgv1 =UIImageView(frame:CGRect.init(x:100, y:260, width:44, height:44))

            imgv1.image=UIImage.init(named:"tabbar_friends_hl")

            imgv1.backgroundColor = .blue

            imgv1.layer.cornerRadius=5.0

            imgv1.layer.masksToBounds=true

            self.view.addSubview(imgv1)

            //图片只设置了背景色

            let imgv2 =UIImageView(frame:CGRect.init(x:100, y:340, width:44, height:44))

            imgv2.image=UIImage.init(named:"tabbar_mine_hl")

            imgv2.layer.cornerRadius=5.0

            imgv2.layer.masksToBounds=true

            self.view.addSubview(imgv2)

    XCode查看离屏渲染 测试效果

    从上图可知,1、3中写法触发了离屏渲染,2、4两种写法未触发离屏渲染;

    APP渲染流程

    渲染流水线示意图

    从图中可以看到(图片来源:WWDC),Application以及RenderServer部分是运行在CPU上的,Application处理好数据后,提交到Render Server,然后Core Animation在会将具体操作转换成GPU的Draw calls(OpenGL/Metal);也就是CPU+GPU共同完成渲染工作

    什么是离屏渲染:

    如果要在显示屏上显示内容,我们至少需要一块与屏幕像素数据量一样大的frame buffer,作为像素数据存储区域,而这也是GPU存储渲染结果的地方。如果有时因为面临一些限制,无法把渲染结果直接写入frame buffer,而是先暂存在另外的内存区域,之后再写入frame buffer,那么这个过程被称之为离屏渲染(offscreen buffer)

    通过下面两幅图结合上面的概念可以清晰离屏渲染的原理:

    渲染结果直接给到FrameBuffer,然后展示到屏幕上 渲染结果线给到offscreenBuffer,在到FrameBuffer,然后展示到屏幕上

    那么离屏渲染是如何工作的呢?通过圆角触发离屏渲染来进行分析

    设置圆角触发offscreen render

    从上图可知,给UIButton设置背景图片并且添加圆角涉及3部分内容

    1、backgroundColor

    2、contents

    3、borderColor/borderWidth

    当每一部分layer会独立绘制,然后保存到offscreenBuffer中,当全部layer绘制后,对这3部分整体进行添加圆角,所以需要先存入到offscreenBuffer中,否则绘制完会自动销毁,当设置圆角时无法获取到之前的layer;

    绘制原理图示

    GPU离屏渲染,“画家画法”

    通过渲染流水线示意图中我们可以看到,主要的渲染操作都是由CoreAnimation的Render Server模块,通过调用显卡驱动所提供的OpenGL/Metal接口来执行的。通常对于每一层layer,Render Server会遵循“画家算法”,按次序输出到frame buffer,后一层覆盖前一层,就能得到最终的显示结果(值得一提的是,与一般桌面架构不同,在iOS中,设备主存和GPU的显存共享物理内存,这样可以省去一些数据传输开销)

    然而有些场景并没有那么简单。作为“画家”的GPU虽然可以一层一层往画布上进行输出,但是无法在某一层渲染完成之后,再回过头来擦除/改变其中的某个部分——因为在这一层之前的若干层layer像素数据,已经在渲染中被永久覆盖了。这就意味着,对于每一层layer,要么能找到一种通过单次遍历就能完成渲染的算法,要么就不得不另开一块内存,借助这个临时中转区域来完成一些更复杂的、多次的修改/剪裁操作

    当sublayer绘制到屏幕上之后,就会将sublayer从帧缓存区移除,从而节省空间 将要混合的内容各自渲染完,暂时存在离屏缓存区 等所有layer全部绘制完成,从offscreenBuffer中获取数据,进行组合

    通过上面的理解,可以发现,App需要进行额外的渲染和合并,必须使用Offscreen Buffer,然后进行组合放入到Frame Buffer中,然后现在到屏幕上

    离屏渲染性能问题:

    1,需要额外的存储空间,

    2,将结果从Offscreen buffer转存到Frame Buffer中也是需要时间的

    Offscreen Buffer的空间也是有限制的(限制大小是屏幕像素大小的2.5倍)

    既然离屏渲染容易带来性能问题,为什么还要用呢?

    1.非常多的特殊效果,并不能一次用一个图层就能画出来,所以需要使用额外的offscreen Buffer来保持中间状态(不得不使用),比如:圆角,阴影

    2.能带来效率的优势:既然效果会多次出现在屏幕上,可提前渲染好,保存在offscreen Buffer中,从而达到复用的结果  比如:光珊化shouldRasterize

    常见离屏渲染:

    能引起离屏渲染

    所以我们在项目开发的过程中,根据实际情况进行相关的优化,突出其优势避免其劣势

    特别说明:关于光珊化并不是所有的光珊化都会带来性能问题,以下是关于光珊化的使用建议

    光珊化使用建议

    相关文章

      网友评论

        本文标题:离屏渲染

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