资讯详情

Feature2图调度引擎:DAG遍历与refCount缓冲管理解析

📅 2026/10/28 4:43:01 | 华诺云谱 👁 阅读
Feature2图调度引擎:DAG遍历与refCount缓冲管理解析
简介本资源是一份面向高通平台Camera驱动开发工程师与Android底层视觉系统研发人员的技术解析文档聚焦Chi-CDK Feature2框架的架构设计与运行机制解决Camera HAL层多pipeline协同调度、缓冲区高效管理及实时图像处理链路构建等核心问题。文档以PDF格式呈现共1个文件大小约1000KB内容涵盖FeatureGraph与FeatureNode建模逻辑、CHITargetBufferManager缓冲区分配策略、Request/Execute/Result三阶段处理流程、RealTime/RTBay2YUV/RTMFSRYUV实时数据流组件作用、Camx XML pipeline定义方式、FeatureInternalLink跨节点连接机制及Link状态机管理等关键模块并辅以FeatureGraph简易模型图示与RTBayer2YUV执行时序详解。目前已有8061人学习下载适合具备Camera HAL基础、正深入高通QCS平台影像子系统开发的中高级工程师系统掌握Feature2框架设计思想与落地实践要点。1. Feature2 不是“另一个 pipeline 封装”它用图遍历替代了 XML 静态调度让多 stage 协同真正可编程你写完一个RTBayer2YUV的 FeatureNode编译进feature2.rt.so再改一行 XML 就能跑通——这感觉很顺错。那只是表象。真正让你的 MFSR 多帧融合不卡顿、ZSL 预览不丢帧、RealTime Display 能同时吐出 Raw Meta YUV 三路 buffer 的底层机制根本不是 Camx 的 DRQ 队列在干活而是 Feature2 在内存里动态构建了一张有向无环图DAG并用深度优先遍历DFS 状态机驱动的方式把“谁该等谁”“谁该先申请 buffer”“谁该后释放资源”全部从编译期绑定搬到了运行时按需决策。这不是语法糖这是调度模型的代际升级Camx 的 pipeline 是“铁轨”固定拓扑、静态依赖Feature2 的 FeatureGraph 是“交通管制中心”它不预设路径只根据每个 SinkPort 是否被上层消费即ExtSinkLink-linkState OutputPending实时反向推导出哪些上游 Node 必须启动、哪些 input buffer 必须提前就位、哪些 output port 实际上根本没人要直接设为Disabled。所以当你看到WalkAllExtSinkLinks()从 Sink 开始往回扫、ProcessInputDepsForUpstreamFeatureRequest()把 requestIndex 塞进上行 Node 的requestTable、CheckAllOutputLinksReadyToProcessRequest()判定 ready 状态——你看到的不是一堆冗余函数调用而是一个基于数据流驱动的轻量级 DAG 调度引擎在裸机上跑。搞不懂这个你永远在 patch XML、硬塞 buffer count、对着InputResourcePending日志抓狂搞懂了你才能把MFSR的maxProcessSequence设为 4 而不是拍脑袋写 8才能让Virtual InternalLink真正撑起 ZSL 的跨帧 buffer 复用而不是靠堆内存硬扛。2. FeatureGraph不是容器是带状态的 DAG 调度器其核心是 m_linkData DFS 遍历协议FeatureGraph 表面看是个管理FeatureNode的容器但它的本质是 Feature2 框架的运行时调度中枢。它不持有 pipeline 实例不执行图像处理但它精确控制着每一个FeatureNode何时OnProcessRequest()、何时HandleInputResourcePending()、何时ReleaseTargetBuffer()。这种控制力源于两个不可拆分的设计全局唯一的m_linkData数据结构和一套严格定义的 DFS 遍历协议。没有m_linkDataFeatureGraph就是空壳没有 DFS 协议m_linkData只是一堆死数据。它们共同构成 Feature2 区别于 Camx 静态 pipeline 的第一道分水岭。2.1 m_linkDataLink 状态的唯一真相源所有 ready 判定都查它m_linkData是ChiFeature2Graph类中一个std::vectorChiFeature2GraphLinkData类型的成员变量它不是缓存不是副本而是整个 FeatureGraph 中所有 Link 状态的唯一、权威、可变的真相源Single Source of Truth。每一个ExtSrcLink、ExtSinkLink、InternalLink的实例其linkState字段NotVisited/Disabled/OutputPending都直接映射到m_linkData中对应索引的元素。这意味着WalkAllExtSinkLinks()扫描到某个ExtSinkLink并将其linkState设为OutputPending修改的就是m_linkData[i].linkStateCheckAllOutputLinksReadyToProcessRequest(pSrcGraphNode)判断一个 Node 是否 ready就是遍历该 Node 所有outputLinkIndex去m_linkData里查每个对应 link 的linkState是否为OutputPendingProcessDependency()从上行 Node 获取 dependency buffer其触发条件InputResourcePending的判定同样依赖m_linkData中 upstream link 的状态是否已满足。提示m_linkData的生命周期与FeatureGraph绑定其 size 在Create()时由ChiFeature2Descriptor中解析出的 Link 总数决定。它不增长、不收缩所有 Link 操作都是原地状态变更。这是 Feature2 高性能的关键——无锁、无拷贝、无动态分配只有原子状态切换。// ChiFeature2Graph.cpp 关键片段m_linkData 的状态更新入口 void ChiFeature2Graph::SetLinkState( UINT32 linkIndex, ChiFeature2GraphLinkState newState) { if (linkIndex m_linkData.size()) { // 直接修改真相源无中间层 m_linkData[linkIndex].linkState newState; // 后续所有 ready 判定、dependency 查询都基于此值 } }这段代码背后是 Feature2 的设计哲学状态必须集中、必须唯一、必须廉价访问。m_linkData就是那个“廉价”的载体。它让FeatureGraph的调度逻辑可以完全脱离 Camx 的DRQ队列状态自成闭环。2.2 DFS 遍历协议从 Sink 入口反向推导执行路径而非正向驱动Camx 的DRQ是正向驱动Node 准备好 input就入ready队列dependency 满足就DispatchReadyNodes()。Feature2 的WalkAllExtSinkLinks()则是严格的、单向的、从出口Sink反向追溯入口Source的 DFS 协议。它不关心“谁准备好了”只关心“谁被需要了”。其执行流程是确定性的入口扫描WalkAllExtSinkLinks()遍历所有ExtSinkLink即上层 APP 显式消费的输出端口如 Preview、Metadata、Raw状态标记对每个被上层实际请求的 SinkPort通过portId匹配m_extSinkPortIdToChiStreamMap将其linkState设为OutputPending反向递归对每个OutputPending的ExtSinkLink调用WalkBackFromLink()找到其pSrcGraphNode上行 FeatureNode然后若该 Node 的所有 output link当前都是Disabled则将其所有 input link也设为Disabled剪枝避免无效计算若该 Node 的所有 output link中至少有一个是OutputPending且其余非Disabled则判定该 Node 为ready为其创建FeatureRequestObjectFRO并进入ProcessRequest()递归继续向上游pSrcGraphNode的 input link 执行WalkBackFromLink()直到抵达图的起点如 Sensor Input。这个协议的威力在于它天然支持稀疏执行。例如APP 只请求 Preview 流不请求 Metadata 流那么所有与 Metadata 相关的ExtSinkLink保持Disabled其上游所有InternalLink和ExtSrcLink也会被WalkBackFromLink()一路Disabled相关 Node 根本不会被创建 FRO更不会申请任何 buffer 或提交任何 request。这比 Camx 在DRQ中挂起一堆deferredNode 等待 metadata dependency 要高效得多。2.3 FeatureNode 的“ready”状态一个动态的、基于 output link 的布尔表达式在 Camx 中“Node ready” 意味着其 input buffer 已到位、fence 已 signalled。在 Feature2 中“FeatureNode ready” 是一个纯逻辑判断其公式为Node.ready ( (所有 output link 的 linkState ! Disabled) (至少一个 output link 的 linkState OutputPending) )注意这里不检查 input linkready状态只由该 Node 的 output 是否被下游消费OutputPending决定input 的 availability 是ProcessRequest()进入InputResourcePending状态后由ProcessDependency()去解决的问题。这是一个关键的设计解耦ready解决“要不要启动这个 Node”InputResourcePending解决“启动后input 从哪来”。这使得 Feature2 能优雅处理RealTime这类特殊 Node它的 input 是 sensor 的实时流理论上永不“pending”所以它的ready状态一旦满足比如RTDisplay的OutputPending被置位它就会立刻进入Executing无需等待任何 input dependency。而Bayer2YUV这类 Node其ready状态满足后会先进入InputResourcePending等待RealTime的 output buffer 到位。// ChiFeature2Graph.cpp: CheckAllOutputLinksReadyToProcessRequest 的核心逻辑 BOOL ChiFeature2Graph::CheckAllOutputLinksReadyToProcessRequest( ChiFeature2GraphNode* pGraphNode) { BOOL isReady FALSE; UINT32 numOutputLinks pGraphNode-outputLinkIndexMap.size(); if (0 numOutputLinks) { // 无 output link 的 Node如纯 sink视为 ready isReady TRUE; } else { UINT32 outputPendingCount 0; for (UINT32 i 0; i numOutputLinks; i) { UINT32 linkIndex pGraphNode-outputLinkIndexMap[i]; if (linkIndex m_linkData.size()) { ChiFeature2GraphLinkState state m_linkData[linkIndex].linkState; if (ChiFeature2GraphLinkState::OutputPending state) { outputPendingCount; } else if (ChiFeature2GraphLinkState::Disabled ! state) { // 存在非 Disabled 也非 OutputPending 的状态如 NotVisited说明图未完全 walk不 ready return FALSE; } } } // 至少一个 OutputPending且无 NotVisited则 ready isReady (outputPendingCount 0); } return isReady; }这段代码清晰地展示了ready的判定逻辑它只读m_linkData只看outputLinkIndexMap不碰任何 buffer manager 或 fence。isReady是一个纯粹的、瞬时的、基于当前图状态的布尔值是 Feature2 调度引擎的“心跳信号”。2.4 Link 的三种类型ExtSrc/ExtSink/Internal定义了数据流的边界与所有权FeatureGraph中的 Link 不是同质的ExtSrcLink、ExtSinkLink、InternalLink这三种类型划定了数据流的逻辑边界和buffer 所有权转移点是理解 Feature2 buffer 生命周期的钥匙。ExtSinkLink外部接收链路代表 FeatureGraph 的输出边界。它的pSinkGraphNode是上层 APP或 Chi-CDK wrapperpSrcGraphNode是图内最后一个 FeatureNode如Bayer2YUV。当 APP 调用configure_streams()请求 Preview 流时ChiFeature2Graph::WalkAllExtSinkLinks()就是通过匹配ExtSinkLink的portId来定位并激活这条链路。ExtSinkLink的OutputPending状态是整个 DFS 遍历的唯一触发源。ExtSrcLink外部提供链路代表 FeatureGraph 的输入边界。它的pSrcGraphNode是上层 APP或 Chi-CDK wrapperpSinkGraphNode是图内第一个 FeatureNode如RealTime。它通常用于将 APP 预先分配的 buffer如 sensor raw buffer注入图中。ExtSrcLink的linkState一般不参与ready判定它更多是SetupTargetBuffer()时的配置依据。InternalLink内部连接链路代表 FeatureGraph内部的数据流动。它的pSrcGraphNode和pSinkGraphNode都是图内的FeatureNode如RealTime-Bayer2YUV。InternalLink的linkState是ready判定的核心也是ProcessDependency()查找上游 output buffer 的路径。InternalLink的存在使得FeatureGraph可以将多个物理 pipelineStage无缝编织成一个逻辑处理单元Bayer2YUV的 input 不再是 Camx 的某个固定 port而是RealTime的某个OutputPendingport。这三种 Link 共同构成了 Feature2 的“数据契约”ExtSinkLink定义了“我要给谁”ExtSrcLink定义了“我从谁拿”InternalLink定义了“我在内部怎么传”。m_linkData就是这份契约的数字化账本。3. CHITargetBufferManager不是 malloc/free 封装是基于 refCount 的三级队列 buffer 生命周期控制器CHITargetBufferManager常被误认为是ImageBufferManager的简单包装。大错特错。它是一个精密的、基于引用计数refCount和三级队列ImportList/ProducerList/ConsumerList的 buffer 生命周期控制器。它的核心目标不是“分配 buffer”而是“精确追踪每一个 buffer 在 FeatureGraph 数据流中的位置、状态和所有权”确保RealTime输出的 YUV buffer 能被Bayer2YUV正确识别为 input且在Bayer2YUV处理完毕后能被RTDisplay消费最后在无人使用时被安全释放。refCount是它的灵魂三级队列是它的骨架SetupTargetBuffer()和UpdateTarget()是它的脉搏。3.1 三级队列Import/Producer/Consumer定义 buffer 的三个生命阶段CHITargetBufferManager内部维护三个DoublyLinkedNode链表m_importList、m_producerList、m_consumerList。每个DoublyLinkedNode封装一个CHIBufferInfo含hBufferhandle、pBufferpointer、size等并有一个refCount字段。这三个队列并非并列而是代表 buffer 的线性演进阶段ImportList导入队列存放上层 APP 主动传入的 external buffer。当 APP 调用ImportExternalTargetBuffer()时CHITargetBufferManager会为该 buffer 创建一个DoublyLinkedNode将hBuffer和pBuffer保存在pData中并将该 node 挂入m_importList。此时refCount为 0。ImportList是 buffer 的“入境口岸”只进不出除非被SetupTargetBuffer()“征用”。ProducerList生产者队列存放已被 FeatureNode 申请并准备作为 output 使用的 buffer。当FeatureNode如RealTime执行SetupTargetBuffer()时若该 buffer 是 external则从m_importList中摘下对应 node将其refCount设为 1并挂入m_producerList若该 buffer 是 internal由ChiBufferManager在 Camx 中申请则新建 node申请 bufferrefCount1挂入m_producerList。ProducerList是 buffer 的“上岗预备队”refCount1表示它已被一个 FeatureNode 认领为 output。ConsumerList消费者队列存放已被下游 FeatureNode 作为 input 消费的 buffer。当FeatureNode如Bayer2YUV的ProcessResult()回调收到OnBufferResult()时它会调用CHITargetBufferManager::UpdateTarget()。该函数会从m_producerList中摘下该 buffer 对应的 node将其refCount--变为 0并挂入m_consumerList。同时如果该 buffer 将被用作下一个FeatureNode如RTDisplay的 input则UpdateTarget()会再次refCount变为 1并返回TargetBufferInfo给下游。ConsumerList是 buffer 的“流转中转站”refCount的增减在此发生。提示refCount的语义非常明确refCount 0表示该 buffer 当前没有被任何 FeatureNode 作为 input 或 output 持有refCount 1表示它正在 FeatureGraph 的数据流中“服役”。m_producerList和m_consumerList的 node 可以互相迁移但m_importList的 node 只能单向流入m_producerList。3.2 SetupTargetBuffer一次调用完成 buffer 绑定、refCount 初始化、队列迁移三件事SetupTargetBuffer()是CHITargetBufferManager的核心入口函数它不是一个简单的“设置 buffer info”而是一次完整的 buffer 生命周期初始化仪式。其逻辑高度统一无论处理 external 还是 internal buffer// CHITargetBufferManager.cpp 伪代码 CDKResult CHITargetBufferManager::SetupTargetBuffer( const CHITargetBufferInfo* pTargetBufferInfo, CHITargetBufferHandle* pTargetBufferHandle) { CDKResult result CDKResultSuccess; DoublyLinkedNode* pNode nullptr; if (pTargetBufferInfo-bufferType CHI_BUFFER_TYPE_EXTERNAL) { // Step 1: 从 ImportList 找到对应的 node pNode FindNodeInImportList(pTargetBufferInfo-hBuffer); if (nullptr pNode) { // 失败APP 传入的 buffer 未被 ImportExternalTargetBuffer() return CDKResultFailure; } // Step 2: 从 ImportList 摘下 RemoveNodeFromImportList(pNode); } else { // Step 1: 为 internal buffer 创建新 node pNode CreateNewNode(); // Step 2: 调用 ChiBufferManager 在 Camx 中申请 buffer result m_pChiBufferManager-AllocateBuffer(..., pNode-pBuffer); if (CDKResultSuccess ! result) { FreeNode(pNode); return result; } } // Step 3: 统一初始化 refCount 1 pNode-refCount 1; // Step 4: 挂入 ProducerList表示该 buffer 已被认领为 output AddNodeToProducerList(pNode); // Step 5: 填充返回的 handle *pTargetBufferHandle pNode-handle; return CDKResultSuccess; }这段伪代码揭示了SetupTargetBuffer()的原子性它要么完整成功buffer 进入ProducerListrefCount1要么完全失败node 被清理。这保证了FeatureNode在Sequence(0)阶段拿到的TargetBufferInfo其背后的 buffer 一定是处于一个确定的、可预测的状态ProducerList中refCount1绝不会出现“buffer 已分配但未挂队列”或“refCount 错乱”的情况。这是 Feature2 buffer 管理稳定性的基石。3.3 UpdateTargetrefCount 的舞蹈决定 buffer 是流转还是终结UpdateTarget()是CHITargetBufferManager的另一核心它负责处理 buffer 在 FeatureGraph 中的流转与终结。当FeatureNode的OnBufferResult()被回调意味着它已经完成了对该 buffer 的处理并准备将其交给下游或释放。UpdateTarget()的逻辑就是围绕refCount的加减法展开// CHITargetBufferManager.cpp 伪代码 CDKResult CHITargetBufferManager::UpdateTarget( CHITargetBufferHandle targetBufferHandle, CHITargetBufferUsage usage, CHITargetBufferHandle* pNextTargetBufferHandle) { CDKResult result CDKResultSuccess; DoublyLinkedNode* pNode FindNodeByHandle(targetBufferHandle); if (nullptr pNode) { return CDKResultFailure; } // Step 1: 从 ProducerList 摘下因为它是上游 Node 的 output RemoveNodeFromProducerList(pNode); // Step 2: refCount--因为它不再是上游的 output pNode-refCount--; // Step 3: 挂入 ConsumerList AddNodeToConsumerList(pNode); // Step 4: 如果下游需要此 buffer 作为 input则 refCount if (CHI_TARGET_BUFFER_USAGE_CONSUMER usage) { pNode-refCount; *pNextTargetBufferHandle pNode-handle; } else { *pNextTargetBufferHandle CHI_INVALID_HANDLE; } // Step 5: 检查是否可以释放 if (0 pNode-refCount) { // Buffer 已无任何 FeatureNode 持有可以释放 if (CHI_BUFFER_TYPE_INTERNAL pNode-bufferType) { m_pChiBufferManager-FreeBuffer(pNode-pBuffer); } FreeNode(pNode); // 释放 node 本身 } return result; }UpdateTarget()的精妙在于Step 4和Step 5的配合refCount是为了将 buffer “借给”下游 NoderefCount--是为了确认上游 Node 已完成使用。只有当refCount归零才触发真正的释放。这完美支持了RTBayer2YUV这样的场景RealTime的 output bufferYUV被Bayer2YUV消费refCount从 1 变 2Bayer2YUV处理完后又将它作为自己的 output 交给RTDisplayrefCount从 2 变 1最后RTDisplay消费完毕refCount归零buffer 才被释放。refCount就像一个精准的计数器记录着 buffer 在 FeatureGraph 这条流水线上被多少个工位FeatureNode同时持有。3.4 避坑CHITargetBufferManager 的五大血泪经验Feature2 的 buffer 管理看似自动实则暗藏玄机。以下是某开发者在调试RTMFSRYUV时踩过的五个典型坑每一条都对应一个真实的崩溃或内存泄漏现象RTMFSRYUV的MFSR多帧融合过程中OnBufferResult()回调频繁但Preview流画面卡死log 中大量InputResourcePending。原因MFSR的FeatureNode在ProcessRequest()的Sequence(-1)阶段错误地将refCount为 0 的 buffer已从ConsumerList释放再次SetupTargetBuffer()。这导致ProducerList中出现了一个指向已释放内存的 dangling node。后续UpdateTarget()访问该 node 时崩溃。解决在SetupTargetBuffer()前务必检查pTargetBufferInfo-hBuffer是否有效且该 buffer 的refCount必须为 0即它必须来自ImportList或刚被释放的 internal buffer。MFSR的多帧 buffer 复用逻辑必须严格管理每个 frame 的 buffer handle 生命周期。现象RealTimeDisplay_OutRaw_OutMeta_OutRDI_InMeta_InMeta_OutYUV_OutyuvmetadataPreview的复合 FeatureGraph 中Metadata流正常但Raw流始终黑屏OnBufferResult()从未被调用。原因ExtSinkLink的portId配置错误。RealTime的Rawoutput port ID 被错误地映射到了Metadata的ExtSinkLink上导致WalkAllExtSinkLinks()只将Metadata的 link 设为OutputPendingRaw的 link 保持Disabled整个Raw分支被 DFS 剪枝。解决仔细核对ChiFeature2Descriptor中ExtSinkLinkDesc的portId必须与CHI_STREAM_PORT_ID枚举值完全一致。使用ChiFeature2Graph::DumpGraph()打印出所有ExtSinkLink的portId和linkState是排查此类问题的最快方法。现象RTBayer2YUV的ProcessResult()中ProcessBufferCallback(yuv)成功但ProcessReleaseInputDependencyMessage()失败log 显示Invalid buffer handle。原因Bayer2YUV的OnProcessRequest()在Sequence(0)阶段调用SetupTargetBuffer()为yuvoutput 申请 buffer 后又错误地调用了ReleaseTargetBuffer()。这导致该 buffer 的refCount被减为 0 并被释放但Bayer2YUV的ProcessResult()仍试图用这个已释放的 handle 去UpdateTarget()。解决ReleaseTargetBuffer()只应在FeatureNode明确放弃对某个 buffer 的 ownership 时调用如 error path绝不能在Sequence(0)的正常流程中调用。Sequence(0)的职责是SubmitRequest()buffer 的 release 由UpdateTarget()在ProcessResult()后自动完成。现象configure_streams()后FeatureGraph创建成功但ExecuteProcessRequest()第一次调用就 crash堆栈指向CHITargetBufferManager::Create()中的m_pChiBufferManager-GetBufferManager()。原因CHITargetBufferManager::Create()被调用时m_pChiBufferManager指针尚未被正确初始化。ChiFeature2Graph::CreateFeatureGraph()的执行顺序中CreateTargetBufferManagers()发生在CreatePipeline()之前而CreatePipeline()才会调用ChiSession::GetBufferManager()来初始化m_pChiBufferManager。解决将CreateTargetBufferManagers()的调用时机从CreateFeatureGraph()的早期推迟到CreatePipeline()之后、SelectFeatureGraphforRequest()之前。确保m_pChiBufferManager在CHITargetBufferManager需要它时已经是有效的。现象RTMFSRYUV的maxProcessSequence设为 4但实际只跑了 2 帧就停止URO的curProcessSequenceId停在 1。原因UROUsecaseRequestObject和FROFeatureRequestObject的ProcessSequenceId同步失败。RTMFSRYUV的StageDescriptor中定义了 4 个StageNameBlendLoop[0]到BlendLoop[3]但FRO的m_numRequests被错误地初始化为 1而非 4导致FRO只为BlendLoop[0]创建了BatchOutputRequestInfo后续BlendLoop[1]的 request 无法被调度。解决FRO的m_numRequests必须严格等于URO的maxProcessSequence。在ChiFeature2RequestObject::Create()中m_numRequests应直接取自URO-maxProcessSequence而不是硬编码或从requestTable.size()推导requestTable只反映当前帧的 active request 数。4. ProcessRequest 状态机从 Initialized 到 OutputResourcePending一个 request 的完整生命周期FeatureRequestObjectFRO的状态机是 Feature2 执行模型的时间轴。它不像传统线程状态机那样关注“运行/阻塞/就绪”而是精确刻画一个 request 在 FeatureGraph 数据流中所处的空间位置和资源状态。Initialized、ReadyToExecute、InputResourcePending、Executing、OutputResourcePending这五个状态不是随意命名而是对request与buffer、dependency、Camx pipeline之间关系的精准建模。理解这个状态机是读懂ProcessRequest()日志、定位ProcessDependency()卡死、优化maxProcessSequence的前提。4.1 状态转换图一条由事件驱动的单向路径FRO 的状态转换是一条强约束的单向路径由特定的事件Event驱动而非定时器或轮询。其核心逻辑是request一旦开始就必须沿着这条路径走到终点OutputResourcePending中间任何一步失败都会导致整个request被 abort状态机重置。路径如下Initialized ↓ (OnPrepareRequest() success) ReadyToExecute ↓ (No dependency OR dependency satisfied) Executing ↓ (SubmitRequestToSession() success) OutputResourcePending ↓ (ReleaseTargetBuffer() success) [End]而InputResourcePending是一个可选的、临时的、等待型分支它只在ReadyToExecute状态下检测到GetDependency()返回FALSE即上游 output buffer 尚未就位时才会进入。一旦上游ProcessResult()触发ProcessDependency()且 dependency 满足状态立即跳回Executing。这个设计的精妙之处在于它将复杂的异步依赖ProcessDependency()封装在一个状态内对外暴露的是一个简洁的、可预测的执行流。ChiFeature2Base::ProcessRequest()的主循环本质上就是在GetCurRequestState()和CanRequestContinue()的引导下沿着这条路径“推”request 前进。4.2 Sequence(-1) 与 Sequence(0)两阶段提交解耦 dependency 设置与 buffer 提交ProcessRequest()的两阶段Sequence(-1)和Sequence(0)设计是 Feature2 对 CamxDRQ模型的继承与超越。Sequence(-1)是“规划阶段”Sequence(0)是“执行阶段”。Sequence(-1)PopulateDependency此阶段的目标是收集所有必要的 input dependency 信息。它不申请任何 buffer不提交任何 request只做两件事调用GetDependency()遍历所有InternalLink查询上游FeatureNode的output port是否已OutputPending。如果已OutputPending则记录其TargetBufferInfo如果未OutputPending则返回FALSE驱动状态机进入InputResourcePending。调用OnPrepareRequest()为即将进行的Sequence(0)做准备例如填充SessionData中的pipelineId、stageId等元数据。Sequence(-1)的成功意味着request的所有 input 来源都已明确FeatureNode已准备好进入执行。Sequence(0)SubmitRequestToSession此阶段的目标是将 request 物理提交给 Camx。它执行Sequence(-1)规划好的所有操作调用SetBufferInfo()为 Camx pipeline 的每个input port和output port设置TargetBufferInfo。这会触发CHITargetBufferManager::SetupTargetBuffer()将 buffer 挂入ProducerList。调用SubmitRequestToSession()将填充好的CamxRequest提交给 Camx 的DRQ。至此FeatureNode的工作移交给了 Camx。Sequence(-1)和Sequence(0)的分离使得 Feature2 可以在Sequence(-1)中进行复杂的、可能失败的 dependency 查询如跨FeatureGraph的 virtual link而不会影响Sequence(0)的原子性提交。这是一种典型的“两阶段提交2PC”思想在图像处理框架中的应用。4.3 InputResourcePending不是 bug是 Feature2 的“等待艺术”InputResourcePending状态常被误解为一种错误或性能瓶颈。恰恰相反它是 Feature2最精巧的设计之一是其实现RealTime、ZSL、MFSR等高级用例的基础设施。RealTime的基石RealTimeNode 的InputResourcePending状态几乎总是FALSE因为它的 input 是 sensor 的实时流Camx 会保证其fence在每一帧到来时 signalled。因此RealTime的request会瞬间从ReadyToExecute跳到Executing实现真正的零延迟。ZSL的关键ZSLZero Shutter Lag要求 preview 和 capture 共享同一组 buffer。ZSL的FeatureNode会设置virtual InternalLink其InputResourcePending状态依赖于RealTime的output port。当用户按下快门ZSL的request进入InputResourcePending它并不等待一个新帧而是等待RealTime刚刚输出的、尚未被 preview 消费的那一帧 buffer。InputResourcePending在这里是ZSL实现“所见即所得”捕获的精确同步点。MFSR的保障MFSR的BlendLoop阶段其InputResourcePending状态依赖于前一帧BlendLoop的output port。InputResourcePending确保了 本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