外观
Stage05|Performance
Lesson059|GPU 性能:DrawCall、Overdraw、带宽和 Fill Rate
Tags: #Creator2.x #Stage05 #GPU #DrawCall #Overdraw #FillRateDifficulty: ⭐⭐⭐⭐☆
真实问题:CPU 只有 6 ms,帧率为什么仍上不去
目标 60 FPS,CPU 主线程每帧约 6 ms,实际却只有 35 FPS。关闭全屏后处理或将渲染分辨率减半后,帧率明显恢复。
这是一组强 GPU 证据:
text
CPU 有余量
降低像素工作量显著改善
→ 优先检查 GPU / Fill Rate / 带宽一、本课目标
本课把 Renderer 阶段的知识转成性能判断:
- DrawCall 为什么会影响 CPU 和 GPU。
- Overdraw、Fill Rate 和分辨率的关系。
- 为什么透明 UI 常常比不透明背景更贵。
- 如何区分 GPU 瓶颈和 CPU 提交瓶颈。
二、GPU 每帧要做什么
简化链路:
text
CPU 提交绘制命令
→ Vertex Shader 处理顶点
→ 光栅化生成片元
→ Fragment Shader 计算颜色
→ Blend / Depth / Stencil
→ 写入 Render TargetGPU 成本取决于顶点数量、片元数量、Shader 复杂度、纹理访问和状态切换。
三、DrawCall 的含义
一次 DrawCall 可以粗略理解为 CPU 向 GPU 提交一组绘制命令。它通常包含:
text
材质和 Shader
纹理
Render State
顶点和索引缓冲
绘制数量DrawCall 多会增加提交和状态切换,但低 DrawCall 不代表 GPU 一定轻松。
四、Overdraw 和 Fill Rate
如果同一个屏幕像素被多层透明图片覆盖:
text
背景绘制一次
半透明面板一次
阴影一次
图标一次
文字一次这个像素可能被处理多次,这就是 Overdraw。Fill Rate 表示 GPU 处理片元和填充屏幕的能力。
常见高风险区域:
- 大面积半透明面板。
- 全屏特效。
- 粒子。
- 多层阴影和模糊。
- 大尺寸图片缩放显示。
五、分辨率为什么影响 GPU
分辨率越高,片元数量越多:
text
1920 × 1080 ≈ 207 万像素
1280 × 720 ≈ 92 万像素相同 UI 层数下,高分辨率可能需要处理更多片元。验证 GPU 瓶颈时,降低分辨率或关闭特效是有价值的对照实验。
六、Shader 和纹理带宽
Fragment Shader 中的复杂计算、分支和多次纹理采样都会增加成本。纹理尺寸、格式和访问次数还会影响带宽:
text
更大纹理
更多采样
更高分辨率
→ 更高带宽压力优化 Shader 时不要只减少代码行数,应观察实际片元执行和目标设备表现。
七、案例:DrawCall 不高但仍然卡
场景可能只有 20 个 DrawCall,但每个都是:
- 全屏半透明。
- 多次纹理采样。
- 复杂 Fragment Shader。
- 高分辨率 Render Target。
这时瓶颈可能在片元和带宽,而不是 DrawCall 数量。应该检查 Overdraw、GPU 时间和分辨率对照,而不是继续只压 DrawCall。
八、CPU/GPU 瓶颈的对照实验
text
降低分辨率后明显变快 → GPU 可能是瓶颈
关闭半透明特效后明显变快 → Overdraw 可能是瓶颈
减少脚本后变快、分辨率无影响 → CPU 可能是瓶颈
DrawCall 降低但帧时间不变 → 主要瓶颈可能不在提交对照实验一次只改一个变量,才能解释结果。
九、常见误区
误区一:DrawCall 越少越好
需要结合 GPU 时间、Overdraw、Shader 和分辨率判断。
误区二:所有透明图片都必须删除
应优先减少覆盖面积、层数和不必要的全屏区域。
误区三:降低图片尺寸一定解决 Fill Rate
Fill Rate 主要与片元数量和覆盖范围有关,纹理尺寸还涉及带宽和显存。
误区四:开发机 GPU 时间低,目标手机也一定低
GPU 架构、带宽和驱动差异很大,必须在目标设备验证。
十、练习与答案
- DrawCall 主要增加哪两类成本?
- Overdraw 为什么会让透明 UI 变贵?
- 如何用分辨率做 GPU 瓶颈对照实验?
- DrawCall 很低但仍卡,应该检查什么?
答案:
- CPU 提交/状态管理成本和 GPU 绘制批次成本。
- 同一片元被多次处理和混合。
- 只改变渲染分辨率,比较 GPU Frame Time 和总帧时间。
- Overdraw、Shader、纹理带宽、分辨率和同步等待。
判断 GPU 瓶颈的对照实验
在不改变业务逻辑的前提下:
降低分辨率
若帧时间显著下降,Fragment/带宽嫌疑上升。
关闭全屏透明层和后处理
若改善,检查 Overdraw、采样和 RenderTexture。
用简单 Material 替换复杂效果
若改善,Shader/纹理采样嫌疑上升。
保持画面但减少 DrawCall
若 CPU 改善而 GPU变化小,瓶颈可能在提交。
单个实验不是绝对证明,组合结果形成证据链。
Creator 2.4.x 实验:CPU 与 GPU 两组压力
GpuEvidenceProbe.ts
ts
const { ccclass, property } = cc._decorator;
@ccclass
export default class GpuEvidenceProbe extends cc.Component {
@property(cc.Node)
overdraw: cc.Node = null;
@property(cc.Node)
manySmallNodes: cc.Node = null;
toggleOverdraw() {
this.overdraw.active = !this.overdraw.active;
}
toggleManyNodes() {
this.manySmallNodes.active =
!this.manySmallNodes.active;
}
scaleOverdraw(value: number) {
this.overdraw.scale = value;
}
}Overdraw 组使用少量全屏透明层;ManySmallNodes 使用大量小节点但控制覆盖面积。
记录
| 状态 | CPU | GPU/总帧 | DrawCall | 覆盖面积 |
|---|---|---|---|---|
| 基线 | 实测 | 实测 | 实测 | 小 |
| 大量小节点 | 实测 | 实测 | 实测 | 小 |
| 全屏透明层 | 实测 | 实测 | 实测 | 大 |
| 透明层缩半 | 实测 | 实测 | 实测 | 约 1/4 |
目标是区分提交/脚本与像素阶段,而非得出跨设备固定数值。
带宽问题不只来自大 Texture
GPU 带宽包括:
- 纹理读取;
- FrameBuffer 写入;
- Blend 读取目标颜色再写回;
- 多 Pass RenderTexture 读写;
- 高精度格式;
- MSAA/深度模板;
- CPU→GPU 动态数据上传。
一个低算术复杂度 Shader 也可能因为多次大纹理采样而带宽受限。
真实项目故障:RenderTexture 特效在 2K 屏幕翻倍变慢
效果使用全分辨率两个中间 RenderTexture 和三次全屏 Pass。分辨率提升后像素数不是线性按宽度增长,而是宽×高共同增长。
优化:
- 中间纹理降采样;
- 合并可合并 Pass;
- 降低格式精度;
- 缩小效果区域;
- 静态内容按需刷新;
- 低端档关闭。
验收必须在不同 GPU 和分辨率测试,不能用开发机独立显卡代替移动设备。
GPU 故障诊断
DrawCall 高但 GPU 不高
可能 CPU/驱动提交瓶颈,先看 CPU;合批仍可能有收益。
DrawCall 低但 GPU 高
看覆盖、Shader、纹理带宽、RenderTexture、分辨率和粒子。
降分辨率无改善
像素瓶颈证据变弱,检查 CPU、同步、顶点、DrawCall 或实验设置是否真改变渲染分辨率。
版本边界与练习答案
Creator 内置 Profiler 对 GPU 的可见度有限,Web/Android/iOS 应结合对应 GPU Frame 工具。不同图形后端行为不完全一致。
- 为什么分辨率减半实验有价值? 宽高各减半可显著降低片元与 RenderTarget 像素量,隔离像素成本。
- DrawCall 是 CPU 还是 GPU 指标? 它同时关联提交和 GPU 工作入口,但数量本身更常用于判断 CPU/状态批次,不能代表 GPU 总成本。
- Blend 为什么消耗带宽? 通常需要读取目标颜色、计算混合并写回。
- 如何证明 Shader 是主因? 在相同几何/覆盖下替换简化 Shader,结合 GPU 帧时间和平台 Frame Capture。
十一、本课总结
text
DrawCall、Overdraw、Fill Rate、Shader 和带宽是不同维度。
GPU 优化必须结合实际 GPU 时间和目标设备。十二、下一课预告
text
Lesson060|Texture Memory:图片为什么最容易吃掉内存?