Skip to main content

Chapter 95: Android Graphics Pipeline

Android 图形管线要回答的主问题是:一次应用界面变化如何从 View 层进入渲染线程、图形缓冲区、系统合成服务、Hardware Composer HAL 和显示硬件,最终变成屏幕上的一帧。读完本章后,读者应能追踪一帧的责任链,判断掉帧发生在 UI 遍历、渲染提交、缓冲区交换、系统合成、硬件合成还是显示提交阶段。

本章贯穿材料选择一个常见行为:用户滑动列表,某个 View 内容变化并触发重绘。这个行为足够小,能落到 invalidatemeasurelayoutdrawChoreographerViewRootImpl;它又足够完整,能继续进入 RenderThreadSurfaceBufferQueueSurfaceFlinger、Hardware Composer HAL 和 display controller。Android 官方 Graphics architecture 把系统级图形架构的焦点放在 graphical data buffers 如何穿过系统,本章沿用这个观察角度,把“界面绘制”拆成“命令生成、缓冲区生产、系统合成、硬件呈现”四段。

这一章讨论的是传统 Android View/HWUI 管线。Jetpack Compose、游戏引擎、视频播放器、相机预览和自定义 OpenGL ES / Vulkan 渲染会改变 App 侧生产者模型,但它们进入系统显示链路后仍会遇到同一组对象:SurfaceBufferQueue、fence、SurfaceFlinger、HWC 和 display。版本边界上,本文以 Android 10 之后的图形架构为主,保留 Android 8.0 之后 HWC2/Composer HAL 的通用模型;具体设备上的 GPU backend、HWC 能力、overlay plane 数量、刷新率策略和 vendor display driver 行为会随 SoC、面板和系统版本变化。

下面先给出贯穿路径,后续每节都回到这条路径定位责任边界:

这条路径有一个稳定判断顺序:先看 App 主线程是否按 VSync 完成输入、动画和 View 遍历;再看 RenderThread 是否及时把渲染命令提交给 GPU;再看 buffer 是否按 producer/consumer 协议进入 SurfaceFlinger;接着看 SurfaceFlinger 是否完成 latch、layer transaction 和 composition decision;最后看 HWC 是否能把 layer 分配到 overlay plane 或要求 GPU 做 client composition。掉帧排查的误判通常来自把“App 画慢了”和“系统合成慢了”混在一起,本章的目标是把它们拆开。

95.1 Android View Hierarchy 与 UI 渲染入口

Android View Hierarchy 是应用 UI 的树形对象集合。每个 View 既保存状态,也承担测量、布局和绘制入口。用户滑动列表时,输入事件先改变滚动位置或子项状态,随后某些区域被标记为需要重绘。invalidate 的作用是把 View 的可见内容标记为脏区域,要求下一帧重新生成绘制结果;requestLayout 的作用更重,它要求后续遍历重新执行测量和布局,因为大小或位置可能已经变化。

ViewRootImpl 是应用进程中连接 View 树和窗口 surface 的根控制对象。它接收来自 WindowManager 分配的窗口连接,持有 DecorView 到 Surface 的关键桥接状态,并在一帧中组织 traversal。一次 traversal 可能包含三个阶段:measure 决定每个 View 需要多大空间,layout 决定 View 在父容器中的位置,draw 把可见内容转换成绘制命令。列表滑动通常主要触发 draw 和部分 layout;复杂自适应布局、约束变化或文本换行会把成本推到 measure/layout。

Choreographer 是 View 管线的帧节拍入口。Android Developers 的 Choreographer 文档说明它协调 animation、input 和 drawing,并从显示子系统接收 VSync 等 timing pulses,然后把工作安排到下一帧渲染中。应用通常通过 View hierarchy 和 animation framework 间接使用它,例如 View.postInvalidateOnAnimation() 会把一次重绘请求对齐到下一帧。这个设计让输入、动画状态更新和绘制提交围绕同一个显示节奏推进,减少同一帧内状态不一致。

在贯穿例子中,滑动列表的一帧可以按这个顺序观察:输入事件更新 scroll offset;相关 View 标记脏区域;ViewRootImpl 调度 traversal;VSync 到来后,Choreographer 触发 input、animation、traversal 回调;performTraversals 根据标记执行 measure、layout 和 draw;硬件加速开启时,draw 阶段生成或更新后续渲染线程可消费的绘制结构。这里的关键结论是,View 层生产的核心产物并非最终像素,而是下一阶段可以提交给渲染后端的绘制命令和 layer 状态。

