SetPass Calls 深度解析:比 Draw Call 更重要的性能指标
一、先看一个常见的困惑打开 Unity Profiler,你会看到两个指标:Batches: 200 SetPass Calls: 35很多人只关注Draw Call / Batches,但其实SetPass Calls 才是性能杀手。为什么?因为一次 SetPass 的成本,通常是一次 Draw Call 的几倍到几十倍。二、什么是 SetPass Call?定义SetPass Call切换渲染状态的调用次数。当 GPU 要用一个新的渲染配置绘制物体时,CPU 必须先告诉 GPU:“换 Shader!”“换纹理!”“换混合模式!”“换深度状态!”…这一整套切换配置的操作,就是一次 SetPass Call。用做菜类比 回到之前 OpenGL 状态机的类比:SetPass Call 换菜谱、换锅、换调料的过程 Draw Call 开火炒菜!的那一下换一次菜谱(SetPass):耗时 10 分钟(切菜、洗锅、准备调料)炒一盘菜(Draw Call):耗时 1 分钟炒 10 盘同样的菜:10 分钟准备 10 分钟炒 20 分钟炒 10 盘不同菜:10 × (10 分钟准备 1 分钟炒) 110 分钟 SetPass 是准备成本,Draw 是执行成本。准备成本远大于执行成本。三、SetPass 到底做了什么? 一次 SetPass 触发时,GPU 驱动层要做:1. 上传新 Shader 到 GPU(如果没上传过) 2. 绑定 Shader 程序 3. 绑定纹理(可能多张) 4. 上传 Uniform 参数(矩阵、颜色、光照参数...) 5. 设置渲染状态(Blend、ZTest、Cull...) 6. 校验状态合法性 7. 提交 Command Buffer 到 GPU这些操作大部分在 CPU 侧完成,是驱动层的开销。所以 SetPass 是典型的CPU 瓶颈,GPU 反而闲着。四、SetPass Calls vs Batches vs Draw Calls 三个概念常被混淆,一次讲清:Draw CallGPU 层面:CPU 调用glDrawElements/DrawIndexedPrimitive的次数。Batches(批次)Unity 层面:Unity 打包提交的批次数。1 个 Batch 1 个或多个物体合并后的 1 次绘制合批优化(动态合批、静态合批、GPU Instancing)会减少 BatchesSetPass Calls渲染状态切换次数:每次切换 Shader / 材质 / 纹理组合都算一次。关系1 SetPass 1 次状态切换 N 个 Draw Call 例如: 用同一个材质画 10 个物体: 1 SetPass 10 Draw Calls(如果没合批) 1 SetPass 1 Draw Call (如果合批成功) 用 10 个不同材质各画 1 个物体: 10 SetPass 10 Draw Calls直观对比表场景SetPassBatchesDraw Call性能100 个相同材质,无合批1100100中等100 个相同材质,合批111极好100 个不同材质100100100极差100 个物体,50 种材质50100100差五、为什么 SetPass 这么昂贵? 1. CPU-GPU 通信成本每次 SetPass 都涉及 CPU 到 GPU 的命令提交,需要:Command Buffer 打包驱动层校验可能的 GPU 停顿(等前一批命令完成才能切状态)2. GPU 状态重建现代 GPU 依赖Pipeline State,状态切换往往触发:缓存刷新内部状态机重置有时甚至需要重编译 Shader 变体3. 破坏并行性GPU 喜欢连续执行相同任务:好: 1000 次相同状态的绘制 → GPU 流水线满负荷 坏: 1000 次不同状态的切换 → GPU 走走停停六、如何看 SetPass Calls?Unity Statistics 窗口Game 视图 → 右上角 Stats会显示:Batches: 200 Saved by batching: 150 SetPass calls: 35Unity ProfilerWindow → Analysis → Profiler → Rendering看SetPass Calls曲线,识别峰值场景。Frame DebuggerWindow → Analysis → Frame Debugger逐帧查看每一步 SetPass 触发的原因:左侧列表中,每个SetPass ***就是一次状态切换点击可看到具体切换了什么(纹理不同?Shader 不同?)七、什么情况会触发新的 SetPass? 触发 SetPass 的因素(每换一个都可能新增 SetPass)不同的 Shader(最常见)不同的材质(哪怕 Shader 相同,材质不同也算)不同的纹理(材质相同但纹理不同)不同的 Uniform 值(颜色、参数不同)不同的 Render State(Blend / ZTest / Cull 等)不同的 Keyword(Shader 变体不同)Lightmap / Light Probe影响多 Pass Shader(每个 Pass 一次)举例:同一个 Shader 也会多次 SetPass// 材质 A:红色 纹理 1// 材质 B:蓝色 纹理 1// 材质 C:红色 纹理 2// 即使 Shader 都相同,3 个材质 3 次 SetPass除非用GPU Instancing / MaterialPropertyBlock,才可能减少 SetPass。八、优化策略 策略 1:合并材质核心原则:能用同一个材质,就别新建。// ❌ 每个物体一个材质(Instance 化)foreach(varobjinobjects){obj.material.colorColor.red;// 触发材质克隆!}// ✅ 用 MaterialPropertyBlockMaterialPropertyBlockmpbnewMaterialPropertyBlock();foreach(varobjinobjects){mpb.SetColor(_Color,Color.red);obj.GetComponentRenderer().SetPropertyBlock(mpb);}// MaterialPropertyBlock 不会破坏合批和 SetPass 复用⚠️陷阱:访问renderer.material(不是sharedMaterial)会自动创建材质副本,导致 SetPass 翻倍!策略 2:纹理合并(图集/Atlas)多个物体用同一张大图里的不同区域,让它们共享材质:❌ 100 个 UI 元素,每个用独立小图 → 100 SetPass ✅ 100 个 UI 元素,共用 1 个图集 → 1 SetPass策略 3:GPU Instancing同一 Mesh 同一 Material,渲染大量副本:#pragma multi_compile_instancing// 场景中 1000 棵树,启用 Instancing// 结果:1 SetPass 1 Draw Call(而不是 1000 SetPass)策略 4:控制 Shader 变体Shader Keyword 会产生变体,不同变体 不同 Shader 不同 SetPass:物体 A 用 _NORMALMAP 版本 物体 B 用 _NORMALMAP _EMISSION 版本 → 2 SetPass优化:减少multi_compile组合用shader_feature(只打包用到的)统一场景的 Shader 特性开关策略 5:按材质排序渲染Unity 内部会尽量按材质排序,但你也可以手动优化:// ✅ 让同材质的物体连着渲染// (对透明物体不适用,透明必须按深度排)策略 6:减少多 Pass一个 Shader 有 2 个 Pass → 每个物体触发 2 次 SetPass。❌ 手写多 Pass 实现描边(边框 Pass 主 Pass) ✅ 单 Pass 中用 fwidth 或后处理实现描边策略 7:UI 优化Sprite Atlas 合并避免字体图集和图片图集交叉静态部分合并到一张背景图Canvas 拆分(静/动分离)策略 8:光照优化减少实时光(每盏实时光可能触发额外 Pass)使用Lightmap烘焙减少 Light Probe 复杂度使用URP的 Single Pass Forward九、经验值参考 目标值(移动端):项目类型SetPass CallsDraw Calls休闲游戏 30 100中型 3D 60 300大型 3D 100 500端游/主机 300 2000触发警戒的数值:移动端 SetPass 100 → 需要优化移动端 SetPass 200 → 严重问题十、常见误区 ❌ 误区 1:“合批能减少 SetPass”部分错误。合批减少的是Draw Call,不一定减少 SetPass:同一材质的多个物体合批 → SetPass 已经是 1,合批只是减少 Draw Call不同材质的物体 → 合不了批,SetPass 也降不下来❌ 误区 2:“SetPass 少就一定快”部分错误。SetPass 少代表 CPU 端好,但 GPU 端可能因为:单个 Draw Call 里画的物体过多、太复杂Shader 太重Overdraw 严重依然可能卡。要综合看 CPU/GPU 各自的瓶颈。❌ 误区 3:“Draw Call 比 SetPass 重要”通常反了。Draw Call 的成本近年来大幅降低(现代 API 如 Metal / Vulkan 让 DC 便宜了很多),而SetPass 依然是昂贵的状态切换。❌ 误区 4:“MaterialPropertyBlock 会破坏合批”错误。MPB不会破坏静态合批和 GPU Instancing。相反,它可以在同一材质基础上做属性变化,同时保持合批,是低成本改颜色的最佳方案。⚠️ 但 MPB 会破坏动态合批(Unity 的 legacy dynamic batching),因为动态合批要求所有属性一致。❌ 误区 5:“我的场景就 30 个物体,不需要优化 SetPass”30 个物体如果每个都用独立材质 30 SetPass,这在移动端已经是不小的开销了。关键看材质数量,不是物体数量。十一、实战诊断流程 ️步骤 1:看指标SetPass 100(移动)/ 300(PC)→ 需要优化步骤 2:打开 Frame Debugger找出触发 SetPass 的原因:大量 UI 各自图集 → 合并图集每个角色独立材质 → 用 MPB多光源触发额外 Pass → 减光源步骤 3:检查材质使用// 场景中有多少个不同的材质实例?varallMaterialsnewHashSetMaterial();foreach(varrinFindObjectsOfTypeRenderer()){foreach(varminr.sharedMaterials){allMaterials.Add(m);}}Debug.Log($材质总数:{allMaterials.Count});材质数 ≈ SetPass 数(理想情况)。步骤 4:检查 Shader 变体Edit → Project Settings → Graphics → Shader Preloading变体过多会导致 SetPass 分裂。十二、进阶:SRP Batcher(URP/HDRP 的救星)URP/HDRP 的SRP Batcher是针对 SetPass 优化的黑科技:原理:传统渲染:每个材质切换都要重新上传所有 UniformSRP Batcher:Shader 相同时,只更新变化的 Per-Object 数据效果:大幅降低CPU 端渲染成本就算材质不同(参数不同),只要 Shader 相同,几乎免费启用条件:URP / HDRPShader 兼容 SRP Batcher(用 CBUFFER 分组)Graphics Settings → Scriptable Render Pipeline Settings → SRP Batcher ✅ 一句话总结SetPass Calls 是换菜谱的次数,Draw Calls 是开火炒菜的次数。换菜谱的成本远大于炒菜本身,所以优化的核心是尽量用同一份菜谱——合并材质、共享纹理、启用 Instancing、用 MaterialPropertyBlock。SetPass 是 CPU 端的性能红线,尤其在移动端,降 SetPass 比降 DC 更重要。

