Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


title: FrameTracer 与 FrameTimeline 分析 chapter: '14.13' status: finalized applicable_versions: Android 12 (API 31) - Android 17 (API 37) last_verified: '2026-08-13' last_verified_against: AOSP android-17.0.0_r1 / Perfetto v57.2 / official FrameTimeline and Android graphics sync docs confidence: high sources:

  • type: aosp path: frameworks/native/services/surfaceflinger/FrameTracer/FrameTracer.cpp
  • type: aosp path: frameworks/native/services/surfaceflinger/Layer.cpp
  • type: aosp path: external/perfetto/protos/perfetto/trace/android/graphics_frame_event.proto
  • type: research path: DeepResearch/2026-06-26-android17-frametracer-graphics-frame-event.md
  • type: aosp path: frameworks/base/core/java/android/view/Choreographer.java
  • type: aosp path: frameworks/native/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
  • type: aosp path: frameworks/native/services/surfaceflinger/FrameTimeline/FrameTimeline.h
  • type: aosp path: frameworks/base/core/java/android/view/ViewRootImpl.java
  • type: aosp path: frameworks/base/libs/hwui/JankTracker.cpp
  • type: official path: https://perfetto.dev/docs/data-sources/frametimeline
  • type: research path: intake/research-feeds/2026-04-10-07-frame-timeline-perfetto-visualization-choreographer-api33.md tags:
  • perfetto
  • frametracer
  • graphics
  • buffer-lifecycle
  • surfaceflinger
  • gpu
  • Perfetto
  • FrameTimeline
  • Jank
  • Choreographer
  • 渲染性能分析 related_chapters:
  • '14.7'
  • '14.10'
  • '2.9'
  • '13.1'
  • '2.3'
  • '13.9'
  • '14.5' pipeline_stage: ready-to-publish task6_state: reviewed task9_state: reviewed last_consolidated_at: '2026-08-24' consolidated_from:
  • src/part3-tools/ch14-perfetto/18-frametracer-graphics-frame-event.md
  • src/part3-tools/ch14-perfetto/19-frame-timeline-api33-perfetto-analysis.md

FrameTracer 与 FrameTimeline 分析

FrameTimeline 可以判断某个 SurfaceFrame 或 DisplayFrame 是否按预测时间完成。SurfaceFrame 表示某个 layer 提交的一帧,DisplayFrame 表示 SurfaceFlinger 把一个或多个 SurfaceFrame 合成后送去显示的一帧。FrameTracer 则记录 buffer 从 Producer 持有、提交、可供读取、latch(SurfaceFlinger 选中该 buffer 用于合成)到 present(显示系统给出呈现反馈)的时间点。

两套数据经常出现在同一条 trace 中,却使用不同的 ID 和时间含义。这里还涉及三种 fence(同步栅栏):acquire fence 约束 Consumer 何时可以读取 buffer,present fence 标记显示设备何时完成该帧的 present,release fence 通知 Producer 何时可以安全复用旧 buffer。混用 frame_number、FrameTimeline token 和这三种 fence,会让语法正确的查询得出错误归因。

本文核对的平台源码版本是 Android 17 / API 37 / android-17.0.0_r1。FrameTracer 实现在 frameworks/native 的 SurfaceFlinger 中,只解释它发出的事件时不必依赖内核函数;继续追查 DMA-BUF(跨驱动共享的 buffer)、sync_file(把 fence 暴露为文件描述符的内核接口)或 dma-fence(内核中的 fence 同步对象)时,对应的内核版本是 android17-6.18-2026-06_r6。SQL 以 SmartPerfetto v1.3.0 固定的 Perfetto v57.2 trace_processor_shell 为查询环境,并与该版本 importer 的 diff test(固定输入与期望输出的回归测试)对照。

FrameTracer 在系统侧记录图形帧事件,FrameTimeline 用 token 把应用预期、实际提交和显示结果关联起来。Expected 与 Actual timeline 的差值只有结合 layer 和线程上下文才能解释卡顿责任。

FrameTracer 事件生成与 token 关联

两条数据源分别保存什么

SurfaceFlinger(下文简称 SF)注册了两条独立的 Perfetto 数据源:

数据源主要实现观测对象适合回答的问题
android.surfaceflinger.frametimelineScheduler/FrameTimeline.cppexpected(预测)与 actual(实际)的 SurfaceFrame、DisplayFrame帧是否晚到、丢弃或发生 Buffer Stuffing(队列持续积压),App 与 SF 哪一侧错过预测时间
android.surfaceflinger.frameFrameTracer/FrameTracer.cpp每个 layer/buffer 的阶段事件Producer 何时持有和提交 buffer、producer fence 何时 signal(变为已完成)、SF 何时 latch、显示 present 反馈何时到达

FrameTimeline 使用 surface_frame_tokendisplay_frame_token 标识帧,一个 DisplayFrame 可以包含多个 SurfaceFrame。FrameTracer 使用 layer、buffer id 与 BufferQueue frame number:buffer id 标识被循环使用的 buffer 对象,frame number 标识该 BufferQueue 上的一次提交。Perfetto 导入后,buffer id 主要编码在 track name 中,frame_numberlayer_name 则作为 frame_slice 字段提供给 SQL。

两套数据没有可直接等值连接的“全程帧号”。FrameTracer 的 frame_number 不能与 surface_frame_tokendisplay_frame_token 直接比较。联合分析要先限定同一个 layer,再根据时间是否重叠、是否靠近同一次 present,以及相关 transaction(提交给 SF 的状态更新)建立对应关系。

Android 17 的六个实际发射事件

graphics_frame_event.proto 是这类 trace 数据的 Protocol Buffers(protobuf)结构定义,其中列出 14 个枚举值(含 UNSPECIFIED)。枚举只表示协议允许写入哪些值,不代表 AOSP 会发出每一种事件。对 android-17.0.0_r1Layer.cppFrameTracer.cppSurfaceFlinger.cppHWComposer.cpp 核对后,AOSP SurfaceFlinger 只有以下六类 emit(写入 trace)调用:

事件Android 17 发射位置时间含义判读限制
DEQUEUELayer::setBuffer() 处理带有效 dequeueTime 的 buffer transactionProducer 从 BufferQueue 取得该 buffer 的时间只在 transaction 携带正数 dequeueTime 时记录
QUEUEDEQUEUE 相同的 dequeueTime > 0 条件块,时间取 postTimebuffer transaction 携带的提交时间没有有效 dequeueTime 时,AOSP 连 QUEUE 也不会记录;该事件不能当成 GPU 已完成,也不表示 SF 已经采纳该 buffer
ACQUIRE_FENCElatch 路径调用 traceFence()producer/acquire fence 的 signal 时间对 HWUI(Android 硬件加速 UI 渲染器)常对应 GPU 写完;Camera、Video 或原生 Producer 可能来自其他硬件
LATCHLayer::latchBufferImpl() 附近SF 采纳该 buffer 的 latch 时间Android 13+ 允许特定 unsignaled buffer 先 latch,读取前仍须遵守 fence
FALLBACK_COMPOSITIONOutputLayer::requiresClientComposition() 为真时该 layer 被加入 client composition 的时间戳client composition 指 RenderEngine 使用 GPU 合成;这里记录的是任务被安排进入该路径的时间,不是 GPU 完成时间
PRESENT_FENCEpost-composition 路径display present fence signal;无有效 fence 时使用 HWC present timestamp 推导值HWC 是 Hardware Composer(硬件合成器);present fence 按 display 和 frame 生成,FrameTracer 把同一个 display 反馈记到相关 layer 上

