← 返回课程

数据流全链路

Camera全栈开发(Qcom Camx) 第 19 / 30 节

第 17 章:Request → Result 数据流全链路 ⭐⭐⭐


本章导读

前面 6 章拆解了 CamX-CHI 的每个组件——Usecase、Session、Pipeline、Node、Feature2。现在是时候把它们串起来了。

从 App 的 capture() 开始,到 ISP 硬件处理完成、结果回到 App——完整走一遍


17.1 全链路总览

![```text](/api/course-files/camera-fullstack/documents/diagrams/17_full_dataflow.png)
App process_capture_request()

HAL3 入口 (camxhal3entry.cpp)

Usecase::SubmitChiRequest()

├──→ [如果启用了 Feature2] Feature Graph Manager 接管
│ → FGS 选择 Graph → FGM 创建 FRO → 逐个 Feature 处理

├──→ [如果未启用 Feature2] 直接到 Session

Session::ProcessCaptureRequest()
│ ┌────────────────────────────────────┐
├──│ Pipeline 0 (RT, Preview) │
│ │ IFE → IPE → Display (不过 BPS) │
│ └────────────────────────────────────┘
│ ┌────────────────────────────────────┐
├──│ Pipeline 1 (Offline, JPEG/后处理) │
│ │ IFE → BPS → IPE → JPEG (拍照) │
│ └────────────────────────────────────┘

CSLSubmit() → Kernel → ISP HW

HW 处理完成 → CSLFenceSignaled()

Node::ProcessResult()
→ Pipeline::ProcessPipelineResult()
→ Session::ProcessPipelineResult()
→ [如有 Feature2] Feature 汇聚 Result
→ Usecase → HAL → App


---

## 17.2 分步详解

### Stage 1: App → HAL3 入口

App 调用 capture() 或 setRepeatingRequest()
→ CameraService → Provider → camxhal3entry.cpp


```log
// 源码: camx/src/core/hal/camxhal3entry.cpp
[CamX] [HAL ] camxhal3entry.cpp: ProcessCaptureRequest() frame_number=42

你需要知道的camxhal3entry.cpp 是 HAL3 的入口文件。所有 process_capture_request 调用都在这里被截获,然后转发给 CHI 层。如果你在 logcat 中看到这个日志但后面没有 SubmitChiRequest,说明请求在 HAL 入口之后、到达 CHI 之前丢失了。

Stage 2: Usecase → Session

HALDevice → Usecase::SubmitChiRequest() → Session::ProcessCaptureRequest()
// 源码: chi-cdk/core/chiframework/chxusecase.cpp
[CHI] chxusecase.cpp: SubmitChiRequest() frame_number=42

// 源码: camx/src/core/camxsession.cpp
[CamX] [REQMAP] camxsession.cpp: chiFrameNum: 287 <==> requestId: 42

chiFrameNum <==> requestId 是 CHI 帧号和 App Request ID 的映射关系。调试时经常需要从 App 的 Request ID 反查 CHI 帧号。

源码在 camx/src/core/camxsession.cppProcessCaptureRequest() 中生成这个映射——如果你想看这个映射的生成逻辑,直接打开这个文件搜索 "chiFrameNum"。

Stage 3: Session 分发到 Pipeline

Session 解析 PipelineIndex → 分发到 RT 和 Offline Pipeline
[CamX] [CORE] camxpipeline.cpp: Pipeline::ProcessRequest() pipelineId=0 req=42

Stage 4: Pipeline — Node 依赖检查

Pipeline 遍历所有 Node,检查 DependencyUnit
  Preview Pipeline:  IFE 无输入依赖 → ready; IPE 需要 IFE→wait
  Snapshot Pipeline: BPS 需要 IFE→wait; IPE 需要 BPS→wait; JPEG 需要 IPE→wait
[CamX] [CORE] camxnode.cpp: CheckAndSetDependencies() node=IFE satisfied=true
[CamX] [CORE] camxnode.cpp: CheckAndSetDependencies() node=IPE satisfied=false

Stage 5: Node → CSLSubmit

Node 处理 → CSLSubmit() → ioctl → Kernel
[CamX] [CSL ] cslcommon.cpp: CSLSubmit() pipelineId=0 req=42

Stage 6: HW 处理完成

ISP 硬件处理 → DMA 写回 → 中断 → CSLMessageHandler
// 源码: camx/src/core/camxpipeline.cpp
[CamX] [CORE] camxpipeline.cpp:1843 CSLMessageHandler()
       frameMessage.requestID=42, frameMessage.frameCount=37,
       frameMessage.timestamp=265793404062

requestID=42 关联回 App 的 Request | frameCount=37 是 Sensor 输出的物理帧序号 | timestamp 是硬件时间戳

这个日志是 Pipeline 状态机分析的核心——如果你看到 requestID=0,说明 KMD 丢弃了无效帧(通常是 sensor 同步问题)。如果你看到 requestID 连续递增但 frameCount 跳跃,说明有丢帧。

Stage 7: Result 回传——分两段

Result 不是一次性回来的。CamX 支持 Partial Result——metadata 可以先回来,图像 Buffer 后回来。

第一段: Metadata 先回(Partial Result)
  IFE 处理完成 → IFE Node 写回 Stats 数据(AE/AWB/AF 统计值)
    → Pipeline::NotifyNodePartialMetadataDone()
      → Session 收集 partial metadata
        → Usecase::ProcessPartialCaptureResult()
          → 不需要等图像数据,先回调 metadata

第二段: 图像 Buffer 后到(Full Result)
  IPE 处理完成 → Buffer 通过 SinkPortFenceSignaled 到达
    → Pipeline::ProcessPipelineResult()
      → Session 汇聚所有 Pipeline Result → 合并 metadata + buffer
        → Usecase::ProcessCaptureResult()
          → HAL3 process_capture_result() → App
// Partial metadata 先回来
[CamX] [CORE] camxnode.cpp: ProcessPartialMetadataDone() node=IFE req=42
[CHI] chxusecase.cpp: ProcessPartialCaptureResult() requestId=42

// 图像 buffer 后到
[CamX] [CORE] camxsession.cpp: ProcessPipelineResult() pipelineId=0 req=42
[CHI] chxusecase.cpp: ProcessCaptureResult() requestId=42
[CamX] [HAL ] camxhal3entry.cpp: process_capture_result() frame_number=42

💡 Partial Result 的价值: AE 的统计数据在 IFE 处理完就出来了,不需要等 IPE 处理完。Usecase 拿到 partial metadata 后可以提前为下一帧计算 AE 参数——这就是"Pipeline"的真正含义:不等全部做完,每一站有结果就立刻送回来一部分。


17.3 从日志定位问题

// 完整的正常日志链:
① [CamX] [HAL ] camxhal3entry.cpp: ProcessCaptureRequest() frame=42
② [CHI]  chxusecase.cpp: SubmitChiRequest() frame=42
③ [CamX] [REQMAP] chiFrameNum: 287 <==> requestId: 42
④ [CamX] [CORE] camxpipeline.cpp: Pipeline::ProcessRequest() pipelineId=0
⑤ [CamX] [CORE] camxnode.cpp: CheckAndSetDependencies() node=IFE satisfied=true
⑥ [CamX] [CSL ] CSLSubmit() pipelineId=0
⑦ [CamX] [CORE] camxpipeline.cpp: CSLMessageHandler() requestID=42
⑧ [CHI]  chxusecase.cpp: ProcessCaptureResult() requestId=42
日志断在哪里,问题就在哪里:

断在 ① → App 没发起请求 / HAL3 入口问题
断在 ③ → Session 映射失败
断在 ⑤ → Node 依赖一直不满足(死锁!)
断在 ⑥ → CSL 提交失败
断在 ⑦ → HW 处理异常
断在 ⑧ → 回调链路异常

17.4 性能分析

通过日志时间戳可以测量各阶段耗时:

t0: ProcessCaptureRequest (入口)
t1: CSLSubmit (提交到硬件)
t2: CSLMessageHandler (硬件完成)

t1 - t0 = Framework 处理延迟(通常 <5ms)
t2 - t1 = ISP 硬件处理时间(约 1 帧 = 16ms@60fps)

总延迟 = t2 - t0 = ~20~30ms(正常)

17.5 本章总结

Request → Result 经历 8 个阶段:
  App → HAL3 → Usecase → Session → Pipeline → Node → CSL → HW → 回传

关键日志:
  ① [HAL ] ProcessCaptureRequest       — 入口
  ③ [REQMAP] chiFrameNum <==> requestId — 映射关系
  ⑦ CSLMessageHandler requestID        — HW 完成
  ⑧ ProcessCaptureResult               — 回调

日志断在哪里,问题就在哪里

动手验证

  1. 打开相机 App,预览状态下抓一次完整的 logcat:

    adb logcat -c && adb logcat -v threadtime | grep -E "CamX|CHI" > trace.txt
    

    trace.txt 中找到第 17.2 节描述的 7 个阶段的对应日志。

  2. 从 CSLMessageHandler 日志中计算当前帧率(相邻两条的时间戳差)。

  3. 拍一张照片,对比拍照前后的日志新出现的 Pipeline 和 Node 日志。

常见误解


篇末实战:追踪一次拍照的完整链路

这是最重要的一篇实战。

目标:独立追踪一次 capture_request 从 App 到 ISP 再回来的完整链路。

准备

adb root && adb shell setprop persist.vendor.camera.logs.override 0x1F && adb logcat -c
# camxoverridesettings.txt 中添加: dumpNodeProcessingInfo=1, enableFPSLog=1

抓日志

adb logcat -v threadtime | grep -E "CamX|CHI" > trace_capture.txt
# 同时:打开相机 → 拍一张照片 → 关闭

按阶段分析

按照第 17 章的 7 个阶段,从日志中找到对应行:

回答以下问题

  1. ProcessCaptureRequest → CSLSubmit 花了多久?(Framework 延迟)
  2. CSLSubmit → CSLMessageHandler 花了多久?(ISP 硬件延迟)
  3. 哪个 Node 耗时最长?
  4. 拍照时是否有独立的 Offline Pipeline(pipelineId=1)?
  5. Offline Pipeline 的状态是否从 STREAM_OFF 变到 STREAM_ON?

17.6 源码走读路线图

这一节把每个核心流程对应的源码位置集中列出来。当你拿到一份 CamX 源码不知道从哪里开始时,就从这里入手。

路线 1:初始化流程(open → initialize)

目的: 追踪从 HAL 加载到 ChiContext 初始化的完整路径

入口: camx/src/core/hal/camxhal3module.cpp
       HAL_MODULE_INFO_SYM → g_camx_module_methods

① camxhal3module.cpp: g_camx_module_methods.open()
   → camxhaldevice.cpp: CamxHalDevice::Open()
      → 创建 CamxHalDevice 对象
      → camxhaldevice.cpp: Initialize()
         → 初始化 ChiContext

② camxchicontext.cpp: ChiContext::Create()
   → camxhwenvironment.cpp: HwEnvironment::GetCameraInfo()
      → 读取 roll/pitch/yaw → 确定 facing/orientation
   → camxchicontext.cpp: InitializeStaticMetadataPool()
      → 构建 CameraCharacteristics 的数据

③ camxhaldevice.cpp: ConfigureStreams()
   → chxusecase.cpp: UsecaseSelector::Select()
      → 根据 Stream 配置选择 Usecase

路线 2:请求处理流程(capture → result)

目的: 追踪一次 capture_request 从入口到结果回传的完整路径

入口: camx/src/core/hal/camxhal3entry.cpp
       CamxHalDevice::ProcessCaptureRequest()

① camxhaldevice.cpp: ProcessCaptureRequest()
   → m_ChiAppCallbacks.chi_override_process_request()
      → CHI ExtensionModule 接管

② chxusecase.cpp: Usecase::SubmitChiRequest()
   → camxsession.cpp: Session::ProcessCaptureRequest()
      → 根据 PipelineIndex 分发

③ camxsession.cpp → camxpipeline.cpp: Pipeline::ProcessRequest()
   → camxnode.cpp: Node::CheckAndSetDependencies()
      → DRQ: m_readyNodes / m_deferredNodes 管理

④ camxnode.cpp: Node::ProcessRequest()
   → cslcommon.cpp: CSLSubmit()
      → ioctl → Kernel → ISP HW

⑤ camxpipeline.cpp: CSLMessageHandler()
   → HW 处理完成回调
   → camxsession.cpp: ProcessPipelineResult()
      → 结果回传

路线 3:查找一个特定行为

你想找什么 搜哪个文件 搜什么关键词
Pipeline 状态迁移 camxpipeline.cpp SetStatus / m_status
Dependency 检查 camxnode.cpp CheckAndSetDependencies
Node 处理完成 camxnode.cpp ProcessRequestResult
Frame 完成回调 camxpipeline.cpp CSLMessageHandler
Static Metadata 构建 camxchicontext.cpp InitializeStaticMetadataPool
Usecase 选择 chxusecase.cpp UsecaseSelector::Select
Facing 判定 camxhwenvironment.cpp GetCameraInfo
Buffer 分配 camxnode.cpp CreateBufferManager