合批的收益

理解 Draw Call、SetPass 与 Static/Dynamic Batching,用合批降低 CPU 提交开销。

学习目标

  • 能解释 Draw Call、Batches、SetPass 三者在 Profiler Rendering 里的含义,以及为什么「物体多」会让 CPU 先扛不住
  • 能说出 Dynamic Batching 与 Static Batching 各自的前提、代价与典型使用场景
  • 能回答:Profiler 显示 Batches 很高时,你会先打开 Frame Debugger 看什么、再尝试哪两种现代合批手段(Instancing / SRP Batcher)?

原书边界:Chapter 3

Packt 第三版把本章命名为 The Benefits of Batching。官方目录列出的核心范围如下;这些条目共同构成本章的一对一边界:

  • Draw calls:纳入本章实验与验收,不拆到别章重复计数。
  • Materials and shaders:纳入本章实验与验收,不拆到别章重复计数。
  • The Frame Debugger:纳入本章实验与验收,不拆到别章重复计数。
  • Dynamic batching:纳入本章实验与验收,不拆到别章重复计数。
  • Static batching:纳入本章实验与验收,不拆到别章重复计数。

把渲染提交成本拆成材质状态切换、批次、顶点处理、内存占用与可见像素,不能只追一个 Draw Call 数字。SRP Batcher、GPU Instancing 与 BatchRendererGroup 是现代补充;它们优化的契约不同,不能与静态合批互换。 因此,本章既保留 2019/2020 语境,也明确标出迁移到现代 Unity 时哪些是稳定原理、哪些只是版本相关接口。

场景里东西一多,CPU 先喊不动

你的关卡里摆了几百个箱子、路灯、装饰物——模型不复杂,贴图也不大,Profiler 里 GPU 时间却只有 6ms,帧率却掉到 30。展开 Rendering,Batches 和 Draw Calls 双双破千。这不是显卡画不动,而是 CPU 在不停地「下订单」:每多一个要单独提交的物体,主线程就要多跑一趟渲染命令。

上一章你已经会判断 CPU bound / GPU bound。这章要解决的,是渲染路径里最常见的一类 CPU bound:提交开销。合批(Batching)的目标很直白——让 CPU 少喊几次,GPU 照样把画面画全

像点外卖:一单一份 vs 拼单配送

想象 GPU 是厨房,CPU 是前台。每画一个物体,前台就要下一单:写地址(网格)、选套餐(材质)、备注口味(Shader Pass)。厨房真正炒菜(光栅化)可能很快,但 前台如果给每个苹果各下一单闪送,单子堆成山,客人照样饿肚子。

就是那一单闪送。 是拼单后的配送趟数。 则是换套餐模板的次数——同一品牌不同口味也要重新填表。

CPU提交命令Draw Call一次绘制指令网格+材质+PassCPU 开销大Batches合批后的组多物体→少提交越少越好SetPass切换 Shader Pass材质/关键字变切换也贵GPUProfiler → Rendering 模块常见读数Draw Calls ≈ 原始指令数Batches ≈ 实际提交组数SetPass Calls ≈ Pass 切换次数合批成功 → Batches 远小于 Draw Calls;材质各异 → SetPass 偏高
Draw Call 是 CPU 向 GPU 发出的一次绘制指令;Batches 是合批后的提交组;SetPass 是着色器 Pass 切换次数——三者都在 Profiler Rendering 里可见。

在 Profiler → Rendering 模块里,这三个数字并排出现。Draw Calls 高说明原始指令多;Batches 接近 Draw Calls 说明合批几乎没生效;SetPass 高说明材质/Shader 太碎,即使用了合批也在频繁换 Pass。

CPU提交命令Draw Call一次绘制指令网格+材质+PassCPU 开销大Batches合批后的组多物体→少提交越少越好SetPass切换 Shader Pass材质/关键字变切换也贵GPUProfiler → Rendering 模块常见读数Draw Calls ≈ 原始指令数Batches ≈ 实际提交组数SetPass Calls ≈ Pass 切换次数合批成功 → Batches 远小于 Draw Calls;材质各异 → SetPass 偏高
Draw Call 是 CPU 向 GPU 发出的一次绘制指令;Batches 是合批后的提交组;SetPass 是着色器 Pass 切换次数——三者都在 Profiler Rendering 里可见。