下面这张图把六个事件放回 Producer、SurfaceFlinger 与显示系统的公共路径。Producer 生成并提交图像内容,BufferQueue 在 Producer 与 Consumer 之间传递 buffer;在这条路径中,SF 选择要使用的 buffer,RenderEngine 或 HWC 再负责后续合成。虚线 release 路径只用于说明真实的 buffer 回收关系,不属于 AOSP FrameTracer 的 emit 集合。

sequenceDiagram
    participant P as Producer
    participant BQ as BufferQueue / BLAST
    participant SF as SurfaceFlinger
    participant RE as RenderEngine
    participant HWC as HWC / Composer HAL
    participant D as Display

    P->>BQ: dequeue buffer
    Note over P,BQ: DEQUEUE
    P->>BQ: queue/post buffer + producer fence
    Note over P,BQ: QUEUE
    BQ->>SF: buffer transaction + acquire fence
    Note over SF: ACQUIRE_FENCE 记录 fence signal
    SF->>SF: latch selected buffer
    Note over SF: LATCH
    opt layer 需要 CLIENT composition
        SF->>RE: prepare / draw client composition
        Note over SF,RE: FALLBACK_COMPOSITION 记录排入时间
        RE->>HWC: client target + fence
    end
    SF->>HWC: DEVICE layers / client target
    HWC->>D: present display frame
    HWC-->>SF: present fence fd + per-layer release fence fds
    D-->>SF: present fence later signals
    Note over SF,D: PRESENT_FENCE
    SF-->>BQ: release callback / merged release fence
    BQ-->>P: release fence signal 后旧 buffer 可复用

图中的 PRESENT_FENCE 是 Android 显示栈的 present 参考时间。它不能证明面板像素已经完成光学响应,也不能代替每个 layer 的 release fence。release fence 决定旧 buffer 何时可以安全复用;AOSP Android 17 虽然在 proto 中保留 RELEASE_FENCE = 9,FrameTracer 没有对应 emit 调用。

允许 unsignaled latch(fence 尚未 signal 时先选中 buffer)时,acquire fence 的等待被推迟到后续真正读取 buffer 的位置。DEVICE composition 由 HWC 直接合成 layer,因此 SF 会把原 layer 的 acquire fence 随 buffer 交给 HWC;CLIENT composition 由 RenderEngine 使用 GPU 合成,它会消费或等待原 layer 的 fence,再把 client target(GPU 合成后的整屏目标 buffer)及其完成 fence 交给 HWC。看到 LATCH 早于 AcquireFenceSignaled,不能据此认定 SF 在没有同步的情况下读取了 buffer。

HWC_COMPOSITION_QUEUEDRELEASE_FENCE

HWC_COMPOSITION_QUEUED = 6RELEASE_FENCE = 9 在 Android 17 的 AOSP SurfaceFlinger 中都没有 emit 调用。trace 中出现前者时,应记录设备 build 与来源实现,再判断是否来自 OEM instrumentation(设备厂商自行增加的跟踪代码)。只看 proto 注释,无法把它归为标准 AOSP 事件。

缺少 RELEASE_FENCE 事件也不表示系统没有 release fence。HWC 仍会返回每个 layer 的 release fence,SurfaceFlinger 仍会按需要合并,并通过 release callback / BufferQueue 传给 Producer;FrameTracer 数据源没有把这条信号写入 GraphicsFrameEvent

traceFence() 为什么可能缺尾部事件

traceFence() 遇到已 signal 的 fence 时,会按 signal time(fence 变为完成状态的时间)写入事件;遇到 pending fence(仍在等待完成的 fence)时,会把它保存到对应 layer、buffer id 的 pendingFences。同一个 buffer 以后再次触发 FrameTracer 调用时,tracePendingFencesLocked() 才会重新检查,并补写此时已经 signal 的事件。

这套实现会影响以下三种判断:

  1. 即使补写发生得晚,TracePacket(trace 文件中的 protobuf 记录单元)的 timestamp 仍使用原 fence signal time;Trace Processor 按时间排序后,事件会出现在原来的 signal 位置;
  2. trace 结束前没有后续同 buffer 调用时,尾部 pending fence 可能没有机会补写;
  3. signal time 距检查时刻超过 60 秒的 pending fence 会被丢弃,防止前一次 trace 遗留的 fence 被写进后续采集。

因此,缺少某一帧的 AcquireFenceSignaledPresentFenceSignaled 需要结合 trace 尾部、buffer 是否继续复用、fence 有效性和数据源采集区间判断,不能直接写成“fence 从未 signal”。

Perfetto v57.2 怎样导入 Graphics Frame Event

Perfetto 的 GraphicsFrameEventParser 会把一条 proto 事件导入为 raw event(单个原始事件)和 phase slice(由起止事件配对生成的阶段区间)。在 Perfetto v57.2 中,SQL 查询入口是 gpu_trackframe_slice,筛选条件为 gpu_track.scope = 'graphics_frame_event'

track nameslice 内容dur 的含义
Buffer: <bufferId> <layer>DequeueQueueAcquireFenceSignaledLatchFallbackCompositionPresentFenceSignaled 等 raw eventimporter 支持带 duration_ns 的 span(持续区间);Android 17 的六类 AOSP emit 调用没有传入正 duration,因此均导入为 instant slice(零时长事件)
APP_<bufferId> <layer>一帧对应一个 phase sliceDEQUEUE → QUEUE
GPU_<bufferId> <layer>一帧对应一个 phase sliceQUEUE → ACQUIRE_FENCE signal
SF_<bufferId> <layer>一帧对应一个 phase sliceLATCH → PRESENT_FENCE signal
Display_<layer>连续 present 之间的 phase slice当前 present → 同 layer 下一次 present

APP_ phase 表示 Producer 持有 buffer 的时间。它可能包含 CPU 准备、GPU 提交、主动 pacing(按目标帧率控制提交节奏)、锁等待或其他 Producer 行为,不能统一命名为“App 绘制耗时”。GPU_ phase 表示从提交到 producer fence signal 的间隔;对于 HWUI,它常能反映等待 GPU 完成的时间,对 Camera、Video 和其他硬件 Producer 则要按对应设备的完成信号解释。