这一区域最常见的失败表现是主线程错过帧截止时间。原因可能是 onMeasure 递归过深、onLayout 触发多轮布局、onDraw 执行过多 CPU 工作、RecyclerView 绑定数据过慢,或者主线程同时被 Binder 回调、I/O、JSON 解析和图片解码占用。判断时先看 UI thread 是否在 VSync 之后长时间运行 traversal 或业务回调,再看 RenderThread 和系统合成;如果主线程已经晚交,后面的图形链路只能显示上一帧或延迟显示新帧。

95.2 RenderThread 与主线程解耦

RenderThread 是 Android HWUI 管线中负责执行渲染后端工作的线程。主线程仍负责 View 状态、事件分发、measure、layout 和大部分 display list 更新;RenderThread 接收已同步的渲染树状态,把绘制命令转换成 GPU 可执行工作,并管理与 Surface 相关的 buffer 提交。这个拆分让部分渲染工作脱离主线程,降低主线程业务回调和 GPU 提交之间的相互阻塞。

Display list 是 View 绘制命令的记录形式,RenderNode 是 HWUI 用来保存节点绘制属性和显示列表的对象。硬件加速下,View.draw() 并不等于马上把像素写进屏幕 buffer;更常见的路径是把 Canvas 调用记录到 RenderNode,把透明度、位移、裁剪、阴影、圆角等属性保存在渲染树中。这样一来,某些 property animation 可以由渲染线程推进,主线程只需要提交属性变化,渲染线程能在不重新执行完整 View draw 的情况下更新合成结果。

主线程和渲染线程之间仍存在同步点。主线程必须在帧截止前完成输入处理、动画状态更新、View 遍历和渲染树同步;RenderThread 随后才能基于最新状态提交 GPU 命令。同步屏障的作用是让帧相关异步消息在消息队列中获得合适顺序,防止普通同步消息把 VSync 驱动的 traversal 长时间压在后面。这个机制提高了帧调度确定性,但它无法替主线程消除过长业务任务。

在滑动列表例子里,主线程负责把新的 scroll offset、子项可见范围和 display list 更新提交出来;RenderThread 负责把这些更新转换成 GPU backend 调用,并把渲染结果写入当前 Surface 的图形 buffer。主线程晚提交会让 RenderThread 拿到旧状态;RenderThread 或 GPU 晚完成会让 buffer 带着未完成 fence 进入后续等待;两者在 Perfetto 中表现为不同线程和不同等待点。

这一区域的可复用判断是:App 主线程瓶颈看 Choreographer#doFrameperformTraversals、业务回调和 View draw;渲染线程瓶颈看 RenderThread 上的 frame building、GPU command submission、纹理上传、shader 编译和等待 fence。二者都属于 App 进程,但责任不同。主线程问题通常通过减少布局层级、降低 onDraw CPU 工作、把解码和数据准备移出帧路径解决;渲染线程问题通常指向过度绘制、大纹理上传、复杂裁剪、阴影、模糊、离屏渲染和 GPU driver 等成本。

95.3 Skia、Canvas、OpenGL ES / Vulkan 的渲染角色

Canvas 是 Android 应用层看到的 2D 绘制接口。开发者在 onDraw(Canvas) 中调用 drawTextdrawPathdrawBitmapdrawRoundRect 等 API,表达“画什么”。Canvas 本身不等同于某个固定硬件后端;它可以记录命令,可以走 CPU raster,也可以在 HWUI 管线中进入 Skia 的 GPU backend。理解这一点后,Canvas 调用、Skia、OpenGL ES / Vulkan 之间的边界就清楚了:Canvas 是 API 表面,Skia 是 2D 图形实现库,OpenGL ES / Vulkan 是 GPU 后端接口。

Skia 在 Android 图形系统中承担 2D 几何、路径、文字、图片、采样、混合和光栅化相关工作。它把高层绘制命令转换成可执行的 raster 或 GPU 绘制任务。对应用开发者来说,drawPath 只是一个 View 绘制调用;对渲染后端来说,它可能变成 tessellation、mask、纹理采样、blend state 和 command buffer。路径复杂度、裁剪范围、透明混合、文字缓存和 bitmap 尺寸都会影响这个阶段的成本。

