Draw Call 优化:原理、合批与性能提升指南
什么是 Draw Call
Draw Call(绘制调用)是 CPU 向 GPU 发出的渲染命令,指示 GPU 绘制一组几何体。每次 Draw Call 都包含渲染状态设置、顶点数据绑定和绘制指令。在实时渲染中,Draw Call 数量是性能关键指标,过多的 Draw Call 会导致 CPU 瓶颈,使 GPU 利用率下降。
Draw Call 对性能的影响
每个 Draw Call 都有 CPU 开销,包括:
- **状态切换**:切换着色器、纹理、混合模式等渲染状态。
- **数据提交**:将顶点、索引、常量等数据上传到 GPU。
- **驱动开销**:图形 API 和驱动程序的验证与调度。
现代 GPU 可以处理大量 Draw Call,但 CPU 往往成为瓶颈。当 Draw Call 数量过多(如超过数千)时,帧率会显著下降,尤其在移动平台和低端设备上。
优化方法
合批(Batching)
合批是将多个 Draw Call 合并为一个,减少 CPU 开销。主要方式有:
- **静态合批**:将不移动的静态物体合并为一个大网格,前提是使用相同材质。在 Unity 中,勾选 Static Batching 即可。
- **动态合批**:在运行时将小网格合并,适用于顶点数较少且材质相同的物体。Unity 对动态合批有顶点数限制(通常小于 300 顶点)。
纹理图集(Texture Atlas)
将多个小纹理合并到一张大纹理中,使不同物体可以使用相同材质,从而促进合批。需要注意 UV 调整和纹理空间利用率。
GPU 实例化(GPU Instancing)
对于大量相同网格和材质的物体(如草地、树木),使用 GPU 实例化可以在一个 Draw Call 中绘制多个实例,大幅减少 Draw Call。在 Unity 和 Unreal 中,材质需启用 Instancing。
减少材质和着色器种类
合并材质,减少着色器复杂度。使用共享材质和纹理图集可以减少状态切换。
使用 SRP Batcher(Unity)
Unity 的可编程渲染管线(SRP)提供 SRP Batcher,通过缓存材质属性减少 CPU 开销,即使不减少 Draw Call 也能提升性能。
其他优化
- **LOD(细节层次)**:远处物体使用低模,减少顶点处理量。
- **遮挡剔除**:不渲染被遮挡的物体,减少 Draw Call。
- **分帧加载**:避免同时创建大量物体。
合批的注意事项
合批需要满足条件,如相同材质、相同着色器、相同光照贴图等。动态合批受顶点数限制,且可能增加内存。过度合批可能导致网格过大,影响内存和加载时间。因此,需要根据场景特点选择合适的优化策略。
常见问题
Draw Call 和 Batch 有什么区别?
Batch 是渲染批次,一个 Batch 可能包含多个 Draw Call(如实例化)。在 Unity 中,Batches 统计通常指合批后的绘制次数。
为什么合批需要相同材质?
因为不同材质意味着不同的渲染状态,合批需要统一的渲染状态,否则无法合并。
静态合批和动态合批有什么区别?
静态合批在构建时合并网格,适用于静态物体;动态合批在运行时合并,适用于小网格且可移动的物体。
GPU 实例化与合批有何不同?
GPU 实例化允许一个 Draw Call 绘制多个相同网格实例,但需要在着色器中支持,适合大量重复物体。
移动端如何优化 Draw Call?
移动端 CPU 性能较弱,应优先使用静态合批、纹理图集和 GPU 实例化,控制 Draw Call 在几百以内。
常见问题
Draw Call 和 Batch 有什么区别?
Batch 是渲染批次,可能包含多个 Draw Call(如实例化)。在 Unity 中,Batches 统计通常指合批后的绘制次数。
为什么合批需要相同材质?
因为不同材质意味着不同的渲染状态,合批需要统一的渲染状态,否则无法合并。
静态合批和动态合批有什么区别?
静态合批在构建时合并网格,适用于静态物体;动态合批在运行时合并,适用于小网格且可移动的物体。
GPU 实例化与合批有何不同?
GPU 实例化允许一个 Draw Call 绘制多个相同网格实例,但需要在着色器中支持,适合大量重复物体。
移动端如何优化 Draw Call?
移动端应优先使用静态合批、纹理图集和 GPU 实例化,控制 Draw Call 在几百以内。