SF_ phase 把 latch 到 display present feedback 合成一个区间,覆盖 SF 调度、CLIENT/DEVICE composition 和显示后段,无法单独量出 HWC 或 RenderEngine 的执行时间。Display_ phase 表示相邻 present 的 cadence(重复出现的帧节奏),不表示 buffer 从 present 到 release 的生命周期。末尾的 Display_ slice 没有下一次 present 与它配对时,dur-1,表示区间尚未闭合。

还要考虑一个 parser 分支:acquire fence 可能在 QUEUE packet 被 importer 处理前已经 signal。此时 importer 不创建 GPU_ phase,以免生成起止顺序相反或时长错误的区间。某一帧缺少 GPU_ slice,不应直接解释为数据损坏。

确认 trace 中是否有这组数据

下面的片段只展示两条 SurfaceFlinger 数据源,需合并到已经定义采集时长与 buffer 的完整配置中。

data_sources {
  config { name: "android.surfaceflinger.frame" }
}
data_sources {
  config { name: "android.surfaceflinger.frametimeline" }
}

这只是可合并的配置片段,不能单独作为完整采集配置运行。完整排障应按问题补充 sched(CPU 调度)、atrace、GPU counter 或应用 track event,并根据采集时长与数据量设置 buffer。只打开 gfx atrace category,不能据此判断这两条原生 Perfetto 数据源已经启用。

SQL 1:列出目标 layer 的 raw event 与 phase

这条查询采用 Perfetto v57.2 importer diff test 使用的表关系,并增加目标 layer 过滤。tsdur 的单位是纳秒,GLOB 使用通配符匹配 layer 名称。

SELECT
  fs.ts,
  fs.dur,
  gt.name AS track_name,
  fs.name AS slice_name,
  fs.frame_number,
  fs.layer_name
FROM gpu_track AS gt
JOIN frame_slice AS fs
  ON fs.track_id = gt.id
WHERE gt.scope = 'graphics_frame_event'
  AND fs.layer_name GLOB '*com.example.app*'
ORDER BY fs.ts;

把包名替换为目标 layer 名称中稳定不变的片段。Buffer: 轨迹用于查看六类 raw event;APP_GPU_SF_Display_ 轨迹已经由 importer 计算好阶段时长。查询结果中 dur = -1 的未闭合 slice 不能参与均值、P95(第 95 百分位值)或总时长统计。

SQL 2:按 frame number 汇总四类 phase

下面的查询把 importer 生成的 phase 归入四列,不使用 SQLite 不支持的 PIVOT 语法。

WITH phase AS (
  SELECT
    fs.layer_name,
    fs.frame_number,
    fs.dur,
    CASE
      WHEN gt.name GLOB 'APP_*' THEN 'app_hold'
      WHEN gt.name GLOB 'GPU_*' THEN 'producer_fence_wait'
      WHEN gt.name GLOB 'SF_*' THEN 'latch_to_present'
      WHEN gt.name GLOB 'Display_*' THEN 'present_interval'
    END AS phase_name
  FROM gpu_track AS gt
  JOIN frame_slice AS fs
    ON fs.track_id = gt.id
  WHERE gt.scope = 'graphics_frame_event'
    AND fs.dur >= 0
    AND fs.layer_name GLOB '*com.example.app*'
)
SELECT
  layer_name,
  frame_number,
  MAX(CASE WHEN phase_name = 'app_hold' THEN dur END) / 1e6
    AS app_hold_ms,
  MAX(CASE WHEN phase_name = 'producer_fence_wait' THEN dur END) / 1e6
    AS producer_fence_wait_ms,
  MAX(CASE WHEN phase_name = 'latch_to_present' THEN dur END) / 1e6
    AS latch_to_present_ms,
  MAX(CASE WHEN phase_name = 'present_interval' THEN dur END) / 1e6
    AS present_interval_ms
FROM phase
WHERE phase_name IS NOT NULL
GROUP BY layer_name, frame_number
ORDER BY latch_to_present_ms DESC;

NULL 表示该 phase 在当前 trace 中没有闭合或没有生成,不能用 COALESCE(..., 0) 把缺失证据改写成零耗时。排序只用于定位候选帧;判断原因时还要结合 active refresh rate(当前生效刷新率)、FrameTimeline expected/actual、线程状态、GPU 数据和 composition type(合成类型)。

这条汇总还假设查询窗口内的完整 layer_name 只对应一个 layer 生命周期。窗口销毁后重建、出现同名 layer,或镜像到多个 Display 时,同名对象可能从较小的 frame number 重新计数。对长 trace 做自动化查询时,应进一步限制时间窗,并保留原始 track name 或 layer 标识,避免合并来自不同 BufferQueue 的同名帧。

FrameTimeline 与 FrameTracer 怎样配合

FrameTimeline 从 Android 12 起提供 expected/actual SurfaceFrame 与 DisplayFrame。Android 17 的 ActualSurfaceFrameStart 包含多组判断字段:present_type 表示早到、准时或晚到,on_time_finish 表示 App 是否按时完成,gpu_composition 表示是否使用 GPU 合成,jank_typejank_severity_type 描述卡顿类型和严重程度,prediction_type 描述预测是否仍有效,is_buffer 区分 buffer 帧与动画状态。present_delay_millisvsync_resynced_jitter_millisjank_severity_score 则提供延迟、VSync 重新同步抖动与评分。SQL 视图会把 jank bitmask(用不同二进制位同时表示多个原因的整数)转换成可读分类。

Android 17 的 proto 相比 android-16.0.0_r1 增加了以下五个枚举值:

Android 17 名称诊断含义
2048JANK_NON_ANIMATING当前内容不按动画帧节奏分类;它位于源码的 non-jank 集合,不能按普通卡顿原因计数
4096JANK_APP_RESYNCED_JITTERApp 侧重新同步相关抖动
8192JANK_DISPLAY_NOT_ONDisplay 未处于 on 状态
16384JANK_DISPLAY_MODE_CHANGE_IN_PROGRESS显示模式切换进行中
32768JANK_DISPLAY_POWER_MODE_CHANGE_IN_PROGRESS显示电源模式切换进行中

这些值是 bitmask,所以一帧可以同时带有多个原因。Android 17 还增加了 jank_type_experimentalpresent_type_experimentaljank_debug_metadata;proto 明确要求 experimental 字段不得用于正式 jank 判定。上述新增版本来自 Android 16 与 Android 17 固定 tag 的 proto 对比,不能因为某个字段存在于当前文件,就推断更早版本也支持它。

gpu_compositionFALLBACK_COMPOSITION

Android 17 的 Layer.cppOutputLayer::requiresClientComposition() 为真时记录 FALLBACK_COMPOSITION,并对相关 SurfaceFrame 调用 setGpuComposition()。所以 FrameTimeline 的 gpu_composition 与 FrameTracer raw event 来自同一条 client-composition 判定分支。

