Files
oak-editor/docs/zh/plans/riir/M3-oaknode.md
T
Mike-Solar d77348ad9f refactor(node): de-Qt oaknode and wrap it in a pure C ABI
- copy engine/node (188 files) to src/node/src, de-Qt in waves:
  core infra (Node/Param/Value/Variant/mathtypes), project/serializer,
  block/output, color, effect leaves, generator, gizmo/plugins
- strip QObject/signals/slots: notifications move to the facade's
  oakengine_event channel, ownership becomes explicit (unique_ptr,
  add_keyframe/add_gizmo), sender() replaced by current_gizmo
- QVariant replaced by olive::Variant, Qt math types by POD mathtypes,
  QXmlStreamReader/Writer by oakcommon's expat-based classes
- sink VideoParams/SubtitleParams/LoopMode/ColorTransform to oakcommon
  (M3.5); polygon/text rasterization behind backend hooks
- pure C ABI in include/node + src/node/c_api (oaknode_ prefix,
  OAKNODE_E_* codes, undoable variants take OakUndoCommand out-params)
- fix Project::clear() root_ reset + disconnect assert, Sequence
  TrackList leak
- 96 gtest cases green in standalone build (build-oaknode)
- docs: signal/slot handling strategy + M3 implementation status
2026-08-05 23:55:54 +08:00

