《移动游戏产品的架构·客户端与移动运行时》9.3 功耗与发热的验收方法

手机发热后降频,平均帧率会掩盖最后十分钟的塌陷。本节以《潮汐消消》为主案例,给出长时间挂机与连续对局两套场景、四档温控曲线、帧率保持率与降频起点指标,并明确机内传感器只能建模:验收必须来自真机证据,模型输出不能签字,同时给出一套可复现的真机验收流程。

事故:平均 29fps 掩盖了十分钟后的塌陷

《潮汐消消》夏季版本上线后,案例设定中有玩家反馈「挂在家园二十分钟,手机烫手,画面越来越卡」。性能看板显示该会话平均 29fps,距离 30fps 只差一点,看起来不像故障。

把会话按分钟拆开后,问题很清楚:前 10 分钟稳定在 30fps,第 15 分钟开始掉到 26fps,最后 5 分钟只有 22–24fps。平均值被前半段拉高,玩家的体感却集中在后半段。

另一个团队更典型:在模拟环境里跑 10 分钟温控曲线,曲线平稳,就签了验收;真机连续挂机 30 分钟才进入持续降频。10 分钟只覆盖了升温阶段,没有覆盖热平衡后的稳态。

本节要解决的不是「怎么让手机不热」,而是怎么验收才不会被平均值和短时曲线骗过去。结论先说:至少跑挂机与连续对局两种场景,用保持率、降频起点与温控状态判定;机内传感器只能用来建模与写脚本,签字必须来自真机证据。

两种场景:长时间挂机与连续对局

单个场景无法覆盖发热问题。挂机看持续热平衡,对局看峰值与交互负载,两者的失败模式不同。

场景案例设定时长负载特征能暴露的问题常见误判
长时间挂机30 分钟家园动画、低频输入、屏幕常亮热平衡后的持续降频、电池温升只看前 5 分钟,曲线还没起来
连续对局10 分钟连续 90 秒关卡、结算、特效峰值温升、帧率保持、输入延迟只看平均帧率,忽略后半段

消消单局约 90 秒,连续对局用多局拼接,覆盖关卡开始、结算与家园切换;方舟单场 8–12 分钟,对局场景本身就是长时负载,但仍要补挂机场景,因为玩家在主界面停留的时间往往更长。

两个场景都要跑,不是二选一。挂机合格不代表对局合格,对局合格也不代表挂机半小时后不降频。验收报告必须分别给出结论,不能合并成一个总分。

温控档位与降频曲线

温控策略把设备状态映射为帧率上限、渲染比例上限与特效开关。下表是案例模型的比较参数,不是厂商标准;项目要按目标机型与外壳结构重新标定。

状态皮肤温度阈值帧率上限渲染比例上限非必要特效动作
nominal<40°C60fps1.00保留正常
warm≥40°C60fps0.85保留降分辨率,继续观察
hot≥44°C30fps0.75关闭降帧率与特效
critical≥48°C30fps0.65关闭最低画质,提示或限制入口

状态升级要立即执行,降温要带回滞:温度降到阈值以下 2°C,并连续 3 个采样保持,才允许降一级。没有回滞,温度在 44°C 附近抖动时,帧率会在 30/60 之间反复跳。

平台给出的热状态与温度模型可能不一致,策略取两者中更严重的一个。温度是连续值,平台状态是离散值;只信温度会漏掉厂商的保守限频,只信状态会丢掉细粒度趋势。

对消消这类默认 30fps 的产品,hot 状态并不会继续降帧率,因为已经在 30fps;它的温控手段是降分辨率与关闭特效。这意味着不能用帧率保持率单独判断消消的温控是否生效,还要看渲染比例、特效状态与温控状态时长。

状态机:升级、回滞与冷却

温控不是一个查表函数,而是一个带记忆的状态机。同一温度下,从 nominal 升到 warm 与从 hot 降到 warm 的条件不同。