requiresClientComposition() 在当前 output layer 没有 HWC state,或 HWC composition type 为 CLIENT 时返回真。一次 display frame 可以同时包含由 HWC 直接合成的 DEVICE layer 和由 RenderEngine 生成的 CLIENT target。某个 layer 出现 FallbackComposition,只说明它进入 client composition;这条事件不能单独证明 overlay plane(显示硬件可直接合成的图层通道)已经用完,也不能量化 RenderEngine 的 GPU 时长。像素 format、几何 transform、alpha blend、dataspace、color transform、protected content 和 vendor HWC 策略都可能影响这一选择。

不要把两个 frame identity 直接 JOIN

FrameTimeline 查询应使用 surface_frame_token 对齐 App expected/actual,使用 display_frame_token 对齐 SurfaceFrame 与 DisplayFrame。FrameTracer 查询则使用 layer_name 与 BufferQueue frame_number。两者联合分析时可按以下步骤操作:

  1. actual_frame_timeline_slice 中选出目标进程和目标 layer 的 late/dropped frame;
  2. 记录该 SurfaceFrame 的时间区间与 display_frame_token
  3. 在相同 layer、相邻时间范围内查找 APP_GPU_SF_ phase 和 raw event;
  4. 回到 Perfetto UI 检查 layer、transaction、BufferTX - <layerName>,以及该帧前后的 present 事件;
  5. 需要自动关联时,限定时间窗并输出匹配置信度,不能把 frame token 与 frame number 写成等值条件。

标准 HWUI App Window 的 FrameTimeline 覆盖较完整。SurfaceView、Camera、Video、游戏、WebView overlay(独立叠加层)、Flutter PlatformView 等路径可能有独立 Producer、独立 layer,或没有完整的 App SurfaceFrame。此时应先识别真正提交 buffer 的 Producer 与目标 layer,再决定能否主要依靠 FrameTimeline 定位帧。

四类耗时怎样读

APP_ 偏长:Producer 长时间持有 buffer

DEQUEUE → QUEUE 增长说明 Producer 拿到 buffer 后较晚提交。对标准 HWUI,可检查 MainThread、RenderThread、DrawFrame、Skia/GPU submit 与 pacing;对游戏检查 Logic、Render/RHI(Rendering Hardware Interface,渲染硬件抽象层)和 swap;对 Camera/Video 检查 HAL、codec 与时间戳策略。即使 CPU 线程不忙,也可能存在主动 pacing、GPU backpressure(GPU 处理不过来导致上游放慢)或同步依赖。

GPU_ 偏长:producer completion fence 晚

QUEUE → ACQUIRE_FENCE signal 增长表示 Consumer 等了较久,才获得可以安全读取的 buffer。分析 HWUI 或游戏时,通常还要补充 GPU stage、frequency、utilization、shader 和内存带宽证据。Camera、Video 和硬件 blitter(用于复制、缩放或格式转换图像的硬件单元)要按对应生产设备的 completion fence 解释。固定使用 16.5 ms 判定,会在 90/120 Hz、30 fps 内容、VRR(Variable Refresh Rate,可变刷新率)、ARR(Adaptive Refresh Rate,自适应刷新率)和非整数帧节奏场景中产生误报,应改为与当前 expected timeline 和目标帧率比较。

acquire 已 signal,latch 仍晚:检查 transaction 与 SF 调度

FrameTracer importer 没有生成独立的 acquire-to-latch phase track。可以从 raw event 时间或事件参数计算二者间隔,再查看 transaction readiness(transaction 是否满足提交条件)、同步 transaction、BufferTX - <layerName>、SF actual timeline,以及该 layer 是否被本轮合成选中。Android 13+ 的 unsignaled latch 还会让 latch 与 fence signal 的先后关系更灵活,只看事件顺序无法确定真正的读取等待发生在哪里。

SF_ 偏长:系统输出段的综合延迟

LATCH → PRESENT_FENCE signal 同时包含 SF 调度、HWC validate/present、可选的 RenderEngine client composition、显示模式节奏和 display 后段。若出现 FallbackComposition,继续检查 RenderEngine 与 GPU;若全部目标 layer 都走 DEVICE composition,继续检查 HWC、DisplayHAL、刷新率或 mode change。present fence 仍只是显示栈中的参考时间,panel(显示面板)扫描和光学响应需要额外测量。

Display_ 偏长:相邻 present 的帧节奏出现空档

Display_ 的 duration 是同一 layer 相邻两次 present feedback 的间隔,适合观察帧节奏和继续显示旧 buffer 的时段,但不等于某个 buffer 的 release latency(从 present 到允许复用的延迟)。30 fps 内容在 60 Hz display 上出现约 33.3 ms 间隔可能完全符合预期,必须结合 requested frame rate、内容帧率与 FrameTimeline 判断。

按出图类型修正解释

出图类型FrameTracer 中要锁定的对象不能直接套用的解释
标准 App Window宿主 App Window layerAPP_ 仍不等于完整 doFrame;主线程工作可能发生在 dequeue 前
SurfaceView / 游戏 Surface独立 Surface layer宿主窗口 token 不能代替独立 layer 的 buffer frame number
TextureView宿主 App Window layer,外部 SurfaceTexture 另查外部 Producer 的 ready 时间不等于宿主窗口 present
软件绘制 / 离屏渲染最终上传或提交到窗口的 layer;纯离屏 Bitmap 没有 SF layer离屏 CPU 绘制可能发生在 dequeue 前,不能要求 FrameTracer 覆盖这段工作
Native EGL / Vulkan实际 ANativeWindow / Surface 对应的独立 layer不能套用 MainThread、RenderThread 或 HWUI 的线程归因
Camera / 普通 Video Surfacepreview/video layeracquire fence 可能来自 HAL 或 codec,不能统一归为 GPU
Tunneled / sideband videosideband layer 与 HAL/HWC 证据;sideband 表示视频数据通过专用硬件路径交给显示系统可能没有普通的逐帧 BufferQueue / FrameTracer 事件
WebView / Flutter / React Native先区分宿主合成和独立 overlayframework frame id 不能直接作为 BufferQueue frame number
多窗口 / 多 Display每个窗口的 layer 与各自目标 Display共享进程或线程不等于共享 BufferQueue;present fence 必须按 Display 区分

这张表按渲染管线中的 Producer、Surface、layer 与 composition 关系区分场景。如果只看事件名,很容易把宿主窗口、独立 Surface 和 display frame 都当成同一条“App 帧”。

不同 Android 版本的能力

平台数据能力判读影响
Android 11 / API 30FrameTracer 数据源已经存在缺少 Android 12 的 FrameTimeline 配套;可单独分析 buffer phase
Android 12 / API 31FrameTimeline 成为现代 trace 基线可以用 SurfaceFrame/DisplayFrame 选 jank 候选,再按 layer 与时间匹配 FrameTracer
Android 13 / API 33AutoSingleLayer unsignaled latch 成为默认策略,允许符合条件的单 layer buffer 在 fence signal 前先 latchlatch 与 acquire fence signal 不再保持简单的“必须先 signal 再 latch”假设
Android 14~16 / API 34~36两条数据源的公共模型延续仍要固定设备、vendor build、刷新率和 Perfetto 版本
Android 17 / API 37本文核对的源码与 proto 版本;扩展 FrameTimeline jank bitmask使用 Android 17 调用名、emit 集合和五个新增 jank 值

