ScreenTest.Pro 分数是怎样计算的
这是方法说明页。这里没有为了宣传而简化的内容;如果代码和本页说法不一致,那就是一个缺陷,欢迎告诉我们。(撰写时的基准测试版本:2.5.0。)
基准测试提出的问题
大多数图形基准会固定工作负载,再测量帧率。我们的做法刚好相反:固定帧时间目标,然后寻找设备能够稳定维持的最重工作负载。每个场景的输出是“最大稳定负载”,例如 840,000 个星云粒子,最终分数就以这些结果为基础。
为什么要反过来测?因为帧率会撞上上限。固定工作负载在高性能设备上很快就会达到屏幕刷新率,此后数字不再提供任何信息;真正有意思的问题是它原本还能多处理多少。寻找性能上限,让同一组场景从五年前的手机到桌面 GPU 都能产生有意义的结果。
实际运行的内容
标准配置包括五个标志性场景和一个对照场景,分别施加不同类型的压力:
| 场景 | 负载单位 | 压力方向 |
|---|---|---|
| 星云漂移 | GPU 粒子 | 着色器粒子更新 + 填充率 |
| 霓虹都市 | 实例 | 实例化几何吞吐量 |
| 液态铬 | 内部分辨率 | 光线步进 / 片段处理成本 |
| 星落瀑布 | CPU 粒子 | JavaScript 物理 + 缓冲区上传 |
| 超新星爆发 | 爆发粒子 | 混合型终场 |
| Canvas 2D 基础圆形 | 2D 圆形 | 受 CPU 限制的对照组,不使用 WebGL |
渲染的像素比最高限制为 1.5×,内部尺寸也有上限,因此 5K 显示器和 1080p 笔记本面对的像素数量大致可比。随机数使用固定种子,模拟步长也保持不变,所以同一版本的每次运行都会完成相同工作。
稳定性怎样判定
引擎从不去读 FPS 计数器。它从浏览器的动画时钟里把帧时间收集进大约 0.7–1.6 秒的窗口,然后对每个窗口提出两个要求:
- 超过通过阈值的帧不得多于 5%——自适应同步显示器为 17.5 毫秒;固定刷新率显示器则取预算之内的工作永远无法落上的第一个垂直同步时隙(60 Hz 为 25.0 毫秒,120 Hz 为 20.8 毫秒);
- 截尾均值——丢掉最差的 2% 帧——必须守在 17.5 毫秒之内,这是每一类显示器共用的同一个持续预算。
统计「超标帧数」而不是把它们平均掉,会让一次垃圾回收停顿只算作一帧坏帧,而不是一个失败的窗口;截尾均值则是持续吞吐的界限,而且从 2.3.0 起,它在所有地方都是同一个数——真实的垂直同步时间戳比 2.2.0 假设的固定刷新率模型更乱,而旧的、更宽松的截尾容差,一直在悄悄给固定刷新率显示器一个比自适应显示器宽松约 9% 的预算。
为什么超标阈值仍然要顾及你的显示器:固定的 60 Hz 显示器只能在 16.7 毫秒或 33.3 毫秒时呈现一帧,中间没有第三种可能,而自适应同步的笔记本屏幕会把 18 毫秒的一帧原样交还为 18 毫秒。因此在第一个场景之前,一次约 600 毫秒的节奏探测会观察你的屏幕如何交还帧,掉帧阈值随之调整,让「稳住 60 fps」在每一类屏幕上都是同一句话。接着,同一次探测还会花约 2 秒检查机器本身在零负载下是否已经卡顿——忙碌的机器会在三分钟的测试之前就收到警告,而不是测完才意外得到一个下限。如果探测发现屏幕被限制在约 50 Hz 以下——低电量模式、一根 30 Hz 的线缆——这次测试会拒绝开始,而不是隔着一根 30 Hz 的吸管给 GPU 打分。
16.67 毫秒的预算本身不随刷新率变化。120 Hz 的屏幕在轻松时每 8.3 毫秒送出一帧,但通过只要求守在「每秒 60 帧」的预算之内——高刷新率硬件被平等地测量,不会因为多显示了几帧而被惩罚。
搜索过程
每个场景都走一台状态机:preflight → baseline → warmup → ramp → bracket → confirm → anchor。
- Baseline 测量一个已知的轻负载——它成为热基准。
- Ramp 每通过一个窗口就把负载乘以约 1.5×,直到某个窗口失败。
- Bracket 在最后一次通过与第一次失败之间二分搜索,直到区间收窄到约 6%。
- Confirm 要求候选负载上出现连续三个通过的窗口,总预算为四个。开头的一个坏窗口还能挽回;连续两次失败,或者用完预算仍凑不出连续三次,就否决这一档,候选负载下调一级。
- Anchor 重跑基准负载。如果它现在比开始时慢了 15% 以上,说明设备在我们脚下变了——几乎总是热降频——这个场景会被标记为漂移,而不是假装什么都没发生。变慢的幅度也会被记录下来,而不只是记录这件事发生过。
每次负载变化之后的几帧会在统计恢复之前被丢弃:改变负载会重新分配 GPU 缓冲区和渲染目标,浏览器把这笔账记成一帧超长帧。那次卡顿属于过渡,不属于工作负载——2.1.0 把它算了进去,而在那个每一档都要重设渲染目标尺寸的场景里,它让搜索远远停在硬件真实能力之下。
每个阶段都有硬性时限(每场景 30 秒,每次测试 200 秒,只有在确实排入重试时才会短暂延长)。测量前的预热也会自适应:首次访问时要编译着色器,预检阶段会在帧时间仍显示编译卡顿时继续等待——最多 4 秒——让负载阶梯从一台已经热身的机器开始,而不是淹没在卡顿里。把标签页切到后台会暂停测量并作废当前窗口;调整窗口大小会重建场景并重新预热。另外,如果某个场景什么都没测到,而同一次测试里后面的场景已经恢复正常,它会在末尾恰好追加一次测量——前提是一次新的静默检查表明机器已经安定下来。没有任何数据是捏造的;场景只是重新跑一遍,重试的结果会替换那个空结果。状态机里没有任何一条路径会卡死。
从负载换算为分数
计分是对已记录结果的纯函数——你完全可以手算一遍:
- 每个场景的最大稳定负载除以该场景的参照负载(每个版本冻结的校准常数;粗略地说,一台 2025 年的可靠台式机大约落在 1.0)。
- 比值下限为 0.05,上限则取该场景自己的梯子天花板——这个场景在物理上能摆出的最重负载,大约设在我们测过的最快硬件的 10 倍之外——这样分数只能在测量本身结束的地方停止上升。(2.1.0 把每个场景都卡在统一的 20×;一台 M4 Max 有两个场景顶住这个上限、第三个顶住测量上限,这正是彼此无关的苹果旗舰不断报出几乎相同总分的原因。)
- 经过限制的比值汇入一个加权几何平均——五个 Signature 场景承担大部分权重,Canvas 2D 对照占 10%。取几何平均是因为它奖励均衡:样样都好的机器胜过一项传奇、另一项糟糕的机器。
- 乘以 1000,套用稳定性扣分——每个被标记的场景扣 3%,无论标记是漂移还是未确认的档位,总扣分最多 10%——然后取整。
等级是每个版本的固定阈值,而由于分数是参照机器的 1000 倍,每个等级其实都是它的倍数:入门低于 1000,均衡到 2999,快速到 8999,极致在其之上——也就是不足 1×、至多 3×、至多 9×,再往上就是超出。它们的存在只是给数字一个把手;各场景的分项才是真正的信息。
哪些情况会让比较失效
- 不同基准测试版本之间绝不比较。 改动一个着色器就改变了工作负载;结果单上的版本号是分数的一部分。
- 兼容模式测试(没有 WebGL2)只使用 Classic 场景,并在自己的计分空间里计分,且有明确标注。
- 降频的测试——每个场景都测得了,但设备变热变慢,或者守住了一个能通过、却没能连续三个窗口得到确认的档位——是关于持续性能的真实测量,因此正常计分,并带着行内可见的标记进入最高得分榜。绝大多数手机测试都落在这里;那是物理规律,不是故障。
- 降级的测试——某个场景没有产出可用结果——是下限而非峰值,所以不进榜,也不计入公开的中位数。它们仍会被保留,结果单也会说明是哪个场景、为什么。
- 两种标记都会随着这次测试进入我们的匿名数据集,因此任何公开数字都能把它们区分开。
连续测试会波动多少
在接着电源、处于空闲的机器上,连续几次测试通常相差几个百分点——确认阶段对连续窗口的要求正是这份稳定的来源。用电池、开省电模式或者机身发热时,请预期更大的离散,并且很可能带上降频标记;那不是基准测试情绪化,而是你的设备此刻确实只能给出这么多。测试前让它凉下来并接上电源,是取得有代表性数字最有效的一件事。浏览器基准测试能测到什么、测不到什么讲的是拿到这个数字之后该怎么用它。