美文网首页
渲染和iOS的离屏渲染

渲染和iOS的离屏渲染

作者: 那些喧嚣 | 来源:发表于2020-07-08 16:43 被阅读0次

    iOS下的渲染流程

    1.png

    知识点:

    • 整个渲染流程,可以分为OpenGL client(客户端)和OpenGL server(服务端)
    • client是在CPU里面执行,不断的驱动数据传递给server
    • server是在GPU里面执行,直接操作GPU绘制图形
    • Vertex Shader(顶点着色器),由client从三个通道:Attributes(ins)、Uniforms、Texture Data将数据传递进来
    • Fragment Shader(片元着色器),由client从两个通道:Uniforms、Texture Data传递数据进来,Attribute只能通过定点着色器传递进来
    • 顶点着色器不能直接操作片元着色器,需要由顶点着色器输出数据(开发者无法干涉)到下一个阶段(Primitive Assembly:图元装配/光栅化),将数据拷贝到着色器中
    • render 渲染图形

    离屏渲染

    On-Screen Rendering (当前屏幕渲染)

    • GPU的渲染操作是在当前用于显示的屏幕缓冲区进行

    Off-Screen Rendering (离屏渲染)

    • GPU在当前屏幕缓冲区以外开辟的一个缓冲区进行渲染操作

    Off-Screen Rendering 和 On-Screen Rendering的比较

    • 创建新的缓冲区
    • 上下文切换,即:从当前屏幕切换到离屏,离屏渲染结束后,讲渲染结果显示到屏幕上,需要将上下文环境从离屏切换到当屏(更耗性能)

    触发离屏渲染的方式

    主要出现在处理图层操作较多的情况下会发生离屏渲染(以下包含不仅限于)

    1、layer的mask

    2、layer的投影(layer.Shadow)

    3、layer的光栅化(layer.ShouldRasterize)

    4、layer.masksToBounds = true 可能会产生离屏渲染

    • 打开该属性,如果只设置了背景色或者图片,也不会产生离屏渲染,即:图层只有一层时,不触发离屏渲染
    func demo1() {
            /// 设置了背景色+圆角:无离屏渲染
            let imgView1 = UIImageView.init(frame: CGRect(x: 30, y: 80, width: 100, height: 100))
            imgView1.backgroundColor = .gray
            imgView1.layer.cornerRadius = 10
            view.addSubview(imgView1)
            
            /// 设置了背景色+圆角+图片:无离谱渲染
            /// 打开masksToBounds属性:出现离谱渲染
            let imgView2 = UIImageView.init(frame: CGRect(x: 30, y: 200, width: 100, height: 100))
            imgView2.layer.masksToBounds = true
            imgView2.image = UIImage.init(named: "user_image")
            imgView2.layer.cornerRadius = 10
            imgView2.backgroundColor = .gray
            view.addSubview(imgView2)
            
            /// 设置了背景色+圆角+多张只设置背景色叠加:无离谱渲染
            /// 设置了背景色+图片+圆角+多张设置背景色+图片叠加:无离谱渲染
            /// 打开masksToBounds属性:出现离谱渲染
            let imgView3 = UIImageView.init(frame: CGRect(x: 30, y: 320, width: 100, height: 100))
            imgView3.layer.cornerRadius = 10
            imgView3.image = UIImage.init(named: "user_image")
            imgView3.backgroundColor = .gray
            view.addSubview(imgView3)
            
            let imgView3_1 = UIImageView.init(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
            imgView3_1.backgroundColor = .red
            imgView3_1.image = UIImage.init(named: "user_image")
            imgView3.addSubview(imgView3_1)
            
            let imgView3_2 = UIImageView.init(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
            imgView3_2.backgroundColor = .green
            imgView3_2.image = UIImage.init(named: "user_image")
            imgView3_1.addSubview(imgView3_2)
            
            let imgView3_3 = UIImageView.init(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
            imgView3_3.backgroundColor = .blue
            imgView3_3.image = UIImage.init(named: "user_image")
            imgView3_2.addSubview(imgView3_3)
        }
        
        func demo2() {
            /// 设置了图片+圆角:无离屏渲染
            let imgView1 = UIImageView.init(frame: CGRect(x: 30, y: 80, width: 100, height: 100))
            imgView1.image = UIImage.init(named: "user_image")
            imgView1.layer.cornerRadius = 10
            view.addSubview(imgView1)
            
            /// 设置了圆角+图片:无离谱渲染
            /// 只设置图片,不设置背景色,打开masksToBounds属性:无离谱渲染
            /// 设置图片,设置背景色,打开masksToBounds属性:出现离谱渲染
            let imgView2 = UIImageView.init(frame: CGRect(x: 30, y: 200, width: 100, height: 100))
            imgView2.layer.masksToBounds = true
            imgView2.backgroundColor = .blue
            imgView2.image = UIImage.init(named: "user_image")
            imgView2.layer.cornerRadius = 80
            view.addSubview(imgView2)
            
            /// 设置了圆角+多张只设置图片叠加:无离谱渲染
            /// 设置了图片+圆角+多张图片叠加:无离谱渲染
            /// 打开masksToBounds属性:出现离谱渲染
            let imgView3 = UIImageView.init(frame: CGRect(x: 30, y: 320, width: 100, height: 100))
            imgView3.layer.cornerRadius = 10
            imgView3.image = UIImage.init(named: "user_image")
            view.addSubview(imgView3)
            
            let imgView3_1 = UIImageView.init(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
            imgView3_1.image = UIImage.init(named: "user_image")
            imgView3.addSubview(imgView3_1)
            
            let imgView3_2 = UIImageView.init(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
            imgView3_2.image = UIImage.init(named: "user_image")
            imgView3_1.addSubview(imgView3_2)
            
            let imgView3_3 = UIImageView.init(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
            imgView3_3.image = UIImage.init(named: "user_image")
            imgView3_2.addSubview(imgView3_3)
        }
    
    • 使用了mask的layer(layer.mask)
    • masksToBounds

    卡顿产生的原因和解决方案

    从上述的一些知识点可知,屏幕产生卡顿的原因,主要还是在CPU和GPU上。
    接下来我们看下显示过程:


    26.jpg

    在VSync信号到来后,系统图形服务会通过CADisplayLink等机制通知APP,APP主线程再CPU中计算显示内容,如计算布局、图片解码、文本绘制等。CPU计算好的内容再提交到GPU中,由GPU进行交换、合成、渲染。然后GPU就把结果提交到帧缓冲区,等待下一次VSync信号到来时显示到屏幕上。由于垂直同步的机制,如果再一个Vsync时间内,CPU或者GPU没有完成内容提交,则那一帧就会丢失,等待下一次机会在显示,而这个时候显示屏会保留之前的内容不变。这就是界面的卡顿原因。

    从上图可以看到,无论是CPU还是GPU,只要有阻碍,都会造成掉帧现象。所以开发时,也需要分别对CPU和GPU进行评测。


    相关文章

      网友评论

          本文标题:渲染和iOS的离屏渲染

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