Android 14 SurfaceFlinger 架构与实现深度解析:从服务初始化到合成管线的完整技术指南

Android 14 SurfaceFlinger 架构与实现深度解析

第一部分:服务初始化与客户端-服务器架构

本部分详细阐述 SurfaceFlinger 服务的引导过程,以及支撑 Android 图形系统所有交互的基础——客户端-服务器模型。

1.1 入口点: main_surfaceflinger.cpp

main_surfaceflinger.cpp 文件是 surfaceflinger 这个原生服务可执行文件的入口。它负责执行所有必要的初始设置,为整个图形合成系统的运行奠定基础。

  • main() 函数分析

    • 作为进程的入口点,main() 函数执行了关键的初始化步骤。
    • 其核心任务是实例化 SurfaceFlinger 对象。这是通过调用 surfaceflinger::createSurfaceFlinger() 函数完成的。
    • 一旦 SurfaceFlinger 实例被创建,main 函数便继续进行进程的调度策略和优先级配置,这是确保流畅视觉体验的关键一步。
  • 线程与调度策略初始化

    • 为了保证图形系统的实时性和响应性,SurfaceFlinger 对其主线程和 Binder 线程的调度策略进行了精心的设计。这是避免界面卡顿(Jank)的核心机制之一。
    • 代码明确地将 Binder 线程池的调度策略设置为 SCHED_FIFOSCHED_FIFO 是一种实时调度策略,它确保了来自客户端(如 WindowManager)的 Binder 事务能够被高优先级处理,避免因系统其他任务的干扰而产生延迟。
    • 设计原理:SurfaceFlinger 位于显示每一帧图像的关键路径上。如果其主线程或负责接收状态更新的 Binder 线程被延迟,结果将是错过 VSync 信号,用户感知到的就是卡顿或屏幕冻结。标准的 Linux 调度器(如 CFS)为公平性和吞吐量而优化,而非低延迟的截止时间。SCHED_FIFO 策略则保证了高优先级的任务一旦获得 CPU,就会持续运行直到完成或被更高优先级的任务抢占。
    • 通过将 Binder 线程置于 SCHED_FIFO 策略下,Android 确保了 WindowManager 发出的场景变更请求(例如打开一个应用)能够被立即处理。同时,主线程本身也以高优先级(PRIORITY_URGENT_DISPLAY)运行,以确保合成与硬件交付能够在 VSync 的严格时间窗内完成。这是实现流畅用户体验的基石设计。
  • 服务注册

    • SurfaceFlinger 对象实例化并完成初步配置后,它必须将自己注册到系统的 ServiceManager,才能被其他系统服务和应用客户端发现和使用。这一步骤通过调用 sm->addService() 来完成。
    • 服务的名称通过 SurfaceFlinger::getServiceName() 获取,通常就是 "SurfaceFlinger"
    • 一个值得注意的细节是,该服务在注册时使用了 IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL 标志。这个标志确保了即使在系统高负载或濒临崩溃的情况下,dumpsys 工具也能优先获取到 SurfaceFlinger 的状态信息,这对于调试复杂的图形问题至关重要。
    • 此外,系统还会注册一个独立的、基于 AIDL 的 gui::ISurfaceComposer 接口,其服务名为 "SurfaceFlingerAIDL",这反映了 Android 系统向全面采用 AIDL 进行跨进程通信的演进趋势。

1.2 客户端-服务器接口: ISurfaceComposer 与 Client.cpp