OpenGL ES 和 Vulkan 是 Android 设备上常见的 GPU API 后端。传统 HWUI 早期主要以 OpenGL ES 作为后端,现代 Android 设备可以使用 Vulkan 相关路径,具体启用状态取决于平台版本、设备能力和 vendor 配置。本文不把某个设备的 backend 当作固定事实;稳定事实是:GPU 后端负责把 Skia/HWUI 准备好的绘制工作提交给 GPU driver,driver 再把命令送入内核图形驱动和硬件队列。

在贯穿例子中,列表滑动本身通常不需要 App 直接调用 OpenGL ES 或 Vulkan。App 调用的是 View 和 Canvas;HWUI 与 Skia 把这些绘制命令组织成 GPU 可执行工作;GPU 把结果写进从 Surface 取得的 buffer。游戏引擎或自定义渲染器会绕开 View 的 onDraw,直接使用 SurfaceViewTextureViewANativeWindow、OpenGL ES 或 Vulkan 生产 buffer,但进入 BufferQueue 之后仍接受系统合成和显示硬件的约束。

这个边界对性能判断有直接作用。Canvas.drawBitmap 造成卡顿时,原因可能在主线程解码、bitmap 上传、纹理采样、GPU 带宽或合成阶段;drawPath 成本高时,原因可能在路径复杂度、抗锯齿、clip 和离屏层。排查顺序应先把“命令生成”和“命令执行”分开:主线程长时间执行 onDraw 属于命令生成成本;RenderThread 或 GPU track 延迟属于后端执行成本;SurfaceFlinger 或 HWC 延迟属于系统合成与显示提交成本。

95.4 Surface、BufferQueue 与跨进程图形数据传递

Surface 是图形生产者向系统提交 buffer 的入口。对普通 Activity 窗口来说,View/HWUI 最终会渲染到窗口关联的 Surface;对视频、相机预览和游戏来说,SurfaceViewTextureViewANativeWindow 也会提供可提交 buffer 的表面。Surface 把 App 侧的绘制结果从“进程内渲染对象”变成“系统 compositor 可消费的图形缓冲区”。

BufferQueue 是 Android 图形系统中连接 producer 和 consumer 的队列。AOSP 的 BufferQueue and Gralloc 文档说明,producer 通过 dequeueBuffer() 取得可写 buffer,填充后用 queueBuffer() 归还;consumer 通过 acquireBuffer() 取得内容,使用完成后用 releaseBuffer() 释放。BufferQueue 自身不复制大块图像数据,跨进程传递的是 buffer handle;Gralloc 根据宽高、像素格式和 usage flags 分配能被 CPU、GPU、HWC、video encoder 等硬件角色使用的内存。

跨进程图形传递的关键不只是 handle,还有 fence。Fence 是同步对象,用来表达“这块 buffer 何时可安全读写”。当 App 侧 GPU 仍在写入 buffer 时,consumer 需要等待 acquire fence 信号;当 consumer 释放 buffer 时,producer 需要等待 release fence,确认旧使用者已经完成读取或显示。这样 CPU、GPU、display controller 可以并行推进,只有在真实依赖点才阻塞。HWC 文档也把 sync fences 描述为 Android graphics system 的核心对象,因为它们让 CPU work 和 concurrent GPU work 在依赖边界上同步。

在滑动列表例子中,App 是窗口 surface 的 producer,SurfaceFlinger 是后续 consumer。App 从 BufferQueue 取得一个 buffer,RenderThread 和 GPU 在里面写入当前帧,随后 App 把 buffer 连同 fence queue 给 SurfaceFlinger。如果 App 生产速度慢,队列里没有新 buffer,SurfaceFlinger 会继续使用上一帧已 latch 的 buffer;如果 App 生产过快或 consumer 消费慢,producer 可能在 dequeue 阶段等待可用 buffer。

这一节的工程判断是:BufferQueue 问题往往表现为等待 buffer、队列积压、fence 未信号或 producer/consumer 节奏不匹配。视频 30 fps 在 60 Hz 屏幕上不需要每个 VSync 生产新 buffer;UI 动画则通常希望每个目标帧都有新 buffer。相机预览、视频播放和 UI 窗口可以各自拥有独立 Surface 和 BufferQueue,系统合成时再把多个 layer 组合到同一显示输出上。

