UI视图面试相关(下)
图像显示原理
CPU和GPU2个硬件通过总线连接起来,cpu输出位图适当时机通过总线传给gpu,
gpu用位图用图层的渲染,纹理合成,把结果放到帧缓冲区,由视频控制器根据vSync到帧缓冲区提取内容
显示到屏幕上
创建UIView之后,显示部分是由CALayer负责,CALayer有个contents属性就是要绘制到屏幕上的位图。
例如我们要创建一个UILabel,系统在适当时机回调我们一个drawRect:方法,绘制自定义的内容,绘制好的位图会通过Core Animation框架
提交给GPU的OpenGL渲染和合成,显示到屏幕上。(Core Animation及之前步骤是发生在CPU OpenGL是发生在GPU之上)
CPU的工作
Layout:UI布局 文本计算(.frame)
Display:绘制(drawRect:)
Prepare:(UIImageView图片编解码)
Commit:(CoreAnimation提交位图)
GPU渲染管线
顶点着色
图元装配
光栅化
片段着色
片段处理
这五步做完会把像素点提交到帧缓冲区
UI卡顿 掉帧的原因
人肉眼看到60帧就会觉得流畅,把1秒分成60份,就是每16.7ms发送一个vSync向帧缓冲区提取画面,
就是说每一帧画面CPU和GPU合成的时间不能超过16.7ms,如果CPU这次的计算和布局时间过长,那GPU渲染就超过16.7ms,
在下一次vSync来的时候不能提供结果,就会出现卡顿的情况。
UITableView滚动更流畅优化
CPU:
对象创建,调整,销毁放子线程
预排版(布局计算 文本计算)放子线程
预渲染(文本异步绘制 图片编解码)
GPU:
纹理渲染(避免离屏渲染)
视图混合
UIView的绘制原理
调用[UIVIew SetNeedsDisplay]没有马上执行的原因是在,先调用[view.layer setNeedsDisplay]在runloop快结束的时候
调用[CALayer display];看layer的代理是否响应displayLayer方法,不响应该方法,会进入系统的绘制流程,响应的话会进入异步绘制入口。
系统的绘制流程
CALayer内部会创建backing store(CGContextRef,一般在drawRect方法中可以获取到)
layer判断是否有代理,没有代理走[CALayer drawInContext],
有代理走[layer.delegate drawLayer:inContext]做绘制,合适的时机给我们一个回调方法
[UIView drawRect:]允许我们自己做一些绘制的工作
两个分支最终都是CALayer上传backing store(位图) to GPU结束
异步绘制流程
主队列自定义view调用setNeedsDisplay方法,当前runloop结束之后系统调用CALayer display,代理实现displayLayer方法,就会displayLayer方法
子线程切换做位图绘制,创建位图上下文,调用coregraphics绘制的api,然后生成上下文位图,回到主线程调用CALayer setContents:把位图传给它。
离屏渲染
在屏渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行离屏渲染,指的是GPU的渲染操作在当前屏幕缓冲区以外开辟了一个新的缓冲区进行
(指定UI视图的某些属性,把他们标记为在合成之前不能用于在当前屏幕上直接显示的时候就会触发离屏渲染)
何时触发离屏渲染
圆角+maskToBounds = YES同时设置
图层蒙版
阴影
光栅化
为何要避免?
触发离屏渲染 会增加GPU的工作量,很有可能让CPU+GPU的总耗时超过16.7ms,造成卡顿和掉帧