ISurfaceComposer 接口是客户端与 SurfaceFlinger 服务之间通信的正式契约。它定义了一套客户端可以执行的操作,构成了整个显示系统的编程接口。

  • ISurfaceComposer Binder/AIDL 接口

    • 该接口历史上是基于原生 Binder 实现的,但在现代 Android 版本中,它已迁移到使用 AIDL(Android 接口定义语言)来定义。
    • AIDL 能够自动生成大量进程间通信(IPC)的模板代码,简化了开发和维护。
    • 在该接口定义的所有函数中,setTransactionState() 是最核心、最关键的一个。
  • 函数 setTransactionState(): 原子化更新机制

    • setTransactionState() 是 SurfaceFlinger 所有状态变更的基石。它允许客户端将任意数量的图层(Layer)和显示(Display)状态变更打包成一个单一的、原子性的事务进行提交。这种设计可以有效防止屏幕出现部分或不一致的更新。
    • 该函数的关键参数包括:
      • ComposerState: 一个包含每个图层状态变更的向量,例如位置、透明度、裁剪区域等。
      • DisplayState: 一个包含每个显示器状态变更的向量,例如屏幕方向。
      • flags: 一组用于修改事务行为的标志位。
    • 事务模型优势
      • 原子性:一个 UI 更新(如一个窗口一边滑动一边淡入)通常涉及在多个图层上同时改变多个属性,事务模型确保了整个场景变化在同一帧中呈现。
      • 性能:它将多次 IPC 调用合并为一次,显著降低了 Binder 通信的开销。
      • 同步applyToken 等参数允许事务与缓冲区更新同步,确保图层的几何属性变化与新内容准备就绪的时刻精确匹配。这是一个强大且基础的设计模式,是构建健壮图形系统的关键。
  • 事务标志 (eAnimation, eEarlyWakeupStart)

    • 这些标志位为 SurfaceFlinger 提供了关于事务性质的额外提示,使其能够做出更智能的调度决策。
    • 例如,eAnimationeEarlyWakeupStart 标志向系统表明,接下来可能会有一系列密集的合成操作。
    • 收到此信号后,Scheduler 会提前唤醒 SurfaceFlinger 主线程(相对于 VSync 信号),给予其更充裕的时间来完成合成工作,从而有效防止动画期间的丢帧。

    表 1: ISurfaceComposer::setTransactionState 标志

    标志名称 枚举值 描述与目的 主要调用者
    eAnimation 0x02 提示此事务是动画的一部分。 WindowManager
    eEarlyWakeupStart 0x08 明确指示当前及后续事务可能涉及大量图层的合成,要求 SurfaceFlinger 提前唤醒以避免错过帧截止时间。SurfaceFlinger 将保持此状态直到收到 eEarlyWakeupEnd WindowManager
    eEarlyWakeupEnd 0x10 标志着由 eEarlyWakeupStart 启动的“提前唤醒”周期的结束。 WindowManager
    eOneWay 0x20 指示此事务为单向调用,客户端不等待 SurfaceFlinger 的返回状态。用于性能优化。 WindowManager
    • 这张表格为开发者提供了一个快速、可操作的参考。在进行性能优化或问题调试时,理解这些标志至关重要。例如,了解 eEarlyWakeupStart 是对抗动画卡顿的机制,为开发者指明了清晰的调查路径:“在我的转场动画中,WindowManager 是否正确设置了此标志?”它将客户端与调度器之间复杂的交互逻辑精炼成一个简单的查找表。
  • Client 类: 服务器端的客户端代理

    • 当一个客户端(如 WindowManager)调用 createConnection() 时,SurfaceFlinger 会在服务端实例化一个 Client 对象。
    • 这个对象作为该客户端连接在服务器端的代理,负责追踪和管理该连接所拥有的所有资源,最主要的就是图层(Layers)。
  • 函数 Client::createSurface(): 创建绘图目标

    • 这是客户端获取一个可绘制表面的入口点。Client 对象接收到这个调用后,会将其转发给 SurfaceFlinger::createLayer
    • 此调用的关键输出是一个 gui::CreateSurfaceResult 结构体。
    • SurfaceFlinger::createLayer 内部,系统会创建一个 Layer 对象。在该 Layer 对象的初始化过程中(具体在 onFirstRef() 方法中),一个 BufferQueue 实例会被创建。BufferQueue 拥有一个生产者端(IGraphicBufferProducer)和一个消费者端(IGraphicBufferConsumer)。
    • 生产者端接口被打包进 CreateSurfaceResult 结构体,通过 Binder 调用返回给客户端应用。
    • 而消费者端则保留在 SurfaceFlinger 进程内部,由一个与该 Layer 关联的 SurfaceFlingerConsumer 对象进行管理。
    • 生产者-消费者模型:这种解耦是 Android 图形栈的核心设计哲学。客户端(生产者)和 SurfaceFlinger(消费者)运行在不同的进程中,BufferQueue 则是连接它们的“胶水”。通过仅向客户端返回生产者接口,系统强制实现了一种清晰的隔离。客户端可以向队列中填充缓冲区,而无需关心合成的任何细节。同样,SurfaceFlinger 可以消费这些缓冲区,而无需关心它们是如何产生的(例如,是通过 OpenGL、Vulkan 还是视频解码器)。这种解耦对于整个图形管线的模块化和稳定性至关重要。

