【游戏优化】 - Drawcall
什么是Drawcall?
DrawCall 是 CPU 向 GPU 发送的绘制命令,用于告诉 GPU 如何渲染一个物体或一组物体。它是图形渲染管线中的关键环节,直接影响性能。
数据准备
- CPU 将物体的网格数据(顶点、法线、UV)、材质参数、纹理资源、变换矩阵(位置、旋转、缩放)等传递到 GPU。
- 这些数据需要从 CPU 内存(RAM)传输到 GPU 显存(VRAM),涉及带宽和时间开销。
状态配置
- 配置 GPU 渲染管线的状态,例如:
- 使用的着色器(Shader)。
- 渲染模式(如混合、深度测试、光照计算)。
- 全局参数(如摄像机投影矩阵、光照参数)。
- 配置 GPU 渲染管线的状态,例如:
指令提交
- CPU 调用图形 API(如 OpenGL 的
glDrawElements或 DirectX 的DrawIndexedPrimitive)触发 GPU 开始渲染。
- CPU 调用图形 API(如 OpenGL 的
DrawCall 为什么影响性能?
CPU 与 GPU 的通信开销
- 每次 DrawCall 需要 CPU 和 GPU 协同工作,频繁的通信会导致 CPU 成为性能瓶颈。
- 例如:100 个物体逐个绘制需要 100 次 DrawCall,CPU 需要 100 次准备数据并提交命令。
GPU 利用率低下
- GPU 的吞吐量(处理能力)远高于 CPU 的 DrawCall 提交速度。如果 DrawCall 过多,GPU 会频繁“空转”,等待 CPU 提交任务。
纹理切换与状态切换
- 如果多个物体使用不同材质或纹理,GPU 需要频繁切换状态,这会显著增加开销。
透明物体的特殊处理
- 透明物体需要按“从后往前”的顺序渲染,且不能与其他物体合批,导致额外的 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 发送的绘制命令)。
甲方频繁下指令:
- 甲方每次只告诉画家:“画一棵树。”、“画一座房子。”、“画一个角色。”……
- 每个指令都是一张独立的“指令单”(DrawCall)。
- 画家需要频繁切换工具(比如从画树的笔换成画房子的笔)、调整颜色(比如从绿色换成灰色)、甚至更换画布区域。
结果:
- 画家效率极低,因为每次切换工具和颜色都需要时间。
- 整幅画的完成速度变慢(性能下降)。
现实如何优化
合并指令(批处理)
- 甲方策略:
- 如果多棵树是同一个颜色和形状(比如松树),甲方可以合并指令:
- “请一次性画 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/
- 版权声明: 版权所有 © 宋,禁止转载。