Dynamic Batching:运行时拼单

像闪送平台的「顺路拼单」——帧与帧之间,引擎尝试把同材质、顶点数够小的物体合并再提交。

Dynamic Batching:运行时自动合批同材质✓ 可合顶点 ≤900✓ 可合无多 Pass✓ 可合非 Skinned✗ 打断缩放一致✓ 可合合批前4 Draw Calls合批后1 Batch(CPU 合并顶点)代价:CPU 每帧合并顶点缓冲;移动平台默认常关,顶点数限制更严
Dynamic Batching 在运行时把满足条件的小网格合并提交;蒙皮网格、多 Pass 材质、顶点过多都会打断合批。

必须同时满足的条件

  • 同一材质(含相同 Shader 与关键字)
  • 网格顶点数低于阈值(常见约 300–900,随平台而异)
  • 单 Pass 材质(多 Pass 如 Forward Add 会打断)
  • 非蒙皮网格(Skinned Mesh Renderer 不参与)
  • 缩放一致(非统一缩放可能打断)

Dynamic Batching 的代价在 CPU:每帧可能要复制、合并顶点缓冲。移动平台上默认常关闭或限制更严——顶点合并本身也可能比少几次 Draw Call 更贵。所以它适合 少量、小网格、同材质 的场景,不适合整片森林。

Static Batching:装修前把家具焊死

像装修前把一百把同款椅子焊成一个整体再搬进场景——运行时 CPU 几乎零合并成本,但 内存里会多一份合并后的网格副本,且 物体不能再动

InspectorStatic ✓Batching Static勾选后不可移动构建期合并同材质静态物→ 大网格 + 子网格占更多内存运行时一次 Draw Call 画整片无 CPU 顶点合并Batches ↓ 稳定典型用途:关卡静态道具、建筑、地形装饰移动 Static 物体会打断合批并触发重建——只适合永不动的物体
勾选 Static(Batching Static)后,构建期合并同材质网格;运行时 CPU 不再逐物体提交,但会占用额外内存且物体不可移动。

在 Inspector 右上角勾选 Static,并确保 Batching Static 子项启用(Unity 版本 UI 略有差异)。适合:建筑、地形装饰、永不移动的道具。不适合:可交互、会动画、会被脚本 transform.position 改位置的物体——移动 Static 物体会 打断合批并触发重建

Static 与 Dynamic 可以并存,但 同物体不会双重合并;大场景里 Static 往往是降 Batches 的主力,Dynamic 补漏网小物。

GPU Instancing 与 SRP Batcher

传统 Dynamic/Static Batching 在 URP/HDRP 项目里常让位给两条更现代的路径:

GPU Instancing)适合 完全相同网格 + 材质、数量巨大 的对象——草、子弹、相同 Prefab 组成的 crowd。一次 drawinstanceCount = N

SRP Batcher)适合 同一 Shader 变体、不同材质参数 的大量 Lit 物体——颜色、金属度不同仍可批量提交,前提是 Shader 使用兼容的 CBUFFER 布局(URP Lit 默认兼容)。在 URP Asset 中确认 SRP Batcher 已开启。

用 Frame Debugger 验收合批

Profiler 告诉你 Batches 是多少 告诉你 为什么是这个数

未合批时,事件列表里 每个物体单独一行 Draw,材质各异、Static 未标记、Instancing 未开,都会呈现这种「一长串」。

合批生效后,多条 Draw 归入 同一 Batch 条目(Static Batched、SRP Batch、Instanced 等前缀)。优化前后应在 同场景、同镜头 下对比 Batches 与 SetPass,而不是只看 FPS 主观感受。

Frame Debugger 与 Ch1 介绍的 Profile Analyzer 互补:前者看 单帧结构,后者看 多次采样是否稳定下降

动手:走一遍合批排查

下面四步把「Batches 太高」从测量到验证走通。每切一步,示意图会对应当前关注点——建议在你项目 Batches 最高的场景里同步打开 Profiler 与 Frame Debugger。

猜一猜:100 个相同 Prefab、同材质、会动的小物体,Static Batching 和 GPU Instancing 哪个更可能帮上忙?

分步1 / 4