第二部分:核心原语与状态管理

本部分深入剖析 SurfaceFlinger 中最基本的对象——Layer 类。它代表了一个独立的合成单元,是理解整个合成流程的基础。

2.1 合成的基本单元: Layer

Layer 是客户端 SurfaceControl 在服务端的具象化表示。它封装了与一个表面相关的所有状态,是 SurfaceFlinger 进行操作的最小单位。

  • Layer 类的架构

    • Layer 类负责封装一个可合成单元的所有属性,包括其几何状态、内容以及元数据。其核心成员变量包括:
      • mFlinger: 一个指回 SurfaceFlinger 服务实例的指针,用于回调和访问全局状态。
      • mDrawingState: 描述了图层当前用于绘制和合成的状态。这个状态在合成周期开始时被锁定,是不可变的。
      • mCurrentState: 描述了图层的下一个状态。所有来自客户端的事务都会先更新这个状态。
      • mSurfaceFlingerConsumer: BufferQueue 的消费者端,由 Layer 持有,用于从队列中获取新的图形缓冲区。
      • mProducer: BufferQueue 的生产者端,其 Binder 句柄被传递给客户端,供客户端提交图形缓冲区。
    • mDrawingStatemCurrentState 的存在构成了状态管理的双缓冲机制。事务在任何时候都可以通过 Binder 线程更新 mCurrentState,而合成主线程则在一个稳定的、不被修改的 mDrawingState 上工作,从而避免了数据竞争。
  • 构造函数与 onFirstRef(): 实例化与延迟初始化

    • Layer 对象由 SurfaceFlinger::createLayer 方法根据客户端请求(LayerCreationArgs)创建。
    • 然而,一些重量级资源的分配并不会在构造函数中立即进行。关键的初始化逻辑被延迟到 onFirstRef() 方法中执行。该方法在第一个强引用指向 Layer 对象时被调用。
    • 正是在这里,BufferQueue 通过 BufferQueue::createBufferQueue() 被实际创建。
    • 延迟初始化的目的:这是一种效率优化。客户端可能创建了一个 SurfaceControl 但从未实际向其绘制任何内容。通过将 BufferQueue 的创建(这涉及到共享内存和其他资源的分配)推迟到首次实际使用时,SurfaceFlinger 避免了不必要的资源开销,这对于整个系统的内存和资源管理是一个虽小但重要的优化。
    • onFirstRef() 中,BufferQueue 的生产者和消费者端被分离。Layer 将自身注册为消费者端的回调监听器(mSurfaceFlingerConsumer->setContentsChangedListener(this)),以便在客户端提交新帧时得到通知。
  • 缓冲区处理回调

    • onFrameAvailable(const BufferItem& item):
      • 当生产者(客户端)成功将一个新的缓冲区排入 BufferQueue 时,这个回调函数会被触发。
      • 它的主要职责是将描述新缓冲区的 BufferItem 添加到 Layer 内部的一个待处理队列(mQueueItems)中。
      • 随后,它会调用 mFlinger->signalLayerUpdate()。这个调用并不会立即触发重绘,而是向 Scheduler 发出一个信号,表明该图层有新的内容需要处理,具体的合成工作将在下一个 VSync 周期中被调度。
    • onFrameReplaced(const BufferItem& item):
      • 这个回调处理一种特殊情况:当生产者以极高的频率提交缓冲区,以至于前一帧还未被消费,新的一帧就已经到来时。
      • 在这种情况下,为了避免待处理队列中积压大量最终会被丢弃的过时帧,onFrameReplaced 会直接替换掉队列中的最后一项。这是一个重要的优化,确保 SurfaceFlinger 总是合成最新鲜的内容,降低延迟。
  • 锁存机制: latchBufferImpl()

    • latchBufferImpl()Layer 生命周期中至关重要的一个函数。如果说 onFrameAvailable 仅仅是“报告”新帧的到来,那么 latchBuffer 则是 SurfaceFlinger 在合成阶段为即将到来的合成工作“接受”并“锁定”这一帧的决定性时刻。
    • 该函数由 SurfaceFlinger 的主合成循环在处理每个有待处理帧的图层时调用。其执行步骤如下:
      1. 检查 Acquire Fence: 函数首先检查与待处理缓冲区关联的 acquireFence。这个栅栏(Fence)是一个同步原语,用于确保生产者(例如 GPU 或视频解码器)已经完成了对该缓冲区的所有写入操作。如果栅栏尚未触发(signaled),意味着内容还未完全准备好,函数会立即返回。SurfaceFlinger 会在后续的合成周期中再次尝试锁存。
      2. 更新纹理: 如果 acquireFence 已经触发,函数会调用 updateTexImage()。这个调用会使缓冲区的内容对于底层的合成引擎(RenderEngine)变为可用,通常是将其包装成一个硬件纹理。
      3. 更新状态: 函数会用新锁存的缓冲区的属性(如数据空间 dataspace、裁剪矩形 crop、变换 transform 等)来更新图层内部的当前状态(mBufferInfo, mCurrentFrameNumber)。
      4. 发出几何变更信号: 函数会比较新旧缓冲区的属性。如果尺寸、裁剪或变换等几何信息发生了变化,它会将 recomputeVisibleRegions 标志位置为 true。这个标志会通知 SurfaceFlinger,该图层的几何形状已改变,因此所有图层的可见性计算(即判断哪些部分被遮挡,哪些部分可见)需要重新进行。
    • acquireFence 的重要性acquireFence 的使用是实现生产者和消费者异步执行的关键。它将生产者的渲染管线与 SurfaceFlinger 的合成管线解耦。生产者可以完成其工作并立即将缓冲区入队,而 SurfaceFlinger 只会在两个条件同时满足时才“锁存”该缓冲区:VSync 信号到来,并且生产者的工作已由 acquireFence 确认完成。这从根本上防止了对未完全渲染内容的合成,是整个异步图形管线稳定运行的保障。

