SRP Batcher 可编程渲染线合并器在传统的Built-in管线中游戏卡顿往往不是因为模型面数太高而是因为DrawCall过多或者更准确的说是SetPassCall渲染状态切换过多。传统渲染流程CPU要绘制一个物体时它需要向GPU发送指令SetPassCall把物体的材质贴图Shader参数上传到GPU,然后调用DrawCall告诉GPU把这个物体的网格画出来如果场景里有100个物体1.如果他们使用的是同一个材质并且材质上的所有参数都一样如果开了动态合批或静态合批就会把这些同参数同材质的物体的mesh合并到一个大的mesh在静态合批里勾选static勾选框这样cpu就会把这些材质贴图mesh的顶点一起发给gpu执行一次setpasscall然后调用一次drawcall就可以将这些mesh一次性的画出来如果是动态合批就要求这个mesh是不能有位移的并且不能进行更换材质操作并且顶点如果超限之后也不能进行将多个mesh合并到一个大的mesh2.如果这些物体使用的是同一个材质不同的材质参数参数变了至少会多一次 Draw Call是否多一次 SetPass 要看 shader pass 有没有变、Unity 能不能复用同一次 pass 设置。同一个 Material 资源 MaterialPropertyBlock推荐renderer.sharedMaterial sharedMat; // 同一个 Materialrenderer.SetPropertyBlock(mpb); // 每个物体不同参数.传统 SRP 渲染无 SRP Batcher每画一个物体CPU 大致要做绑定 Shader Pass→ 上传/绑定 Material 全部属性纹理、颜色、向量…→ 上传 Object 数据矩阵、Lightmap 等→ Draw换下一个物体哪怕 shader 相同、只是 _Color 不同时往往又要 重新走一遍 Material 绑定流程CPU 开销大。SRP Batcher 换了一种完全不同的思路。它发现在现代图形 API 中切换材质参数其实很快真正慢的是切换 Shader 本身即切换底层的渲染管线状态 PSOs。只要物体的 Shader 相同即使材质球不同它们底层的渲染内核也是一样的。因此SRP Batcher 在 GPU 显存里开辟了两块连续的专属缓冲区Constant Buffers常量缓冲区全局每对象缓冲区UnityPerDraw CBUFFER 存放每个物体独特的数据比如物体的世界坐标Transform、矩阵信息等。全局每材质缓冲区UnityPerMaterial CBUFFER 存放材质球的专属属性比如颜色、贴图的缩放Tiling/Offset等。第一步数据预载极快CPU 不再像以前那样“画一个物体传一次数据”而是把场景里所有物体的坐标数据和材质数据一次性、批量地写进 GPU 的这两个 CBUFFER 显存缓冲区中。第二步命令流Command Loop当数据都在显存里对齐后CPU 只需要发送一个极快的、包含大量 Draw 调用的指令流。GPU 自己通过更改“指针偏移量”就能直接去缓冲区里抓取第 1 个物体的坐标和材质进行绘制画完立刻抓取第 2 个中途不需要 CPU 重新介入发送 SetPass Call。绘制时流程变成① 遇到新 Shader Variant → 绑定一次 Shader Pass一次「重」切换② 画物体 A → 只更新 PerDraw buffer 指向 Material 的 CBUFFER → Draw③ 画物体 B同 Shader Variant不同 Material→ 只换 Material CBUFFER 指针 更新 PerDraw → Draw④ 画物体 C同 Material→ 只更新 PerDraw → Draw打断 SRP batch 的条件换了 Shader Variant不同 shader、不同 pass、不同 keyword 组合不打断的条件同一 Shader Variant 下换 Material、换物体参数 —— 只更新 CBUFFER不必完整重绑 Material所以100 个物体、100 个不同 Material、但同一个 URP Lit Shader → 可能仍是 1 个 SetPass 100 个 DrawCPU 侧比传统路径轻很多100 个物体、10 种 Shader Variant → 大约 10 个 SRP batch每种 variant 一段SRP Batcher 不合并 meshDraw Call 数在 Profiler 里可能看起来没少但 SetPass Calls 和 RenderLoop 耗时 会明显下降。SRP Batcher 依赖 固定内存布局Shader 必须满足// 所有材质属性 — 必须在一个 CBUFFER 里CBUFFER_START(UnityPerMaterial)float4 _BaseColor;float4 _BaseMap_ST;// …CBUFFER_END// 所有引擎内置 per-object 属性 — 必须在一个 CBUFFER 里CBUFFER_START(UnityPerDraw)// unity_ObjectToWorld 等由引擎注入CBUFFER_ENDPass 之间 CBUFFER 声明 不一致遍历待渲染物体│├─ Shader Variant 变了 ──→ 新 SRP Batch重新 SetPass│├─ Material 变了 ──→ 切换 GPU 上已缓存的 Material CBUFFER轻量│└─ Object 变了 ──→ 更新 UnityPerDraw 大 Buffer 中对应槽位轻量│└── Draw Mesh减少 Shader Variant 数量少 keyword、少 pass→ 一个 SRP batch 里能塞更多 draw同功能尽量共用一个 Shader用不同 Material 区分参数自定义 Shader 严格遵循 CBUFFER 规范不要用 MPB在 URP/HDRP 下用 Frame Debugger / Profiler 看 SRP Batch 和 SetPass Calls不要只看 Batches 数量SRP Batcher 通常会比内置管线的传统渲染路径多占一些 GPU 内存但这是用 显存换 CPU 的权衡而且和 Static Batching 那种「暴涨式」占内存不是一回事。SRP Batcher 的核心设计是把数据长期放在 GPU 上少做每帧重复上传。三个内存块1.Material CBUFFERUnityPerMaterial每个兼容 SRP Batcher 的 Material 在 GPU 上有一份持久常量缓冲通常 几 KB / Material和 shader 属性数量有关2.Per-Object 大 BufferUnityPerDraw存矩阵、Lightmap、SH 等 per-renderer 数据随 可见 Renderer 数量 增长仍是常量缓冲量级3.纹理 / Mesh贴图、模型本身不变SRP Batcher 不复制 mesh实际影响大不大对大多数项目SRP Batcher 带来的显存增量相对纹理、Mesh、Render Target 可以忽略。真正要留意的场景Unique Material 极多几千种各不相同的材质→ Material CBUFFER 数量线性增加同屏 Renderer 极多 → Per-Object 大 Buffer 变大极低端机、显存非常紧 → 需要实测但瓶颈更常见的是贴图/分辨率而不是 SRP Batcher 本身运行时频繁改 Material 属性 → CBUFFER 要重传CPU 优势下降显存占用仍在一帧里 CPU 和 GPU 各做什么以「100 艘船、同 URP Lit Shader、100 个不同 Material」为例首帧 / Material 第一次出现CPU:① Mesh、Texture 已在 VRAM加载阶段完成② 为新 Material 创建并上传 UnityPerMaterial CBUFFER → 留在 GPU③ 绑定 Shader Variant一次 SetPass / SRP Batch 开始④ 写 UnityPerDraw 里物体 A 的 slot矩阵等⑤ Draw告诉 GPU 用哪个 VBO、哪张 Texture、哪个 Material CBUFFER、哪个 Draw slot后续每帧Material 参数没变CPU:① Mesh / Texture / Material CBUFFER → 不动已在 VRAM② 只更新 UnityPerDraw 大 Buffer 里各物体的 transform 等③ 对每个物体发 Draw换 Material CBUFFER 指针 换 PerDraw slot 同一 VBO/Texture 绑定GPU:按 Draw 命令从 VRAM 取顶点、贴图、常量 → 跑 Vertex/Fragment Shader → 输出像素你说的「告诉 GPU 去内存里取东西组装」——对 GPU 侧就是这样但 CPU 每帧不是重新上传 mesh/贴图/Material而是 更新 PerDraw 发绑定与 Draw 命令。和内置管线对比帮助记忆内置管线无 SRP Batcher:每 Draw 可能整套重绑 Material 属性CPU 重Mesh/Texture 同样常驻 GPU也不是每帧传整块SRP Batcher:Material 属性已在 GPUUnityPerMaterial每 Draw 主要换 PerDraw slot 轻量绑定Mesh/Texture 机制不变