Android 帧绘制耗时全链路溯源

本档旨在从底层 AOSP 源码视角,拆解 JankStats 捕获的每一项耗时指标的物理来源,并精确到具体的类、函数及源码路径。

全链路耗时概览图一帧的产生经历了:Vsync 信号(Choreographer) -> UI 线程处理(MainThread) -> Sync 同步 -> RenderThread 处理 -> GPU 渲染 -> Buffer 交换。

mermaid 代码

sequenceDiagram

participant C as Choreographer

participant M as MainThread

participant R as RenderThread

participant G as Graphics

Note over C, M: 1 VSync (INTENDED_VSYNC / VSYNC_TIMESTAMP)

C->>M: VSync 信号触发

rect rgb(240, 240, 240)

Note right of M: UNKNOWN_DELAY_DURATION

M->>M: 2 do other work (Handler/Broadcast)

end

rect rgb(230, 255, 230)

M->>M: 3 handle input (INPUT_HANDLING_DURATION)

M->>M: 4 play animation (ANIMATION_DURATION)

M->>M: 5 measure & layout (LAYOUT_MEASURE_DURATION)

M->>M: 6 draw (DRAW_DURATION)

end

M->>R: 7 sync and upload (SYNC_DURATION)

rect rgb(230, 230, 255)

R->>G: 8 dequeue buffer (阻塞点)

R->>R: 9 execute

R->>G: 10 flush command (COMMAND_ISSUE_DURATION)

R->>G: 11 swap buffer (SWAP_BUFFERS_DURATION)

end

G->>G: 12 compose (系统合成)

Note over G: GPU_DURATION (GPU 异步执行耗时)

Note over C, G: TOTAL_DURATION (1-11 阶段总和)

流程详细说明与 FrameMetrics 参数对应

流程编号

流程名称

详细说明

对应 FrameMetrics 参数

1

VSync

Choreographer 接收到硬件 VSync 信号,触发主线程。

INTENDED_VSYNC_TIMESTAMP (预期) / VSYNC_TIMESTAMP (实际)

2

do other work

主线程在处理 UI 前执行的其他任务(如 Handler 消息、广播)。

UNKNOWN_DELAY_DURATION

3

handle input

处理触摸、按键等输入事件。

INPUT_HANDLING_DURATION

4

play animation

执行属性动画、帧动画等。

ANIMATION_DURATION

5

measure & layout

对 View 树进行测量和布局,确定每个控件的大小和位置。

LAYOUT_MEASURE_DURATION

6

draw

录制绘制指令 (DisplayList),将 Canvas 绘制转化为 Native 指令。

DRAW_DURATION

7

sync and upload

将主线程录制的绘制信息和资源(如 Bitmap)同步到渲染线程。

SYNC_DURATION

8

dequeue buffer

渲染线程从 BufferQueue 申请缓冲区。此处是著名的阻塞点。

(包含在 TOTAL_DURATION 中,或由 DequeueBufferDuration 统计)

9-10

execute & flush

渲染线程回放绘制指令,调用 GPU 渲染并刷新指令队列。

COMMAND_ISSUE_DURATION

11

swap buffer

将渲染完成的 Buffer 提交给 SurfaceFlinger。

SWAP_BUFFERS_DURATION

12

compose

SurfaceFlinger 进行画面合成并显示到屏幕。

(属于系统层,不计入应用内耗时)

FrameMetrics 详细指标参考表

FrameMetrics 的常量值

含义

可能导致耗时多久的情况

UNKNOWN_DELAY_DURATION

UI 线程延迟处理绘制任务的耗时

主线程消息队列中正在执行的非绘制任务耗时太久,导致开始执行绘制任务太晚

INPUT_HANDLING_DURATION

输入事件处理函数的耗时

点击事件耗时太久

ANIMATION_DURATION

动画回调函数的耗时

动画太多或者回调函数耗时太久

LAYOUT_MEASURE_DURATION

整个 View 树的测量和布局的耗时

布局层级复杂,频繁改变尺寸或位置(比如复杂的属性动画)

DRAW_DURATION

布局绘制函数的耗时

draw/onDraw 里执行了耗时操作

SYNC_DURATION

主线程同步 DisplayList 到 RenderThread 的耗时

过度绘制,导致要更新的 DisplayList 过多

COMMAND_ISSUE_DURATION

RenderThread 发送绘制命令到 GPU 的耗时

绘制内容复杂

SWAP_BUFFERS_DURATION

将绘制的 buffer 交换到前台显示的耗时

—

TOTAL_DURATION

绘制一帧的总耗时

前面任意一个阶段耗时,最终这个值都会变大

FIRST_DRAW_FRAME

这帧是否是当前窗口的第一帧

—

INTENDED_VSYNC_TIMESTAMP

预期接收到 VSync 信号(也就是这帧开始执行)的时间戳

—

VSYNC_TIMESTAMP

真正接收到 VSync 信号的时间

主线程的耗时任务太多,会导致主线程响应 VSync 信号过慢

GPU_DURATION

GPU 完成这帧绘制的耗时

—

其他核心参数解析

TOTAL_DURATION: 从预期 VSync 信号发出到帧绘制完成(Swap Buffer 结束)的总时间。即 1-11 阶段的总和。

GPU_DURATION: GPU 执行渲染指令的实际耗时(从指令提交完成到 GPU 硬件执行完毕)。反映了 GPU 的真实压力。

DEADLINE: 系统为该帧设定的截止时间。如果 TOTAL_DURATION > DEADLINE,通常意味着发生了卡顿。

FIRST_DRAW_FRAME: 标记该帧是否为窗口可见后的第一帧。由于需要初始化资源,首帧耗时通常较高。

Android 帧绘制耗时全链路溯源第一阶段:起跑线前的等待 (Schedule & Input)核心指标:UNKNOWN_DELAY_DURATION(未知延迟时长)

物理意义:从系统产生 Vsync 信号到 UI 线程真正开始执行 doFrame 任务的时间差。

精确路径:

起点:DisplayEventReceiver 收到底层信号,通过 JNI 回调发送消息到 Looper。

源码:frameworks/base/core/java/android/view/DisplayEventReceiver.java

关键函数:Choreographer$FrameDisplayEventReceiver.run() 被 Looper 唤醒,调用 doFrame。

源码:frameworks/base/core/java/android/view/Choreographer.java

终点:Choreographer.doFrame() 开始执行。

计算逻辑:UNKNOWN_DELAY = FrameInfo[HANDLE_INPUT_START] - FrameInfo[INTENDED_VSYNC]

解析:

INTENDED_VSYNC:理想的帧开始时间戳(Vsync 信号发生的时刻)。

HANDLE_INPUT_START:UI 线程 Looper 实际分发到该渲染消息并开始处理的时刻。

参数价值:反映 UI 线程的消息队列积压情况。如果此值大,说明 UI 线程正在执行非 UI 的耗时任务(如长耗时的 Handler 消息或过重的业务逻辑)。

第二阶段:UI 线程的奔跑 (Logic & Construction)核心指标:INPUT_HANDLING(输入处理) + ANIMATION(动画) + LAYOUT_MEASURE(布局测量) + DRAW(绘制)

物理意义:UI 线程执行业务逻辑并构建绘制指令的过程。

精确路径:

Input Handling:Choreographer.doCallbacks(Choreographer.CALLBACK_INPUT) -> InputEventReceiver.consumeEvents()。

源码:frameworks/base/core/java/android/view/InputEventReceiver.java

Animations:Choreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION) -> 执行所有已注册 of the ValueAnimator 等回调。

Layout & Measure:Choreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL) -> ViewRootImpl.performTraversals()。

performTraversals 核心步骤解析:

relayoutWindow:通过 IPC 与 WindowManagerService 通信,获取或调整 Surface,确定窗口的物理大小(Frame Size)。

**performMeasure()**:从顶层 DecorView 开始深度优先遍历 View 树。每个 View 根据父容器的 MeasureSpec 计算自己的 MeasuredWidth 和 MeasuredHeight。

**performLayout()**:根据测量结果确定 View 在窗口中的最终位置坐标(Left, Top, Right, Bottom),并触发 View 的 onLayout()。

**performDraw()**:启动绘制阶段,将 View 树的视觉信息转化为渲染指令。

源码:frameworks/base/core/java/android/view/ViewRootImpl.java

**Draw (Recording)**:ViewRootImpl.performDraw() -> ThreadedRenderer.draw() -> View.updateDisplayListIfDirty()。

源码:frameworks/base/core/java/android/view/ThreadedRenderer.java

注意:此处是在录制指令到 DisplayList,而非真正渲染。

参数价值:反映 布局复杂度 (Over-nesting) 和 主线程计算压力。

第三阶段:交接棒 (Handoff / Sync)核心指标:SYNC_DURATION(同步时长)

物理意义:UI 线程阻塞等待,将绘制指令和 Bitmap 资源同步给 RenderThread。

精确路径:

Java 端入口:ThreadedRenderer.nSyncAndDrawFrame() (JNI 调用)。

源码:frameworks/base/core/java/android/view/ThreadedRenderer.java

Native 端核心:CanvasContext::prepareTree()。

源码:frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

资源上传:在此阶段,新创建的 Bitmap 会被上传到 GPU 纹理缓存。

计算逻辑:记录从 UI 线程发起同步请求到 RenderThread 成功接收并完成资源拷贝,且 UI 线程被唤醒的时间。

参数价值:Bitmap 优化的核心指标。如果 SYNC 耗时高,通常是由于该帧上传了过大的 Bitmap 或触发了大量的资源回收与重新分配。

第四阶段:渲染线程的冲刺 (Recording & Issue)核心指标:COMMAND_ISSUE_DURATION(指令发布时长)

物理意义:RenderThread 将 DisplayList 转换为真正的 GPU 指令(OpenGL/Vulkan)并提交给驱动。

精确路径:

RenderThread 循环:RenderThread::threadLoop()。

源码:frameworks/base/libs/hwui/renderthread/RenderThread.cpp

核心执行:CanvasContext::draw() 调用渲染管线。

渲染管线:SkiaPipeline::renderFrame() (Android 8.0+ 默认使用 Skia)。

源码:frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp

参数价值:反映 绘制指令的复杂程度。DrawCalls 越多、Path 路径越复杂、或者使用了复杂的 Shader,此项耗时越高。

第五阶段:终点线 (GPU & Swap)核心指标:SWAP_BUFFERS_DURATION(缓冲区交换时长)

物理意义:RenderThread 调用 eglSwapBuffers 后,等待 GPU 渲染完成并交换缓冲区的阻塞时间。

精确路径:

核心函数:CanvasContext::swapBuffers()。

底层调用:EglManager::swapBuffers() -> eglSwapBuffers()。

源码:frameworks/base/libs/hwui/renderthread/EglManager.cpp

系统反馈:底层 SurfaceFlinger 消费 Buffer 的速度(涉及 BufferQueue)。

解析:如果 GPU 还没算完,或者系统显示队列已满(存在背压),此调用会阻塞 RenderThread,直到有可用的 Buffer。

面试价值:反映 GPU 渲染压力 (Overdraw) 或 系统三重缓冲 (Triple Buffering) 的饱和度。也是衡量 SurfaceFlinger 消费压力的关键点。

第六阶段:最终审判 (Judgment)核心指标:TOTAL_DURATION(总耗时) & frameOverrunNanos(帧超时时间) (API 31+)

物理意义:一帧从计划开始到最终出屏全链路总耗时及其与截止日期的偏离量。

精确路径:

Java 层:android.view.FrameMetrics

源码:frameworks/base/core/java/android/view/FrameMetrics.java

Native 层:google::hwui::FrameInfo

源码:frameworks/base/libs/hwui/FrameInfo.cpp

卡顿判定准则:

经典准则:TOTAL_DURATION > 16.6ms (针对 60Hz)。

**JankStats 准则 (API 31+)**:frameOverrunNanos > 0。即该帧物理上错过了 VSync 的 Deadline(最准确的掉帧判定)。

Q&A

Q:如果 UI 线程只用了 2ms,为什么还是掉帧了?

A:通过 FrameMetrics 分析,可能是 UNKNOWN_DELAY 极大(消息队列排队严重)或 SWAP_BUFFERS 阻塞严重(GPU 瓶颈或渲染队列满了)。

Q:SYNC_DURATION(同步时长)过高怎么排查?

A:重点检查该帧是否有大图加载、ImageView 设置了巨大的 Bitmap,或者是否在 onDraw 之外频繁触发了硬件资源更新。

Q:为什么硬件加速下,RenderThread 卡顿会影响 UI 线程?

A:因为 SYNC 阶段是同步的。UI 线程必须在 CanvasContext::prepareTree 期间等待 RenderThread 接收数据,只有等 RenderThread “接手”成功后,UI 线程才能继续处理下一个 Vsync。

Android 帧绘制耗时全链路源码溯源本文档详细记录了从 Java 层到 Native 层的 Android 帧刷新全链路核心函数(Android 15),并对源码关键逻辑进行了中文注释。

Java 层:UI 线程任务调度frameworks/base/core/java/android/view/

