图像渲染原理
CPU & GPU
- CPU: 拥有更多的缓存空间和控制单元,核心诉求是低延时,更多的高速缓存可以快速访问数据;更多的控制单元可以快速处理逻辑分支,适合串行计算。
- GPU: 拥有更多的计算单元,基于大吞吐量设计,每一个缓存连接着一个流处理器,适合大规模并行运算。
图像渲染 pipline
- Application 应用处理阶段: 得到图元
- 由 CPU 处理
- 图元通常是三角形、线段、顶点等
- Geometry 几何处理阶段: 输出图元
- 顶点着色器:图元顶点视角转换,添加光照,增加纹理
- 形状装配:vertex 连接成对应的形状
- 几何着色器: 添加额外的 vertex,构建新的图元
- Rasterization 光栅化阶段: 图元转换为像素
- 遍历像素点找到被图元几何覆盖部分
- Pixel 像素处理阶段: 得到位图
- 片段着色器:Pixel Shader,给每个像素填充正确的颜色,颜色来源于光照纹理等。
- 测试和混合:Merging,处理片段前后位置和透明度,计算 alpha,片段混合。
屏幕呈像与卡顿
GPU 渲染的像素信息存到帧缓冲区(Framebuffer), 由视频控制器(VideoController)读取帧缓冲区,经过数模转换交给显示器(Monitor)进行显示。
- 屏幕撕裂: 由于 CPU+GPU 渲染过程是非常耗时的,电子束在扫描新的一帧时,新的位图还没渲染好,扫描到屏幕中间的时候才放入到帧缓冲区,此时已扫描的部分显示上一帧,未扫描的部分显示下一帧,导致屏幕撕裂。
- 垂直同步信号 Vsync + 双缓冲机制 Double Buffering:
- Vsync 相当于给缓冲区加锁,只有当电子束扫描完一帧发出 vsync 同步信号,视频控制器才会将帧缓冲区更新到下一帧。
- 双缓冲机制增加备用缓冲区(back buffer),渲染效果会先存到 back buffer,收到 vsync 信号后视频控制器会将 back buffer 的信息置换到 frame buffer,完成内存交换。
- 掉帧: 在收到 vync 信号后,新的位图仍然没有渲染出来,视频控制器无法去替换 framebuffer,出现掉帧。为了解决掉帧,增加一个 buffer, 三缓冲。
iOS 渲染框架
- Core Animation Pipline
- Handle Events: 点击事件,处理视图布局和层级
- Commit Transaction: CPU 的前置计算,布局计算,图片解码等,计算结束交给 render server
- Decode: 打包好的图层解码,下一个 Runloop Draw calls
- Draw Calls: CA 调用 OpenGL/Meta API 绘制
- Render: GPU 渲染
- Display: 下一个 Runloop 显示
- Commit Transaction
- Layout: 视图构建和布局
- Display: 视图绘制,重写 drawRect 会直接生成 bitmap 导致额外的 CPU 和内存
- Prepare: CA 额外工作,主要是图片解码和转换
- Commit: 打包发送,图层书递归执行,发送给 render server
- Render Server 工作过程
- GPU 收到 Command Buffer,包含图元信息
- Tiler 顶点着色器对顶点着色,更新图元
- 平铺,将图元转换为像素写入 Parameter Buffer
- Renderer 处理完有所图元得到 bitmap 写入到 Render Buffer
- 渲染好的 bitmap 给显示器 display
离屏渲染
App -> OffscreenBuffer -> FrameBuffer -> Display
离屏渲染的目的
- 特殊效果被动使用离屏渲染, Mask, 圆角阴影,UIBlurEffectView, 多次渲染结果进行叠加
- 提高服用效率主动使用离屏渲染,layer.shouldRasterize=true 实现
shouldRasterize 光栅化
- 开启光栅化会主动触发离屏渲染, Render server 会强制将 CALayer 渲染的位图保存下来,下次直接复用。
- 开启光栅化注意事项
- layer 不被复用不需要开启
- layer 非静态反复修改不需要开启
- 时间限制 100ms,不被使用会被丢弃
- 空间限制,超过屏幕尺寸 2.5 倍失效