相关新闻

光照与阴影优化:游戏里最“烧钱“的视觉大户

光照与阴影优化:游戏里最“烧钱“的视觉大户

🎬 开场:一个"点几盏灯就崩"的惨案小王想让场景更真实,兴冲冲地摆了 20 盏点光源、开了实时阴影, 还给每盏灯都开了阴影……结果——帧率直接腰斩,手机秒烫!🔥 “灯不就是照亮吗&…

2026/10/14 12:57:58 阅读更多 →
深入理解React Native Gallery Toolkit实现原理:Reanimated 2动画与手势系统结合

深入理解React Native Gallery Toolkit实现原理:Reanimated 2动画与手势系统结合

深入理解React Native Gallery Toolkit实现原理:Reanimated 2动画与手势系统结合 【免费下载链接】react-native-gallery-toolkit Reanimated 2 powered gallery implementation 项目地址: https://gitcode.com/gh_mirrors/re/react-native-gallery-toolkit …

2026/10/14 12:57:38 阅读更多 →
Homebrew 应用管理实战:5 个任务走通命令行部署软件的全流程

Homebrew 应用管理实战:5 个任务走通命令行部署软件的全流程

Homebrew 应用管理实战:5 个任务走通命令行部署软件的全流程 【免费下载链接】brew 🍺 The Package Manager for Everywhere 项目地址: https://gitcode.com/GitHub_Trending/br/brew Homebrew(brew)是一款开源命令行软件包…