帧回调起点:Choreographer.doFrame()这是主线程响应 VSYNC 信号的总入口。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667void doFrame(long frameTimeNanos, int frame, DisplayEventReceiver.VsyncEventData vsyncEventData) { final long startNanos; final long frameIntervalNanos = vsyncEventData.frameInterval; boolean resynced = false; try { // 1. 更新当前帧的时间线数据(VsyncId、预期呈现时间、截止时间) FrameTimeline timeline = mFrameData.update(frameTimeNanos, vsyncEventData); if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + timeline.mVsyncId); } synchronized (mLock) { if (!mFrameScheduled) { return; // 如果当前没有安排帧,说明是过期的信号,直接返回 } // 2. 抖动计算 (Jitter):判断主线程是否存在耗时操作导致响应延迟 long intendedFrameTimeNanos = frameTimeNanos; // 信号发出的预期时间 startNanos = System.nanoTime(); // 实际开始处理的时间 final long jitterNanos = startNanos - frameTimeNanos; if (jitterNanos >= frameIntervalNanos) { // 如果延迟超过一个周期,说明主线程发生了卡顿 long lastFrameOffset = jitterNanos % frameIntervalNanos; // 修正 frameTime 为最近的一个模拟 VSYNC 点,保证动画平滑 frameTimeNanos = startNanos - lastFrameOffset; final long skippedFrames = jitterNanos / frameIntervalNanos; if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, "Skipped " + skippedFrames + " frames! 应用程序可能在主线程做了太多工作。"); } // 掉帧后重新同步时间线 timeline = mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos); resynced = true; } // 3. 记录帧统计信息,供后续 JankTracker 统计卡顿 mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.preferredFrameTimeline().vsyncId, vsyncEventData.preferredFrameTimeline().deadline, startNanos, vsyncEventData.frameInterval); mFrameScheduled = false; // 重置安排标记 mLastFrameTimeNanos = frameTimeNanos; } // 4. 锁定动画时钟,确保本帧内所有动画基于相同的时间基准 AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS, timeline.mExpectedPresentationTimeNanos); // 5. 调用 doCallbacks 依次按顺序执行回调:输入 -> 动画 -> 缩进动画 -> 遍历(Layout/Draw) -> 提交 mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameIntervalNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameIntervalNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameIntervalNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}

补充 VsyncEventData

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465#pragma once#include #include namespace android::gui {/** * VsyncEventData 是一个简单的 POD(Plain Old Data)结构,用于存储 Vsync 相关数据。 * 例如,它可以方便地在 DisplayEventReceiver::Event 的联合体(union)中使用。 */struct VsyncEventData { // 帧时间轴的最大容量,设置为 7 是为了在灵活性和内存开销之间取得平衡。 static constexpr int64_t kFrameTimelinesCapacity = 7; // 当前帧被调度时的帧间隔时间(纳秒)。反映了当前的屏幕刷新率。 int64_t frameInterval; // 系统推荐的首选帧时间轴(frameTimelines 数组中的索引)。 uint32_t preferredFrameTimelineIndex; // 平台当前提供的有效帧时间轴数量;最大值为 kFrameTimelinesCapacity。 uint32_t frameTimelinesLength; /** * FrameTimeline 定义了一个特定的渲染预期。 * 现代 Android 系统提供多个候选时间轴,允许应用根据其渲染能力选择最合适的呈现时刻。 */ struct alignas(8) FrameTimeline { // 与此 Vsync 事件对应的唯一 ID。 // 此 ID 将用于填充 ISurfaceComposer::setFrameTimelineVsync 等接口, // 以便系统能够追踪特定帧的流水线状态。 int64_t vsyncId; // 应用必须在该时刻之前完成其帧渲染任务(包括 CPU 和 GPU 上的所有工作)。 // 使用 CLOCK_MONOTONIC 单调时钟,单位为纳秒。 int64_t deadlineTimestamp; // 预期该帧画面在物理屏幕上真正显示的时刻(纳秒)。 int64_t expectedPresentationTime; } frameTimelines[kFrameTimelinesCapacity]; // 按时间顺序排序的候选帧时间轴。 // 获取首选帧时间轴的 Vsync ID。 int64_t preferredVsyncId() const; // 获取首选帧时间轴的截止时间戳(Deadline)。 int64_t preferredDeadlineTimestamp() const; // 获取首选帧时间轴的预期呈现时间戳。 int64_t preferredExpectedPresentationTime() const;};/** * ParcelableVsyncEventData 是 VsyncEventData 的包装类, * 继承自 Parcelable,使其能够通过 Binder 在进程间进行序列化传输。 */struct ParcelableVsyncEventData : public Parcelable { VsyncEventData vsync; status_t readFromParcel(const Parcel*) override; status_t writeToParcel(Parcel*) const override;};} // namespace android::gui

接过来自底层 DisplayEventReceiver.cpp 传上来的“发令枪响时间” (frameTimeNanos)。记录:把主线程此时此刻的“起跑时间” (startNanos) 记录下来。UNKNOWN_DELAY_DURATION = startNanos - frameTimeNanos

任务分发:Choreographer.doCallbacks()这是 Choreographer 内部用于依次执行各阶段任务(输入、动画、遍历、提交)的核心逻辑。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152/** * 执行指定类型的回调任务。 * * @param callbackType 回调类型(INPUT, ANIMATION, INSETS_ANIMATION, TRAVERSAL, COMMIT) * @param frameIntervalNanos 当前设备的帧间隔(例如 60Hz 约为 16.6ms) */void doCallbacks(int callbackType, long frameIntervalNanos) { CallbackRecord callbacks; long frameTimeNanos = mFrameData.mFrameTimeNanos; synchronized (mLock) { final long now = System.nanoTime(); // 提取所有“到期”的回调 callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true; // 特殊处理:提交阶段 (CALLBACK_COMMIT),修正帧时间基准以应对严重掉帧 if (callbackType == Choreographer.CALLBACK_COMMIT) { final long jitterNanos = now - frameTimeNanos; if (frameIntervalNanos > 0 && jitterNanos >= 2 * frameIntervalNanos) { final long lastFrameOffset = jitterNanos % frameIntervalNanos + frameIntervalNanos; frameTimeNanos = now - lastFrameOffset; mLastFrameTimeNanos = frameTimeNanos; mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos); } } } try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); for (CallbackRecord c = callbacks; c != null; c = c.next) { // 执行回调。对于 CALLBACK_TRAVERSAL 类型,会进入 ViewRootImpl.performTraversals() c.run(mFrameData); } } finally { synchronized (mLock) { mCallbacksRunning = false; do { final CallbackRecord next = callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null); } Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}

修正帧时间基准以应对严重掉帧123456789if (callbackType == Choreographer.CALLBACK_COMMIT) { final long jitterNanos = now - frameTimeNanos; if (frameIntervalNanos > 0 && jitterNanos >= 2 * frameIntervalNanos) { final long lastFrameOffset = jitterNanos % frameIntervalNanos + frameIntervalNanos; frameTimeNanos = now - lastFrameOffset; mLastFrameTimeNanos = frameTimeNanos; mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos); } }

这在 Choreographer 的底层逻辑中是一个非常重要的保护机制,用于解决因严重的掉帧或主线程卡顿导致的 “逻辑时间偏差”。

核心意图:为什么要进行这一步计算?在 Android 的动画(Animation)和图形渲染中,所有计算都是基于 frameTimeNanos 这个基准的。计算逻辑通常如下:

progress = (currentTime - startTime) / duration

如果系统发生了严重的卡顿(例如主线程阻塞了 500ms),frameTimeNanos 依然停留在 500ms 之前。如果不进行校正,当代码恢复执行时,程序会使用这个“过时”的 frameTimeNanos。这会导致:

动画“跳变”:动画会瞬间计算出很大的 delta,导致元素直接从起点“瞬移”到终点,产生视觉上的闪烁。

物理引擎异常:如果你的代码里有基于物理模拟的逻辑,巨大的时间差会导致计算出的速度、位移爆炸。

代码逻辑拆解这段代码在 CALLBACK_COMMIT 阶段触发,其逻辑是在检测到极度严重掉帧时,强行将“当前的逻辑帧时间”重置到最近的一个有效时间点。

**jitterNanos (抖动/延迟量)**:

1jitterNanos = now - frameTimeNanos

这是当前物理时间(now)距离该帧预期执行时间(frameTimeNanos)的偏移量。如果该值很大,说明我们在这一帧上滞后了很多。

**阈值判断 (jitterNanos >= 2 \* frameIntervalNanos)**:

这是为了避免误判。只有当延迟超过 2 个帧周期(在 60Hz 下约 > 33.3ms)时,才会介入。这说明系统已经错过了至少一个 VSYNC 信号,进入了严重的掉帧状态。

**重新对齐 frameTimeNanos**:

12final long lastFrameOffset = jitterNanos % frameIntervalNanos + frameIntervalNanos;frameTimeNanos = now - lastFrameOffset;

这里将 frameTimeNanos 设置为 now 减去一个偏移量。这个偏移量被强制限制在 frameIntervalNanos 到 2 * frameIntervalNanos 之间。

效果:它将 frameTimeNanos “强制拉回”到距离当前 now 最近的、符合渲染周期节奏的时间点。

可视化理解上图展示了当发生卡顿时,虚拟时间轴如何被校准以平滑后续动画。

状态

逻辑时间点

结果

正常情况

frameTimeNanos 是旧的信号时间

动画平滑,delta 很小。

严重卡顿

frameTimeNanos 滞后 > 33ms

如果不修正,动画下一帧会因为 currentTime - frameTimeNanos 过大而发生“ teleport” (瞬移)。

修正后

frameTimeNanos 被重置到 now - interval

动画认为上一帧是 16.6ms 前,从而继续平滑过渡,掩盖了卡顿期间的时间断层。

与 View 相关的耗时统计核心分类逻辑:通过 callbackType 实现“打标签”Choreographer 内部维护了一个数组:

1234567/** * 必须与下面的 CALLBACK_* 整数保持同步,用于索引此数组。 * @hide */private static final String[] CALLBACK_TRACE_TITLES = { "input", "animation", "insets_animation", "traversal", "commit"};

当 doCallbacks(int callbackType, ...) 被调用时,代码执行的是: Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);

对于 Input:传入 callbackType = 0,字符串就是 "input"。

对于 Animation:传入 callbackType = 1,字符串就是 "animation"。

核心对应关系表

CALLBACK_TRACE_TITLES (Trace 标签)

FrameMetrics 对应指标 (Duration)

核心职能

“input”

INPUT_HANDLING_DURATION

处理 MotionEvent、按键事件等。

“animation”

ANIMATION_DURATION

处理 ValueAnimator 等动画逻辑。

“insets_animation”

INSETS_ANIMATION_DURATION

处理窗口插屏(如键盘弹出)动画。

“traversal”

LAYOUT_MEASURE_DURATION

执行 performTraversals,即 Measure + Layout。

“commit”

SYNC_DURATION

同步 RenderNode 数据到渲染线程 (RenderThread)。

核心负载:Choreographer.FrameData封装了 VSYNC 信号的时间戳和多组呈现时间线(Frame Timeline),这是现代 Android 系统平滑显示的核心数据结构。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131/** * {@link VsyncCallback} 的负载,包括帧信息(例如开始渲染帧的时间) * 以及多个可能的帧时间轴及其信息(包括截止时间和预期呈现时间)。 */public static class FrameData { // 当前帧的时间戳(纳秒),用于动画计算的基准 private long mFrameTimeNanos; // 包含多个候选的帧时间轴。每个时间轴对应不同的预期呈现时间 private FrameTimeline[] mFrameTimelines; // 系统推荐的时间轴索引。如果应用无法在 16.6ms 内完成,可能会调整此索引 private int mPreferredFrameTimelineIndex; // 标记当前是否处于 vsync 回调处理流程中,防止非法访问 private boolean mInCallback = false; FrameData() { // 初始化时间轴数组,容量通常由 DisplayEventReceiver 决定 allocateFrameTimelines(DisplayEventReceiver.VsyncEventData.FRAME_TIMELINES_CAPACITY); } /** 开始渲染帧的时间(以纳秒为单位)。 */ public long getFrameTimeNanos() { checkInCallback(); return mFrameTimeNanos; } /** 可能的帧时间轴,按时间顺序排序。包含不同刷新率下的预测数据。 */ @NonNull @SuppressLint("ArrayReturn") // 为了 API 一致性和速度。 public FrameTimeline[] getFrameTimelines() { checkInCallback(); return mFrameTimelines; } /** 平台首选的帧时间轴。应用应优先尝试达到此时间轴定义的 deadline。 */ @NonNull public FrameTimeline getPreferredFrameTimeline() { checkInCallback(); return mFrameTimelines[mPreferredFrameTimelineIndex]; } /** 设置回调状态标记 */ void setInCallback(boolean inCallback) { mInCallback = inCallback; for (int i = 0; i < mFrameTimelines.length; i++) { mFrameTimelines[i].setInCallback(inCallback); } } /** 安全检查,确保 FrameData 仅在 VSYNC 回调期间被有效读取 */ private void checkInCallback() { if (!mInCallback) { throw new IllegalStateException( "FrameData 在 vsync 回调之外无效"); } } /** 分配时间轴存储空间 */ private void allocateFrameTimelines(int length) { // 维护一个默认帧时间轴以保持 API(如 getFrameTimelines 和 getPreferredFrameTimeline)的一致性。 length = Math.max(1, length); if (mFrameTimelines == null || mFrameTimelines.length != length) { mFrameTimelines = new FrameTimeline[length]; for (int i = 0; i < mFrameTimelines.length; i++) { mFrameTimelines[i] = new FrameTimeline(); } } } /** * 正常更新:使用从底层 DisplayEventReceiver 接收的 Vsync 数据同步更新。 */ FrameTimeline update( long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) { allocateFrameTimelines(vsyncEventData.frameTimelinesLength); mFrameTimeNanos = frameTimeNanos; mPreferredFrameTimelineIndex = vsyncEventData.preferredFrameTimelineIndex; for (int i = 0; i < mFrameTimelines.length; i++) { DisplayEventReceiver.VsyncEventData.FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i]; mFrameTimelines[i].update(frameTimeline.vsyncId, frameTimeline.expectedPresentationTime, frameTimeline.deadline); } return mFrameTimelines[mPreferredFrameTimelineIndex]; } /** * 掉帧后补偿更新:当主线程延迟启动(jitterNanos > frameInterval)时,重新寻找一个有效的时间轴。 * * @param frameTimeNanos 修正后的帧时间 * @param displayEventReceiver 用于在本地缓存失效时向系统索要最新 Vsync 数据 * @param jitterNanos 实际开始时间与预定时间的偏差(抖动量) */ FrameTimeline update( long frameTimeNanos, DisplayEventReceiver displayEventReceiver, long jitterNanos) { int newPreferredIndex = 0; // 计算新的最低可接受截止时间 final long minimumDeadline = mFrameTimelines[mPreferredFrameTimelineIndex].mDeadlineNanos + jitterNanos; // 1. 在现有时间轴中查找一个 deadline 尚未过期的时间轴 while (newPreferredIndex < mFrameTimelines.length - 1 && mFrameTimelines[newPreferredIndex].mDeadlineNanos < minimumDeadline) { newPreferredIndex++; } long newPreferredDeadline = mFrameTimelines[newPreferredIndex].mDeadlineNanos; // 2. 如果现有的时间轴全部已经过期(主线程卡顿太久),则发起 Binder 跨进程调用 // 获取 SurfaceFlinger 端最新的 VsyncEventData if (newPreferredDeadline < minimumDeadline) { DisplayEventReceiver.VsyncEventData latestVsyncEventData = displayEventReceiver.getLatestVsyncEventData(); if (latestVsyncEventData == null) { Log.w(TAG, "无法获取最新的 VsyncEventData。SurfaceFlinger 是否崩溃了?"); } else { update(frameTimeNanos, latestVsyncEventData); } } else { // 3. 否则,直接更新为本地查找到的更靠后的有效时间轴 update(frameTimeNanos, newPreferredIndex); } return mFrameTimelines[mPreferredFrameTimelineIndex]; } /** 内部状态更新 */ void update(long frameTimeNanos, int newPreferredFrameTimelineIndex) { mFrameTimeNanos = frameTimeNanos; mPreferredFrameTimelineIndex = newPreferredFrameTimelineIndex; }}

遍历起点:ViewRootImpl.performTraversals()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680private void performTraversals() { // 缓存 mView 到本地变量 host,因为在整个遍历过程中会频繁使用,且需要保证一致性 final View host = mView; // 如果调试模式开启,打印调试信息 if (DBG) { System.out.println("======================================"); System.out.println("performTraversals"); host.debug(); } // 检查 host 是否为空或者是否尚未添加,如果满足其中之一,记录原因并直接返回 if (host == null || !mAdded) { mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added"; return; } // 如果存在同步请求导致的暂停计数,则记录 Trace 事件并跳过本次遍历 if (mNumPausedForSync > 0) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.instant(Trace.TRACE_TAG_VIEW, TextUtils.formatSimple("performTraversals#mNumPausedForSync=%d", mNumPausedForSync)); } if (DEBUG_BLAST) { Log.d(mTag, "Skipping traversal due to sync " + mNumPausedForSync); } mLastPerformTraversalsSkipDrawReason = "paused_for_sync"; return; } // 标记当前正在遍历中 mIsInTraversal = true; // 标记即将进行绘制 mWillDrawSoon = true; // 绘制取消标记及原因初始化 boolean cancelDraw = false; String cancelReason = null; // 是否为同步请求标记 boolean isSyncRequest = false; // 窗口大小是否可能发生变化的标志位 boolean windowSizeMayChange = false; // 获取窗口当前的布局参数 WindowManager.LayoutParams lp = mWindowAttributes; // 期望的窗口宽高 int desiredWindowWidth; int desiredWindowHeight; // 获取当前视图可见性及其原因 final int viewVisibility = getHostVisibility(); final String viewVisibilityReason = getHostVisibilityReason(); // 检查可见性是否改变:非首次且 (记录的可见性不等 或 需要新 Surface 或 应用可见性改变) final boolean viewVisibilityChanged = !mFirst && (mViewVisibility != viewVisibility || mNewSurfaceNeeded || mAppVisibilityChanged); // 重置应用可见性改变标记 mAppVisibilityChanged = false; // 检查用户感知的可见性状态是否发生了实质性切换(VISIBLE 与非 VISIBLE 之间) final boolean viewUserVisibilityChanged = !mFirst && ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE)); // 是否应该优化测量过程 final boolean shouldOptimizeMeasure = shouldOptimizeMeasure(lp); // 初始化 relayout 相关的参数和帧引用 WindowManager.LayoutParams params = null; Rect frame = mWinFrame; // 如果是第一次执行 performTraversals if (mFirst) { // 第一次必须执行完整重绘和布局 mFullRedrawNeeded = true; mLayoutRequested = true; // 获取资源配置 final Configuration config = getConfiguration(); // 根据布局参数确定期望的窗口尺寸 if (shouldUseDisplaySize(lp)) { // 系统代码:直接获取屏幕真实物理尺寸 Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { // Wrap content 情况,获取系统栏内边距后的可用边界 final Rect bounds = getWindowBoundsInsetSystemBars(); desiredWindowWidth = bounds.width(); desiredWindowHeight = bounds.height(); } else { // 默认使用窗口管理器的 frameHint 尺寸,减少后续 relayout 导致的重测 desiredWindowWidth = frame.width(); desiredWindowHeight = frame.height(); } // 现代窗口默认 32 位,设置绘制标志位 mAttachInfo.mUse32BitDrawingCache = true; // 初始化 AttachInfo 中的可见性状态 mAttachInfo.mWindowVisibility = viewVisibility; // 禁止全局属性重计算 mAttachInfo.mRecomputeGlobalAttributes = false; // 备份当前配置 mLastConfigurationFromResources.setTo(config); // 备份系统 UI 可见性 mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; // 若初始方向为继承,则设置宿主视图的布局方向 if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { host.setLayoutDirection(config.getLayoutDirection()); } // 分发视图附加到窗口的消息,这会触发所有子视图的 onAttachedToWindow host.dispatchAttachedToWindow(mAttachInfo, 0); // 通知 TreeObserver 窗口已附加 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true); // 首次应用 Insets dispatchApplyInsets(host); // 如果未启用新版返回拦截且无自定义回调,则注册兼容性返回键回调 if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled() && mWindowlessBackKeyCallback == null) { registerCompatOnBackInvokedCallback(); } } else { // 非首次,直接取当前帧的宽高 desiredWindowWidth = frame.width(); desiredWindowHeight = frame.height(); // 如果宽高与当前记录的不符,标记需要布局和重绘 if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame); mFullRedrawNeeded = true; mLayoutRequested = true; windowSizeMayChange = true; } } // 处理可见性变化后的分发和资源清理 if (viewVisibilityChanged) { mAttachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); mAttachInfo.mTreeObserver.dispatchOnWindowVisibilityChange(viewVisibility); if (viewUserVisibilityChanged) { host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); } // 若不可见或需要新 Surface,结束缩放并销毁硬件资源 if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { endDragResizing(); destroyHardwareResources(); } // 可见时尝试提升帧率 if (shouldEnableDvrr() && viewVisibility == View.VISIBLE) { boostFrameRate(FRAME_RATE_BOOST_TIME); } } // 不可见窗口清理辅助功能焦点 if (mAttachInfo.mWindowVisibility != View.VISIBLE) { host.clearAccessibilityFocus(); } // 执行运行队列中的动作(如 View.post) getRunQueue().executeActions(mAttachInfo.mHandler); // 首次遍历初始化触摸模式 if (mFirst) { mAttachInfo.mInTouchMode = !mAddedTouchMode; ensureTouchModeLocally(mAddedTouchMode); } // 测量阶段:若请求了布局且非停止状态 boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); if (layoutRequested) { if (!mFirst) { // 非首次,针对 wrap content 再次评估期望宽高 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { windowSizeMayChange = true; if (shouldUseDisplaySize(lp)) { Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else { final Rect bounds = getWindowBoundsInsetSystemBars(); desiredWindowWidth = bounds.width(); desiredWindowHeight = bounds.height(); } } } // 核心测量逻辑:计算视图树的大小 windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure); } // 收集并可能更新窗口属性 if (collectViewAttributes()) { params = lp; } if (mAttachInfo.mForceReportNewAttributes) { mAttachInfo.mForceReportNewAttributes = false; params = lp; } // 软键盘调整模式的动态确定 if (mFirst || mAttachInfo.mViewVisibilityChanged) { mAttachInfo.mViewVisibilityChanged = false; int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST; if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { final int N = mAttachInfo.mScrollContainers.size(); for (int i=0; i mLastSyncSeqId) { mLastSyncSeqId = mSyncSeqId; if (DEBUG_BLAST) { Log.d(mTag, "Relayout called with blastSync"); } reportNextDraw("relayout"); mSyncBuffer = true; isSyncRequest = true; if (!cancelDraw) { mDrewOnceForSync = false; } } final boolean surfaceControlChanged = (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED) == RELAYOUT_RES_SURFACE_CHANGED; if (mSurfaceControl.isValid()) { updateOpacity(mWindowAttributes, dragResizing, surfaceControlChanged /*forceUpdate */); } } catch (RemoteException e) { } finally { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } if (DEBUG_ORIENTATION) Log.v( TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface); // 更新 AttachInfo 中的窗口偏移 mAttachInfo.mWindowLeft = frame.left; mAttachInfo.mWindowTop = frame.top; // 修正本地宽高记录 if (mWidth != frame.width() || mHeight != frame.height()) { mWidth = frame.width(); mHeight = frame.height(); } // 同步 SurfaceHolder 的回调和状态 if (mSurfaceHolder != null) { if (mSurface.isValid()) { mSurfaceHolder.mSurface = mSurface; } mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight); mSurfaceHolder.mSurfaceLock.unlock(); if (surfaceCreated) { mSurfaceHolder.ungetCallbacks(); mIsCreating = true; SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { c.surfaceCreated(mSurfaceHolder); } } } if ((surfaceCreated || surfaceReplaced || surfaceSizeChanged || windowAttributesChanged) && mSurface.isValid()) { SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { c.surfaceChanged(mSurfaceHolder, lp.format, mWidth, mHeight); } } mIsCreating = false; } if (surfaceDestroyed) { notifyHolderSurfaceDestroyed(); mSurfaceHolder.mSurfaceLock.lock(); try { mSurfaceHolder.mSurface = new Surface(); } finally { mSurfaceHolder.mSurfaceLock.unlock(); } } } // 配置 ThreadedRenderer final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer; if (threadedRenderer != null && threadedRenderer.isEnabled()) { if (hwInitialized || mWidth != threadedRenderer.getWidth() || mHeight != threadedRenderer.getHeight() || mNeedsRendererSetup) { threadedRenderer.setup(mWidth, mHeight, mAttachInfo, mWindowAttributes.surfaceInsets); mNeedsRendererSetup = false; } } // Relayout 后的最终测量:处理因 relayout 导致的尺寸差异 if (!mStopped || mReportNextDraw) { if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || dispatchApplyInsets || updatedConfiguration) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width, lp.privateFlags); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height, lp.privateFlags); if (DEBUG_LAYOUT) Log.v(mTag, "糟糕,出了一些变化! mWidth=" + mWidth + " measuredWidth=" + host.getMeasuredWidth() + " mHeight=" + mHeight + " measuredHeight=" + host.getMeasuredHeight() + " dispatchApplyInsets=" + dispatchApplyInsets); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 实现权重分配逻辑 int width = host.getMeasuredWidth(); int height = host.getMeasuredHeight(); boolean measureAgain = false; if (lp.horizontalWeight > 0.0f) { width += (int) ((mWidth - width) * lp.horizontalWeight); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); measureAgain = true; } if (lp.verticalWeight > 0.0f) { height += (int) ((mHeight - height) * lp.verticalWeight); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measureAgain = true; } if (measureAgain) { if (DEBUG_LAYOUT) Log.v(mTag, "嘿,让我们再测量一次:width=" + width + " height=" + height); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); } layoutRequested = true; } } } else { // 简单处理移动 maybeHandleWindowMove(frame); } // 处理之前推迟的测量 if (mViewMeasureDeferred) { performMeasure( MeasureSpec.makeMeasureSpec(frame.width(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(frame.height(), MeasureSpec.EXACTLY)); } // 布局阶段判定 final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); if (didLayout) { // 执行真正的布局分发 performLayout(lp, mWidth, mHeight); } // 处理焦点请求 if (mFirst) { if (sAlwaysAssignFocus || !isInTouchMode()) { if (DEBUG_INPUT_RESIZE) { Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus()); } if (mView != null) { if (!mView.hasFocus()) { mView.restoreDefaultFocus(); if (DEBUG_INPUT_RESIZE) { Log.v(mTag, "First: requested focused view=" + mView.findFocus()); } } else { if (DEBUG_INPUT_RESIZE) { Log.v(mTag, "First: existing focused view=" + mView.findFocus()); } } } } else { View focused = mView.findFocus(); if (focused instanceof ViewGroup && ((ViewGroup) focused).getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS) { focused.restoreDefaultFocus(); } } if (shouldEnableDvrr()) { boostFrameRate(FRAME_RATE_BOOST_TIME); } } // 触发辅助功能状态改变事件 final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible; if (changedVisibility) { maybeFireAccessibilityWindowStateChangedEvent(); } // 重置首次遍历和准备绘制标记 mFirst = false; mWillDrawSoon = false; mNewSurfaceNeeded = false; mViewVisibility = viewVisibility; // 通知 IME final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible; mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); // 首次 relayout 报告 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { reportNextDraw("first_relayout"); } mCheckIfCanDraw = isSyncRequest || cancelDraw; // 【关键点】PreDraw 回调分发,允许在此拦截并取消绘制 boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw(); boolean cancelAndRedraw = cancelDueToPreDrawListener || (cancelDraw && mDrewOnceForSync); // 如果不取消,准备同步和开始绘制通知 if (!cancelAndRedraw) { if (mActiveSurfaceSyncGroup != null) { mSyncBuffer = true; } createSyncIfNeeded(); notifyDrawStarted(isInWMSRequestedSync()); mDrewOnceForSync = true; if (mActiveSurfaceSyncGroup != null && mSyncBuffer) { updateSyncInProgressCount(mActiveSurfaceSyncGroup); safeguardOverlappingSyncs(mActiveSurfaceSyncGroup); } } // 最终绘制决策 if (!isViewVisible) { // 不可见,跳过绘制 if (mLastTraversalWasVisible) { logAndTrace("Not drawing due to not visible. Reason=" + viewVisibilityReason); } mLastPerformTraversalsSkipDrawReason = "view_not_visible"; if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { mPendingTransitions.get(i).endChangingAnimations(); } mPendingTransitions.clear(); } handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions, mPendingTransaction, "view not visible"); } else if (cancelAndRedraw) { // 被取消绘制,重新请求遍历 if (!mWasLastDrawCanceled) { logAndTrace("Canceling draw." + " cancelDueToPreDrawListener=" + cancelDueToPreDrawListener + " cancelDueToSync=" + (cancelDraw && mDrewOnceForSync)); } mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason() : "cancel_" + cancelReason; scheduleTraversals(); } else { // 执行绘制 if (mWasLastDrawCanceled) { logAndTrace("Draw frame after cancel"); } if (!mLastTraversalWasVisible) { logAndTrace("Start draw after previous draw not visible"); } if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { mPendingTransitions.get(i).startChangingAnimations(); } mPendingTransitions.clear(); } // 【核心】执行绘制流程 if (!performDraw(mActiveSurfaceSyncGroup)) { handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions, mPendingTransaction, mLastPerformDrawSkippedReason); } } // 状态位更新 mWasLastDrawCanceled = cancelAndRedraw; mLastTraversalWasVisible = isViewVisible; if (mAttachInfo.mContentCaptureEvents != null) { notifyContentCaptureEvents(); } mIsInTraversal = false; mRelayoutRequested = false; // 善后清理 if (!cancelAndRedraw) { mReportNextDraw = false; mLastReportNextDrawReason = null; mActiveSurfaceSyncGroup = null; if (mHasPendingTransactions) { mPendingTransaction.apply(); mHasPendingTransactions = false; } mSyncBuffer = false; if (isInWMSRequestedSync()) { mWmsRequestSyncGroup.markSyncReady(); mWmsRequestSyncGroup = null; mWmsRequestSyncGroupState = WMS_SYNC_NONE; } } // VRR 帧率动态调整逻辑 if (mDrawnThisFrame) { mDrawnThisFrame = false; if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) { mInvalidationIdleMessagePosted = true; mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS); } // 设定最终的首选帧率和类别 setCategoryFromCategoryCounts(); updateInfrequentCount(); updateFrameRateFromThreadedRendererViews(); setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); if (mPreferredFrameRate > 0 || (mLastPreferredFrameRate != 0 && mPreferredFrameRate == 0) ) { mHandler.removeMessages(MSG_FRAME_RATE_SETTING); mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING, FRAME_RATE_SETTING_REEVALUATE_TIME); } // 计数器衰减 mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0 ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount; mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0 ? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount; mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0 ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount; mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT; mPreferredFrameRate = -1; mIsFrameRateConflicted = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } else if (mPreferredFrameRate == 0) { // 重置 setPreferredFrameRate(0); mPreferredFrameRate = -1; }}

指令录制:ThreadedRenderer.draw()将 UI 线程的 Java 代码绘制逻辑录制为 Native 层的 DisplayList。

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253/** * 绘制指定的视图。 * * @param view 要绘制的视图(通常是 DecorView)。 * @param attachInfo 绑定到该视图的 AttachInfo 包含渲染所需的状态信息。 * @param callbacks 绘制过程中的回调接口。 */void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) { // 1. 记录绘制开始的时间点,用于性能监控和 FrameMetrics 统计 attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart(); // 2. 更新根显示列表(DisplayList)。 // 这一步会遍历视图树,录制每个 View 的绘制指令(如 drawRect, drawText 等)。 updateRootDisplayList(view, callbacks); // 3. 处理在渲染器创建之前就开始执行的动画。 // 典型的场景是在第一帧绘制前就启动了动画,这些动画节点需要注册到当前的硬件渲染器中。 if (attachInfo.mPendingAnimatingRenderNodes != null) { final int count = attachInfo.mPendingAnimatingRenderNodes.size(); for (int i = 0; i < count; i++) { // 将待处理的动画渲染节点注册到渲染引擎 registerAnimatingRenderNode( attachInfo.mPendingAnimatingRenderNodes.get(i)); } // 清空并释放待处理列表 attachInfo.mPendingAnimatingRenderNodes.clear(); // 之后的动画会通过 ViewRootImpl#attachRenderNodeAnimator 直接注册,不再需要此暂存列表 attachInfo.mPendingAnimatingRenderNodes = null; } // 4. 获取当前帧最新的信息(包含各种时间戳和状态) final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo(); // 5. 核心步骤:同步并绘制。 // 此函数会将 UI 线程录制的绘制信息“同步”到渲染线程(RenderThread), // 并触发 GPU 开始执行实际的绘制操作。 int syncResult = syncAndDrawFrame(frameInfo); // 6. 错误处理:如果 Surface 丢失 if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { Log.w("HWUI", "Surface 丢失,强制进行重新布局 (relayout)"); // 标记下次遍历时强制执行 WindowManager 的 relayout, // 期望从中获取一个新的、有效的 Surface。 attachInfo.mViewRootImpl.mForceNextWindowRelayout = true; attachInfo.mViewRootImpl.requestLayout(); } // 7. 调度重绘:如果同步结果要求重绘 // 例如:动画正在运行,或者渲染引擎需要再画一帧来更新状态。 if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) { attachInfo.mViewRootImpl.invalidate(); }}

Native 层:RenderThread 异步渲染 (frameworks/base/libs/hwui/)资源同步:CanvasContext::prepareTree()在正式绘制前,将 UI 线程录制的变化应用到 Native 渲染树中。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146/** * 准备渲染树(Sync 阶段)。 * 此方法在渲染线程(RenderThread)中执行,负责将 UI 线程的变更同步到渲染节点。 * * @param info 渲染树信息,用于存储同步过程中的各种状态(如是否有动画、脏区等) * @param uiFrameInfo 指向 UI 线程传递过来的帧时间戳数据的指针 * @param syncQueued 同步请求进入队列的时间点 * @param target 目标渲染节点(通常是根节点) */void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target) { // 1. 清除之前可能存在的帧回调,准备开始新的帧流程 mRenderThread.removeFrameCallback(this); // 2. 检查上一帧是否被跳过,并维护跳帧信息 const auto reason = wasSkipped(mCurrentFrameInfo); if (reason.has_value()) { if (!mSkippedFrameInfo) { switch (*reason) { case SkippedFrameReason::AlreadyDrawn: case SkippedFrameReason::NoBuffer: case SkippedFrameReason::NoOutputTarget: // 记录因技术原因(非业务逻辑)跳过的帧信息,用于后续的 FrameTimeline 统计 mSkippedFrameInfo.emplace(); mSkippedFrameInfo->vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); break; case SkippedFrameReason::DrawingOff: case SkippedFrameReason::ContextIsStopped: case SkippedFrameReason::NothingToDraw: break; } } } else { // 如果上一帧正常执行,则开启 JankTracker 对新一帧的监控 mCurrentFrameInfo = mJankTracker.startFrame(); mSkippedFrameInfo.reset(); } // 3. 导入 UI 线程的时间信息,并记录同步开始的时间戳 mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued; mCurrentFrameInfo->markSyncStart(); // 4. 初始化同步环境 info.damageAccumulator = &mDamageAccumulator; // 用于累加每一层产生的脏区 info.layerUpdateQueue = &mLayerUpdateQueue; // 待更新的硬件图层队列 info.damageGenerationId = mDamageId++; info.out.skippedFrameReason = std::nullopt; // 5. 遍历渲染节点树,执行实际的同步操作(核心递归逻辑) mAnimationContext->startFrame(info.mode); for (const sp& node : mRenderNodes) { // 如果节点是目标节点,执行完整同步;否则仅同步渲染线程相关的部分 info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY); node->prepareTree(info); // 这里会递归调用所有子 View 的同步逻辑 GL_CHECKPOINT(MODERATE); } // 运行剩余的渲染线程动画(如 RenderNodeAnimator) mAnimationContext->runRemainingAnimations(info); GL_CHECKPOINT(MODERATE); // 6. 释放预取但不再需要的图层 freePrefetchedLayers(); GL_CHECKPOINT(MODERATE); mIsDirty = true; // 7. 各种跳帧预判(性能优化) // 7.1 检查是否有渲染输出目标(Surface) if (CC_UNLIKELY(!hasOutputTarget())) { info.out.skippedFrameReason = SkippedFrameReason::NoOutputTarget; mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); return; } // 7.2 检查是否已经在极短时间内(2ms内)画过同一帧 Vsync,避免重复渲染 if (CC_LIKELY(mSwapHistory.size() && !info.forceDrawFrame)) { nsecs_t latestVsync = mRenderThread.timeLord().latestVsync(); SwapHistory& lastSwap = mSwapHistory.back(); nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync); if (vsyncDelta < 2_ms) { info.out.skippedFrameReason = SkippedFrameReason::AlreadyDrawn; } } else { info.out.skippedFrameReason = std::nullopt; } // 7.3 检查内容是否可渲染(例如只有 2 个节点且第二个节点内容为空) if (mRenderNodes.size() > 2 && !mRenderNodes[1]->isRenderable()) { info.out.skippedFrameReason = SkippedFrameReason::NothingToDraw; } // 8. 关键步骤:尝试从 BufferQueue 预留(reserve)一个缓冲区 if (!info.out.skippedFrameReason) { int err = mNativeSurface->reserveNext(); if (err != OK) { // 如果拿不到 Buffer(通常是 SurfaceFlinger 消费太慢),标记本帧跳过 info.out.skippedFrameReason = SkippedFrameReason::NoBuffer; mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); ALOGW("reserveNext 失败,错误 = %d (%s)", err, strerror(-err)); if (err != TIMED_OUT) { setSurface(nullptr); // 严重错误则断开 Surface return; } } } else { mCurrentFrameInfo->setSkippedFrameReason(*info.out.skippedFrameReason); } // 9. 处理动画和重绘逻辑 bool postedFrameCallback = false; if (info.out.hasAnimations || info.out.skippedFrameReason) { // 如果渲染线程有动画,或者由于某种原因本帧没画,需要请求下一帧 if (CC_UNLIKELY(!Properties::enableRTAnimations)) { info.out.requiresUiRedraw = true; } if (!info.out.requiresUiRedraw) { // 在渲染线程注册一个 Vsync 回调,以驱动动画持续运行 mRenderThread.postFrameCallback(this); postedFrameCallback = true; } } // 10. 处理动图(如 GIF/WebP)的播放延迟逻辑 if (!postedFrameCallback && info.out.animatedImageDelay != TreeInfo::Out::kNoAnimatedImageDelay) { const nsecs_t kFrameTime = mRenderThread.timeLord().frameIntervalNanos(); if (info.out.animatedImageDelay <= kFrameTime) { // 如果延迟小于一帧时间,立即请求下一帧 mRenderThread.postFrameCallback(this); } else { // 否则,在指定延迟后请求重绘 const auto delay = info.out.animatedImageDelay - kFrameTime; int genId = mGenerationID; mRenderThread.queue().postDelayed(delay, [this, genId]() { if (mGenerationID == genId) { mRenderThread.postFrameCallback(this); } }); } }}

绘制与提交:CanvasContext::draw()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248/** * 执行实际的绘制操作。 * 这是渲染管线的核心阶段:计算脏区 -> 调用 GPU 绘制 -> 交换缓冲区 -> 报告性能指标。 * * @param solelyTextureViewUpdates 是否仅更新 TextureView(影响性能提示策略) */void CanvasContext::draw(bool solelyTextureViewUpdates) {#ifdef __ANDROID__ // 1. 安全检查:确保 GPU 上下文(GrContext)可用且未丢失 if (auto grContext = getGrContext()) { if (grContext->abandoned()) { if (grContext->isDeviceLost()) { LOG_ALWAYS_FATAL("意外丢失 GPU 设备"); return; } LOG_ALWAYS_FATAL("在 CanvasContext::draw 开始时 GrContext 已被放弃"); return; } }#endif // 2. 计算并获取当前帧的脏区(需要重新绘制的矩形区域) SkRect dirty; mDamageAccumulator.finish(&dirty); // 暂存并重置同步延迟和空闲时长(用于后续性能计算) nsecs_t syncDelayDuration = mSyncDelayDuration; nsecs_t idleDuration = mIdleDuration; mSyncDelayDuration = 0; mIdleDuration = 0; // 3. 跳帧逻辑检查:判断这一帧是否真的需要绘制 const auto skippedFrameReason = [&]() -> std::optional { if (!Properties::isDrawingEnabled()) { return SkippedFrameReason::DrawingOff; // 全局绘制已关闭 } // 如果脏区为空,且允许跳过空帧,且 Surface 不需要强制重绘,则跳过 if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { return SkippedFrameReason::NothingToDraw; } return std::nullopt; }(); if (skippedFrameReason) { // 如果决定跳过本帧,记录原因并清理状态 mCurrentFrameInfo->setSkippedFrameReason(*skippedFrameReason);#ifdef __ANDROID__ if (auto grContext = getGrContext()) { grContext->flushAndSubmit(); // 依然提交之前的异步任务 }#endif waitOnFences(); // 等待之前的栅栏同步完成 for (auto& func : mFrameCommitCallbacks) { std::invoke(func, false /* didProduceBuffer: 未生成新缓冲区 */); } mFrameCommitCallbacks.clear(); return; } // 4. 开始正式绘制流程 ScopedActiveContext activeContext(this); mCurrentFrameInfo->set(FrameInfoIndex::FrameInterval) = mRenderThread.timeLord().frameIntervalNanos(); // 标记:开始向 GPU 发送绘制命令 mCurrentFrameInfo->markIssueDrawCommandsStart(); // 获取当前待渲染的 Frame 对象(此处触发 Surface::dequeueBuffer(),是潜在的强阻塞点) Frame frame = getFrame(); // 计算相对于窗口的最终脏区 SkRect windowDirty = computeDirtyRect(frame, &dirty); ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty)); // 5. 调用渲染管线(SkiaGL/Vulkan)执行真正的绘制命令提交 IRenderPipeline::DrawResult drawResult; { drawResult = mRenderPipeline->draw( frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler()), mBufferParams, profilerLock()); } uint64_t frameCompleteNr = getFrameNumber(); // 等待 GPU 指令执行的同步信号 waitOnFences(); // 6. 向底层系统窗口设置帧时间轴信息(用于同步 Vsync 和处理输入事件延迟) if (mNativeSurface) { const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) { const auto inputEventId = static_cast(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId)); const ANativeWindowFrameTimelineInfo ftl = { .frameNumber = frameCompleteNr, .frameTimelineVsyncId = vsyncId, .inputEventId = inputEventId, .startTimeNanos = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime), .useForRefreshRateSelection = solelyTextureViewUpdates, .skippedFrameVsyncId = mSkippedFrameInfo ? mSkippedFrameInfo->vsyncId : UiFrameInfoBuilder::INVALID_VSYNC_ID, .skippedFrameStartTimeNanos = mSkippedFrameInfo ? mSkippedFrameInfo->startTime : 0, }; native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), ftl); } } bool requireSwap = false; // 是否需要交换缓冲区(将画面提交给屏幕) bool didDraw = false; // 是否成功绘制 // 7. 提交缓冲区(Swap Buffers) int error = OK; bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult, windowDirty, mCurrentFrameInfo, &requireSwap); // 记录 GPU 指令提交完成的时间点 mCurrentFrameInfo->set(FrameInfoIndex::CommandSubmissionCompleted) = std::max( drawResult.commandSubmissionTime, mCurrentFrameInfo->get(FrameInfoIndex::SwapBuffers)); mIsDirty = false; if (requireSwap) { didDraw = true; error = mNativeSurface->getAndClearError(); if (error == TIMED_OUT) { // 如果获取 Buffer 超时,请求下一帧重试 mRenderThread.postFrameCallback(this); didDraw = false; } else if (error != OK || !didSwap) { // 如果发生严重错误,断开 Surface 绑定 setSurface(nullptr); didDraw = false; } // 记录交换历史,用于性能追溯 SwapHistory& swap = mSwapHistory.next(); if (didDraw) { swap.damage = windowDirty; } else { float max = static_cast(INT_MAX); swap.damage = SkRect::MakeWH(max, max); } swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); // 记录 Dequeue(出队)和 Queue(入队)Buffer 的耗时,这是 Jank 的常见原因 if (didDraw) { nsecs_t dequeueStart = ANativeWindow_getLastDequeueStartTime(mNativeSurface->getNativeWindow()); if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) { swap.dequeueDuration = 0; } else { swap.dequeueDuration = ANativeWindow_getLastDequeueDuration(mNativeSurface->getNativeWindow()); } swap.queueDuration = ANativeWindow_getLastQueueDuration(mNativeSurface->getNativeWindow()); } else { swap.dequeueDuration = 0; swap.queueDuration = 0; } mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration; mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration; mHaveNewSurface = false; mFrameNumber = 0; } else { mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0; mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0; } // 标记:缓冲区交换操作完成 mCurrentFrameInfo->markSwapBuffersCompleted(); // 如果开启了调试宏,计算并打印移动平均帧耗时#if LOG_FRAMETIME_MMA float thisFrame = mCurrentFrameInfo->duration(FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F; if (sFrameCount) { sBenchMma = ((9 * sBenchMma) + thisFrame) / 10; } else { sBenchMma = thisFrame; } if (++sFrameCount == 10) { sFrameCount = 1; ALOGD("平均帧耗时: %.4f", sBenchMma); }#endif // 执行所有注册的帧提交回调(通知应用层这一帧已经发给系统了) if (didSwap) { for (auto& func : mFrameCommitCallbacks) { std::invoke(func, true /* didProduceBuffer */); } mFrameCommitCallbacks.clear(); } // 8. 上报 Jank(卡顿)数据和帧指标 if (requireSwap) { if (mExpectSurfaceStats) { // 如果支持 SurfaceStats,则异步报告包含 Present Time(实际显示时间)的指标 reportMetricsWithPresentTime(); { std::lock_guard lock(mLast4FrameMetricsInfosMutex); FrameMetricsInfo& next = mLast4FrameMetricsInfos.next(); next.frameInfo = mCurrentFrameInfo; next.frameNumber = frameCompleteNr; next.surfaceId = mSurfaceControlGenerationId; } } else { // 否则直接结束当前帧记录 mCurrentFrameInfo->markFrameCompleted(); mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted) = mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted); std::scoped_lock lock(mFrameInfoMutex); mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr, mSurfaceControlGenerationId); } } // 9. 性能调节:向系统 HintSession 反馈实际工作负载 int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync); int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline); int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration); // 更新目标耗时(截止时间 - 预期 Vsync 时间) mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync); if (didDraw) { int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime; // 计算实际 CPU 工作耗时,排除掉等待 Buffer 和空闲的时间,以便更准地调整 CPU 频率 int64_t actualDuration = frameDuration - (std::min(syncDelayDuration, mLastDequeueBufferDuration)) - dequeueBufferDuration - idleDuration; mHintSessionWrapper->reportActualWorkDuration(actualDuration); // 如果有 WebView 渲染线程,也一并通知系统这些线程的活跃状态 mHintSessionWrapper->setActiveFunctorThreads( WebViewFunctorManager::instance().getRenderingThreadsForActiveFunctors()); } mLastDequeueBufferDuration = dequeueBufferDuration; // 清理缓存管理器中的过期资源 mRenderThread.cacheManager().onFrameCompleted(); return;}

COMMAND_ISSUE_DURATION (指令发布)时间的计算计时起点在代码第 40 行,它通过一个名为 markIssueDrawCommandsStart() 的函数打入了一个时间戳:

12// 标记:开始向 GPU 发送绘制命令mCurrentFrameInfo->markIssueDrawCommandsStart();

计时终点在代码第 104 行,记录了命令提交完成的时间:

12mCurrentFrameInfo->set(FrameInfoIndex::CommandSubmissionCompleted) = std::max( drawResult.commandSubmissionTime, mCurrentFrameInfo->get(FrameInfoIndex::SwapBuffers));

你可能会疑惑:为什么要取 drawResult.commandSubmissionTime 和 mCurrentFrameInfo->get(FrameInfoIndex::SwapBuffers) 的最大值?

这主要是为了防止 时间戳逻辑倒置:

理想情况: 命令提交(Submission)应该发生在 SwapBuffers 之前。

实际情况(驱动行为): 在某些图形驱动实现中,SwapBuffers(交换缓冲区)操作实际上包含了隐含的“命令刷新(Flush)”。也就是说,虽然 HWUI 认为提交在 SwapBuffers 之前就完成了,但实际上驱动程序可能在 SwapBuffers 调用时才真正完成了最后一块指令的推送。

为什么用 max: 如果 drawResult.commandSubmissionTime(我们测量的提交完成时间)比 SwapBuffers 的时间还要早,这在统计上是没有问题的。但如果驱动程序的 SwapBuffers 动作比我们记录的提交时间还晚,为了保证性能数据的逻辑性(提交动作不可能在交换缓冲区之后才完成),系统强制将 CommandSubmissionCompleted 的时间点至少设为 SwapBuffers 的时间。

将渲染完成的 Buffer 提交给 SurfaceFlingerSkiaOpenGLPipeline

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316/* * Copyright (C) 2016 The Android Open Source Project * ... (License header remains unchanged) */#include "pipeline/skia/SkiaOpenGLPipeline.h"#include #include #include #include #include #include #include #include #include #include "DeferredLayerUpdater.h"#include "FrameInfo.h"#include "LightingInfo.h"#include "hwui/Bitmap.h"#include "pipeline/skia/LayerDrawable.h"#include "pipeline/skia/SkiaGpuPipeline.h"#include "pipeline/skia/SkiaProfileRenderer.h"#include "private/hwui/DrawGlInfo.h"#include "renderstate/RenderState.h"#include "renderthread/EglManager.h"#include "renderthread/Frame.h"#include "renderthread/IRenderPipeline.h"#include "utils/GLUtils.h"using namespace android::uirenderer::renderthread;namespace android {namespace uirenderer {namespace skiapipeline {// 构造函数:初始化渲染流水线,并将自身注册到 RenderThread 的状态监听中SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread) : SkiaGpuPipeline(thread), mEglManager(thread.eglManager()) { thread.renderState().registerContextCallback(this);}// 析构函数:移除上下文回调注册SkiaOpenGLPipeline::~SkiaOpenGLPipeline() { mRenderThread.renderState().removeContextCallback(this);}// 将当前的 EGL 上下文设置为当前线程的上下文MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() { bool wasSurfaceless = mEglManager.isCurrent(EGL_NO_SURFACE); // 如果 Surface 在之前(例如 trimMemory 内存修剪)被销毁,则需要在此处重建 if (mHardwareBuffer) { mRenderThread.requireGlContext(); } else if (!isSurfaceReady() && mNativeWindow) { setSurface(mNativeWindow.get(), mSwapBehavior); } EGLint error = 0; // 尝试使当前 Surface 处于激活状态 if (!mEglManager.makeCurrent(mEglSurface, &error)) { return MakeCurrentResult::AlreadyCurrent; } EGLint majorVersion = 0; eglQueryContext(eglGetCurrentDisplay(), eglGetCurrentContext(), EGL_CONTEXT_CLIENT_VERSION, &majorVersion); // 处理 GL ES 3.x 在默认帧缓冲区状态切换时的兼容性问题 // 当默认帧缓冲区从“无 Surface”切换到“有 Surface”时,不同厂商的实现对 Read/Draw Buffer 的状态定义不一致 // 这是一个 Khronos 规范中的已知模糊点,通过显式设置确保状态正确 if (error == 0 && (majorVersion > 2) && wasSurfaceless && mEglSurface != EGL_NO_SURFACE) { GLint curReadFB = 0; GLint curDrawFB = 0; glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &curReadFB); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &curDrawFB); GLint buffer = GL_NONE; glBindFramebuffer(GL_FRAMEBUFFER, 0); glGetIntegerv(GL_DRAW_BUFFER0, &buffer); if (buffer == GL_NONE) { const GLenum drawBuffer = GL_BACK; glDrawBuffers(1, &drawBuffer); } glGetIntegerv(GL_READ_BUFFER, &buffer); if (buffer == GL_NONE) { glReadBuffer(GL_BACK); } glBindFramebuffer(GL_READ_FRAMEBUFFER, curReadFB); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, curDrawFB); GL_CHECKPOINT(LOW); } return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;}// 获取当前帧信息,准备开始渲染Frame SkiaOpenGLPipeline::getFrame() { LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no surface!"); return mEglManager.beginFrame(mEglSurface);}// 核心渲染方法:执行每一帧的绘制操作IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector>& renderNodes, FrameInfoVisualizer* profiler, const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) { // 如果不是在捕获 SKP 或者没有使用硬件缓冲区,则处理损坏区域(Damage Region),减少绘制消耗 if (!isCapturingSkp() && !mHardwareBuffer) { mEglManager.damageFrame(frame, dirty); } // 设置 OpenGL 的帧缓冲区(FBO)信息 SkColorType colorType = getSurfaceColorType(); GrGLFramebufferInfo fboInfo; fboInfo.fFBOID = 0; // 0 表示默认帧缓冲区 // 根据 Surface 的颜色格式配置 GL 格式 if (colorType == kRGBA_F16_SkColorType) { fboInfo.fFormat = GL_RGBA16F; } else if (colorType == kN32_SkColorType) { fboInfo.fFormat = GL_RGBA8; } else if (colorType == kRGBA_1010102_SkColorType) { fboInfo.fFormat = GL_RGB10_A2; } else if (colorType == kAlpha_8_SkColorType) { fboInfo.fFormat = GL_R8; } else { LOG_ALWAYS_FATAL("Unsupported color type."); } // 创建 Skia 后端渲染目标 auto backendRT = GrBackendRenderTargets::MakeGL(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo); SkSurfaceProps props(mColorMode == ColorMode::Default ? 0 : SkSurfaceProps::kAlwaysDither_Flag, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); sk_sp surface; SkMatrix preTransform; // 如果存在硬件缓冲区,则使用它创建 Surface,否则使用默认的后端渲染目标 if (mHardwareBuffer) { surface = getBufferSkSurface(bufferParams); preTransform = bufferParams.getTransform(); } else { surface = SkSurfaces::WrapBackendRenderTarget(mRenderThread.getGrContext(), backendRT, getSurfaceOrigin(), colorType, mSurfaceColorSpace, &props); preTransform = SkMatrix::I(); } // 计算光照信息 SkPoint lightCenter = preTransform.mapXY(lightGeometry.center.x, lightGeometry.center.y); LightGeometry localGeometry = lightGeometry; localGeometry.center.x = lightCenter.fX; localGeometry.center.y = lightCenter.fY; LightingInfo::updateLighting(localGeometry, lightInfo); // 执行实际的渲染流程 renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, preTransform); // 绘制调试用的视觉辅助工具(例如脏区域矩形、性能分析图表) if (CC_UNLIKELY(Properties::showDirtyRegions || ProfileType::None != Properties::getProfileType())) { std::scoped_lock lock(profilerLock); SkCanvas* profileCanvas = surface->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height()); profiler->draw(profileRenderer); } // 提交 GPU 指令 { ATRACE_NAME("flush commands"); skgpu::ganesh::FlushAndSubmit(surface); } layerUpdateQueue->clear(); // 调试模式下记录资源缓存使用情况 if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) { dumpResourceCacheUsage(); } return {true, IRenderPipeline::DrawResult::kUnknownTime, android::base::unique_fd{}};}// 交换前后缓冲区,将渲染好的帧显示到屏幕上bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, IRenderPipeline::DrawResult& drawResult, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) { GL_CHECKPOINT(LOW); // 即使取消了渲染,为了保持帧率统计的连续性,也需标记交换缓冲区 currentFrameInfo->markSwapBuffers(); if (mHardwareBuffer) { return false; } *requireSwap = drawResult.success || mEglManager.damageRequiresSwap(); // 执行底层的 swap 操作 if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) { return false; } return *requireSwap;}// 创建纹理层(用于支持 View 的 layer 功能)DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); return new DeferredLayerUpdater(mRenderThread.renderState());}// 当 GL 上下文销毁时,销毁 EGL Surfacevoid SkiaOpenGLPipeline::onContextDestroyed() { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; }}// 停止渲染时,解绑当前 EGL Surfacevoid SkiaOpenGLPipeline::onStop() { if (mEglManager.isCurrent(mEglSurface)) { mEglManager.makeCurrent(EGL_NO_SURFACE); }}// 设置 NativeWindow (Surface),即建立与屏幕或 Buffer 的连接bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) { mNativeWindow = surface; mSwapBehavior = swapBehavior; // 清除旧的 Surface if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } // 创建新的 Surface if (surface) { mRenderThread.requireGlContext(); auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace); if (!newSurface) { return false; } mEglSurface = newSurface.unwrap(); } // 设置缓冲区的保留策略 if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); return true; } return false;}// 刷新指令并等待同步栅栏(Fence),确保 GPU 任务完成[[nodiscard]] android::base::unique_fd SkiaOpenGLPipeline::flush() { int fence = -1; EGLSyncKHR sync = EGL_NO_SYNC_KHR; mEglManager.createReleaseFence(true, &sync, &fence); // 如果不支持原生栅栏,则使用 EGL 同步对象进行阻塞等待 if (sync != EGL_NO_SYNC_KHR) { EGLDisplay display = mEglManager.eglDisplay(); EGLint result = eglClientWaitSyncKHR(display, sync, 0, 1000000000); if (result == EGL_FALSE) { ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", eglGetError()); } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); } eglDestroySyncKHR(display, sync); } return android::base::unique_fd(fence);}bool SkiaOpenGLPipeline::isSurfaceReady() { return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);}bool SkiaOpenGLPipeline::isContextReady() { return CC_LIKELY(mEglManager.hasEglContext());}// 调用自定义 Functor(用于旧版 SurfaceView 或特定的 GL 绘制插件)void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; if (thread.eglManager().hasEglContext()) { mode = DrawGlInfo::kModeProcess; } (*functor)(mode, nullptr); // 如果有 GL 上下文,需要重置 Skia 的状态,防止 Functor 污染 GL 状态机 if (mode != DrawGlInfo::kModeProcessNoContext) { thread.getGrContext()->resetContext(); }}} /* namespace skiapipeline */} /* namespace uirenderer */} /* namespace android */

SkiaOpenGLPipeline 职能通过 mEglManager.swapBuffers(...) 将绘制好的 Buffer 放入了 BufferQueue。SurfaceFlinger (系统侧): 在下一次 VSync 到来时,感知到 BufferQueue 中有了新数据。合成动作: SurfaceFlinger 将你提交的这个 Buffer,与其他所有可见层(如状态栏)合并。最终显示: 将合成后的结果发给屏幕控制器。

flush commands 耗时监控ATRACE_NAME 利用了 C++ 的 RAII (Resource Acquisition Is Initialization) 机制。你不需要手动写“开始”和“结束”,它会自动管理生命周期:

执行宏时: 它会在当前作用域创建一个临时的 Trace 对象,内部调用 ATRACE_BEGIN("flush commands"),告诉系统:“我要开始记录了,名字叫 flush commands”。

离开作用域时: 当代码执行到该大括号 {} 的末尾,临时对象被销毁,自动调用 ATRACE_END(),告诉系统:“这个动作结束了”。

1234{ ATRACE_NAME("flush commands"); // 在这里调用 ATRACE_BEGIN skgpu::ganesh::FlushAndSubmit(surface);} // 在这里自动触发 ATRACE_END

这意味着系统会自动计算 FlushAndSubmit 这一行代码到底消耗了多少微秒/毫秒。

COMMAND_ISSUE_DURATION 和 ATRACE_NAME(“flush commands”) 耗时的不同COMMAND_ISSUE_DURATION 的定义是:从“开始处理绘制指令”到“指令提交完毕”的完整周期。

COMMAND_ISSUE_DURATION = [绘制指令生成 (CPU)] + [FlushAndSubmit (指令提交)]

而 ATRACE_NAME("flush commands") 仅仅覆盖了其中的后半部分:

ATRACE("flush commands") = [仅 FlushAndSubmit 这一步的耗时]

SWAP_BUFFERS_DURATION (将渲染完成的 Buffer 提交给 SurfaceFlinger。)计时开始 (Start)

在哪里出发: SkiaOpenGLPipeline::swapBuffers() 函数中。

动作: 当代码执行到 currentFrameInfo->markSwapBuffers() 这一行时,它会立刻记录下当前的系统时间戳,作为一个“记号”。

含义: “计时开始,现在开始进行缓冲区交换。”

计时结束与计算 (End)

在哪里结束: JankTracker::finishFrame() 函数中。

动作: 这一帧的所有渲染工作全部完成后,系统会调用这个函数来“结算”这一帧的表现。

底层图形系统 (frameworks/native/libs/gui/)关键阻塞点:Surface::dequeueBuffer()该函数在 CanvasContext::draw() 执行过程中被调用(对应源码中的 Frame frame = getFrame(); 这一行),用于向 BufferQueue 索要一个可写的图形缓冲区。

阻塞逻辑: 如果当前所有的 Buffer 都在 SurfaceFlinger 端等待显示,或者 GPU 上一帧还没跑完导致没有空闲 Buffer,渲染线程会在此处挂起。这是全链路中最著名的潜在阻塞点。

监控: 通过 dumpsys gfxinfo 查看到的 Dequeue Buffer 耗时(或 Trace 中的 DequeueBufferDuration)即代表此处的等待时长。较高的数值通常意味着系统负载极重、SurfaceFlinger 合成压力大或 GPU 性能达到瓶颈。

JankTracker 源代码(Android 15)123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351#include "JankTracker.h"#include #include #include #include #include #include #include #include #include #include #include "DeviceInfo.h"#include "Properties.h"#include "utils/TimeUtils.h"#include "utils/Trace.h"namespace android {namespace uirenderer {// 定义各类卡顿检测的比较规则struct Comparison { JankType type; // 卡顿类型 std::function computeThreadshold; // 计算阈值的函数 FrameInfoIndex start; // 起始时间点索引 FrameInfoIndex end; // 结束时间点索引};// 具体的卡顿判定标准static const std::array COMPARISONS{ // 1. 错过 Vsync:预期启动时间与实际启动时间偏差 > 1ns 即视为错过 Comparison{JankType::kMissedVsync, [](nsecs_t) { return 1; }, FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync}, // 2. UI 线程慢:从 Vsync 信号到 Sync 开始,耗时超过 0.5 倍帧间隔 Comparison{JankType::kSlowUI, [](nsecs_t frameInterval) { return static_cast(.5 * frameInterval); }, FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart}, // 3. Sync 阶段慢:主线程与渲染线程数据同步耗时超过 0.2 倍帧间隔 Comparison{JankType::kSlowSync, [](nsecs_t frameInterval) { return static_cast(.2 * frameInterval); }, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart}, // 4. 渲染线程慢:从提交命令到帧完成耗时超过 0.75 倍帧间隔 Comparison{JankType::kSlowRT, [](nsecs_t frameInterval) { return static_cast(.75 * frameInterval); }, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},};// 如果单帧耗时超过 10 秒,则认为不是普通的卡顿而是 ANR,不再计入卡顿统计static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);/* * 目前不跟踪通过 Surface:lockHardwareCanvas() 直接进行的绘制。 * TODO: kSurfaceCanvas 会占用渲染线程时间,可能负面影响其他绘制,未来需要研究如何归因。 */static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;// 测试环境下,过滤掉帧起始阶段的未知延迟,将其视为测试基建的开销static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;JankTracker::JankTracker(ProfileDataContainer* globalData) : mData(globalData->getDataMutex()) , mDataMutex(globalData->getDataMutex()) { mGlobalData = globalData; nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod(); nsecs_t sfOffset = DeviceInfo::getCompositorOffset(); nsecs_t offsetDelta = sfOffset - DeviceInfo::getAppOffset(); // 根据 App 偏移和 SF 偏移的差值,计算 dequeueBuffer 阻塞的容忍时间(Legacy) if (offsetDelta <= 4_ms && offsetDelta >= 0) { // SF 将在 VSYNC-app + offsetDelta 开始合成。 // 在三缓冲情况下,这是由于 VSYNC-app 和 VSYNC-sf 交错而预期 dequeueBuffer 返回的时间。 mDequeueTimeForgivenessLegacy = offsetDelta + 4_ms; } mFrameIntervalLegacy = frameIntervalNanos;}/** * 计算旧版(Legacy)卡顿指标。 * 主要处理 dequeueBuffer 阻塞时间的宽赦逻辑,避免因为 Buffer 调度导致的误判。 */void JankTracker::calculateLegacyJank(FrameInfo& frame) REQUIRES(mDataMutex) { // 基础总耗时:从预期开始到 Swap 完成 int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::SwapBuffersCompleted); // 如果存在 dequeueBuffer 耗时(>500us),尝试进行“宽赦” if (mDequeueTimeForgivenessLegacy && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) { // 预期 dequeue 时间 = 偏移容忍值 + Vsync时间 - 发送绘制命令的时间 nsecs_t expectedDequeueDuration = mDequeueTimeForgivenessLegacy + frame[FrameInfoIndex::Vsync] - frame[FrameInfoIndex::IssueDrawCommandsStart]; if (expectedDequeueDuration > 0) { // 宽赦量取“预期阻塞值”和“实际阻塞值”的较小者 nsecs_t forgiveAmount = std::min(expectedDequeueDuration, frame[FrameInfoIndex::DequeueBufferDuration]); if (forgiveAmount >= totalDuration) { ALOGV("不可能的出队耗时! reported %" PRId64 ", total %" PRId64, forgiveAmount, totalDuration); return; } // 从总时长中减去宽赦的部分 totalDuration -= forgiveAmount; } } if (totalDuration <= 0) { ALOGV("总时长异常 %" PRId64 " start=%" PRIi64 " gpuComplete=%" PRIi64, totalDuration, frame[FrameInfoIndex::IntendedVsync], frame[FrameInfoIndex::GpuCompleted]); return; } // 排除特殊标记的帧(如 lockHardwareCanvas) if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) { return; } // 如果扣除宽赦后依然超过 16.6ms(对于 60Hz),记为卡顿 if (totalDuration > mFrameIntervalLegacy) { mData->reportJankLegacy(); (*mGlobalData)->reportJankLegacy(); } // 更新 Swap 截止时间 if (mSwapDeadlineLegacy < 0) { mSwapDeadlineLegacy = frame[FrameInfoIndex::IntendedVsync] + mFrameIntervalLegacy; } // 判断是否处于三缓冲状态:当前截止时间与预期开始时间的偏差 > 0.1倍帧间隔 bool isTripleBuffered = (mSwapDeadlineLegacy - frame[FrameInfoIndex::IntendedVsync]) > (mFrameIntervalLegacy * 0.1); mSwapDeadlineLegacy = std::max(mSwapDeadlineLegacy + mFrameIntervalLegacy, frame[FrameInfoIndex::IntendedVsync] + mFrameIntervalLegacy); // 如果在截止时间内完成,或总耗时小于帧间隔,说明没有产生肉眼可见的卡顿 if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadlineLegacy || totalDuration < mFrameIntervalLegacy) { if (isTripleBuffered) { // 虽然没卡顿,但如果触发了三缓冲,属于高延迟状态 mData->reportJankType(JankType::kHighInputLatency); (*mGlobalData)->reportJankType(JankType::kHighInputLatency); } return; } // 否则记为错过旧版截止日期 mData->reportJankType(JankType::kMissedDeadlineLegacy); (*mGlobalData)->reportJankType(JankType::kMissedDeadlineLegacy); // 发生卡顿,根据实际完成时间重置下一个周期的截止时间 nsecs_t jitterNanos = frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::Vsync]; nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalLegacy; mSwapDeadlineLegacy = frame[FrameInfoIndex::FrameCompleted] - lastFrameOffset + mFrameIntervalLegacy;}/** * 帧渲染结束后的统计入口。 * @param frame 包含帧各阶段时间戳的信息 */void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr& reporter, int64_t frameNumber, int32_t surfaceControlId) { std::lock_guard lock(mDataMutex); // 1. 先计算旧版卡顿指标 calculateLegacyJank(frame); // 2. 计算当前帧的总时长(从预期 Vsync 到帧完成) int64_t totalDuration = frame.duration(FrameInfoIndex::IntendedVsync, FrameInfoIndex::FrameCompleted); if (totalDuration <= 0) { ALOGV("总时长异常 %" PRId64, totalDuration); return; } // 上报帧时长数据到直方图 mData->reportFrame(totalDuration); (*mGlobalData)->reportFrame(totalDuration); if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) { return; } int64_t frameInterval = frame[FrameInfoIndex::FrameInterval]; // 判断是否触发“三缓冲”(Buffer Stuffing) // 如果本帧开始时间早于(假设未发生堆积时的)预期开始时间,说明前面有帧堆积了 bool isTripleBuffered = (mNextFrameStartUnstuffed - frame[FrameInfoIndex::IntendedVsync]) > (frameInterval * 0.1); int64_t deadline = frame[FrameInfoIndex::FrameDeadline]; // 在三缓冲情况下,队列中有足够的缓冲区来支撑一次掉帧而不产生卡顿 // 因此根据堆积情况动态调整截止日期判定 if (isTripleBuffered) { int64_t originalDeadlineDuration = deadline - frame[FrameInfoIndex::IntendedVsync]; deadline = mNextFrameStartUnstuffed + originalDeadlineDuration; frame.set(FrameInfoIndex::FrameDeadline) = deadline; } // 3. 检查是否达到了性能截止日期 (Deadline) if (frame[FrameInfoIndex::GpuCompleted] < deadline) { if (isTripleBuffered) { // 虽未掉帧,但属于高延迟输入 mData->reportJankType(JankType::kHighInputLatency); (*mGlobalData)->reportJankType(JankType::kHighInputLatency); // 除非有明显的“暂停”,否则三缓冲状态会延续到下一帧 mNextFrameStartUnstuffed += frameInterval; } } else { // 判定为掉帧 (Jank) mData->reportJankType(JankType::kMissedDeadline); (*mGlobalData)->reportJankType(JankType::kMissedDeadline); mData->reportJank(); (*mGlobalData)->reportJank(); // 计算抖动偏移,用于修正下一帧的期望开始时间 nsecs_t jitterNanos = frame[FrameInfoIndex::GpuCompleted] - frame[FrameInfoIndex::Vsync]; nsecs_t lastFrameOffset = jitterNanos % frameInterval; // 记录在非堆积情况下,下一帧应该开始的时间 mNextFrameStartUnstuffed = frame[FrameInfoIndex::GpuCompleted] - lastFrameOffset + frameInterval; // 4. 卡顿溯源分析:遍历所有的 COMPARISONS 规则,找出是哪个阶段慢了 recomputeThresholds(frameInterval); for (auto& comparison : COMPARISONS) { int64_t delta = frame.duration(comparison.start, comparison.end); // 如果某阶段耗时超过对应阈值,则上报该类型的卡顿原因 if (delta >= mThresholds[comparison.type] && delta < IGNORE_EXCEEDING) { mData->reportJankType(comparison.type); (*mGlobalData)->reportJankType(comparison.type); } } // 5. "Davey" 严重卡顿日志 // 如果一帧耗时超过 700ms,打印极其详细的时间轴数据辅助排查 if (totalDuration >= 700_ms) { static int sDaveyCount = 0; std::stringstream ss; ss << "Davey! duration=" << ns2ms(totalDuration) << "ms; "; for (size_t i = 0; i < static_cast(FrameInfoIndex::NumIndexes); i++) { ss << FrameInfoNames[i] << "=" << frame[i] << ", "; } ALOGI("%s", ss.str().c_str()); // 并在 Trace 中标记 ATRACE_INT(ss.str().c_str(), ++sDaveyCount); } } // 6. 上报 GPU 绘制时长(如果平台支持) int64_t totalGPUDrawTime = frame.gpuDrawTime(); if (totalGPUDrawTime >= 0) { mData->reportGPUFrame(totalGPUDrawTime); (*mGlobalData)->reportGPUFrame(totalGPUDrawTime); } // 7. 如果注册了 Metrics 监听器(如 FrameMetrics API),同步上报底层数据 if (CC_UNLIKELY(reporter.get() != nullptr)) { reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */, frameNumber, surfaceControlId); }}/** * 根据帧预算(取决于刷新率)重新计算各阶段的卡顿判定阈值 */void JankTracker::recomputeThresholds(int64_t frameBudget) REQUIRES(mDataMutex) { if (mThresholdsFrameBudget == frameBudget) { return; } mThresholdsFrameBudget = frameBudget; for (auto& comparison : COMPARISONS) { // 调用每个 Comparison 规则自带的 lambda 函数计算阈值 mThresholds[comparison.type] = comparison.computeThreadshold(frameBudget); }}/** * 将卡顿统计数据导出 */void JankTracker::dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data) {#ifdef __ANDROID__ if (description) { switch (description->type) { case JankTrackerType::Generic: break; case JankTrackerType::Package: dprintf(fd, "\nPackage: %s", description->name.c_str()); break; case JankTrackerType::Window: dprintf(fd, "\nWindow: %s", description->name.c_str()); break; } } if (sFrameStart != FrameInfoIndex::IntendedVsync) { dprintf(fd, "\nNote: Data has been filtered!"); } data->dump(fd); dprintf(fd, "\n");#endif}/** * 打印最近所有帧的时间戳详细数据 */void JankTracker::dumpFrames(int fd) {#ifdef __ANDROID__ dprintf(fd, "\n\n---PROFILEDATA---\n"); for (size_t i = 0; i < static_cast(FrameInfoIndex::NumIndexes); i++) { dprintf(fd, "%s", FrameInfoNames[i]); dprintf(fd, ","); } for (size_t i = 0; i < mFrames.size(); i++) { FrameInfo& frame = mFrames[i]; if (frame[FrameInfoIndex::SyncStart] == 0) { continue; } dprintf(fd, "\n"); for (int i = 0; i < static_cast(FrameInfoIndex::NumIndexes); i++) { dprintf(fd, "%" PRId64 ",", frame[i]); } } dprintf(fd, "\n---PROFILEDATA---\n\n");#endif}/** * 重置卡顿统计 */void JankTracker::reset() REQUIRES(mDataMutex) { mFrames.clear(); mData->reset(); (*mGlobalData)->reset(); // 根据系统属性决定是否过滤掉测试导致的起始开销 sFrameStart = Properties::filterOutTestOverhead ? FrameInfoIndex::HandleInputStart : FrameInfoIndex::IntendedVsync;}} /* namespace uirenderer */} /* namespace android */