联合诊断范围从 Android 12 开始,因为这条路径依赖 FrameTimeline。Android 11 的 FrameTracer 可以作为历史兼容路径保留。Android 10 及更早版本应使用当时可用的 BufferQueue、atrace、SF 与 fence 证据,不能假定存在这条 Perfetto data source。

使用限制

FrameTracer 提供的是一组参考时间点和 importer 生成的阶段,不能直接当作端到端的用户可见延迟。使用时要遵守以下条件:

  • QUEUE 之后 Producer 仍可能写 buffer,完成时刻由 acquire fence 约束;
  • LATCH 表示 SF 采纳 buffer,不保证内容已经完成读取或显示;
  • FALLBACK_COMPOSITION 表示进入 client composition,不提供 RenderEngine 完成时刻;
  • PRESENT_FENCE 是每个 display 的 present 反馈,不是每个 layer 的 release,也不是 panel 光学响应;
  • FrameTimeline token 与 BufferQueue frame number 属于两套不同的 ID;
  • phase 缺失、dur = -1 和 trace 尾部 pending fence 都要按缺失证据处理;
  • 固定毫秒阈值只能作为筛选条件,帧预算应来自实际刷新率、内容帧率与 expected timeline。

遵守这些条件后,FrameTimeline 用来选择“哪一帧值得查”,FrameTracer 用来判断“buffer 路径哪一段出现等待”,线程、GPU、HWC 和 display 证据再负责解释等待来源。

FrameTracer 部分的参考源码与验证材料

Expected、Actual 与 Jank 分类

事件生产路径明确后,FrameTimeline 可以按 token 连接 App 和 SurfaceFlinger。deadline miss、prediction error 和 composition 结果需要分别读取。

FrameTimeline 使用一组可关联的帧 ID 记录调度预测、应用出帧、SurfaceFlinger 合成和显示提交,适合回答三个问题:哪一帧偏离了预测,偏差发生在应用侧还是显示合成侧,下一步应查看哪条线程或 buffer 路径。

本文核对的平台源码版本是 Android 17 / API 37 / android-17.0.0_r1。FrameTimeline trace 数据源从 Android 12 / API 31 起可用;标题中的 API 33 指 Choreographer.VsyncCallbackFrameDataFrameTimeline 公共 API 的引入版本。涉及 DMA-BUF(跨驱动共享的 buffer)、sync_file(把 fence 暴露为文件描述符的内核接口)或 dma-fence(内核中的同步对象)时,对应的内核版本是 android17-6.18-2026-06_r6。SQL 已按 Perfetto v57.2 的内置表验证。

FrameTimeline 的内部对象与分类流程见 §2.12,buffer 阶段事件见本文前文,CUJ 聚合见 §14.7。这里集中处理 Expected/Actual 语义、API 33 回调、采集配置和可执行 SQL。

两类帧

FrameTimeline 同时记录应用的 SurfaceFrame 和 SurfaceFlinger(下文简称 SF)的 DisplayFrame。SurfaceFrame 表示某个 layer 提交的一帧,DisplayFrame 表示 SF 把一个或多个 SurfaceFrame 合成后送去显示的一帧:

对象surface_frame_tokendisplay_frame_token代表什么
应用 SurfaceFrame非空非空某进程向一个 layer 提交的一帧
SF DisplayFrameNULL非空SurfaceFlinger 组合后提交给某个 display 的一帧

token 在这里是用于关联记录的帧 ID。一个 DisplayFrame 可以组合来自多个进程、多个 layer 的 SurfaceFrame,因此多条应用行可以共享同一个 display_frame_token。它适合从应用帧追到显示帧,不能直接用于连接应用 Expected 与应用 Actual;后一种连接要使用同一 upid 下的 surface_frame_tokenupid 是 Trace Processor 为进程分配的内部唯一 ID,可避免直接使用可能被系统复用的 PID。

DisplayFrame 的 protobuf 记录只有自己的 display token,没有 surface token。Perfetto v57.2 因此把 DisplayFrame 行的 surface_frame_token 保留为 NULL,也就是 SQL 中的空值;部分文档示例仍把这一格显示为 0。查询应按字段是否为空区分两类帧,不能依赖数值 0。

标准 HWUI(Android 硬件加速 UI 渲染器)App Window 的 SurfaceFrame 数据最完整。SurfaceView 由独立 Producer(生成并提交 buffer 的一方)和独立 Surface 出帧,Perfetto 官方文档仍将其列为 FrameTimeline 不支持的路径。Camera、Video、游戏 Surface、WebView overlay(独立叠加层)和跨进程嵌入也要先确认真正的 Producer 与 layer,不能拿宿主窗口的 token 代替独立 Surface 的帧 ID。

Expected 与 Actual 各自量什么

每个有可见帧的应用进程会得到 Expected Timeline 和 Actual Timeline,SurfaceFlinger 也有自己的一对 track。

应用侧

  • Expected slice 的起点是 Choreographer 回调被计划运行的时刻;slice 表示系统为应用准备该帧安排的时间窗口。
  • Actual slice 的起点是 Choreographer#doFrameAChoreographer_vsyncCallback 开始运行的时刻。
  • Actual slice 的终点取 GPU 完成时间与 post time 的较晚者;post time 表示应用把这一帧提交给 SurfaceFlinger 的时刻。

Choreographer#doFrame 只覆盖主线程回调区间。Actual SurfaceFrame 还考虑异步 GPU 工作和提交时刻,所以较短的 doFrame 不能证明该帧在 ready deadline(必须准备完成的时间)之前就绪。Android 17 的 trace 名为 Choreographer#doFrame <vsyncId>,例如 Choreographer#doFrame 12345;方括号形式不符合该 tag 的源码。

SurfaceFlinger 侧

SF Expected slice 表示当前 DisplayFrame 的预测工作窗口。SF Actual slice 从 SF 主线程开始处理该帧,覆盖 Composer 与 Display HAL(显示硬件抽象层)相关路径,终点是 Android 显示栈报告 on-screen update 的时刻。

这个 present 时间可以用于分析 Android 显示管线,但不能证明面板像素已经完成扫描和光学响应。测量从触摸到屏幕发光的端到端延迟,仍要同时记录输入时间戳、显示链路,并使用外部光学设备。

不能把 Expected 宽度当刷新周期

Expected slice 的宽度来自该回调的调度预算,即系统预计 App 或 SF 完成工作的可用时间。官方示例中应用 Expected slice 约 20.5 ms,SF Expected slice 约 10.5 ms;它们都不能用“90 Hz 就应固定为 11.1 ms”来校验。