95.5 SurfaceFlinger 作为系统合成服务

SurfaceFlinger 是 Android 的系统 compositor。它接收来自 App、系统 UI、视频解码器、相机预览等来源的 layer buffer 和 layer metadata,在显示刷新节奏下决定哪些 layer 进入本帧,并把组合后的结果送往 HWC。AOSP 的 SurfaceFlinger and WindowManager 文档说明,SurfaceFlinger 接收、合成并把 buffers 发送到 display;WindowManager 则向它提供窗口 metadata,用于确定 layer 的位置、尺寸、z-order、变换、焦点和生命周期。

Layer transaction 是 SurfaceFlinger 理解窗口状态的控制面。一个 App 窗口的 buffer 内容来自 Surface,但它在屏幕上的位置、裁剪、alpha、圆角、旋转、层级、可见性和动画状态来自 transaction。Activity 转场、状态栏展开、输入法弹起、画中画缩放、窗口 resize 都会更新 layer metadata。SurfaceFlinger 合成一帧时,需要同时处理 buffer latch 和 transaction 状态,这也是“buffer 已经到达但画面仍未按预期显示”的常见原因之一。

Buffer latch 是 SurfaceFlinger 在某个 VSync 周期选择每个可见 layer 当前 buffer 的动作。它会检查 layer 是否有新 buffer、acquire fence 是否可用、transaction 是否匹配本帧时序,并决定使用新 buffer 还是继续使用旧 buffer。若某个 layer 从未提交 buffer,它可以被忽略;若某个 layer 本帧没有新内容,旧 buffer 仍可继续参与显示。这个策略让静态内容不需要每帧重复生产 buffer,系统也可以在无更新时减少唤醒和合成工作。

Composition decision 是 SurfaceFlinger 和 HWC 之间的协商。SurfaceFlinger 收集 visible layers 后,把 layer 列表、buffer、几何属性、blend、dataspace、transform 和 display state 提交给 HWC 检查。HWC 根据硬件 plane、格式、缩放、旋转、保护内容、HDR、色彩空间和带宽等能力,决定哪些 layer 可由 display hardware 直接合成,哪些 layer 需要 client composition。client composition 指 SurfaceFlinger 使用 GPU 把一组 layer 先合成到 client target buffer,再交给 HWC 与其余硬件 layer 一起呈现。

Present fence 是本帧显示提交的可观察边界。presentDisplay 返回后,HWC 会提供代表显示完成时机的 fence;它可以帮助系统和工具把 App 生产、系统合成、硬件呈现串进同一条时间线。对掉帧分析来说,SurfaceFlinger 不是“简单转发 buffer”的进程;它是系统级合成调度者,负责在 VSync、layer metadata、fence、HWC 能力和显示状态之间做每帧决策。

95.6 Hardware Composer HAL 与显示硬件合成

Hardware Composer HAL,简称 HWC,是 SurfaceFlinger 与显示硬件能力之间的 HAL 边界。SurfaceFlinger 负责系统合成策略和 layer 状态管理;HWC 负责暴露 vendor display pipeline 能力,例如 physical display、display mode、color mode、hardware plane、client target、present fence 和 hotplug。AOSP 的 Implement Hardware Composer HAL 文档把一次显示合成描述为:SurfaceFlinger 更新 layer 状态,调用 validateDisplay 让 HWC 检查层状态并决定组合方式,随后必要时接受 composition type 变化,最后调用 presentDisplay 完成显示提交。

validateDisplay 是 HWC 决策的核心入口。HWC 查看每个 layer 的 buffer 格式、尺寸、裁剪、目标矩形、alpha、transform、dataspace、blend mode、secure/protected 标记和 display 当前模式。它会返回哪些 layer 可以由 HWC 处理,哪些 layer 需要 SurfaceFlinger 用 GPU 合成。由于 overlay plane 数量有限,且每个 plane 支持的格式、缩放、旋转和混合能力不同,同一个 App 在不同设备上可能得到不同分配结果。

