GPU三维渲染优化 - 批量渲染

原文链接

“We should forget about small efficiencies, say about 97% of the time: Premature optimization is the root of all evil.” —Donald Knuth

首先问一个问题: 批量渲染是什么?

在三维渲染中,批量渲染(Batch Rendering)指的是将原本分散的多个渲染对象或绘制命令合并在一起,在一次或更少的绘制调用(Draw Calls)中完成渲染。这样做的核心目的是减少 CPU 和 GPU 之间的往返指令(即 Draw Call 数量),从而提高渲染效率。

CPU 与 GPU 之间的通信可以视为一条“数据高速公路”。只要数据(顶点、纹理、指令等)在这条公路上来回奔跑,就一定会消耗时间和带宽频繁的小批量通信不仅会让 CPU 和 GPU 相互等待、切换状态,还可能造成带宽被零碎占用,导致整体“堵车”,从而降低渲染效率。

下面从几个维度分析 CPU 与 GPU 之间的频繁通信带来的问题:

  • 带宽与时延的开销

    • CPU 和 GPU 通常通过 PCIe 或类似的总线进行数据交互。

    • 带宽有限:如果频繁地在 CPU 与 GPU 之间传输大块或很多小块数据,就有可能“挤占”带宽,造成排队延迟。

    • 时延累积:一次数据传输并不会马上完成,往返时延叠加多了会降低吞吐量。

  • 命令开销与调度

    • 除了传输数据本身,每条渲染命令(如 Draw Call)都要经过 CPU 的准备、驱动层打包、再发往 GPU。

    • 如果命令过于零碎、频繁,就会在 CPU 端积累非常多的小调用和上下文切换。

  • 同步阻塞(Synchronization Overhead)

    • 当 CPU 与 GPU 之间需要对同一段资源进行读写时,需要做同步或等待。

    • 如果频繁地更新 GPU 正在使用的数据,或从 GPU 读回数据到 CPU,都可能触发阻塞,导致流水线停顿

综上所述,频繁、小规模、零散的通信 就是性能的“隐性杀手”;不断在 CPU 和 GPU 之间“跑腿”的过程,会拉低整体的渲染效率。

为什么减少 CPU 和 GPU 的往返通信, 就可以提高渲染效率?

下面从三个维度分析:

  • 降低“指令发起”成本

    • 当通信次数少时,CPU 不必不停地执行状态切换、数据传输命令、同步命令等,能够更快地发起下一步渲染或逻辑处理。

    • 减少 API 调用的频度(如每帧要发送给 GPU 的命令数减半),就能让 CPU 空出更多时间用于游戏逻辑、物理模拟或其他并行任务。

  • 减少总线与驱动的负载

    • 如果把许多小数据合并成一次大传输,往往比让 CPU 多次传输小数据更有效率,能更好地利用总线的带宽、减少驱动层开销。

    • GPU 也不需要因接收“碎片”数据而多次切换上下文。

  • 减轻同步等待

    • 频繁的小数据更新通常需要 CPU 等待 GPU 或 GPU 等待 CPU,以确保数据在正确的时机读写。

    • 当通信次数减少后,流水线得以更好地并行运作,CPU 和 GPU 能更持续地“各干各的事”,避免互相等待。

下面用“物流运输”作简单对比: 

  • 频繁通信的情况

    • 好比有很多小包裹,每次都要单独装车、运送、卸货,然后再回头取下一个包裹。

    • 在这个过程里,卡车司机(相当于 CPU)要反复地发车、停车,等待装卸;公路(PCIe 总线)上也一直被占用,但利用率又并不高。

    • 结果就是整体的效率很差。

  • 减少通信后的情况

    • 如果将这些小包裹汇总打包到一个或几个大包裹,一次装车、一趟送完,司机(CPU)就能在装车后立刻专注做别的事(例如下一个订单的处理或其他工作),公路(总线)的利用率也更高。

    • 卡车(GPU)一旦接到大包裹,就能持续处理里面的内容,不用因为频繁装卸和路上折返而闲置。

所以,减少 CPU–GPU 通信解决的问题就是

  • 避免 CPU 和 GPU 因为频繁的“单次、零散”传输和命令执行而浪费时间在命令调度、带宽占用、同步等待等方面。

  • 让整个流水线更流畅、减少阻塞,提高并行度,从而提升整体渲染帧率降低延迟

那么, 有哪些 批量渲染(Batch Rendering) 方案?

方案适用场景优点缺点
静态批处理 (Static Batching)

- 场景中静止不动或很少变动的物体

- 例如:固定建筑、远处景观

大幅减少 Draw Call 数量

- 一次合并后使用方

便

- 对静态场景优化效果显著

内存占用增加(合并后网格大)- 无法单独剔除或移动子物体(粒度变粗)- 视锥剔除效率变低
动态批处理 (Dynamic Batching)

- 需要在运行时移动或变化的中小型物体

- 例如:游戏中的小道具、交互物体

- 减少绘制调用,减少渲染状态切换

- 能在场景变化时灵活“合批”

CPU 开销较高,需要实时合并网格或数

- 对物体材质、网格格式要求相似

- 如果物体变化过于频繁,收益可能有限

实例化渲染 (GPU Instancing)

几何形状相同但位置、朝向、颜色等不同的海量物

- 例如:森林中的树、草地、粒子系统

- 显著降低 Draw Call 数量

- 充分利用 GPU 并行处理能力

- 只需存一份几何数据

- 要求实例间的网格完全相同

- 需管理大量实例参数(矩阵、材质索引等)

- 实例过多时,需注意缓冲区传输和更新效率

Multi-Draw Indirect / Indirect Drawing

- 同一渲染管线下需要绘制大量子对

- 可在 GPU 侧生成或更新绘制指令

- 如:GPU 实时剔除后批量发起绘制

减少 CPU提交的绘制调用

- 结合 GPU 计算可批量剔除无用对象

- CPU 干预更少,渲染管线更顺畅

- 对引擎和 API 支持要求较高

- 需要定制化的数据结构与管线

- 实现较为复杂,需管理 GPU 侧的绘制参数缓冲

纹理图集 (Texture Atlas)

- 大量尺寸较小、多种不同贴图的场

- 例如:游戏 UI、2D Tilemap、贴图种类多且单张图很小

减少纹理绑定的次数

- 提高渲染批次合并效率

- 适合贴图数量庞多而单个贴图很小的情况

- 需要对 UV 坐标做偏移管理

- Mipmaps 处理和纹理边缘过渡可能复杂

- 一张图集过大时,会有浪费或调度复杂度

批量渲染的关键思路在于: 

  1. 合并:将相似或相同类型的渲染数据合并到同一份绘制调用或数据提交中;

  2. 减少:缩减 CPU 发给 GPU 的命令(Draw Calls)和状态切换,从而降低通信与调度的成本;

  3. 充分利用 GPU 并行:一旦 GPU 获得大批量的数据或指令,可更有效地利用内部的并行计算与流水线。

下一篇说说 Cesium的批量渲染

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值