Android 17 的 VSyncDispatchTimerQueueEntry::schedule() 以预测的目标 VSYNC 为参考时间,并使用 workDurationreadyDuration 计算 wakeup(唤醒)与 ready(应当就绪)时刻:

nextReadyTime  = nextVsyncTime - readyDuration
nextWakeupTime = nextReadyTime - workDuration

公式先从目标 VSYNC 减去 readyDuration 得到应当就绪的时间,再向前减去 workDuration 得到唤醒时间。固定的“VSYNC-app 提前 1~3 ms、再叠加 SF 预算”模型无法描述动态刷新率、不同工作预算、VSync 重同步或 Buffer Stuffing(队列持续积压),不应用来反推 Expected slice。

三个字段要一起看

Actual slice 至少包含以下三组相互独立的信息:

字段问题常见值
present_type该帧何时 presentEarly PresentOn-time PresentLate PresentDropped Frame
on_time_finish生产该帧的工作是否按时结束0 或 1
jank_type / jank_tagFrameTimeline 检测到的原因,以及原因归在当前进程还是其他进程App Deadline MissedBuffer StuffingSelf JankOther Jank

时间轴上的偏差适合发现候选帧,分类字段用于说明系统怎样判断这帧。on_time_finish = 0 只表示工作超出 ready deadline,不能作为全部 jank 的过滤条件。Buffer Stuffing 常见 on_time_finish = 1Late Present 同时出现:应用生产工作虽然按时结束,队列中积压的 buffer 仍会增加输入延迟。

Perfetto UI 用颜色表示状态,以及原因归在当前进程还是其他进程:

颜色UI 含义判读
绿色good frame没有检测到 jank
浅绿色high-latency state帧节奏可能平滑,但帧持续晚 present,输入延迟增加
红色self jank当前 slice 所属进程被判为原因
黄色other jank只用于应用 track;当前应用帧受 SF/display 侧问题影响
蓝色dropped frame应用状态更新未及时交给 RenderThread,或 SF 选择较新的显示帧

Android 17 SurfaceFrame::isSelfJanky()AppDeadlineMissedAppResyncedJitterUnknown 视为应用自身 jank。SF scheduling、SF CPU/GPU deadline、Display HAL 和 Prediction Error 会形成系统侧原因。黄色只说明 FrameTimeline 把当前帧归到系统侧;复杂 layer、GPU 负载或 composition 变化仍可能由应用行为触发,排障时还要检查 flow(跨 track 的事件关联线)、layer 和系统负载。

jank_type 是 bitmask(用不同二进制位同时表示多个原因的整数)转换成的字符串,一帧可以同时带多个原因。Perfetto v57.2 还提供 jank_tag,把结果归并为 Self JankOther JankBuffer StuffingSurfaceFlinger StuffingDropped FrameNon-perceivable Jank 等预计算类别。做统计时应优先使用这个分类,避免靠字符串包含关系自行分组。

Android 17 的 Actual SurfaceFrame protobuf 记录还包含 present_delay_millis(present 延迟)、vsync_resynced_jitter_millis(VSync 重同步抖动)、jank_severity_typejank_severity_score;DisplayFrame 没有 VSync 重同步抖动字段。Perfetto v57.2 在内置表中把 severity score 命名为 jank_score。这些值可以描述偏差幅度和严重程度,但不能脱离 jank_type、present 状态与采集版本,另行定义一套 jank 判定。protobuf 中带 experimental 的 jank、present 与 debug 字段明确标注为调试数据,不应进入正式 jank 指标。

API 33 的 FrameData 用法

API 33 新增 Choreographer.postVsyncCallback(VsyncCallback)。这个一次性回调接收 FrameData,公开方法如下:

类型方法返回内容
FrameDatagetFrameTimeNanos()当前回调使用的 frame time
FrameDatagetFrameTimelines()按时间排序的候选 FrameTimeline[]
FrameDatagetPreferredFrameTimeline()平台当前选中的候选 timeline
FrameTimelinegetVsyncId()与 HWUI、SurfaceFlinger trace 关联的 VSYNC id
FrameTimelinegetExpectedPresentationTimeNanos()预测 present 时间,使用 System.nanoTime() 时基
FrameTimelinegetDeadlineNanos()该候选帧应当 ready 的时间,使用 System.nanoTime() 时基

FrameData.getLastFrameTimeNanos()FrameData.getIntervalNanos()Choreographer.getFrameData() 都不属于公共 API。deadline 也不等同于“主线程 CPU 工作结束时间”;应用的 buffer 与 GPU 工作要在这个时刻前满足后续 Consumer 的读取条件。

下面的 Java 片段演示如何在 API 33+ 复制一次回调中的标量值。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    Choreographer choreographer = Choreographer.getInstance();
    choreographer.postVsyncCallback(frameData -> {
        Choreographer.FrameTimeline preferred =
                frameData.getPreferredFrameTimeline();

        long frameTimeNanos = frameData.getFrameTimeNanos();
        long vsyncId = preferred.getVsyncId();
        long deadlineNanos = preferred.getDeadlineNanos();
        long expectedPresentNanos =
                preferred.getExpectedPresentationTimeNanos();

        recordFramePlan(
                frameTimeNanos,
                vsyncId,
                deadlineNanos,
                expectedPresentNanos);
    });
}

这段代码要在带 Looper(Android 线程消息循环)的线程调用,回调会在 Choreographer 绑定的同一线程执行。FrameData 与其中的 FrameTimeline 只在 onVsync 执行期间有效;Android 17 源码在回调外访问时会抛出 IllegalStateException。需要异步记录时,只复制 long 等标量值。postVsyncCallback() 执行一次后会移除,连续观测要由调用方再次注册,并控制日志写入和对象分配成本。

Android 17 内部的 DisplayEventReceiver.VsyncEventData 为候选数组预留 7 个槽位,但 FrameData.update() 会按事件携带的 frameTimelinesLength 重新分配数组。公共 API 没有承诺固定返回 7 项,业务代码应读取实际数组长度,并使用平台标出的 preferred 项。

这些 API 提供的是本次回调可选的调度计划,不能替代 FrameTimeline trace。应用进程无法只凭 FrameData 得到最终 present、SF 分类、其他 layer 或 Display HAL 结果。

正确启用数据源

FrameTimeline 是原生 Perfetto 数据源 android.surfaceflinger.frametimelinegfxview 属于 atrace category(Android trace 事件分类),可以补充 Choreographer#doFrameDrawFrame 和图形 slice,但不会自动打开 FrameTimeline 数据源。

下面的配置片段同时启用 FrameTimeline、FrameTracer、应用图形 slice 和线程调度信息,需合并到已经定义采集时长与 buffer 的完整配置中。

