外观
Stage05|Performance
Lesson056|FPS、Frame Time、刷新率与帧预算
Tags: #Creator2.x #Stage05 #FPS #FrameTime #FrameBudgetDifficulty: ⭐⭐⭐☆☆
真实问题:平均 60 FPS,为什么玩家仍觉得卡
10 秒内大多数帧约 16 ms,但每秒有一帧达到 100 ms。平均 FPS 仍可能接近 60,玩家却会稳定感到顿一下。
text
平均值隐藏了长帧
长帧决定可感知卡顿性能验收需要 Frame Time 分布和高分位,而不只是平均 FPS。
一、本课目标
本课把“帧率”转换成可计算的帧预算:
- FPS 和 Frame Time 的关系。
- 为什么平均 FPS 会掩盖尖峰。
- 16.67 ms 到底代表什么。
- 如何为 CPU、GPU 和加载阶段分配预算。
二、FPS 和 Frame Time
text
Frame Time = 1 秒 / FPS
FPS = 1 秒 / Frame Time常用目标:
| FPS | 单帧时间 |
|---|---|
| 30 | 33.33 ms |
| 60 | 16.67 ms |
| 90 | 11.11 ms |
| 120 | 8.33 ms |
60 FPS 的目标不是“每秒调用 60 次”这么简单,而是每帧所有 CPU、GPU 和同步工作都尽量落在约 16.67 ms 内。
三、平均值为什么不够
两组帧时间:
text
A:16, 16, 17, 16, 16
B:5, 5, 5, 5, 85B 的平均值可能看起来不差,但 85 ms 的尖峰会明显造成卡顿。实际分析应关注:
text
平均值
P95 / P99
最大帧时间
尖峰出现位置四、帧预算不是只有一份
一帧可以粗略拆成:
text
CPU 业务和引擎更新
CPU 渲染准备和提交
GPU 顶点和像素处理
输入、音频、网络等其他工作CPU 和 GPU 可能并行,但最终帧完成时间受更慢的一侧以及同步点限制。不要简单把 16.67 ms 平均切成“CPU 8 ms、GPU 8 ms”就当作万能标准,应通过工具观察真实瓶颈。
五、刷新率和目标帧率
屏幕刷新率是显示设备能力,游戏 FPS 是应用实际提交速度:
text
120 Hz 屏幕不保证游戏 120 FPS
60 FPS 游戏也可能运行在 120 Hz 屏幕如果目标 60 FPS,单帧预算约 16.67 ms;如果设备只有 30 FPS,预算约 33.33 ms。移动端应以目标机型和产品体验确定预算,而不是只看开发机。
六、帧时间尖峰的常见来源
- GC 或大量临时对象。
- 场景切换和资源解码。
- 首次创建纹理或 Shader。
- Layout / Widget 批量重算。
- 大量 Node 创建和销毁。
- 同步等待 GPU 或 IO。
- 日志、调试绘制和 Profiler 自身开销。
尖峰排查要关联时间线,不能只看某一个函数的平均耗时。
七、案例:同样 55 FPS 的两个页面
页面 A:
text
每帧稳定约 18 ms页面 B:
text
大部分帧 10 ms
每隔几秒出现 100 ms页面 A 是持续超预算,页面 B 是尖峰问题。优化策略不同:
- A:减少每帧持续工作。
- B:找到周期性任务、加载、GC 或同步点。
八、性能记录表
建议记录:
text
设备:型号、系统、分辨率
场景:入口和操作步骤
目标:30 / 60 / 120 FPS
CPU Frame Time:平均、最大、P95
GPU Frame Time:平均、最大、P95
内存:初始、峰值、场景后
版本:构建号和资源版本没有这些信息,优化前后的“变快”很难复现。
九、常见误区
误区一:FPS 从 60 变 55 就一定是严重问题
需要看帧时间分布、设备目标和用户是否感知到尖峰。
误区二:平均 FPS 高就没有卡顿
尖峰可能被平均值掩盖。
误区三:刷新率越高就必须让游戏跑得越快
需要根据产品目标、设备功耗和逻辑稳定性决定。
误区四:只为 CPU 分配预算
GPU、IO、内存和同步也会限制一帧完成时间。
十、练习与答案
- 60 FPS 的单帧预算是多少?
- 为什么 P95/P99 比单纯平均值更有价值?
- 5 ms、5 ms、5 ms、100 ms 的问题属于持续超预算还是尖峰?
- 为什么需要记录设备、分辨率和构建版本?
答案:
- 约 16.67 ms。
- 它们能反映尾部延迟和用户感知到的卡顿。
- 尖峰问题。
- 性能受硬件、分辨率、平台和资源版本影响,必须保证可复现。
帧预算先从目标推导
text
60 FPS → 每帧约 16.67 ms
30 FPS → 每帧约 33.33 ms
120 FPS → 每帧约 8.33 ms这是总预算,不是 JavaScript 可以独占的预算。还要留给引擎、渲染提交、GPU、平台调度等。
若 GPU 需要 20 ms,即使 CPU 只用 5 ms,也无法稳定 60 FPS。CPU/GPU 是否并行以及工具如何显示,取决于平台。
Creator 2.4.x 实验:记录长帧而不是平均值
FrameTimeProbe.ts
ts
const { ccclass } = cc._decorator;
@ccclass
export default class FrameTimeProbe extends cc.Component {
private samples: number[] = [];
private elapsed = 0;
update(dt: number) {
this.samples.push(dt * 1000);
this.elapsed += dt;
if (this.elapsed >= 10) {
this.report();
this.samples.length = 0;
this.elapsed = 0;
}
}
createSpike() {
const begin = Date.now();
while (Date.now() - begin < 80) {
// 测试场景中故意阻塞主线程。
}
}
private report() {
const sorted = this.samples.slice().sort((a, b) => a - b);
const percentile = (ratio: number) => {
const index = Math.min(
sorted.length - 1,
Math.floor(sorted.length * ratio)
);
return sorted[index].toFixed(2);
};
cc.log(
'[frame]',
'count=', sorted.length,
'p50=', percentile(0.5),
'p95=', percentile(0.95),
'p99=', percentile(0.99),
'max=', sorted[sorted.length - 1].toFixed(2)
);
}
}dt 是游戏循环提供的时间间隔,并非硬件级精确 CPU 测量;实验用于观察长帧分布,正式分析结合 Profiler。
步骤
- 静置 10 秒记录基线。
- 每秒触发一次 80 ms Spike。
- 比较平均 FPS 与 p95/p99/max。
- 在 30/60 目标帧率设置下分别测试。
- 记录设备刷新率与是否连接调试器。
为什么 55 FPS 可能代表两种体验
text
页面 A:帧时间稳定约 18 ms
页面 B:大多数 16 ms,偶尔 80~150 ms两者平均 FPS 可以接近,B 的顿挫更明显。报告至少包含:
- 中位数;
- p95/p99;
- 最大帧;
- 超预算帧比例;
- 测试时长;
- 用户动作时间点。
VSync 与刷新率的台阶
当渲染不能赶上刷新窗口,呈现可能从 60 附近掉到更低台阶,具体取决于平台的交换与调度。不要把 30 FPS 简单理解为代码正好耗时 33 ms。
高刷新率设备上,60 FPS 可能仍是项目目标;必须记录:
text
设备刷新率
游戏目标帧率
实际呈现帧率
CPU/GPU 帧时间真实项目故障:每秒整点顿一下
通过时间线发现每秒 p99 峰值。代码在整秒同时做:
text
更新 100 个倒计时 Label
→ 刷新 Layout
→ 保存本地进度
→ 上报埋点优化不是把四项都删掉,而是:
- Label 仅更新可见项;
- 固定布局尺寸;
- 存档与上报错峰;
- 合并序列化;
- 对比 p99 与功能正确性。
版本边界与练习答案
Creator 2.4.x 的 dt、目标帧率与平台呈现受浏览器/原生调度影响。精确帧时间应结合平台工具。
- 60 FPS 的理论预算是多少? 约 16.67 ms 总帧预算。
- 为什么平均 FPS 不能发现规律尖峰? 少数长帧会被大量正常帧稀释,而玩家对长帧敏感。
- p99 表示什么? 约 99% 样本不超过该值,最慢约 1% 帧达到或超过附近范围。
- 优化目标为什么应写“p99 < X”而非“更流畅”? 前者可测、可回归、可判断是否达标。
十一、本课总结
text
FPS 是结果,Frame Time 是成本。
帧预算要看分布,不能只看平均值。
目标设备和刷新率决定实际预算。
持续超预算和偶发尖峰需要不同排查路径。十二、下一课预告
text
Lesson057|Creator Profiler 应该怎么看?