事件条件动作记录
升温跨阈值温度达到下一档,或平台状态更严重立即升级,可跨多档温度、平台状态、目标档
降温温度低于当前档阈值 2°C,连续 3 个采样只降一档连续样本数、退出温度
平台状态恶化平台声明比温度模型更严重立即升级平台状态与温度
温度抖动在阈值附近来回,未满足退出条件保持当前档抖动区间与停留时长

降温一次只降一档,不允许从 critical 直接跳回 nominal。跨档跳回会让帧率瞬间翻倍,温度很快再次升高,形成更大的振荡;逐档退出给系统一个观察窗口。

状态机还要记录「为什么升级」。只有当前温度,无法区分是环境温度升高、充电发热还是玩法负载变化;把平台状态、充电状态与场景 id 一起记下来,才能定位热源。

充电与握持:先控制变量

温控验收最容易失效的地方不是脚本,而是条件不统一。同一台设备,充电时与不充电时的温度曲线可能差出一个档位。

变量影响验收时的处理
是否充电充电本身发热,叠加游戏负载充电与不充电各跑一轮,分别出结论
是否戴壳外壳隔热,皮肤温度与内部温度背离记录壳型,至少覆盖官方壳与裸机
环境温度夏季与空调房差异明显记录室温,目标区间内对比
屏幕亮度屏幕与背光发热固定亮度或固定百分比
网络状态弱网重试增加 CPU 与射频负载固定网络类型与信号强度

这些变量不固定,报告之间不可比。验收记录里至少要写清设备、系统、构建号、场景、室温、充电状态与壳型;缺一项,结论就只能当参考,不能签字。

降频曲线怎么标定

阈值不是拍脑袋定的,也不是从别的项目复制的。标定要先用固定负载找到「温度—帧率」的拐点,再给拐点留安全余量。

步骤做法产出
1. 固定负载用固定场景、固定时长、固定画质跑多轮温度与帧率原始曲线
2. 找拐点找帧率开始持续下降的温度区间初步阈值
3. 留余量在拐点前 2–3°C 设降档线正式阈值
4. 多机验证至少 3 台同类机型复跑阈值是否稳定
5. 周期复核系统升级、外壳变化、季节变化后重跑阈值版本

拐点之前的降档是「预防」,拐点之后的降档是「补救」。预防降档能保住帧率稳定性,但会提前牺牲画质;补救降档画质保得久,但玩家已经先看到卡顿。案例模型选择在拐点前降档,并用回滞避免频繁切换。

阈值要版本化。系统升级可能改变调度策略,新外壳可能改变散热;没有版本号的阈值,半年后没人说得清当前值是怎么来的。

指标:不要用平均值签字

验收指标要能定位「什么时候开始变差」和「差了多少」,而不只是「整体平均是多少」。

指标口径作用案例阈值
最高皮肤温度全程采样最大值判断是否烫手43°C
最高电池温度全程采样最大值判断电池安全与充电策略45°C
帧率保持率最后 20% 窗口中位数 ÷ 最前 20% 窗口中位数判断长时衰减0.8
降频起点首次进入非 nominal 的时间判断是否过早降频不早于总时长 50%
温控状态时长各状态累计毫秒判断是否长期停留在 hothot 不超过 30%
帧时间 p95按窗口统计判断卡顿尾部不超过预算 1.2 倍

平均帧率不在表里。平均 29fps 可能是 30fps 跑了 25 分钟、22fps 跑了 5 分钟,也可能是全程 29fps;前者是热降频,后者是性能不足,处理方式完全不同。

保持率也要看窗口。用最后 5 分钟对比最前 5 分钟,比全程平均更接近玩家体感;窗口太短会被单次结算污染,窗口太长又会掩盖中期塌陷。案例模型取首尾各 20%,项目可以按场景时长调整。

传感器只能建模,验收必须真机

机内温度、平台热状态、帧率与渲染比例都通过 Host 接口读取。模型 Host 用一条合成升温曲线驱动状态机,source 固定为 model;真机 Host 的 source 为 device,并携带设备 id、系统版本与构建号。