175 lines
9.9 KiB
Markdown
Raw 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.
# M3 · oaknode 拆分手册
> 内容:`engine/node/`Node/NodeGroup/NodeKeyframe/NodeInput/
> NodeValue/NodeFactory/NodeTraverser/NodeGroup/Project/Folder/
> Sequence/Track/TrackList/Block/ClipBlock/Footage/Serializer/
> ColorManager/各类节点实现)。
> 被依赖:timeline 32、task 38、render 38、plugin 6、codec 3、capi 110。
> 拆分顺序第 3 位(最大的模块;**链接级独立推迟到 M7 复核**,裁决
> 见 02 §4)。
## 1. 目标形态
```
oaknode/
include/oaknode/ # node.h, project.h, track.h, block.h, footage.h,
# keyframe.h, factory.h, traverser.h, serializer.h,
# colormanager.h, types.h, export.h
src/ # 现有 node/ 全量迁入(含全部具体节点实现子目录)
tests/ # oaknode_gtest
```
## 2. 冻结 C API(跨界类清单)
跨界类 = 被其他模块引用的类(来源:02 依赖矩阵的热门头统计)。
每类按 01 §1 生成 `init/free/func`,此处冻结**函数族与特殊约定**
(逐函数签名照 oakengine 现有 facade 同族对齐——oakengine/node.h、
project.h、timeline.h 的对应函数就是模板,参数命名前缀换
`oaknode_`):
| 类 | 主要消费方 | 函数族(每族含 init/free |
|---|---|---|
| Node | 全部 | label/color/enabled 存取、input 列举与存取(`oak_node_value`)、connect/disconnect、output_connections 枚举、links、context positions、id() 字符串 |
| NodeGroup | nodeparamview 链 | passthrough add/remove/列举、resolve_input |
| NodeKeyframe | curve/keyframe | time/value/type/bezier 存取(live + undoable 变体) |
| NodeFactory | factorymenu、构造点 | id_count/id_at/name_from_id/create_from_id/node_at |
| NodeTraverser | nodevaluetree 等 | traverse db 创建/行枚举/释放 |
| Project | task/render | root、name/filename/cache_path、modified、is_modified、add/remove_node |
| Folder | projectexplorer | child 增删/列举/move_children |
| Sequence | timeline/render | track_list、workarea、markers、video/audio params、playhead |
| Track / TrackList | timeline | height/mute/lock/index/type、block 增删、split/ripple 原语 |
| Block / ClipBlock / GapBlock / TransitionBlock | timeline | in/out/length/media_in、speed/reverse/loop、links |
| Footage | task | filename、streams、proxy、duration |
| ProjectSerializer | task / oakstorage | **剪贴板** copy/paste + 节点图 XML 的内存形态(SaveData/LoadData,照 oakengine/serializer.h 模板);**落盘 save/load 迁 oakstorageM10),不在本模块** |
| ColorManager | render | config、default config、display transform |
**特殊约定**
1. undoable 变体与 live 变体成对(`_live` 后缀或
`, OakUndoCommand *command` 尾参——有类型句柄,禁 void*),
与 oakengine 现状一致。
2. 无事件订阅接口(2026-08 修订,04 §3):oaknode 的所有修改都经
命令函数完成,调用方知道影响;Node 族的变更通知(label/input/
keyframe/context 等)由 facade 在执行命令后经既有 `oakengine_event`
通道发出(事件 id 沿用 `oakengine/events.h` 70-95 段,值不变),
oaknode 自身不持有任何上层回调。
3. `Node *``Project *` 等句柄即 `OakNodeNode *`/`OakNodeProject *`
不透明。
4. 虚函数不出模块(01 §5);具体节点类型经
`oaknode_factory_create_from_id` 构造,消费侧不碰子类。
## 3. 切割点(node 的对下引用)
| 现状(次数) | 处理 |
|---|---|
| node → render/ 47colorprocessor 8、videoparams 5、footagejob 4、rendermanager 3、pluginjob 3 等) | videoparams/colortransform 随 M3.5 下沉 oakcommon;其余 **M7 时**改经 oakrender C ABI02 §4 裁决 A:M3 暂不断链,禁止新增) |
| node → codec/ 8decoder 4、frame 2、encoder 1、proxymanager 1 | M5 时改经 oakcodec C ABIM5 手册已含 frame/decoder 家族) |
| node → timeline/ 5timelinecommon 2、marker 1、workarea 1、timelineundogeneral 1 | timelinecommon 的枚举/常量下沉 oakcommon/types.hmarker/workarea 引用(均在 node/project/ 序列化路径)M4 时改经 oaktimeline C ABI |
| node → audio/ 4 | M6 时改经 oakaudio C ABI |
| node → undo/ 4undocommand.h | M2 后改 include oakundo 公共头 + 适配类 |
M3 阶段判据(放宽版):oaknode 目录就位、C API 实现、oaknode_gtest
全绿、对上述各向**无新增引用**(grep 快照对比)。M7 后复核
"oaknode 只经 C ABI 调下"。
## 4. 测试(映射 03 §2/§3
- 重点:Node 增删连边、Project/Folder 层级、Track 属性、
keyframe live/undoable 对称、serializer 剪贴板往返、
factory 枚举与创建;每个变更命令执行后直接读状态断言生效
(无事件可断言——通知在 facade 层测)。
- 枚举序数:NodeValue::Type ⇄ oak_node_value_type 映射表(已在
nodevaluehandle.h 钉过一次,oaknode 测试再钉一次,防两侧漂移)。
- `oaknode_debug_alive_count()` 泄漏断言。
## 实施现状(2026-08-05
M3 已落地并可独立构建、测试全绿(96 个用例全部通过,无 skip)。
以下为与上文计划的实际差异。
### 最终目录结构
- `src/node/src/` — 去 Qt 化 C++ 实现(`olive::` 命名空间),target
`oaknode`SHARED);平铺结构,`src/node/src` 为 include 根,
模块内 include 无前缀(`"value.h"``"block/block.h"`)。
- `src/node/c_api/` — 纯 C ABI 包装(node/group/keyframe/factory/
traverser/project/folder/footage/serializer/block/track/sequence/
colormanager 共 13 个 .cpp + 内部头 `valueconvert.h`/`alivecount.h`),
通过 `target_sources` 合并进 `oaknode`,不单独成库。
- `src/node/tests/` — gtest,单一 target `oaknode-gtest`13 个
_test.cpp + 共享夹具 `testnode.h`),`gtest_discover_tests`
`DISCOVERY_MODE PRE_TEST`)。
- `include/node/`(仓库根)— 公共 C 头:`error.h` + node/group/
keyframe/factory/traverser/project/folder/footage/serializer/block/
track/sequence/colormanager.h。
- `src/node/standalone/CMakeLists.txt` — 独立构建 driver(见下)。
- `src/node/DEQT.md` — 去Qt化替换约定与逐波次裁决记录。
- `src/node/transition/` — 过渡 stub 头(见「实际依赖」)。
### 独立构建与测试
```sh
cmake -S src/node/standalone -B build-oaknode
cmake --build build-oaknode -j
ctest --test-dir build-oaknode --output-on-failure
```
driver 照 src/common/standalone 模式:EXPAT/OpenColorIO/OpenImageIO
用 Homebrew 的 config 包(`find_package(... CONFIG)`)并映射到
`${OCIO_LIBRARIES}` 等变量;`add_subdirectory` 引入真实 in-repo
targetcore→olivecore、ffmpeg_bridge、src/undo→oakundo、
src/common→oakcommon,各自 BUILD_TESTS 关闭),不再链接预构建
dylib;禁用 OpenTimelineIO`/opt/otio``@loader_path` 问题,
oaknode 不需要)。
### 实际依赖
- Oak 内部:oakcommonXML/Current/工具)、oakundoUndoCommand/
UndoStack)、olivecore`olive::core::Rational/Color/Bezier`
C ABI 包装,真实符号)、ffmpeg_bridge(经 oakcommon 间接)。
- 第三方:EXPAT、OpenColorIO、OpenImageIO、Imath(头)、FFmpeg
(经 ffmpeg_bridge 间接)、GTest(仅测试)。
- **transition stub 机制**(裁决 A):对尚未拆分的
render/codec/timeline/audio/pluginSupport 模块的引用允许悬空——
头文件由 `src/node/transition/` 的过渡 stub/转发头提供(engine 头
仍是 Qt 版),符号经 `-undefined dynamic_lookup`macOS)留到
运行时解析。测试进程启动时必须能解析这些符号:oaknode-gtest
链接真实 targetolivecore/oakcommon/oakundo + OCIO/OIIO/Imath
dylib)并 `-Wl,-force_load` 预构建的
`build/third_party/openfx/HostSupport/libOfxHost.a`OFX 符号与
typeinfo,否则二进制启动即崩,PRE_TEST 发现模式也会挂;路径用
`find_library` 定位,可由 `OAKNODE_OFX_HOST_ARCHIVE` 覆盖)。
- ColorManager 构造时需要有效 OCIO 配置(`:/ocioconf` qrc 提取是
Qt 资源遗留、必然失败):ctest 经 `ENVIRONMENT OCIO=...` 指向
`engine/render/ocioconf/config.ocio`colormanager_test 另用
`OAK_OCIO_TEST_CONFIG` 编译定义兜底。
### 与冻结 C API 的主要差异
- 函数族命名与约定照 oakcommon/oakundo 既有契约:`oaknode_<族>_<动词>`
int 错误码 + out 参数,字符串两段式 bufferundoable 变体成对
`_undoable` 后缀,部分经 `OakUndoCommand *` 尾参)。
- §2 冻结表中**跳过/未实现**的函数族:
- Sequence 的 workarea/markers 族(timeline 边界类型
TimelineMarker/TimelineWorkArea 未拆,留 M4);
- ProjectSerializer 的落盘 save/load(按计划迁 oakstorage M10),
本模块只实现内存形态 `save_to_xml`/`load_from_xml` + SaveData/
LoadData 句柄;
- Node 的 `oak_node_value` 句柄族未单独成族,输入值经
`oaknode_node_get/set_input[_string][_undoable]` 直接收发;
- 无任何事件订阅接口(与 §2 特殊约定 2 一致)。
- `oaknode_debug_alive_count()` 泄漏断言已实现并在测试中使用。
### 已知问题
- `NodeGroupAddInputPassthrough` 的上游 bug 原样保留(未修,待裁决)。
- polygon/text 光栅化后端钩子(PathFillBackend/TextMeasureBackend/
TextRenderBackend)未安装前输出空白;footage 离线警示帧丢失文字
叠层;track 默认高度固化为 13px——均为被迫行为差异,见
`notes.md`「oaknode 去Qt化的删除与语义变更」。
- 去Qt化顺带修了三个上游 bug(行为与旧版不同):
`Project::clear()` 重置 `root_`(可重新 initialize)、clear 删除
顺序修正(不再触发 disconnect_edge 的 parent assert)、
`Sequence` 析构删除三个 `TrackList`(原泄漏)。详见 notes.md。
- 库本体对 transition stub 模块的符号悬空在 M7/M9 复核前是预期
状态;禁止新增对 render/codec/timeline/audio/pluginSupport 的引用。