2026/10/14 7:57:27 阅读更多 →

最新新闻

SpringBoot 入门+ApiPost 测试接口

SpringBoot 入门+ApiPost 测试接口

创建springboot项目,选择springboot,语言选java,类型选maven,打包选jar选完下一步添加依赖,勾选web里的springweb,然后就创建完成了代码演示:打包运行:选择右边的m也就是maven&#…

2026/10/14 16:57:11 阅读更多 →
Akka.Hosting 日志系统实战:Akka.NET 与 Microsoft.Extensions.Logging、Serilog 的集成与日志过滤

Akka.Hosting 日志系统实战:Akka.NET 与 Microsoft.Extensions.Logging、Serilog 的集成与日志过滤

后端微服务 【免费下载链接】akka.net Canonical actor model implementation for .NET with local distributed actors in C# and F#. 项目地址: https://gitcode.com/gh_mirrors/ak/akka.net 点击查看 免费下载 在 Akka.NET 应用中,ActorSystem 的日…

2026/10/14 16:57:11 阅读更多 →
ORA-12537根因诊断:listener.ora配置错误精准定位指南

ORA-12537根因诊断:listener.ora配置错误精准定位指南

1. 项目概述:这不是网络故障,是监听器在“装死”“ORA-12537:TNS连接已关闭”——这个报错在Oracle DBA的日常里出现频率极高,但它的杀伤力却常被低估。它不像ORA-00942(表或视图不存在)那样直白&#xff0…

2026/10/14 16:57:11 阅读更多 →
WuKongIM 代码质量台账:从缺陷证据到确定性测试的治理实践

WuKongIM 代码质量台账:从缺陷证据到确定性测试的治理实践

即时通讯后端 【免费下载链接】WuKongIM More than just IM 不只是即时通讯(IM) 项目地址: https://gitcode.com/gh_mirrors/wu/WuKongIM 点击查看 免费下载 docs/development/CODE_QUALITY.md 是 WuKongIM 仓库维护的一组按日期归档的代码质量笔记:每一…

2026/10/14 16:57:11 阅读更多 →
为什么越来越多课题组,开始重视实验室供应链的稳定性

为什么越来越多课题组,开始重视实验室供应链的稳定性

以前大家聊实验室建设,更多聊仪器配置、课题方向。现在越来越多课题组开始重视一个词:供应链稳定性。原因很简单,科研节奏越来越快,谁也不想因为试剂断供、批次不稳、物流延迟而停摆。稳定的供应链,首先要有足够全的品…

2026/10/14 16:57:11 阅读更多 →
蓝鲸智云配置平台 bk-cmdb「更新模型定义」接口(update_object)实战指南

蓝鲸智云配置平台 bk-cmdb「更新模型定义」接口(update_object)实战指南

后端企业应用运维 【免费下载链接】bk-cmdb 蓝鲸智云配置平台(BlueKing CMDB) 项目地址: https://gitcode.com/gh_mirrors/bk/bk-cmdb 点击查看 免费下载 本篇指南面向需要调用蓝鲸智云配置平台(bk-cmdb,BlueKing CMDB) OpenAPI …

2026/10/14 16:56:10 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →