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进行评测。
网友评论