第三部分:同步、调度与步调控制

本部分将探讨 SurfaceFlinger 主循环的现代化、复杂核心:Scheduler,以及它如何与系统范围的 VSync 信号进行交互,实现精确的帧步调控制。

3.1 系统的脉搏: VSync 与 DispSync

VSync(垂直同步)信号是同步整个显示管线的基准时钟。它协调着应用渲染、SurfaceFlinger 合成以及硬件合成器(HWC)最终将图像呈现到屏幕上的所有环节。

  • VSync 偏移 (HW_VSYNC_0, VSYNC, SF_VSYNC)

    • 为了最大限度地降低从用户输入到屏幕显示的延迟,现代 Android 系统并不依赖单一的 VSync 事件。取而代之的是一个基于相位偏移的精巧模型。
      • HW_VSYNC_0: 这是物理硬件发出的 VSync 信号,代表显示器开始扫描输出新一帧的精确时刻。
      • VSYNC: 这是一个软件生成的、提前于 HW_VSYNC_0 的信号。它被发送给应用程序,目的是让应用有足够的时间去渲染下一帧的内容。
      • SF_VSYNC: 这是另一个软件生成的信号,专门发送给 SurfaceFlinger。它的时间点被精确地安排在应用渲染截止之后、硬件显示截止之前。
    • 流水线模型:这种偏移模型在图形管线中创建了一个多级流水线。一个简单的模型是:VSync 信号到达 -> 应用绘制 -> SurfaceFlinger 合成。这会产生至少两帧的固有延迟。而流水线模型则允许这些操作并行进行:在同一个 VSync 间隔内,显示器正在显示第 N 帧,SurfaceFlinger 正在合成第 N+1 帧(使用应用在上一个间隔中渲染好的缓冲区),而应用则正在处理输入并渲染第 N+2 帧。这种流水线技术是计算机体系结构中用于隐藏延迟和提高吞吐量(帧率)的经典方法在图形系统中的应用。
    • 这些偏移量(如 VSYNC_EVENT_PHASE_OFFSET_NS)是作为设备属性被精心调整的,旨在延迟缩减和避免错过截止时间的风险之间取得最佳平衡。
  • DispSync: 软件锁相环 (PLL)

    • 物理硬件产生的 VSync 信号可能存在抖动(Jitter)。为了解决这个问题,Android 引入了 DispSync
    • DispSync 本质上是一个软件实现的锁相环(Phase-Locked Loop),它对硬件 VSync 信号进行建模。它接收带有噪声的硬件 VSync 作为输入,并为系统的其他部分(如 Choreographer 和 SurfaceFlinger)生成稳定、可预测且具有正确相位偏移的 VSYNCSF_VSYNC 信号。
    • DispSync 使用来自 HWC 的“退休栅栏”(Retire Fences)作为反馈信号,不断校正其内部模型,以确保其生成的软件 VSync 与物理显示刷新周期保持精确同步。