模型能做什么:

  • 验证验收脚本会不会在 30 分钟内正确采样、分窗口与计算保持率。
  • 验证温控状态机的升级、回滞、降级顺序与边界。
  • 验证报告字段、失败原因与签字规则。
  • 在没有真机排队时做回归,防止脚本改坏。

模型不能做什么:

  • 不能代表某台设备的真实发热曲线。外壳材质、握持方式、环境温度、是否充电、是否戴壳,都会改变结果。
  • 不能代表厂商的调度与限频策略。同样的温度,不同厂商可能给出不同的帧率。
  • 不能作为验收证据。模型跑出的 pass 只能说明脚本正确,不能说明设备合格。

因此验收函数把 model 证据直接拒绝:即使报告结论是 pass,签字也会返回 model-source-cannot-signoff。真机证据还必须满足设备 id 匹配、系统版本与构建号非空、报告结论为 pass。

验收流程

一次可复现的温控验收至少包含下面几步:

  1. 选目标机型:消消优先覆盖 35% 的低端机,方舟覆盖默认 60fps 的中高端机与低档 30fps 的边界机。
  2. 固定环境:室温、是否充电、是否戴壳、屏幕亮度、音量与网络状态都要记录;这些条件不固定,结果不可比。
  3. 跑挂机 30 分钟与连续对局 10 分钟,采样间隔 1 秒,保留原始样本而不是只留汇总。
  4. 计算首尾窗口保持率、最高温度、降频起点与各状态时长,逐项对照阈值。
  5. 每类机型至少 3 台,覆盖充电与不充电两种条件;只跑一台不能代表批次。
  6. 签字时附设备 id、系统版本、构建号、场景 id 与原始样本哈希;模型报告单独存放,不进入验收结论。

第 6 步是流程里最容易被省略的一步。没有构建号与设备 id,报告无法复现;没有原始样本,无法解释「为什么这次 pass、上次 fail」。

可运行模型:thermal-demo.ts

完整实现在 src/runtime/thermal.ts,包含温控状态机、模型 Host、两套场景、验收报告与签字规则。下面的 demo 用连续对局场景跑一遍模型,再尝试签字。

// file: fixtures/thermal-demo.ts
// tier: L1
// verify: node fixtures/thermal-demo.ts

import {
  BATTLE_PROFILE,
  BATTLE_SCENARIO,
  modelThermalHost,
  runAcceptance,
  signOff,
} from "../src/runtime/thermal.ts";

const host = modelThermalHost(BATTLE_PROFILE, BATTLE_SCENARIO.durationMs, BATTLE_SCENARIO.sampleMs);
const report = runAcceptance(host, BATTLE_SCENARIO);
console.log(JSON.stringify({
  scenario: report.scenarioLabel,
  source: report.source,
  samples: report.samples,
  maxSkinC: report.maxSkinC,
  maxBatteryTempC: report.maxBatteryTempC,
  fpsRetention: report.fpsRetention,
  degradeOnsetMs: report.degradeOnsetMs,
  finalState: report.finalState,
  minRenderScale: report.minRenderScale,
  verdict: report.verdict,
  reasons: report.reasons,
}, null, 2));

const sign = signOff({
  report,
  deviceId: report.deviceId,
  osVersion: "Android 15",
  buildId: "1.2.3",
});
console.log(JSON.stringify({ accepted: sign.accepted, reasons: sign.reasons }));

运行 node fixtures/thermal-demo.ts,输出可以读成一张验收失败记录:

字段输出含义
sourcemodel这是模型,不是设备证据
samples60010 分钟、每秒一个样本
maxSkinC / maxBatteryTempC44 / 46超过案例阈值 43 / 45
fpsRetention1消消锁 30fps,降频不体现在帧率上
degradeOnsetMs117000约第 2 分钟进入 warm,早于总时长 50%
finalState / minRenderScalehot / 0.75已降分辨率并关闭特效
verdictfail温度与降频起点均不达标

