UPL6-1——DrawCall

DrawCall

DrawCall(绘制调用)在 Unity 中有时也会被称为 SetPass Call(设置参数调用),本质上是 CPU 向 GPU 发送一条绘制指令
告诉 GPU 要画什么、怎么画、用什么材质、用哪段几何数据,从 CPU 到 GPU 的一次DrawCall的流程包含:

  1. CPU设置渲染状态

    • 绑定要用的 Shader 程序(顶点着色器、片段着色器等)
    • 绑定材质参数(纹理、颜色、常量缓冲区)
    • 绑定几何数据(网格顶点缓冲、索引缓冲)
    • 设置混合模式、深度测试、剔除方式等 GPU 状态
  2. CPU发送绘制命令

    告诉 GPU 从哪块缓冲取数据、绘制多少个顶点/三角形

  3. GPU执行绘制

    按状态和数据进行顶点处理、光栅化、片元处理等,最终输出到帧缓冲

关键点:一次 DrawCall ≈ 一次 准备渲染状态 + 发送命令,即使画很少的三角形,也有固定的 CPU 提交开销

如果 DrawCall 数量过多,会严重影响 CPU 性能

DrawCall 为什么会影响 CPU 性能

DrawCall 过程中常见的开销:

  1. 状态切换的开销

    每个 DrawCall 可能要重新绑定材质、贴图、Shader,GPU 需要刷新缓存,CPU 也要组织新的状态命令

  2. 数据绑定的开销

    为每个批次上传模型矩阵、材质参数等常量缓冲区数据

  3. 命令组织的开销

    CPU 需要调用驱动 API 生成命令缓冲,驱动内部也会有锁、同步、内存分配等操作

  4. 排序与剔除的开销

    为减少状态切换,CPU 会在提交前排序批次;有时还要做可见性计算

  5. 多Pass重复提交的开销

    阴影、深度、后处理等,每个物体可能需要额外 DrawCall

等等

每个 DrawCall 都需要进行 CPU 准备数据、切换状态、调用图形 API(OpenGL、DirectX 等)等操作,都会有固定的CPU提交成本
而这些成本会随着DrawCall的数量增多,累积耗时到毫秒级别,从而占满 CPU 渲染线程(Render Thread) 时间

并且如果 DrawCall 太多,CPU 花太多时间准备批次,GPU 可能处于等待状态,
从而导致帧率被 CPU 限制,而 GPU 其实还是空闲状态

在 Unity 中做什么会增加 DrawCall

  1. 材质、Shader 不一致:纹理、颜色、参数不同
  2. 相同材质,不同纹理:同一个Shader,但是使用的纹理贴图不同
  3. 不同的渲染队列:不同渲染队列会分开渲染
  4. 不同的透明度:透明队列中,Unity 会按深度排序
  5. 不同的Shader变体:看似一个 Shader 文件,但变体不同
  6. UI 元素位于不同 Canvas
  7. Shader 中多个 Pass(阴影、深度、后处理等)

等等

减少 DrawCall 的优化思路

  1. 合批处理
  2. 合并贴图(将若干个 UI 小图合并为图集)
  3. 减少 Shader 变体
  4. 优化 UI

等等