3.2 指挥家: Scheduler 类

Scheduler 是替代了旧版基于 MessageQueue 的主循环的现代化组件。它负责编排所有与时间相关的活动,是 SurfaceFlinger 的“大脑”。

  • 主要职责

    • Scheduler 的职责范围广泛,涵盖了 VSync 事件的分发、合成任务的触发、显示器电源状态的管理,以及至关重要的一点——动态选择显示器的刷新率
  • 事件处理: onFrameSignal

    • onFrameSignal 函数由一个 EventThreadSF_VSYNC 信号到达时调用,标志着一个合成周期的正式开始。
    • 它作为入口点,驱动整个帧的生成过程,依次调用 compositor.commit()(提交状态变更)和 compositor.composite()(执行合成)。
  • RefreshRateSelector 的交互

    • SchedulerRefreshRateSelector 紧密协作,以实现动态刷新率的调整。
    • getRankedFrameRates(): 这是刷新率选择的核心决策函数。
    • 决策的输入:
      • ContentRequirements: Scheduler 通过 recordLayerHistory 持续追踪活动图层的帧率。例如,如果一个视频正在以 24fps 的速率播放,这个需求就会被传递给选择器。
      • GlobalSignals: 这个结构体封装了全局系统状态。touch 信号表示用户正在与屏幕交互,而 idle 信号则表示系统处于闲置状态。
      • 帧率覆盖: Game Manager 或其他特权服务可以为特定的应用(通过 UID)设置一个强制的帧率。
    • 决策逻辑: RefreshRateSelector 会权衡这些因素。
      • 一个活跃的触摸事件通常会强制系统切换到最高的刷新率以保证交互的流畅性。
      • 如果系统处于闲置状态,它会选择最低的刷新率以节省电量。
      • 如果有一个 24fps 的视频正在播放,它会尝试选择一个刷新率是 24 的整数倍的显示模式(例如 120Hz、96Hz,或者在没有更优选项时选择 60Hz),以避免画面产生抖动(Judder)。
    • 演进意义:从固定的 60Hz 刷新率到如今复杂的调度模型,这代表了 Android 图形架构的一次巨大飞跃。SchedulerRefreshRateSelector 的引入,标志着系统从一个被动的、响应式的模型转变为一个主动的、整体的模型。说它“主动”,是因为它试图根据内容和用户交互来预测最佳刷新率,而不仅仅是响应到达的缓冲区。说它“整体”,是因为它同时考虑了多个维度的因素——应用需求、用户输入、电源状态和系统策略。这种复杂性是在现代可变刷新率显示设备上同时实现高性能和长续航所必须付出的代价。

