1. CPU 和 GPU 的设计目的分别是什么?
CPU是运算核心和控制核心,需要有很强的运算通用性,兼容各种数据类型,同时需要处理大量的跳转、中断等指令;
CPU的设计目标是低延时,更快的访问数据(因此有更多的高速缓存);同时复杂的控制单元也能更快的处理逻辑分支,更适合做串行运算
GPU主要是用来做大规模的并行计算
2. CPU 和 GPU 哪个的 Cache\ALU\Control Unit 的比例更高?
硬件 | Cache | ALU | ControlUnit |
---|---|---|---|
CPU | 高 | 低 | 高 |
GPU | 低 | 高 | 低 |
3. 计算机图像渲染流水线的大致流程是什么?
- 应用处理阶段:得到图元信息
- 几何处理阶段:处理图元
- 图元转换为像素
- 像素处理阶段:处理像素,得到位图
4. Framebuffer 帧缓冲器的作用是什么?
用来存储显卡芯片处理过或者即将提取的渲染数据,app通过CPU和GPU的合作不停的将渲染的内容放到Framebuffer中供视频控制器读取显示。
5. Screen Tearing 屏幕撕裂是怎么造成的?
屏幕显示的时候根据屏幕的刷新率从帧缓存去读取内容展示到屏幕上,同时电子束是左上角开始横向的逐行扫描的;如果读取的时候内容还没有渲染好,那么缓存区还是之前的内容,当某个时刻渲染好了,读取的内容就是渲染好的帧的内容,如果两帧的内容差别很大就看着像是撕裂了
6. 如何解决屏幕撕裂的问题?
苹果的解决方案:垂直同步信号+双缓冲机制
- 垂直同步信号Vsync:就是给帧缓冲器加锁,当电子束完成一帧扫描后,需要进行下一次扫描的时候,就会发一个同步信号,当视频控制器收到垂直同步信号后才会将帧缓冲器中的内容替换成下一帧的,这样就避免了每次显示的内容都是同一帧的。
-
视图控制器在收到Vsync信号之后,就要将下一帧的位图传入,这显然不现实,CPU+GPU渲染视图也是需要耗时的;所以苹果也采用双缓存机制,增加一个备用缓冲器(back buffer),渲染结果会预先存储在back buffer,当收到Vsync信号的时候,视频控制器会将back buffer内容置换到FrameBuffer(交换内存地址)
图片.png
7. 掉帧是怎么产生的?
由于使用了Vsync,当 收到Vsync的时候,CPU和GPU如果还没有渲染好位图,那么视频控制器就不会替换FrameBuffer中的内容,电子束周期性去扫描展示的还是之前的FrameBuffer中的内容(掉帧了);当这个渲染的时间比较长的时候,就会导致多个屏幕刷新周期展示的都是同一帧数据,当这个周期数超过人眼可以看出的范围就能明显感觉到卡顿了。
8. CoreAnimation 的职责是什么?
一个复合引擎,主要职责包含:渲染、构建和实现动画。
9. UIView 和 CALayer 是什么关系?有什么区别?
在创建一个UIView的时候,会自动创建一个CALayer,UIView持有这个layer,并将layer的代理设置为UIView;所以UIView和CALayer具有相同的层级结构
UIView设计的主要是用来绘制与动画、布局以及子视图的管理、事件的处理;而CALayer主要是用来管理内部的可视内容(内容的展示contents、以及一些效果比如圆角之类的)
UIView继承自UIResponder可以处理事件,而CALayer是继承自NSObject
10. 为什么会同时有 UIView 和 CALayer,能否合成一个?
为了职责分离,拆分功能,方便代码的复用。通过 Core Animation 框架来负责可视内容的呈现,这样在 iOS 和 OS X 上都可以使用 Core Animation 进行渲染
11. 渲染流水线中,CPU 会负责哪些任务?
CPU负责的流程:
Handle Event: 处理触摸事件
Commit Transaction: Layout、Display、Prepare、Commit
- Layout:视图的构建和布局
- Display:绘制视图得到图元信息
- Prepare:主要包括图片的解码和转换
- Commit:将图层打包并发送给Render Server;这里commit是按照视图的层级递归调用的,所以图层过于复杂就会增加commit的开销,这也是为什么在做优化的时候说要减少图层结构
渲染流水线的其他任务
Decode
打包好的图层被传到Render Server之后,会进行解码得到新的图元;完成解码之后需要等待下一个 RunLoop 才会执行下一步 Draw Calls
Draw Calls
解码完成后,Core Animation 会调用下层渲染框架(比如 OpenGL 或者 Metal)的方法进行绘制,进而调用到 GPU
Render
这一阶段主要由 GPU 进行渲染。
Display
显示阶段,需要等 render 结束的下一个 RunLoop 触发显示
整个流程如下图所示:

12. 离屏渲染为什么会有效率问题?
- 创建新的缓冲区
- 上下文切换。离屏渲染的整个过程,需要多次切换上下文环境:先从当前屏幕切换到离屏,等待离屏渲染结束后,将离屏缓冲区的渲染结果显示到到屏幕上,这又需要将上下文环境从离屏切换到当前屏幕。
13. 什么时候应该使用离屏渲染?
当一些效果需要保存图层的中间状态的时候,系统会触发离屏渲染,当界面中这种效果用的较少的时候可以考虑使用,但是当这样的视图多了就会有性能瓶颈
处于效率目的,可以将内容提前渲染保存在 Offscreen Buffer 中,达到复用的目的。
14. shouldRasterize 光栅化是什么?
是否开启光栅化,当开启的时候,会触发离屏渲染,系统会将layer渲染的bitmap保存起来,这样下次再需要渲染的时候就可以复用,从而提高效率;需要注意的是缓存的实效是100ms,如果100ms内没有被使用就会被丢弃,同时离屏渲染的空间也是有限的是屏幕的2.5倍,超过该限制也会被丢弃
15. 有哪些常见的触发离屏渲染的情况?
先了解下问什么会触发离屏渲染
视图的渲染是用的画家算法,由远及近去画图层,丢弃掉之前的;由于有一些效果需要对视图的所有图层都产生效果,那么就需要将这些中间效果保存,后续取出来进行效果的加工
不需要对所有图层生成额外的效果(比如圆角)的场景

需要对所有图层生成额外的效果(比如圆角)的场景

触发的场景
- 圆角+裁切
- 阴影
- 遮照
- 开启光栅化
- 设置了group opacity组合透明度,并且透明度不为1 layer.allowsGroupOpacity/layer.opacity
15. cornerRadius 设置圆角会触发离屏渲染吗?
只设置cornerRadius不会触发离屏渲染,只有当需要裁切视图的时候才会需要保存中间态;cornerRadius对background color和layer的border有效果
16. 圆角触发的离屏渲染有哪些解决方案?
- 使用圆角背景图,对于复用多的图也可以代码绘制一个。
- 使用贝塞尔曲线画圆角
- 给layer加遮罩,四个角的颜色跟背景色一致,内部跟内容需要展示的颜色一致。
- 使用CG去绘制跟背景色一样的图片,根据视图的class决定怎么设置
if ([self isKindOfClass:[UIImageView class]]) {
((UIImageView *)self).image = image;
} else if ([self isKindOfClass:[UIButton class]] && backgroundImage) {
[((UIButton *)self) setBackgroundImage:image forState:UIControlStateNormal];
} else if ([self isKindOfClass:[UILabel class]]) {
self.layer.backgroundColor = [UIColor colorWithPatternImage:image].CGColor;
} else {
self.layer.contents = (__bridge id _Nullable)(image.CGImage);
}
17. 重写 drawRect 方法会触发离屏渲染吗?
不会drawRect是将视图的渲染交给cpu去处理了,并且需要额外开辟内存空间,它和标准意义上的离屏渲染并不一样,在 Instrument 中开启 Color offscreen rendered yellow 调试时也会发现这并不会被判断为离屏渲染
网友评论