Files
oak-editor/docs/zh/investigation-edge-display-and-playback.md
Mike-Solar a59c33715f fix: node graph edge display, teardown crashes, and event/audio lifetime bugs
- capi: oakengine_node_output_connection_at/_at_ex returned the source
  node as the connection destination; the actual destination is
  conn.second.node(). Out-edge enumeration was useless, so the node
  view could only draw in-edges and randomly lost whichever edges
  needed the out-edge path (random per context build order).
  Regression test in oakengine_node_test
- project teardown: Project::clear() pre-notifies node removal while
  nodes are fully constructed (observers used to crash on
  half-destroyed nodes); childEvent suppresses the removal dance while
  clearing; Node::disconnect_all/disconnect_edge get a silent mode for
  teardown so no invalidation/events touch dying members
  (is_being_cleared); ClipBlock marker disconnect guarded against
  dead viewer/markers; ProjectCopier and PreviewAutoCacher drop
  project references on Project::destroyed instead of disconnecting
  dead objects at shutdown
- preview: add oakengine_preview_request_get_audio_sample_count; the
  viewer queried sample count by passing nullptr to get_audio_samples
  which rejects it, so all playback audio was silently dropped
- app: fix unterminated input-id memcpy in ResolveGroupInput
  (nodeparamviewitem, widgetbridge) that corrupted every parameter id
- app: unsubscribe raw C-API event subscriptions in destructors of
  NodeParamViewKeyframeControl, NodeParamViewConnectedLabel and
  ExportDialog; playhead events used to fire into dead widgets
  (crash when dragging the playhead)
- tests: preview request roundtrip (video frame + audio range) and
  free-while-active teardown coverage; env-gated OAK_DEBUG_EDGES /
  OAK_DEBUG_INVALID_INPUT diagnostics
- docs: investigation notes in docs/zh/
2026-08-02 14:29:49 +08:00

