【游戏优化】 - Drawcall

游戏开发

什么是Drawcall?

DrawCall 是 CPU 向 GPU 发送的绘制命令,用于告诉 GPU 如何渲染一个物体或一组物体。它是图形渲染管线中的关键环节,直接影响性能。

  1. 数据准备

    • CPU 将物体的网格数据(顶点、法线、UV)、材质参数、纹理资源、变换矩阵(位置、旋转、缩放)等传递到 GPU。
    • 这些数据需要从 CPU 内存(RAM)传输到 GPU 显存(VRAM),涉及带宽和时间开销。
  2. 状态配置

    • 配置 GPU 渲染管线的状态,例如:
      • 使用的着色器(Shader)。
      • 渲染模式(如混合、深度测试、光照计算)。
      • 全局参数(如摄像机投影矩阵、光照参数)。
  3. 指令提交

    • CPU 调用图形 API(如 OpenGL 的 glDrawElements 或 DirectX 的 DrawIndexedPrimitive)触发 GPU 开始渲染。

DrawCall 为什么影响性能?

  1. CPU 与 GPU 的通信开销

    • 每次 DrawCall 需要 CPU 和 GPU 协同工作,频繁的通信会导致 CPU 成为性能瓶颈。
    • 例如:100 个物体逐个绘制需要 100 次 DrawCall,CPU 需要 100 次准备数据并提交命令。
  2. GPU 利用率低下

    • GPU 的吞吐量(处理能力)远高于 CPU 的 DrawCall 提交速度。如果 DrawCall 过多,GPU 会频繁“空转”,等待 CPU 提交任务。
  3. 纹理切换与状态切换

    • 如果多个物体使用不同材质或纹理,GPU 需要频繁切换状态,这会显著增加开销。
  4. 透明物体的特殊处理

    • 透明物体需要按“从后往前”的顺序渲染,且不能与其他物体合批,导致额外的 DrawCall 开销。

如何降低 DrawCall?

降低 DrawCall 的核心思想是 “合批”(Batching),即减少 CPU 向 GPU 提交绘制命令的次数。

  • 静态合批(Static Batching)

    • 适用场景:静态物体(如场景中的建筑、地形、不可移动的装饰物)。
    • 原理:在编辑器或运行时将多个静态物体的网格数据合并为一个大网格,减少 DrawCall 数量。
    • 优点
    • 合并后只需一次 DrawCall 即可渲染所有物体。
    • 减少 CPU 与 GPU 的数据传输。
    • 缺点
    • 合并后的网格会占用更多显存。
    • 无法对单个物体进行独立的动画或动态修改。
  • 动态合批(Dynamic Batching)

    • 适用场景:小规模动态物体(如粒子、小型道具、顶点数较少的物体)。
    • 原理:在运行时将多个动态物体的顶点数据打包到一个批次中,由 GPU 一次性渲染。
    • 优点
    • 适合动态物体,无需预处理。
    • 减少 CPU 提交命令的次数。
    • 缺点
    • 仅支持顶点数较少的物体(Unity 默认限制为 900 个顶点)。
    • 不支持复杂材质或蒙皮网格。
  • 使用图集(Texture Atlas)

    • 适用场景:需要频繁切换纹理的小物体(如 UI 元素、2D 精灵)。
    • 原理:将多个小纹理合并到一张大纹理图集中,减少纹理切换次数。
    • 优点
    • 避免频繁的纹理绑定操作。
    • 提高 GPU 渲染效率。
    • 缺点
    • 图集会占用更多显存。
    • 修改单个纹理需要重新生成图集。
  • 使用 GPU Instancing(实例化)

    • 适用场景:大量重复物体(如草地、树木、敌人)。
    • 原理:通过一次 DrawCall 渲染多个相同模型的实例,每个实例可以有不同的位置、颜色等属性。
    • 优点
    • 显著减少 DrawCall 数量。
    • 适合大规模重复物体的渲染。
    • 缺点
    • 需要使用支持实例化的着色器(Shader)。
    • 不适合每个实例需要独立材质的场景。
  • 遮挡剔除(Occlusion Culling)

    • 适用场景:复杂场景中被遮挡的物体(如室内房间、复杂地形)。
    • 原理:通过算法判断哪些物体被其他物体完全遮挡,无需渲染。
    • 优点
    • 减少不必要的 DrawCall。
    • 降低 GPU 渲染负担。
    • 缺点
    • 需要预计算场景的遮挡关系(如 Unity 的 Occlusion Culling 工具)。
    • 实时遮挡剔除(如基于 BVH 的算法)计算开销较高。
  • 使用 LOD(Level of Detail)

    • 适用场景:远处或小尺寸物体。
    • 原理:根据物体与摄像机的距离,切换不同精度的模型(低面数模型替代高面数模型)。
    • 优点
    • 减少远距离物体的渲染开销。
    • 降低 DrawCall 数量。
    • 缺点
    • 需要为每个物体准备多个 LOD 模型。
    • 模型切换可能导致视觉突变。
  • 减少透明物体的渲染

    • 适用场景:透明物体(如玻璃、粒子特效)。
    • 原理
    • 透明物体需要按“从后往前”的顺序渲染,且不能与其他物体合批。
    • 尽量减少透明材质的使用,或合并透明物体。
    • 优化策略
    • 合并多个透明物体到一个图集中。
    • 将透明物体的渲染顺序优化为“从后往前”。