最后一行签字结果是 accepted: false,原因首先是 model-source-cannot-signoff,其次才是报告里的温度与降频起点。这个顺序很重要:模型报告即使各项都合格,也不能签字;模型的作用是证明验收脚本能跑,不是证明设备能过关。

配套测试 test/runtime/thermal.test.ts 有 12 条路径,覆盖阈值映射、平台状态升级、回滞降温、两套场景、模型升温、自定义阈值、模型证据拒绝、设备信息匹配与失败报告透传。运行命令是 node --test test/runtime/thermal.test.ts。

模型里的温度、时长与阈值都是比较参数,不是任何一台真机的测量值。这里是模型,不是真机测量结果。

双案例:方舟的 60fps 与更长战斗

方舟默认 60fps,hot 状态会把帧率上限压到 30fps,保持率会直接体现为 0.5;它的验收指标比消消更敏感,也更容易被平均帧率掩盖。

方舟单场 8–12 分钟,连续对局场景本身就更长;它的温控验收要覆盖完整战斗循环与赛季结算,不能照搬消消的 10 分钟窗口与 43°C 阈值。

与相邻章节的关系

9.1 的帧策略决定运行期档位,9.2 的阶段切片决定一帧内谁先让路,本节决定长时间运行后如何降频与验收。机型矩阵与兼容性测试可以参考兼容性设备矩阵 ,埋点字段与指标口径可以参考埋点规范 ,低端机整体降级取舍可以参考低端机降级 。

温控状态也要进入崩溃与卡顿的联合分析:长时间 hot 状态下的长帧,可能同时是热降频与主线程任务堆积,单看一个指标无法归因。

小结

  • 平均帧率会掩盖后段塌陷;验收用首尾窗口保持率、最高温度、降频起点与状态时长。
  • 挂机与连续对局两种场景都要跑,前者看热平衡,后者看峰值与交互负载。
  • 温控状态升级立即执行,降温带回滞;平台状态与温度模型取更严重者。
  • 传感器与模型只能验证脚本与状态机,不能作为验收证据;签字必须来自真机。
  • 验收报告要能复现:设备 id、系统版本、构建号、场景 id 与原始样本缺一不可。

阅读导航:上一节:9.2 每帧预算与主线程任务切片 · 下一节:10.1 存档格式与迁移 。

常见错误与边界

  • 用平均帧率签字。平均 29fps 可能由前 25 分钟 30fps 与后 5 分钟 22fps 拼成;必须看首尾窗口保持率与降频起点。
  • 只跑短时曲线。10 分钟只能覆盖升温阶段,热平衡可能在 20 分钟后才出现;挂机场景至少 30 分钟。
  • 只跑一个场景。挂机合格不代表对局合格,对局合格也不代表长时间停留后不降频;两套场景分别出结论。
  • 把模型输出当成设备数据。模型 Host 的 source 是 model,签字函数必须拒绝;模型只验证脚本与状态机。
  • 温控阈值照搬其他项目。外壳、散热、充电策略不同,43°C 与 30% 状态时长只是案例参数,必须按目标机型重新标定。
  • 报告只留汇总不留原始样本。没有逐秒温度与帧率,事后无法判断是降频、卡顿还是采样错误;原始样本要随报告归档。
  • 用一台设备代表一个批次。同型号设备的散热与电池状态也有差异;每类至少 3 台,并覆盖充电与不充电两种条件。
  • 只看最高温度不看状态时长。短时冲到 45°C 与长期停在 hot 是两种问题;状态时长和降频起点要一起看。
  • 验收环境随季节漂移。夏季室温与空调房差异会改变曲线;记录室温并固定测试时段,否则历史报告不可比。
  • 边界:本节不测量真实功耗与发热,也不替代厂商热管理。所有温度与时长均为模型比较参数,真机验收要固定环境、设备与构建号,并保留原始样本。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《移动游戏产品的架构·商业化与发行工程》附录 C 商业化与发行评审清单
  2. 《移动游戏产品的架构·商业化与发行工程》附录 B 双案例商业化事实表
  3. 《移动游戏产品的架构·商业化与发行工程》附录 A 商业化与发行术语速查