97 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 节点图边显示异常 & 播放问题排查进展(2026-08-02
本文记录当前排查状态,供手工继续排查。随调查更新。
## 当前未解决的两个现象
### A. 节点图边显示随机缺失
- 每次显示都不一样:有时全部显示,有时缺几条,缺的边每次不同,无规律。
- 手工重连能连上;切换选中素材(时间线点选)再切回来,又随机缺。
- 已确认**不是只有 footage 相关边**受影响,缺的边类型随机。
### B. 播放冻结(部分修复后仍有残留报告)
- 历史症状:播放头不动、无声音、画面不动。
- 已修复两个确定的根因(见"已修复"清单 8、9),复测中。
## 已验证的事实(不要再重复验证)
1. **引擎图是稳定的**。用 C ABI 直接加载 `~/Movies/bbb.ove`12 条边在
activate project + 多轮 frame request 后全部存活(/tmp/edge2_repro 验证)。
边不显示 ≠ 边被引擎删除。
2. **项目文件内容正常**。bbb.ove 的 12 条连接(见下"图结构")序列化无误,
clip 节点只声明了 9 个输入(无 pos_in/tex_in/volume_in——那些警告见第 6 条)。
3. **NodeViewContext 的边创建没有走跳过分支**`OAK_DEBUG_EDGES=1` 运行时,
"no item for" 跳过日志一条都没有 → 每条边都调用了 `add_edge_internal`
边对象是创建了的。问题在创建之后:被事件删掉、或绘制/几何异常。
4. **删边路径只有两条**`child_input_disconnected`NODE_INPUT_DISCONNECTED
事件驱动,`nodeviewcontext.cpp:251`)和 `remove_child`(节点移除时递归删边)。
5. **XML/context 解析已排除**。C ABI 实测:引擎解析出的 context 成员与
bbb.ove 完全一致(sequence=[sequence,track,track]
clip1=[transform,clip1,footage]clip2=[footage,volume,clip2]),
12 条边全部存在。图在引擎里 100% 正确,问题只在 app 的
NodeViewContext 的 item_map_ 里缺节点 item。
6. **根因已修复(边显示随机缺失)**`oakengine_node_output_connection_at_ex`
`oakengine_node_output_connection_at``output_connections()`
`conn.first`(其实是 source 自身)当成目标节点返回,正确目标是
`conn.second.node()`。后果:NodeViewContext 的 out-edge 枚举拿到的
"对端"永远是节点自己,`item_map_` 查到自己 → from==to 静默丢弃 →
out-edge 一条都画不出,只能靠 in-edge 补;哪些边缺取决于节点进入
context 的顺序(成员顺序+时序)→ 表现为随机缺边。已加回归测试
oakengine_node_test.cpp test_edgesat_ex 的目标必须是 LUT 而非 solid)。
同路径受益者:timelinewidget multicam、nodeparamview 的删边逻辑。
7. 日志里的 `Failed to retrieve array size of parameter "pos_in"... in Olive.clip`
`"muted_in" ... in Olive.sequence` 是**显示层的错查**,调用栈:
`NodeParamView::update_element_y``app/widget/nodeparamview/nodeparamview.cpp:1174`
把每个 item 的输入都拿去对 `contexts_.first()` 做 group resolve——硬编码
第一个 context,解析错了节点。此 bug 未修,与边显示的关系未确定。
7. bbb.ove 图结构(12 条边):
- folder.child_in → footage(33755451136), sequence(33848831232)
- sequence.tex_in ← track(33796807680)sequence.samples_in ← track(33796810368)
- sequence.track_in_0/1 ← 两个 track
- track_v.block_in ← clip(33755511232)track_a.block_in ← clip(33755497792)
- clip1.buffer_in ← transform(33764063616)transform.tex_in ← footage
- clip2.buffer_in ← volume(33795740032)volume.samples_in ← footage
## 已修复的问题(本批,工作区内未全部提交)
| # | 问题 | 位置 | 状态 |
|---|------|------|------|
| 1 | get_distance_between_nodes 无递归出口栈溢出 | app/widget/nodeparamview/nodeparamview.cpp | 已提交 a4dfc62f0 |
| 2 | viewerdisplay texture_ 悬垂指针(GL 崩溃) | viewerdisplay.{h,cpp} assign_texture | 已提交 a4dfc62f0 |
| 3 | resignal_requests 遍历中改容器 | engine/render/playbackcache.h | 已提交 a4dfc62f0 |
| 4 | preview request 释放后 ticket 回调 UAF | engine/src/capi/preview.cpp | 已提交 a4dfc62f0 |
| 5 | 播放队列帧时间戳全为 0 → 画面不动 | preview.h/.cpp + viewer.cpp | 已提交 a4dfc62f0 |
| 6 | 浮动"查看器"绑定到序列节点 | mainwindow.cpp open_node_in_viewer | 已提交 a4dfc62f0 |
| 7 | Track 析构 UAF + undo remove_track 丢片段 | engine/node/output/track/track.{h,cpp} | 已提交 30853cbcf |
| 8 | 音频 sample_count API 缺失(无声音根因之一) | preview.h/.cpp 新增 get_audio_sample_count | 未提交 |
| 9 | teardown 系列:~Node 中断边事件打到半死对象 | node.cpp silent disconnectproject.cpp is_being_cleared_clip.cpp marker disconnect 空指针;projectcopier/previewautocacher 项目死后野指针 | 未提交 |
| 10 | 输入 id memcpy 未 NUL 终止(参数名腐坏) | nodeparamviewitem.cpp:40、nodeparamviewwidgetbridge.cpp:145 | 未提交 |
| 11 | 裸事件订阅析构不退订(拖播放头崩溃) | nodeparamviewkeyframecontrol、nodeparamviewconnectedlabel、export dialog 各加析构退订 | 未提交 |
测试:`cd cmake-build-debug && ctest -j4` 目前 122/122 通过
(含新增 preview request 端到端回归:单帧+音频请求、teardown 不崩)。
## 仍在工作区里的调试代码(提交前需清理)
- `OAK_DEBUG_EDGES=1`nodeundo.cppAdd/RemoveCommand redo/undo)、
node.cpp disconnect_edge、nodeviewcontext.cppedge added/removed/跳过)。
- `OAK_DEBUG_INVALID_INPUT=1`node.cpp report_invalid_input 打调用栈。
- /tmp 下的复现程序(编译产物在 cmake-build-debug/app/ 下):
- `preview_repro`:C ABI 播放请求全流程(单帧+音频+teardown)
- `edge_repro`:工厂建节点连边 + autocache churn
- `edge2_repro`:加载 bbb.ove 验证 12 条边存活
## 下一步排查方向(按优先级)
1. 用最新构建(含 edge added/removed 日志)跑 `OAK_DEBUG_EDGES=1`
对照 12 条边在视图里的 add/remove 次数,找幽灵 remove。
2. 若证实是 load 线程事件与 GUI 建图交错:检查 EngineEventBridge 的
事件入队时序 vs NodeView::set_contexts 的建图时机(重复边/乱序删边)。
3.`update_element_y``contexts_.first()` 硬编码(应按 item 所属 context resolve)。
4. clip 缺少 transform 输入(pos_in 等)导致的参数面板/关键帧视图查询失败
(bbb.ove 里没有这些输入声明,但 app 代码在查)——需确认 Olive 原版
ClipBlock 是否有这些输入,是被 R8 弄丢的还是本来就不该查。
5. 音频残留:`Tried to allocate sample buffer with invalid audio parameters`
仍在日志出现(audio processor 输出参数 channels=0 → fix_channel_layout
修正为 2,但上游某处仍用 0 声道创建 buffer)。