通俗理解

想象我们是一个甲方,乙方是一个画家,乙方需要按照我们的需求创作一副复杂的艺术画。

  • 甲方(CPU):负责准备工具、材料,并告诉画家如何画。
  • 画家:GPU(负责执行实际的绘制工作)。
  • 画布:屏幕(需要渲染的画面)。
  • 指令单:DrawCall(CPU 向 GPU 发送的绘制命令)。
  1. 甲方频繁下指令

    • 甲方每次只告诉画家:“画一棵树。”、“画一座房子。”、“画一个角色。”……
    • 每个指令都是一张独立的“指令单”(DrawCall)。
    • 画家需要频繁切换工具(比如从画树的笔换成画房子的笔)、调整颜色(比如从绿色换成灰色)、甚至更换画布区域。
  2. 结果

    • 画家效率极低,因为每次切换工具和颜色都需要时间。
    • 整幅画的完成速度变慢(性能下降)。

现实如何优化

合并指令(批处理)

  • 甲方策略
    • 如果多棵树是同一个颜色和形状(比如松树),甲方可以合并指令:
      • “请一次性画 10 棵松树,放在不同位置。”
    • 画家只需一次调整画笔和颜色,快速完成所有松树。
  • 对应技术:静态合批(Static Batching)或动态合批(Dynamic Batching)。

使用统一的调色板(图集)

  • 甲方策略
    • 如果画面中有很多小物体(比如花朵、石头),甲方会将它们的纹理(颜色、图案)提前整理到一张大调色板上(图集)。
    • 画家只需使用这张调色板,无需频繁更换颜料。
  • 对应技术:Texture Atlas(纹理图集),减少材质切换。

预制画布(静态合批)

  • 甲方策略
    • 对于固定不变的背景(比如山脉、建筑),甲方提前将它们画在一张大画布上(静态合批)。
    • 画家只需一次性画这张画布,无需重复操作。
  • 对应技术:Static Batching(静态批处理)。

批量复制(GPU Instancing)

  • 甲方策略
    • 如果画面中有很多重复的物体(比如草丛、路灯),甲方告诉画家:“请用相同的模板,批量画 100 个草丛,位置不同。”
    • 画家只需一次操作,就能完成所有草丛。
  • 对应技术:GPU Instancing(实例化)。

优先画远处(LOD 技术)

  • 甲方策略
    • 对于远处的物体(比如山),甲方会简化细节(用简单的色块代替复杂的纹理)。
    • 画家只需快速勾勒轮廓,节省时间。
  • 对应技术:Level of Detail(LOD),根据距离调整模型复杂度。

减少透明物体(优化渲染顺序)

  • 甲方策略
    • 如果画面中有透明物体(比如玻璃窗),甲方会先让画家画所有不透明物体(比如墙壁),再单独处理透明部分。
    • 避免频繁切换透明/不透明模式。
  • 对应技术:优化透明物体的渲染顺序(从后往前)。

Unity参考链接

  • 标题: 【游戏优化】 - Drawcall
  • 作者:
  • 创建于 : 2023-05-12 22:16:55
  • 更新于 : 2024-10-15 22:01:23
  • 链接: https://sxl-space.tk/2023/05/12/004_DrawCall/
  • 版权声明: 版权所有 © 宋,禁止转载。