第四部分:合成管线

本部分详细介绍将所有可见图层合成为最终一帧并准备在显示器上呈现的过程。

4.1 抽象合成模型: CompositionEngine

CompositionEngine 是一个相对较新的组件,它封装了“合成什么”以及“如何合成”的逻辑,将其与 SurfaceFlinger 类的主体分离开来。SurfaceFlinger 类现在更专注于状态管理和策略决策。

  • 目的与设计
    • 这种分离的设计旨在提高代码的模块化和可测试性。
    • CompositionEngine 定义了一个清晰的接口,如 present()update()。它接收当前的图层集合和显示状态,通过与 HWComposerRenderEngine 交互来编排整个合成过程。
    • 这种架构演进是大型软件项目中的常见模式:随着系统复杂度的增长,将庞大的单体类重构为多个功能单一、接口清晰的小型组件是提升代码质量和可维护性的必然选择。
    • 通过这种方式,CompositionEngine 的核心合成逻辑可以独立于 SurfaceFlinger 的 Binder 接口和调度逻辑进行单元测试。

4.2 硬件抽象: HWComposer

HWComposer 是 SurfaceFlinger 内部直接与硬件合成器 HAL(Hardware Composer HAL)通信的 C++ 类。HWC HAL 本身是一个由芯片(SoC)供应商提供的、与具体设备相关的实现。

  • HWC HAL 交互

    • HWComposer 封装了与 HWC HAL 的所有交互,包括创建和销毁图层、设置图层属性以及执行合成。
  • 验证与提交流程

    • SurfaceFlinger 与 HWC 之间的交互是一个关键的、分为两个阶段的协商过程。
      1. validateDisplay() (验证阶段): 在每个合成周期,SurfaceFlinger 会为每个显示器调用此函数。它向 HWC 提供一个包含所有可见图层及其属性(缓冲区、位置、透明度等)的完整列表,并询问:“你打算如何处理这些图层?” HWC 会审查这个列表,并为每个图层标记一个合成类型:
        • Device (或 HWC): 表示 HWC 将直接使用其硬件覆盖(Overlay)来处理这个图层。这是最高效、最省电的路径。
        • Client (或 GLES): 表示 HWC 无法处理这个图层(例如,覆盖层数量超出限制、不支持的像素格式或变换等)。它会通知 SurfaceFlinger:“这个图层由你来处理。”
      2. acceptDisplayChanges() (接受变更): SurfaceFlinger 读取 HWC 在验证阶段提出的变更请求(例如,哪些图层需要从 Client 变为 Device),并接受这些变更。
      3. presentDisplay() (提交显示): 在处理完所有被标记为 Client 的图层合成后(详见下一节),SurfaceFlinger 调用此函数。它将最终的图层列表传递给 HWC。HWC 负责将所有标记为 Device 的图层显示在屏幕上,同时也会接收并显示由 SurfaceFlinger 合成好的 Client 层的输出缓冲区。此调用还会返回一个“退休栅栏”(Retire Fence),该栅栏会在 HWC 完全完成当前帧的显示工作后触发,用于精确的帧同步。
  • 事件处理: onVsynconHotplug

    • HWC 是硬件 VSync 信号和显示器热插拔事件的源头。
    • 当这些事件发生时,HWC HAL 会通过回调函数通知 HWComposer 类中的 onVsynconHotplug 方法,这些事件随后被路由到 Scheduler 以更新其内部状态和调度决策。

4.3 GPU 回退: RenderEngine