data_sources {
  config { name: "android.surfaceflinger.frametimeline" }
}
data_sources {
  config { name: "android.surfaceflinger.frame" }
}
data_sources {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "sched/sched_switch"
      ftrace_events: "sched/sched_waking"
      ftrace_events: "power/cpu_frequency"
      atrace_categories: "view"
      atrace_categories: "gfx"
      atrace_categories: "wm"
      atrace_apps: "com.example.app"
    }
  }
}

这段内容不能单独作为采集配置运行。采集时长和 buffer 容量应根据复现窗口、设备内存与实际数据速率设定,不存在适用于所有场景的固定数值。android.surfaceflinger.frame 是可选的 buffer 阶段数据源,见 §14.13。定位单个应用时,应把 atrace_apps 换成目标包名;使用全局 * 会增加 ftrace 数据量。生产问题还要按待验证的原因加入 GPU counter、Binder、memory 或 power 数据,避免无关数据占满 ring buffer(写满后覆盖旧数据的环形缓冲区)。

下面的命令使用文本配置采集并拉回 trace。

adb push frame_timeline.pbtxt /data/local/tmp/
adb shell perfetto --txt \
  -c /data/local/tmp/frame_timeline.pbtxt \
  -o /data/misc/perfetto-traces/frame_timeline.perfetto-trace
adb pull /data/misc/perfetto-traces/frame_timeline.perfetto-trace

命令依次把文本配置推到设备、启动 Perfetto 采集,再把 trace 文件拉回电脑。采集结束后,先在 Trace Processor 中确认两张 FrameTimeline 表有数据。若 UI 缺少应用 Expected/Actual track,依次检查平台是否为 Android 12+、原生数据源是否启用、目标应用在采集区间内是否产生可见帧,以及目标路径是否属于独立 Surface 或未覆盖的 SurfaceView。

UI 排障顺序

一次可靠的帧级分析可以按下面的顺序进行:

  1. 在目标进程下选中红色、黄色、浅绿色或蓝色 Actual slice,记录 surface_frame_tokendisplay_frame_token、layer、jank_tagjank_typepresent_typeon_time_finish
  2. surface_frame_token 找同进程的 Expected slice,比较计划起点、实际回调起点、ready 窗口和 Actual 终点。
  3. 沿 UI flow(跨 track 的关联线)跟到 SurfaceFlinger 的 DisplayFrame。一个显示帧可能连接多条应用 layer,不要把同 token 的所有行当成重复数据。
  4. Self Jank 时检查同一 VSYNC id 的 Choreographer#doFrame <id>、RenderThread DrawFrame <id>、CPU scheduling(调度状态)、GPU 与 acquire fence(限制 Consumer 读取时机的同步栅栏)。
  5. Other Jank 时检查 SF actual track、composition type(合成类型)、RenderEngine、HWC/Display HAL、刷新率和 mode/power change。
  6. Buffer Stuffing 时检查 Producer 的提交节奏、连续 late present、dequeue 阻塞和 FrameTracer phase(阶段区间);on_time_finish = 1 不能排除这类高延迟状态。

doFrame 较长只能说明主线程回调占用明显。doFrame 较短而 Actual 较长时,RenderThread、GPU、提交等待或 Producer 与 SF 的交接过程仍可能有问题。要确认原因,还需查询对应线程、GPU、fence 或 BufferQueue 数据。

Perfetto SQL:按正确身份对齐

expected_frame_timeline_sliceactual_frame_timeline_slice 是 Trace Processor 导入 trace 时直接生成的内置表,查询它们不需要 INCLUDE PERFETTO MODULE android.frames.timeline。Perfetto v57.2 的表结构包含 name、两个 token、upidlayer_namepresent_typeon_time_finishjank_typejank_tag,没有 slice_name 列。

查询 1:逐帧比较应用 Expected 与 Actual

下面的查询只选择应用 SurfaceFrame,并用 upid + surface_frame_token 连接 Expected 与 Actual。两个字段一起使用,可以避免不同进程恰好出现相同 token 时发生误连。

WITH app_actual AS (
  SELECT
    a.*,
    p.name AS process_name
  FROM actual_frame_timeline_slice AS a
  LEFT JOIN process AS p USING (upid)
  WHERE a.surface_frame_token IS NOT NULL
),
app_expected AS (
  SELECT
    upid,
    surface_frame_token,
    ts AS expected_ts,
    dur AS expected_dur
  FROM expected_frame_timeline_slice
  WHERE surface_frame_token IS NOT NULL
)
SELECT
  a.process_name,
  a.layer_name,
  a.surface_frame_token,
  a.display_frame_token,
  a.ts / 1e6 AS actual_start_ms,
  a.dur / 1e6 AS actual_dur_ms,
  e.expected_ts / 1e6 AS expected_start_ms,
  e.expected_dur / 1e6 AS expected_dur_ms,
  (a.ts + a.dur - e.expected_ts - e.expected_dur) / 1e6
    AS end_delta_ms,
  a.present_type,
  a.on_time_finish,
  a.jank_tag,
  a.jank_type
FROM app_actual AS a
JOIN app_expected AS e
  USING (upid, surface_frame_token)
WHERE a.process_name = 'com.example.app'
ORDER BY a.ts;

end_delta_ms 是 Actual 终点减去 Expected 终点得到的时间差,只用于排序和定位,不能单独当作 jank 判定。若同一 token 对应多个 layer,结果会保留多条 Actual 行;每一行都代表一个 layer,不能用 DISTINCT 去重后隐藏。

查询 2:按原因归属、present 与 ready 状态统计

下面的查询保留 Buffer Stuffing、Dropped Frame 和 non-perceivable 状态,不使用 on_time_finish = 0 预先删行。

SELECT
  a.jank_tag,
  a.jank_type,
  a.present_type,
  a.on_time_finish,
  COUNT(*) AS frame_count,
  AVG(a.dur) / 1e6 AS avg_actual_ms,
  MAX(a.dur) / 1e6 AS max_actual_ms
FROM actual_frame_timeline_slice AS a
LEFT JOIN process AS p USING (upid)
WHERE a.surface_frame_token IS NOT NULL
  AND p.name = 'com.example.app'
GROUP BY
  a.jank_tag,
  a.jank_type,
  a.present_type,
  a.on_time_finish
ORDER BY frame_count DESC;

这份聚合能区分“工作超 deadline”“present 晚”“队列积压”和“原因归在其他进程”。dur 来自 FrameTimeline 的 Actual slice,不能统一解释为主线程、GPU 或端到端显示耗时。

查询 3:把应用帧连接到 SF DisplayFrame

下面的查询用 display_frame_token 找到应用 SurfaceFrame 对应的 SF DisplayFrame,并通过 surface_frame_token IS NULL 只保留 DisplayFrame 行,防止连接到其他应用 layer。