presentDisplay 是 HWC 把本帧提交给显示硬件的入口。在 presentDisplay 之前,如果存在 client composition,SurfaceFlinger 会先把那些 layer 合成到 client target buffer,然后通过 setClientTarget 交给 HWC。HWC 再把 client target 与硬件可直接处理的 overlay layer 一起送入 display controller。对 HWC 来说,client target 本身也是一个 layer,只是它已经包含一部分由 GPU 预合成的内容。

Overlay plane 是显示硬件中的扫描合成资源。一个 plane 可以从某个 buffer 读取像素,按给定位置、缩放、裁剪和格式参与最终显示。视频播放和相机预览常常适合 overlay plane,因为它们是大面积连续图像,可能使用 YUV 格式,且频率稳定。UI 层、阴影、透明叠加、复杂裁剪和动画则更容易进入 GPU client composition,因为硬件 plane 对几何和混合能力有边界。

这一区域的版本边界需要明确。Android 8.0 之后 HWC2/Composer HAL 成为主路径,现代平台还存在 AIDL 化的 HWC HAL 方向;但本章关注的是稳定职责:SurfaceFlinger 把 layer 状态交给 HAL,HAL 根据设备显示能力返回组合方案并完成 present。任何具体设备的“某个 layer 是否走 overlay”都应以该设备 Perfetto、dumpsys SurfaceFlinger、HWC 日志或 vendor 工具观察为准。

95.7 GPU Composition、Overlay Plane、Display Controller 的任务分配

Android 一帧的最终输出通常由 GPU composition、overlay plane 和 display controller 共同完成。GPU composition 适合处理复杂混合、阴影、模糊、裁剪、纹理变换和多 layer 预合成;overlay plane 适合处理格式匹配、几何简单、面积较大、频率稳定或受保护的内容;display controller 负责把最终 plane 组合结果按时序扫描到面板,并处理 display mode、refresh、色彩转换和硬件接口。

普通 UI 窗口常进入 GPU composition。原因是 UI layer 往往包含多层半透明、圆角、阴影、动画、clip、文本和复杂变换,硬件 plane 的数量和能力难以覆盖全部情况。系统栏、导航栏、输入法、浮窗和 Activity 内容也会形成多 layer 叠加。当这些 layer 的混合关系复杂时,SurfaceFlinger 会把其中一组交给 GPU 合成到 client target,再由 HWC 呈现。

视频播放常争取 overlay plane。视频 buffer 可能是 YUV 格式,尺寸大,帧率稳定,内容由 MediaCodec 生产;如果硬件 plane 支持该格式、缩放和色彩空间,HWC 可以直接把视频 plane 与 UI plane 组合,降低 GPU 纹理采样和内存带宽开销。受 DRM 保护的内容还可能要求 hardware-protected path;AOSP BufferQueue 文档也说明 protected buffers 只能通过硬件保护路径显示,受保护视频需要落在 HWC overlay 方向上。

相机预览也常使用独立 Surface 和硬件合成机会。Camera HAL 或相机服务把 preview buffer 送入应用提供的 Surface,App UI 再以独立 layer 叠加控制按钮、取景框和状态提示。若预览层格式、大小和变换满足 HWC 能力,display hardware 可以直接扫描预览 plane;若存在复杂圆角遮罩、任意旋转、滤镜或叠加处理,预览可能转入 GPU 处理或先被应用渲染为纹理。

任务分配的核心取舍是带宽、功耗、延迟和显示能力。GPU composition 提供通用性,但消耗 GPU 时间和内存带宽;overlay plane 降低通用 GPU 成本,但受 plane 数量、格式、缩放、旋转、alpha、色彩空间和保护路径限制;display controller 必须在当前刷新周期内完成扫描输出。判断某个 layer 应该归谁处理时,先看内容类型,再看几何和格式,再看保护内容和 HDR,再看设备 plane 能力和当前其它 layer 占用情况。

95.8 Android Frame Path:View → RenderThread → Surface → SurfaceFlinger → HWC → Display

把前面各节串起来,一帧 Android View UI 的完整路径如下:View 状态变化触发 invalidaterequestLayoutViewRootImpl 借助 Choreographer 对齐 VSync 执行 traversal;主线程更新 display list 和 RenderNode 树;RenderThread 把渲染树交给 Skia/HWUI backend;GPU 把像素写入从 Surface 获取的 buffer;producer 通过 queueBuffer 把 buffer 和 fence 交给 BufferQueueSurfaceFlinger 在显示周期 latch 可用 buffer 并处理 layer transaction;HWC 通过 validateDisplay 决定 overlay 与 client composition;presentDisplay 把最终结果提交给显示硬件;display controller 按面板时序扫描输出。