当 HWC 将一个或多个图层标记为 Client 合成时,SurfaceFlinger 必须使用 GPU 作为回退方案来合成这些图层。RenderEngine 类就是负责执行此任务的组件。

  • 在客户端合成中的角色
    • RenderEngine 是对 GPU 图形 API(通常是 OpenGL ES)的一个抽象层。
    • SurfaceFlinger 会遍历所有被标记为 Client 合成的图层,并指示 RenderEngine 按照正确的 Z 序(Z-order)将它们绘制到一个临时的“草稿缓冲区”(scratch buffer)中。
    • 这个包含了所有 Client 图层合成结果的缓冲区,随后会通过 setClientTarget 方法被提交给 HWC,由 HWC 将其与那些 Device 图层一起显示在屏幕上。
  • 设计意义与调试
    • 这种设计使得 HWC 成为一种强大的、针对特定硬件的优化手段,而非一个硬性要求。系统被设计为即使在一个“空操作”的 HWC(即将所有图层都标记为 Client 合成)上也能正确运行。在这种情况下,SurfaceFlinger 和 GPU 会完成所有的合成工作,虽然功耗较高,但保证了功能的正确性。
    • 这个特性为调试提供了极大的便利。开发者可以通过“开发者选项”中的“停用 HW 叠加层”开关来强制使用 GPU 合成。这是一个非常有价值的工具,可以帮助开发者快速定位一个视觉错误是源于应用自身的渲染、SurfaceFlinger 的 GPU 合成,还是 HWC 驱动程序。

第五部分:事务处理与最终提交

本部分将客户端 API 与合成管线连接起来,阐述状态变更是如何以事务的方式被原子性地应用和提交的。

5.1 SurfaceFlinger.cpp 中的事务管线

  • 现代化的消息循环

    • 尽管旧版 Android 拥有一个基于 MessageQueue 的显式消息循环,但现代的循环是由 Scheduler 驱动的。
    • 当客户端调用 setTransactionState 时,它最终会触发 Scheduler 上的 scheduleCommit() 调用。这确保了事务处理逻辑会在主线程上、在 VSync 周期的正确时间点被执行。
  • 函数 flushTransactionQueues()

    • 在合成周期的提交(Commit)阶段开始时,系统会调用此函数。它的作用是收集自上一个周期以来所有由客户端提交并处于待处理状态的事务。
  • 函数 applyTransactionsLocked()

    • 此函数遍历收集到的事务列表。对于每个事务,它会调用 applyTransactionState,该函数会解析事务中的 ComposerState 向量,并将其中定义的状态变更(如新的位置、透明度等)应用到相应 Layer 对象的 mCurrentState 成员上。
    • 重要的是,这个阶段只修改了“待定”状态,尚未影响到当前屏幕上正在绘制的内容。
  • 函数 commitTransactionsLocked()

    • 这是决定性的原子提交步骤。它将整个 mCurrentState 的内容完整地复制到 mDrawingState 中。
    • 此调用完成后,新的状态就“生效”了,并将在当前帧的合成过程中被使用。
    • 同时,它还会根据需要设置 mVisibleRegionsDirty 等标志,以触发后续的处理步骤。
  • 函数 handlePageFlip() (及其现代等价物 doComposition)

    • 这个概念性的步骤在状态提交之后发生,它包含了一系列操作:
      1. 重建图层栈 (Rebuilding Layer Stacks): 基于刚刚提交的、最新的 mDrawingState,重新计算和排序每个显示器上的可见图层。
      2. 锁存缓冲区 (Latching Buffers): 对所有有新帧可用的图层调用 latchBufferImpl(),将新的图形内容锁定用于本次合成。
      3. 执行合成 (Composition): 运行 CompositionEnginepresent/update 逻辑,这会启动前文所述的 HWC 验证与提交流程。
  • 状态双缓冲

    • mCurrentStatemDrawingState 的使用是一种经典的状态双缓冲模式,只不过缓冲的对象是状态数据而非图像数据。
    • 来自多个客户端的事务可以随时通过不同的 Binder 线程到达,它们在锁的保护下修改 mCurrentState。与此同时,主合成线程可以安全地从不可变的 mDrawingState 中读取数据进行合成,完全不必担心数据竞争。
    • commitTransactionsLocked() 函数就是那个“交换”操作,它使得待定状态成为当前绘制状态。这种设计是允许多线程、异步系统为渲染维护一个一致性状态的基础,是整个 SurfaceFlinger 稳定运行的核心机制之一。

本文持续更新中,最后更新时间:2025年7月26日