WITH app_frame AS (
  SELECT
    a.*,
    p.name AS process_name
  FROM actual_frame_timeline_slice AS a
  LEFT JOIN process AS p USING (upid)
  WHERE a.surface_frame_token IS NOT NULL
    AND p.name = 'com.example.app'
),
display_frame AS (
  SELECT
    a.*
  FROM actual_frame_timeline_slice AS a
  LEFT JOIN process AS p USING (upid)
  WHERE a.surface_frame_token IS NULL
    AND p.name GLOB '*surfaceflinger'
)
SELECT
  app.surface_frame_token,
  app.display_frame_token,
  app.layer_name,
  app.jank_tag AS app_jank_tag,
  app.jank_type AS app_jank_type,
  sf.jank_tag AS sf_jank_tag,
  sf.jank_type AS sf_jank_type,
  sf.present_type AS sf_present_type
FROM app_frame AS app
LEFT JOIN display_frame AS sf
  USING (display_frame_token)
ORDER BY app.ts;

一个 SF DisplayFrame 对应多条应用 SurfaceFrame,因此查询结果出现重复的 display_frame_token 是正常的多对一关系。GLOB '*surfaceflinger' 使用通配符匹配 SF 进程名。UI flow 还保留时间位置与其他 layer,排查单帧时通常比只看表格直观。

Android 17 新增的分类

Android 17 frame_timeline_event.proto 继续用 bitmask 表示原因,并在 Android 16 固定 tag 的基础上加入五项分类:

bitAndroid 17 枚举诊断含义
2048JANK_NON_ANIMATING非动画内容或无法按动画节奏解释的 present 状态;源码将其放入 non-jank bitmask
4096JANK_APP_RESYNCED_JITTER应用 VSYNC 重同步相关抖动
8192JANK_DISPLAY_NOT_ONdisplay 未处于 on 状态
16384JANK_DISPLAY_MODE_CHANGE_IN_PROGRESS显示模式切换进行中
32768JANK_DISPLAY_POWER_MODE_CHANGE_IN_PROGRESS显示电源模式切换进行中

后三项 display 状态会影响“用户是否能感知”和原因归属,不能全部算进应用 jank rate(卡顿帧比例)。Perfetto v57.2 已把 bitmask 转换为 jank_typejank_tagjank_severity_typejank_score;自动化报告应保存原始类型和工具版本,避免把不同 Perfetto 版本生成的字段混进同一比较基线。

Buffer Stuffing、SurfaceFlinger Stuffing、Non Animating 和三项 display 状态在 Android 17 FrameTimeline.cpp 中都位于 non-jank bitmask。这些位单独出现时不会增加 jank_severity_score,却仍可能描述高延迟、不可感知或显示状态变化。性能报告应分别统计,不能全部并入 No Jank,也不能全部算作应用 jank。

按出图路径选择主要记录

不同渲染路径使用的 Producer、Surface、layer 和 fence 不同,分析时可按下表选择优先用于定位帧的记录:

出图路径优先用于定位帧的记录补充证据
标准 View/Compose App WindowApp Window 的 SurfaceFrame tokenMainThread、RenderThread、HWUI、GPU、FrameTracer
SurfaceView / 游戏独立 Surface先找独立 layer;App Timeline 可能缺失Producer 线程、BufferQueue、FrameTracer、GPU
TextureView宿主 App Window token外部 SurfaceTexture Producer 与宿主合成时序
Camera / Video Surfacepreview/video layer 的 present 节奏HAL、codec、acquire fence;fence 来源未必是 GPU
Tunneled / sideband videoFrameTimeline 可能无法覆盖逐帧 bufferHWC、HAL、sideband(视频经专用硬件路径交给显示系统)与显示状态
WebView / Flutter / 跨进程嵌入区分宿主窗口和独立 overlay各进程 layer、flow、buffer 与 composition type
Software / 离屏渲染只有最终提交到可见 Surface 的帧可能进入 FrameTimelineCPU raster(CPU 像素绘制)、Bitmap/ImageReader Consumer 与后续上传或提交
Native EGL / Vulkan优先定位 ANativeWindow 对应的 layer引擎线程、swap/present、GPU queue、BufferQueue 与 fence
多窗口 / 多 display每个可见窗口或独立 Surface 分别找 layer窗口可见性、目标 display、刷新模式与同一 DisplayFrame 的 layer 集合

queueBuffer 返回只代表 Producer 已提交 buffer;acquire fence 决定 Consumer(读取 buffer 的一方)何时可以安全读取。present fence 按 display/frame 生成,release fence 按 layer/frame 生成并决定旧 buffer 何时可复用。FrameTimeline 的 present 时间不能替代 release fence,也不能从 token 推导 BufferQueue frame number。

不同 Android 版本的能力

平台能力分析影响
Android 12 / API 31FrameTimeline 数据源进入平台Expected/Actual track 与两张内置表可用于判断帧是否偏离计划及原因归属
Android 13 / API 33公共 VsyncCallbackFrameDataFrameTimeline应用可在回调内读取候选 deadline、expected present 与 VSYNC id
Android 14~16 / API 34~36公共模型延续,分类和调度实现继续演进固定设备 build、刷新率和 Perfetto 版本后再比较
Android 17 / API 37本文核对的平台源码与 protobuf 版本使用动态 work/ready budget、Android 17 jank bitmask 与 v57.2 表结构

FrameTimeline trace 的最低平台是 Android 12,API 33 只限定应用代码示例。结论范围不包含 Android 17 之后的行为。

使用限制

  • Expected 是调度预测窗口,不是刷新周期的同义词。
  • Actual 应用 slice 覆盖回调起点到 GPU/post 的较晚时刻,不等于 doFrame CPU 时长。
  • on_time_finishpresent_typejank_typejank_tag 要联合判断。
  • surface_frame_token 对齐同一应用帧;display_frame_token 连接应用帧与显示帧。
  • 一个 display frame 可以组合许多 layer,重复的 display token 不表示重复采样。
  • 红色和黄色只提示原因归在哪一侧;确认 CPU、GPU、HWC 或队列问题时,仍需查看对应数据。
  • SurfaceView 与独立 Surface 先确认覆盖范围,缺少 App Timeline 不能写成没有出帧。
  • present 是 Android 显示栈报告的时间,release 和 panel 光学时刻需要其他数据。

在这些条件下,FrameTimeline 用于选择问题帧并判断原因归在 App 侧还是 SF 侧,线程、GPU、FrameTracer、HWC 与 display 数据再用于解释等待发生在哪里。

小结

FrameTimeline 负责用 expected/actual 时间和 frame token 选出异常帧、区分 App 与 SurfaceFlinger 侧原因;FrameTracer 负责用 BufferQueue frame number 和 Graphics Frame Event 补充 queue、acquire、latch、composition 与 present 阶段。两套 ID 不能直接互换,各阶段时长也不等于端到端可见延迟。先用 FrameTimeline 定帧,再用 FrameTracer、线程状态、GPU/HWC 和 fence 证据解释等待,才能避免把颜色或单个时间点直接写成根因。

FrameTimeline 参考源码与验证材料