Perfetto 和 Systrace 的价值在于把这条路径拆成可观察节点。Systrace 时代常用 gfxviewsched 等 tag 查看 View traversal、BufferQueue 和调度关系;Perfetto 时代可以同时观察 App main thread、RenderThread、GPU completion、FrameTimeline、SurfaceFlinger、HWC、CPU frequency 和 scheduler。Android 官方 BufferQueue 文档也提示可以通过 Systrace 查看 BufferQueue 对象和队列数量变化。工具只是观察入口,结论仍要回到责任链。

dumpsys SurfaceFlinger 更适合看系统合成状态。它可以提供 display、layer、composition、buffer、latency、present 相关信息,具体字段随 Android 版本变化。对本章主题来说,读者应关注这些问题:目标窗口是否有独立 layer;layer 是否持续提交 buffer;buffer 尺寸和 dataspace 是否符合预期;某些 layer 是否走 client composition;present fence 和 latch 时机是否显示系统合成延迟。dumpsys gfxinfo 则更接近 App View 层,可用来辅助观察应用帧耗时和 UI 线程状态。

一个稳定的排查顺序可以写成四步。第一步看 App 主线程:Choreographer#doFrame、input、animation、traversal、业务回调是否越过帧预算。第二步看 RenderThread 和 GPU:是否有大纹理上传、复杂 path、shader 编译、GPU fence 等待。第三步看 buffer 链路:BufferQueue 是否积压,producer 是否等待 dequeue,consumer 是否等待 acquire fence。第四步看系统合成:SurfaceFlinger 是否晚 latch,HWC 是否频繁把 layer 改为 client composition,present 是否错过目标 VSync。

下面这个表把常见观察点映射到责任边界,排查时可按从左到右的顺序缩小范围。

观察节点主要责任主体典型证据常见结论
UI threadApp 与 ViewRootImpldoFrameperformTraversals、业务回调过长输入、动画、布局或绘制命令生成晚交
RenderThreadHWUI 与 Skia backendframe building、纹理上传、GPU submit 延迟渲染后端或 GPU 工作超过预算
BufferQueueProducer 与 consumerdequeue 等待、queue 积压、fence 未信号buffer 生产消费节奏失衡
SurfaceFlinger系统 compositorlatch 延迟、transaction 堆积、client target 更新系统合成阶段成为瓶颈
HWC / displayVendor display pipelinevalidate/present 延迟、overlay 分配失败、present fence 延后显示硬件合成或扫描提交成为瓶颈

本章最终要建立的理解是:Android graphics pipeline 不是一条单线程绘制函数调用链,而是一条跨线程、跨进程、跨硬件队列的 frame path。App 负责生成 UI 状态和绘制命令,RenderThread 和 GPU 负责生产 buffer,BufferQueue 负责跨进程缓冲区交接,SurfaceFlinger 负责系统合成决策,HWC 和 display controller 负责硬件合成与扫描输出。读者掌握这条链路后,就能把“卡顿、掉帧、视频叠 UI、相机预览、系统栏覆盖、overlay 失败”这些现象放回具体责任边界分析。

最小自检任务

给出一个场景:一个聊天应用在用户快速滑动消息列表时出现周期性掉帧,同时顶部正在播放一条视频预览。请写出这一帧从 View 到 display 的路径,并判断至少四个可能瓶颈点。要求说明每个瓶颈点属于哪个责任主体、应该观察什么证据、用户可见结果会是什么。

答案要点

路径应从 App 主线程开始:滑动输入改变列表 scroll offset,相关 View 触发 invalidate 或局部 requestLayoutViewRootImplChoreographer VSync 回调中执行 traversal,主线程更新 display list 和 RenderNode 状态。随后 RenderThread 通过 HWUI/Skia backend 提交 GPU 工作,把聊天列表 UI 渲染到窗口 Surface 的 buffer,并通过 BufferQueue queue 给 SurfaceFlinger。视频预览若使用独立 Surface,通常作为另一个 layer 提交 buffer;若嵌入到 UI 纹理,则会增加 App 或 GPU 渲染成本。SurfaceFlinger latch UI layer 与视频 layer,处理 transaction 和合成决策,HWC 决定视频是否可放入 overlay plane,最后 display controller 扫描输出。

