外观
FrameTime:帧间隔
FrameTime 是 Display Performance 最核心的逐帧事实之一:它描述相邻有效 Display Presentation 之间的时间间隔。它不是 CPU 渲染耗时、GPU Render Time,也不能用 1000 / FPS 近似替代。
2026-10-16 · 静态画面的空值与断链分开判断
静止可能不提交新帧,因此没有新的帧间隔;已发布的单帧数值不会反复填充,后续可为 missing-no-new-frame。不填 0、不复用 16.7 ms,也不用 1000/FPS 制造连续曲线。stale-source / stale-device-source 说明最后帧无法用作当前值,不独立证明连接断开或确无新帧。判断时同时核对 ActivityTrace 的其他回调、帧白名单计数、队列与前台身份;这些各自有范围,不是设备端完整性证明。历史 Build73 管线专项 E4 成果保留,当前静态场景另测,见 本次诊断。
2026-10-16 · 内部177受控静态/动画专项
静态帧显示专项 · 2026-10-16(E4):内部177/自有Lab90.21.7完成五个全新正常UI轮,每轮静止→动画→静止→动画→静止。共5290条实际FrameLifetime、658个发布样本;两动画段FPS/FrameTime最小有值率100.0%、恢复最大573.0ms,五轮中文静态提示均实际可见(每轮三次预定取样、保留全部结果)。本地帧全消费、零drop/undrained、正常Stop durable成立。静止仍可空,不补零/旧值;不是独立物理精度校准。历史Build73成果保留,本专项不改全指标已验收数量;其他指标不自动开测。结果与范围。
2026-11-02 · 每秒帧耗时取“该秒最大值”,曲线跨静止连线
- 每秒发布的帧耗时 = 上次发布以来所有新帧中最大的帧耗时(
activitytrace-presentation-gap.max-since-last-sample.v2)。静止后的长帧、卡顿帧不会被同一秒内后续的短帧覆盖,曲线上可见尖峰,与 PerfDog 逐帧曲线观感一致。平均帧耗时、Jank、P95 等仍按逐帧计算。 - 没有新帧的秒(
missing-no-new-frame、静止时的stale-device-source)不发布数值,读数显示“暂无新帧”,Excel 留空;工作台与报告曲线在这些点直接连线跨过,不补任何值。未确认的空缺(source-gap、stale-source)仍断开。方案与实现
2026-11-02 · iOS 26 静止长间隔按真实值发布(`frameupdate-presentation-gap.v2`)
Owner 决定与 PerfDog 一致:FrameUpdate 帧链首尾相接(FramePrevious 精确等于上一帧呈现时刻)时,静止后第一帧的长间隔(如 3.8 秒)就是一帧真实的帧耗时,照常发布,不再设 200 ms 门;断链仍为 discontinuity 置空。iOS 17 FrameLifetime 规则不变。另修正 args1 最高字节非 0 时 8 字节值带结尾空字节导致的解析失败(推断自真机 9/2982 失败样本,待新包确认)。方案与实现
2026-11-02 · iOS 26 改用 FrameUpdate(首版)
iOS 26 不再发出 FrameLifetime,帧事实并入 backboardd 的 com.apple.coreanimation / IOMFBServer(.Stalls) / FrameUpdate。本页公式含义不变:呈现时刻(i) = base_mct + FrameLifetimeInterval,上一帧呈现时刻 = FramePrevious,FrameTime 为两者之差。设备 A(iOS 26、120 Hz)150 秒实测 1,045 对相邻事件 100% 精确相接,滑动时帧间隔中位 8.34 ms。FrameUpdate 没有刷新间隔字段:长间隔门改为固定 200 ms(60 Hz 下 12 个刷新间隔),该来源下刷新率保持缺失,不从帧间隔反推。证据等级:内部 239 真机 E4 通过(104 秒、9,392 帧,与 sysmontap FPS 相差 0.7%,单设备单 App、120 Hz)。诊断与方案
当前状态
当前实现:ActivityTrace / CoreAnimation FrameLifetime(iOS 17 等);iOS 26 为 FrameUpdate,见上。
Source 证据:iOS 17 真机 E4,prev_frame 连续链复测 59/59 匹配。
产品实现:历史Build73管线专项E4保留;当前内部177静态/动画恢复专项E4通过,范围见上。 历史实现:CoreProfile 0x31800318 Presented Frame 仅保留 legacy replay / 对照背景。
1. 定义 / 概念
一段动画最终出现在屏幕上,可以抽象成连续的 presentation:
text
P0 ---- P1 ---- P2 -------- P3
16ms 16ms 48msPerfowl 的 FrameTime 就是相邻有效 presentation 的时间差。长 FrameTime 表示两个新画面之间等待更久,但它本身还不能告诉你究竟是 CPU、GPU、I/O、锁还是系统调度造成。
2. Scope 与 Unit / 单位
| 字段 | 合同 |
|---|---|
| metricID | ios.frame.time |
| Scope | foreground-device |
| Unit / 单位 | milliseconds |
| source | activitytrace-framelifetime |
| definitionVersion | activitytrace-framelifetime-presentation-gap.v1 |
FrameLifetime 是 compositor 事实,不是目标 PID 私有事件,所以只在目标 App 前台时发布。
3. iOS Display Presentation 原理
图:FrameTime 测量相邻有效 Presentation 的间隔,位于整条显示链的末端。
FrameTime 站在链路末端观察“最终新画面什么时候出现”。因此它和 App 主线程耗时、GPU render duration 可以相关,但不等价。
4. 系统服务 / 底层服务与采集调用
当前链路:
text
com.apple.instruments.server.services.activitytracetap
→ onlySignposts
→ subsystem = com.apple.coreanimation
→ category = IOMFBServer
→ name = FrameLifetime采集命令/实现由 ActivityTraceFrameLifetimeRawAdapter 打开 ActivityTrace DTX stream;FrameLifetimeDecoder 解析字段并做 schema / continuity gate;DisplayPerformanceProjector 再把有效事件发布成实时与报告指标。
5. 原始字段
真实 POC 中稳定观察到:
text
begin_time
end_time
prev_frame
refresh_interval
frame_seed
buffer_count
skip_request
display_id
surface_id第一版 FrameTime 正式计算只依赖:
text
end_time
prev_frame
machTimebaseNumer
machTimebaseDenombuffer_count、skip_request、frame_seed、surface_id 暂时保留为研究 metadata,不擅自解释成用户指标。
6. 公式 / 精确计算
text
FrameTimeMs =
(end_time - prev_frame)
× machTimebaseNumer
÷ machTimebaseDenom
÷ 1_000_000注意顺序:先对 tick 差做 timebase 换算,再得到纳秒/毫秒。不要分别转换两个绝对 tick 后再做差,也不要硬编码某台设备的 numer / denom。
等价演算示例
本次 iOS 17 POC 的设备 timebase 为:
text
numer = 125
denom = 3连续链的中位 FrameTime 约为 49.926 ms。对应的等价 tick 差是:
text
deltaTicks = 1,198,224
1,198,224 × 125 / 3
= 49,926,000 ns
= 49.926 ms这个 deltaTicks 是根据已观测中位值整理出的等价演算样例,不是复制某条原始事件的绝对 tick;真实计算始终直接使用事件自身 end_time - prev_frame。
7. prev_frame 为什么是关键连续性证据
3 秒真机复测:
text
FrameLifetime rows 60
adjacent pairs 59
prev_frame == previous.end_time 59
mismatch 0因此两种算法在连续样本中一致:
text
self-contained:
end_time[n] - prev_frame[n]
chain-derived:
end_time[n] - end_time[n-1]Perfowl 使用事件自身的 prev_frame 作为主要前驱关系,同时用相邻链一致性做质量检查。
8. 为什么第一帧不能无条件计算
刚订阅 source 时,第一条 FrameLifetime 的 prev_frame 可能指向采集开始之前的 presentation。类似问题还会发生在:
text
source start
sourceEpoch change
reconnectGeneration change
foreground resume
display_id switch因此这些边界后的第一帧只建立 anchor,quality 进入 warmup;只有后续连续链成立才发布 FrameTime。
9. Quality / missing / discontinuity
valid:前台、display identity、timebase、timestamp ordering 与连续链都成立;warmup:刚启动或刚发生边界,只建立 anchor;discontinuity:prev_frame与前一有效end_time不一致,跳过跨 gap 的 FrameTime;not-foreground:目标 App 不在前台;unsupported-schema:FrameLifetime 必填字段缺失或类型无法解析;missing-no-new-frame:当前 publisher tick 没有新的有效 FrameTime,不重复上一次值。
FrameTime 是 one-shot event freshness:没有新帧时不能重复旧 FrameTime。
2026-10-04 · 单点数值与当前质量必须同源
最新呈现事件为 warmup、source-gap、discontinuity 或 unsupported-schema 时,单点数值与锚点清空,输出 null 与真实质量,禁止把上一有效值配上本次无效标签。新有效帧才重新发布一次。滚动P95/Jitter保留其有效历史,仍需至少5个有效样本;不把单点缺值误读为滚动窗口必须全空。
每秒都有采样行不代表每项都有新事件。首帧暖机、来源断链和没有新帧都可以合法缺值;严格“所有行有效100%”把这些行也计入分母,就不应承诺全场景达到。修复质量错配不改变 tick差公式、前台scope或缺源语义。本批方案与验证
10. 证据与适用范围
当前已确认的 iOS 17 source 证据:
text
5s ActivityTrace throughput ≈ 0.03696 MiB/s
FrameLifetime rows = 90
3s continuity = 59/59, mismatch 0
median FrameTime ≈ 49.926 ms(当前测试画面)
refresh interval ≈ 16.666 ms这些结果证明 source 和公式在该设备/系统/场景成立。iOS 15/16/18、长稳、前后台、多 Display、ProMotion 与完整产品链仍需要独立 E4。
11. 如何分析 FrameTime
不要只盯一个平均值。推荐观察:
text
FrameTime timeline
+ P50 / P95 / P99 / Max
+ Refresh Rate
+ Jitter
+ Jank / Stutter出现 spike 后再把同一时间窗对齐 APP CPU、GPU、Disk/Network、Thermal 与 Screenshot。FrameTime 只负责证明“画面等待变长”,根因需要更深证据。
11.1 异常之后的局部链恢复(2026-10-04)
长区间超过12刷新间隔仍输出null/source-gap,这个保守标签不证明必为丢源;不以调整阈值达成100%。异常帧的实际end可作为后一帧的局部锚点:来源/重连/目标代际、显示与前台边界不变,后一帧prev精确等于该end,时间正向、refresh合法且本帧未触发长区间门时,当前局部间隔按整数ticks公式发布;不再无条件额外warmup 。原异常区间仍为空,连续异常仍分别拒绝,身份/前台边界仍只建anchor。此变化修正可证局部区间的重复丢弃,不承诺来源完整、所有静态页都有新帧或已通过真机校准。算法版本仍为presentation-gap.v1的公式,质量恢复策略为local-interval-recovery.v1;来源:54项交付方案。
12. 常见误区
FrameTime = 1000 / FPS:错误;一个是事件间隔,一个是窗口吞吐;FrameTime = GPU Render Time:错误;它描述最终 presentation 间隔;FrameTime > RefreshInterval就是 Apple Hitch:错误;需要 Expected/Actual Frame Lifetime 语义闭环;- 跨两个 refresh interval 就等于掉 1 帧:错误;应用可能主动低频更新。
13. 历史 CoreProfile 路线
第一代 FrameTime 从 CoreProfile 0x31800318 Presented Frame 时间戳得到真实逐帧间隔,但长期 transport 会携带大量无关 KPerf/KDebug 数据。真机 exact-filter 仍约 18.76 MiB/s,而 ActivityTrace FrameLifetime 约 0.03696 MiB/s,因此当前新 Session 已退出长期 CoreProfile Frame 主链路。
完整实验史见 Display Performance 技术演进与实验。
14. 版本历史
| 版本 | 日期 | 修订要点 |
|---|---|---|
| 知识增补(公式不变) | 2026-11-03 | 记录 FrameUpdate 其余字段的解码方法与“长帧间隔 ≠ 卡顿”的判别方法(E3 研究,未进入公式)。可行性研究。 |
| frameupdate-presentation-gap.v1 | 2026-11-02 | iOS 26 FrameUpdate 映射为同一呈现间隔公式;无刷新间隔时长间隔门为固定 200 ms,刷新率缺失。239 真机 E4 通过。诊断与方案。 |
| independent-frame.v1(内部候选) | 2026-10-29 | 独立需求、AT资格/串行CP租约、实时与离线分段账本;默认新独立CP仍待资源/E4准入,不改两路公式或正式验收。实施边界。 |
| current-display-device-lag-lower.v1 | 2026-10-04 | 同钟过期下界>2s置空/stale-device-source,数值公式不改;22匹配/全量E2与b116安装E3通过,新真机E4未测、54项100%仍partial。G7。 |
| 局部恢复(当前) | 2026-10-04 | 不把上一异常自动传染给有完整局部链的下一帧;长区间仍保守null。来源:54项方案。 |
| 单点质量修正 | 2026-10-04 | 最新无效帧清空单点缓存;不沿用数值、不清空滚动有效历史。来源:R12。 |
| v1(公式不变) | 2026-09-17 | 从 Frame Experience 拆出独立 FrameTime Canonical;按当前 ActivityTrace Registry 固化原始字段、Mach timebase、prev_frame 连续性与 one-shot quality。 |
继续阅读:P95 FrameTime · Refresh Rate · Jank、Stutter 与帧体验。
2026-10-04 · FrameLifetime当前窗与解析开销
持续收到host事件并不证明设备事件当前:固定五轮140s捕获仅处理20.669–22.166s的设备帧时轴,原host-silence质量门无法识别一直到达的旧时间段;CSV有值/Raw无队列drop/最终封存均不是完整当前窗覆盖。来源时轴与独立自身Metal呈现参照应分列,单surface不能冒充全部foreground-device。
Perfowl自身adapter对pinned11.3.1原row builder只做每tap 64类有界数据类型缓存;不缓存帧值、不改公式、不放宽long-gap/one-shot/白名单,原协议解码指令保留。E2同输入20字段×1000行×5解析wall为原0.027392倍,原缓存生成成本降低不直接意味着E4数据已可信;新包五轮来源覆盖/同轴与逐指标独立准确仍待。方案/实施与失败证据边界。
版本历史增量:2026-10-04,解析类型缓存v1(指标公式版本不变),已证离线效率与字段保真;当前窗迟到/校准仍partial。
2026-10-04 · 缓存修复后的限定真机来源观察
b115固定五轮140s捕获的原生设备帧跨度139.443–139.474s;旧b114同合同五轮仅20.669–22.166s。此受控场景来源处理吞吐/相对时轴跟进改善已观察;原始discontinuity/warmup继续真实保留,单surface呈现不等于全部foreground-device。sysmontap EndMach水位与host elapsed仅提供帧源年龄的下界,没有交付时延上界就不能据低下界认证当前新鲜;首尾host/source跨度差为相对滞后变化,不是绝对延迟。原固定bin与独立校准仍未过,全54项可信/100%未达,candidate状态不提升。固定五轮结果/源码与边界。
2026-10-04 · 当前窗的设备时钟过期保护
同一已核Session/目标代际/重连且两源实测Mach timebase一致,lagLowerNs=max(0,sysmontap.EndMach−FrameLifetime.endMach)×numer//denom是过期下界,不是精确年龄。严格超过2s时,本实时Display指标置空、质量stale-device-source;age上界未知继续null。下界小/无钟不是当前新鲜证明,原host-silence、one-shot、warmup与缺值门保持。旧Raw和native历史数值不回填,完整当前窗/独立scope与五轮100%仍待新包验收。
版本历史增量:current-display-device-lag-lower.v1(原数值公式不改),E2同钟/代际/边界及原迟到数据离线对照通过,E4与全54可信目标仍partial。共享时钟定义 · G7实施与证据。
版本历史增量:2026-10-16,静态 one-shot 与来源健康边界澄清,公式与质量实现未改。E2 新增 8 项诊断及既有 194 项回归通过;一次现有包低活动 E4 观察证明本地 58/58 帧消费、零队列丢弃,不能单凭此认证设备帧完整性或完成静态/动画对照验收。来源与限制。
版本历史增量:2026-10-16 · 内部177静态提示/动画恢复专项G五新轮E4通过;5290实际FrameLifetime、658发布样本,缺值保留不补零或旧FrameTime;同时修复关闭快照竞态,零本地drop/残留且正常Stop durable。不是全指标或独立物理精度通过。完整范围与失败保留。
Owner复测 Update · 2026-10-16
Owner已确认内部177静态画面自测OK;无新presentation间隔仍可为空,不延用旧值。限定E4场景通过不等于独立物理精度校准;修复代码已在main,整体按逐指标计划继续。收口与后续。
2026-10-26 · 静止与动画恢复的排查方法
FrameTime需要两个连续且合格的实际呈现端点;静止期间没有新端点时,不重复旧间隔,也不借用主机经过时间生成间隔。静止one-shot不意味着连接断开,source-gap/stale-device-source要结合来源时钟和其它诊断解释;本地收尽不等于设备端完整。
10月16日专项五轮保留5,290帧、658样本,静止说明与受控动画恢复E4通过,未证明静止零帧窗口完整性或跨设备物理精度。失败轮中关闭深度与实际队列不符,软件安排竞态后确认旧快照提前取值;先等待pump/清理再取最终深度修复了本地计数,不能推论设备不漏帧。静帧专项及关闭竞态。
版本历史增量:2026-10-26,补one-shot排查与关闭竞态的适用边界;原始公式和验收计数不变。月度复盘与来源索引 · 实验判断方法
2026-10-29 · 独立来源改造的口径预登记
本批计划将FTime激活与启动选择解耦,新增independent-frame.v1选源策略;数值仍分别沿用ActivityTrace presentation-gap与CoreProfile PresentedFrame candidate公式,不将FPS倒数当FTime。来源切换或generation/epoch改变必须重建前驱,首点warmup;实时与封存依据同一分段账本,缺失不补零,CP未证逐帧进程归属时仍为foreground-device candidate。P95/refresh/jitter继续要求各自AT证据,不借CP补值。当前为口径预登记,实施与软件/E4证据在本批方案增量记录,尚未新增正式验收。
2026-10-28 · 内部试用的持续帧覆盖与来源辨认
出现“FPS有值而FTime仅启动附近一个值”时,先检查真实FrameLifetime的采集census和启动源关闭事件,不能由FPS推断逐帧源正常。当前用户会话的53个样本中FTime仅1个;ActivityTrace的FrameLifetime匹配为0,旧scoped开关又将启动后的帧continuation关闭。其它UIKit UpdateSequence/CA Commit不是FrameLifetime,放宽白名单或1000/FPS填值都不能证明帧耗时。
本次仅接通已批准的startup+frame串行CoreProfile兼容续采,保留原有one-shot新帧/epoch门;实际ActivityTrace激活后仍优先采用它。FTime逐点来源分别是coreprofilesessiontap.PresentedFrame与activitytracetap.FrameLifetime,算法分别为core-profile-presented-frame-interval.candidate.v1和Registry现有activitytrace-framelifetime-presentation-gap.v1,单位ms,不将前者标成FrameLifetime。纯frame选择不另外启动CoreProfile;P95/Refresh/帧报告等其它指标不从兼容FTime编造。旧会话不会凭此长出缺失帧,静态画面无新帧仍允许为空。
CoreProfile历史高吞吐风险仍适用;新兼容链的持续覆盖、额外CPU/I/O与精度由Owner复测。E1/E2软件修复不等于新E4、物理帧校准或正式验收,正式55/66、11项暂停。本批方案与记录。
| 版本增量 | 日期 | 范围 |
|---|---|---|
| startup-frame-continuation.scoped.v1 | 2026-10-28 | 闭合旧策略开关、分列真实来源,不改FrameLifetime公式/质量,软件证据与手机验证分离 |
2026-10-29 · 来源资格与取消输出的排查方法(E1/E2)
有消息不等于有FTime:检查解码质量、同target/display/epoch、前驱衔接与时钟;无效首载荷不再使源永久active。运行与离线选源读取同一planHash/generation分段账本,缺失或不匹配明确失败,不能仅凭AT曾出现一条消息覆盖CP。换段首点warmup;不同源都有合法interval时,总体统计置空、保留各段,不跨源拼接平均值。
取消FTime或帧派生不代表共享AT绝无附带字段;核对冻结requested IDs、专用源启停以及wire/Metric/report各输出边界。v2单ID报告只发布请求值,原始共享证据可留incidental,内部附带算术裁剪仍未全面完成。这些是软件合同与复现方法,不是新E4持续覆盖或精度结论。实际实现与剩余门。
2026-11-03 · FrameUpdate 其余字段与“长帧间隔不等于卡顿”(E3 研究)
- 问题表现:帧耗时把“画面静止后的第一帧”和“该更新却迟到的帧”都记成长帧;ProMotion 减速时主动降到 80/60 Hz,帧间隔变成 12.5/16.67 ms,按 120 Hz 看像掉帧。
- 机制:iOS 26
FrameUpdate除呈现时刻外,还带帧开始时刻base_mct、实际帧生命周期FrameLifetimeInterval(4 字节结束偏移),以及FrameRender、FrameGPUExecute(8 字节,高 32 位为开始、低 32 位为结束偏移)、FrameReady(就绪偏移);args2低 32 位在设备 A 上只出现 8.335 / 12.50 / 16.67 ms 三种取值,与相邻呈现间隔一致,判断为本帧目标刷新间隔(统计推断,未见官方说明)。 - 判别方法:帧生命周期等于同刷新率下的稳态值(120 Hz 约 25 ms)且本帧开始晚于上一帧上屏,说明是“没有新内容”,不是卡顿;生命周期显著偏长、或开始早于上一帧上屏却呈现迟于预期,才是迟到帧。比较时按每帧自身刷新间隔,并容忍相邻帧的刷新率切换。
- 复现:用 pymobiledevice3
ActivityTraceTap(onlySignposts=1)抓com.apple.coreanimation / IOMFBServer(.Stalls) / FrameUpdate,按格式串说明符与消息非文字元组逐一对齐解码;注意#llx说明符也占一个值位。 - 限制:仅设备 A(iOS 26)一台、150 秒样本;
args1、contributors未解码;这些字段尚未进入帧耗时公式,帧耗时口径不变。卡顿(Hitch)指标的候选算法与校准计划见可行性研究。