外观
静止画面口径、统一每秒 FPS 与 Excel 逐秒/逐帧明细
Owner 决策(2026-11-02)
- 静止页面与 PerfDog 一致:iOS 26 帧链首尾相接时,静止秒的 FPS 记 0,静止后首帧按真实间隔发布帧耗时;帧链断开时仍留空。
- FPS 统一为“逐帧事件每秒计数”,工作台、原生报告、Excel 共用;无逐帧数据时用 Graphics FPS 兜底并标注;平均 FPS 旁附“静止秒数占比”。
- Excel 在“概览”“详细数据”之后增加“逐秒数据”“逐帧数据”两页。
依据:PerfDog 帧统计口径对照、报告审视。两份 PerfDog 导出中,每秒 FPS 之和等于总帧数,静止秒为 0,长间隔帧按真实值记录。
公式
帧耗时(FrameUpdate)
- 本帧
FramePrevious精确等于上一条已解码帧的呈现时刻时,帧耗时 = 两者之差,不再设 200 ms 长间隔门(frameupdate-presentation-gap.v2)。帧链相接本身证明中间没有其他呈现。 - 断链(
FramePrevious不等于上一帧呈现时刻)仍为discontinuity、置空;首帧与来源/代际切换仍为warmup。 - iOS 17
FrameLifetime保持原规则(12 个刷新间隔门)。
实时每秒 FPS
- 设备时间水位
W:ActivityTrace 流中最近收到的任一条消息(含被白名单拒绝的消息)的设备 Mach 时间,换算为 ns。流按设备时间顺序到达,因此W之前不会再有新帧。 - 当
W − 最后一帧呈现时刻 ≥ 1 s:FPS = (W − 1 s, W] 内的呈现帧数,可为 0,质量valid(activitytrace-window-fps.watermark.v2)。 - 否则沿用原逐帧窗口计数;ActivityTrace 流整体沉默(水位不前进)超过 1 s 时仍为
stale-source,不补 0。
报告每秒 FPS 序列
- 以会话第一帧呈现时刻为 0 点,按 1 秒分桶统计呈现帧数,
F[k]为第 k 秒的帧数。 - 只采用完整且被证实的秒:桶完全落在“首帧 … 最后一帧”之间,且不跨越断链区间;断链覆盖的秒不计入(不当 0)。
- FPS 类统计(Avg/Median/Std/Var/≥18/≥25/MedRange/Drop)一律基于
F;Drop = #{F[k−1]−F[k]>8} ÷ (n−1) s × 3600。 - 静止秒数占比 =
#{F[k]=0} ÷ n × 100。 - 无逐帧数据(来源未选中)时退回产品 FPS 采样,公式版本标
graphics-fallback。
Excel 新增页
- 逐秒数据:每秒一行——时间(s)、FPS、帧数中 Jank/BigJank 次数、最大帧耗时、应用 CPU、整机 CPU、应用内存、应用上行/下行 KB/s、GPU、电池温度、标注、截图编号(若有)。
- 逐帧数据:每帧一行——序号、呈现时间(s)、帧耗时(ms)、是否 Jank/BigJank。
验收
- 单元:静止长间隔帧发布真实值;水位推进后的 0 FPS;断链秒不进入每秒序列;两份 PerfDog 导出的 FPS 类与每秒帧数复算一致。
- 真机 E4:新包会话中静止段 FPS 为 0、曲线不断;报告只有一个平均 FPS 且与逐秒页均值一致。
Implementation Update · 2026-11-02
| 文件 | 修改 |
|---|---|
display_performance.py | FrameUpdate 相接长间隔照常发布(v2,去掉 200 ms 门);ActivityTrace 过滤流记录设备时间水位(任意消息的 time,只读时间不留内容),适配器提供 device_watermark();投影器暴露最后呈现时刻与帧时基;8 字节数值后多带空字节时按零填充接受。 |
p95_frame_window.py | FrameUpdate 帧去掉 200 ms 上限,与解码器一致。 |
realtime.py | 实时快照:FrameUpdate 主来源健康、水位在 1.5 s 内刷新且越过最后一帧 ≥ 1 s 时,FPS 记 0(watermark-idle.v2);启动 ActivityTrace 时把适配器水位接入投影器。 |
frame_statistics_policy.py | 新增 PerSecondFrameCounter(首帧为 0 点整秒计数、断链秒排除、末秒不计、可断点恢复)与 PerfDog 形 FPS 统计映射。 |
frame_derivation.py、online_report.py、report_engine.py | 离线/在线帧汇总增加每秒帧数统计与静止占比;统计报告 FPS 类优先使用每秒帧数,无帧流时退回 FPS 采样。断点状态新增 perSecond,旧断点按 offline-required 拒绝。 |
Swift ReportDerivedStatistics / SessionReportView / ReportWorkbookPresentation | 原生报告概览 FPS 卡片显示与帧统计同一平均 FPS 并附“静止 x%”;Excel 概览用“平均FPS(每秒)+ 静止秒数占比”,有每秒统计时不再重复输出 FPS 采样分布组;概览分组合并。 |
Swift ReportWorkbookDetailSheets(新)/ PerfDogLayoutWorkbookWriter | Excel 新增“逐秒数据”(时间、FPS、帧耗时、应用/整机 CPU、内存、上下行、GPU、电池温度、标注、截图编号)与“逐帧数据”(序号、时间、帧耗时、去重 Jank/BigJank);逐帧页流式写出,上限 1,048,575 行。 |
Swift SessionReport | 读取逐帧记录时跳过无时间戳的解码拒绝行(此前一行即令整份逐帧记录丢弃,导致报告逐帧为空)。 |
偏差
- 原计划用 Graphics FPS = 0 佐证静止。实测 Graphics 在 App 静止时常为 1/3/5(整机口径),且上限 60(120 Hz 设备),不可靠,改为 ActivityTrace 设备时间水位。
- 逐秒页暂未包含“该秒 Jank/BigJank 次数、最大帧耗时”:逐帧记录是设备时钟,与会话秒的对齐锚点尚未落档,先在逐帧页按帧标注 Jank/BigJank,避免错位。
- Excel 数值本已按
0.00格式显示、时长为“秒”格式;审视中“未格式化”一条系读取原始值所致,撤回。
验证(E2)
- Python:FrameUpdate 10 项(含静止长间隔、水位 0 FPS 与无证据不补 0、空字节值)、PerfDog 对照 4 项(以 PerfDog 每秒帧数为输入时 FPS 类 8 项完全一致;真实帧序列平均/中位 FPS 分别在 1.5/1.0 以内)。
- Swift:报告/导出/工作台/基础 82 项通过;新增逐秒/逐帧页、无时间戳行跳过断言。
- 真实会话(240,约 86 秒)导出:逐秒 80 行、逐帧 3,056 行(Jank 28 / BigJank 15 标注),概览含总流量与静止占比。
- 全量回归:Swift 442 项(排除既有挂起的
UserReportIntegrationTests),唯一失败为概览分组合并后的旧组名断言,已改为检查概览“画面表现”与详细数据“帧统计”后通过;Python 3,144 项仅 16 项改动前已存在的原生扩展/sw1 环境失败。
Validation Update · 2026-11-02:内部包 241 构建与同址安装(E3)
- 来源:main
3fc4176a(含本批与同日报告导出/CPU/流量修正),Developer ID 签名构建 249.8 秒,来源门禁passed。 - 校验:冻结核心 107 个模块与 CLI 和源码一致,严格签名、DMG 与安装后文件树一致,安装后
probeready;启动器只剩/Applications/Perfowl.app。 - 产物:
MacApp/dist/Perfowl-241-macos-x86_64-internal.dmg,145,169,646 B,SHA-25618072b11899808e84679b5d196a5b06c9d2c9dd7a3fd29d863946fa93d3c31ac;240 安装包与回滚包在私有归档,回滚已核对。 - 未完成:公证暂缓(Gatekeeper exit 3);真机 E4 待 Owner 录含滑动与静止的会话。
Validation Update · 2026-11-02:241 真机复测与帧耗时调整(E4 / E2)
Owner 在 241 上录约 52 秒会话(滑动与静止交替)并导出 Excel(取消勾选截图):
- FPS:47 个采样中 22 秒为 0,曲线不断(E4)。
- 导出:只生成 Excel,无截图 ZIP;逐秒 47 行、逐帧 1,084 行(E4)。
- 解析:1,084 个帧事件 0 个
unsupported-schema,空字节值修正确认(E4)。 - 静止长帧:逐帧数据中 12 个 1.8–2.5 秒的相接长帧按真实值发布(E4)。
- 问题 1 · 帧耗时曲线仍断:没有新帧的秒不应有帧耗时值;且每秒只取采样时刻的最新一帧,长帧被同秒后续短帧覆盖(每秒最大只显示 634 ms)。Owner 决定:曲线跨“暂无新帧”连线、每秒取该秒最大帧耗时。已实现(E2):投影器记录上次发布以来最大有效帧耗时并在发布时清零;
DisplayMetricPresentation.bridgesFrameTimeGap仅对missing-no-new-frame/stale-device-source连线,工作台与报告曲线共用。 - 问题 2 · 静止占比两处不一致:报告 26.7%、Excel 46.8%。原因是整秒切分起点不同(该 App 静止时每 2.5 秒有约 0.27 秒、32 帧的小动画,常跨两个桶)。已改为 Swift 读取封存的
perSecondStaticPercent,与 Avg(FPS) 同一计数(E2)。 - 回归:Swift 全量 443 项 0 失败(排除既有挂起的
UserReportIntegrationTests);Python 3,145 项仅 16 项改动前已存在的原生扩展/sw1 环境失败。