第一个瓶颈点是 App 主线程。证据是 Perfetto 中 UI thread 的 doFrame、数据绑定、图片解码、performTraversalsonMeasureonLayoutonDraw 超过目标帧预算;责任主体是应用代码和 View hierarchy;用户可见结果是列表滑动输入跟手性下降,视频可能仍流畅或出现 UI 覆盖层滞后。

第二个瓶颈点是 RenderThread 和 GPU。证据是 RenderThread 上 frame building、纹理上传、GPU submit 或 fence 等待延后;责任主体是 HWUI、Skia backend、GPU driver 和应用提交的绘制复杂度;用户可见结果是 UI 帧整体延迟,尤其在圆角、阴影、图片、模糊、复杂裁剪较多时更明显。

第三个瓶颈点是 BufferQueue。证据是 UI Surface 或视频 Surface 的 buffer 队列积压、producer dequeue 等待、consumer acquire fence 等待;责任主体横跨 App producer、MediaCodec producer 和 SurfaceFlinger consumer;用户可见结果可能是 UI 更新不连续、视频预览偶发停顿或画面使用上一帧。

第四个瓶颈点是 SurfaceFlinger 与 HWC。证据是 SurfaceFlinger latch 延迟、transaction 堆积、HWC validate 后把视频 layer 改为 client composition、present fence 延后;责任主体是系统 compositor、HWC HAL 和 vendor display pipeline;用户可见结果是视频叠加 UI 时功耗升高、掉帧集中在系统合成阶段,或者视频从硬件 overlay 回退到 GPU composition 后整帧压力增加。

边界判断是:若主线程晚交,先修 App traversal 和业务回调;若主线程按时但 RenderThread 或 GPU 晚完成,检查绘制复杂度和纹理上传;若 buffer 已按时提交但 SurfaceFlinger 晚 latch,检查 layer transaction、fence 和系统合成;若 HWC present 延后或 overlay 分配失败,检查视频 layer 格式、缩放、旋转、保护内容、HDR、系统栏叠加和当前设备 plane 能力。

本章知识点总结

  • 主路径:Android 一帧从 View 状态变化出发,经过主线程 traversal、RenderThread、Surface buffer、SurfaceFlinger、HWC 和 display controller。
  • View 入口invalidate 标记内容重绘,requestLayout 触发布局相关 traversal,二者决定主线程需要完成的工作量。
  • 帧节拍Choreographer 把 input、animation 和 drawing 对齐到显示节奏,帮助 ViewRootImpl 在下一帧执行 traversal。
  • 线程拆分:主线程负责 View 状态和绘制命令生成,RenderThread 负责把渲染树提交给 Skia/HWUI backend 和 GPU。
  • 绘制边界Canvas 是 2D API 表面,Skia 是图形实现库,OpenGL ES / Vulkan 是可能的 GPU 后端接口。
  • Surface 作用Surface 把 App 进程内渲染结果转换成系统 compositor 可消费的图形 buffer。
  • BufferQueueBufferQueue 通过 dequeue、queue、acquire、release 连接 producer 和 consumer,并通过 handle 传递 buffer。
  • Fence 同步:acquire fence 和 release fence 表达 buffer 读写依赖,使 CPU、GPU 和 display 工作能在依赖点同步。
  • 系统合成SurfaceFlinger 负责 latch buffer、处理 layer transaction、决定 composition,并把结果交给 HWC。
  • HWC 职责:Hardware Composer HAL 根据 display hardware 能力决定 layer 走 overlay plane、client composition 或混合方案。
  • 任务分配:GPU composition 提供通用性,overlay plane 降低部分带宽和功耗,display controller 负责最终扫描输出。
  • 视频路径:视频和相机预览常以独立 Surface 形成 layer,满足格式和几何条件时更适合硬件 overlay。
  • 排查顺序:掉帧应先看主线程,再看 RenderThread/GPU,再看 BufferQueue,最后看 SurfaceFlinger、HWC 和 present fence。
  • 责任边界:App 晚交、GPU 晚完成、buffer 交接阻塞、系统合成延迟和硬件 present 延迟分别对应不同修复方向。