① 基线:Profiler Rendering 记下 Draw / Batches / SetPass

CPU提交命令Draw Call一次绘制指令网格+材质+PassCPU 开销大Batches合批后的组多物体→少提交越少越好SetPass切换 Shader Pass材质/关键字变切换也贵GPUProfiler → Rendering 模块常见读数Draw Calls ≈ 原始指令数Batches ≈ 实际提交组数SetPass Calls ≈ Pass 切换次数合批成功 → Batches 远小于 Draw Calls;材质各异 → SetPass 偏高
Draw Call 是 CPU 向 GPU 发出的一次绘制指令;Batches 是合批后的提交组;SetPass 是着色器 Pass 切换次数——三者都在 Profiler Rendering 里可见。

固定镜头 Play 30 秒,打开 Profiler → Rendering,记下 Draw Calls、Batches、SetPass Calls 三个数。若 Batches ≈ Draw Calls,说明合批几乎没工作——记下数字作为对照尺。

代码速查:Instancing 与 Batching 设置

材质侧开启 Instancing(Inspector 或脚本):

// 运行时材质
mat.enableInstancing = true;
 
// Graphics API 绘制实例(概念示意)
Graphics.DrawMeshInstanced(mesh, 0, mat, matrices);

Player / URP 侧常用检查项:

// 仅 Development Build 日志:当前是否启用 SRP Batcher(URP 内部状态)
#if DEVELOPMENT_BUILD && UNITY_URP
// 更可靠:URP Asset Inspector → SRP Batcher 勾选
// Edit → Project Settings → Player → Other Settings → Static/Dynamic Batching
#endif

日常以 Inspector + Project Settings 为准;脚本多用于批量生成 Instancing 矩阵(如草海、弹幕)。

容易踩的坑

小结

  • Draw Call = 一次绘制指令;Batches = 合批后提交组数;SetPass = Shader Pass 切换次数——都在 Rendering 模块
  • Dynamic Batching:运行时合并小网格,条件严、CPU 有合并代价,移动平台常弱化
  • Static Batching:构建期合并静态物,Batches 低、占内存,物体不可动
  • GPU Instancing / SRP Batcher:现代 URP/HDRP 主力,分别解决「同网格多份」与「兼容 Shader 批量提交」
  • Frame Debugger 验收合批是否生效;固定场景对比优化前后 Batches / SetPass

练习

问题 1(改 Demo 型) 在 Batches 最高的场景打开 Profiler 与 Frame Debugger,写下当前 Draw Calls、Batches、SetPass。任选 一种 合批手段(Static / Instancing / 合并材质)只做一处改动,复测并填表——哪两个数下降了?Frame Debugger 里 Batch 条目有何变化?

问题 2(问答型) Dynamic Batching 与 Static Batching 各适合什么物体?各有什么主要代价?

问题 3(问答型) GPU Instancing 和 SRP Batcher 解决的是不是同一种问题?各举一个适用例子。

名词解释

本章出现的专业名词,用大白话再讲一遍。

Draw Call(绘制调用)

CPU 向 GPU 发出的一次绘制指令,含网格、材质与 Pass。每次有固定 CPU 开销。详见「像点外卖」一节。

Batches(批次数)

合批后实际提交组数,Profiler Rendering 中通常 ≤ Draw Calls。详见 DrawCallPipelineDiagram。

SetPass Calls

着色器 Pass 切换次数,材质/关键字越碎越高。详见 DrawCallPipelineDiagram highlight setpass。

Dynamic Batching(动态合批)

运行时合并满足条件的小网格。详见 DynamicBatchingDiagram。

Static Batching(静态合批)

构建期合并标记 Static 的物体,运行时不可移动。详见 StaticBatchingDiagram。

GPU Instancing(GPU 实例化)

一次 Draw 绘制多份相同网格实例。详见 SrpBatcherDiagram mode instancing。

SRP Batcher

URP/HDRP 按兼容 Shader 批量提交,减少 SetPass。详见 SrpBatcherDiagram mode srp。

Frame Debugger

逐帧列出 Draw/Batch 的分析工具,用于验证合批。详见 FrameDebuggerBatchDiagram。

资料与写作方式声明

本章以Packt《Unity Game Optimization》第3版权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…