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
This commit is contained in:
@@ -80,3 +80,95 @@ M3 阶段判据(放宽版):oaknode 目录就位、C API 实现、oaknode_g
|
||||
- 枚举序数: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
|
||||
target(core→olivecore、ffmpeg_bridge、src/undo→oakundo、
|
||||
src/common→oakcommon,各自 BUILD_TESTS 关闭),不再链接预构建
|
||||
dylib;禁用 OpenTimelineIO(`/opt/otio` 的 `@loader_path` 问题,
|
||||
oaknode 不需要)。
|
||||
|
||||
### 实际依赖
|
||||
|
||||
- Oak 内部:oakcommon(XML/Current/工具)、oakundo(UndoCommand/
|
||||
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
|
||||
链接真实 target(olivecore/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 参数,字符串两段式 buffer;undoable 变体成对
|
||||
(`_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 的引用。
|
||||
|
||||
@@ -126,3 +126,90 @@ oakcommon;后续若确认无用途可直接删除。
|
||||
转移、wrapper 被消费(不可再用/再 free);
|
||||
`oakundo_command_multi_child` 返回 borrowed wrapper(free 只释放
|
||||
wrapper)。
|
||||
|
||||
## oaknode 去Qt化的删除与语义变更(2026-08-05)
|
||||
|
||||
`src/node/`(Node 族、Project 族、Sequence/Track/Block 族、serializer、
|
||||
ColorManager)去Qt化过程中的删除与语义变化,迁移调用方时需注意
|
||||
(详细约定见 `src/node/DEQT.md` §4/§7):
|
||||
|
||||
- 信号槽整组删除:Node 的 28 个信号(label_changed、input_connected、
|
||||
keyframe_added、added_to_graph 等)、NodeKeyframe 的 5 个、
|
||||
Project/Folder/Sequence/Footage 的全部信号(name_changed、
|
||||
modified_changed、track_added、begin/end_insert_item 等),变更通知
|
||||
统一由 facade 经 `oakengine_event` 发出,oaknode 不持有上层回调。
|
||||
- QObject 父子生命周期改显式所有权:Project 持有全部节点
|
||||
(`add_node`/`remove_node`,`clear()`/析构删除);nodeundo 用
|
||||
`std::unique_ptr<Node>` 替代 `memory_manager_`;keyframe 经
|
||||
`Node::add_keyframe()/remove_keyframe()` 显式注册(自动 set_parent),
|
||||
gizmo 经 `Node::add_gizmo()/remove_gizmo()` 显式注册(构造不再自
|
||||
注册);`NodeInputImmediate::delete_all_keyframes(reclaimed)` 替代
|
||||
"reparent 续命"。
|
||||
- polygon/text 的 `generate_frame()` 光栅化(QPainter/QTextDocument)
|
||||
改后端钩子:`PathFillBackend`(geometry.h)、`TextMeasureBackend`/
|
||||
`TextRenderBackend`(textbackend.h),facade 未安装前输出空白
|
||||
(被迫行为差异)。
|
||||
- gizmo 绘制与 hit-test 归 app 层:各 gizmo 的 `draw(QPainter*)`、
|
||||
`PointGizmo::get_clicking_rect()/get_drawing_rect()` 等删除;
|
||||
`DraggableGizmo` 的拖拽信号改直调 `parent_node()->gizmo_drag_*()`;
|
||||
drag 回调里的 `sender()` 由 `Node::current_gizmo()` 替代。
|
||||
- footage 离线媒体警示帧:QImage/QPainter 改纯像素循环(深红底+斜纹),
|
||||
"Media Offline" 文字叠层与抗锯齿丢失(被迫行为差异)。
|
||||
- `:/ocioconf` Qt 资源路径不可用:ColorManager 默认配置的 qrc 提取必然
|
||||
失败,需 `OCIO` 环境变量指向磁盘 config.ocio(测试用
|
||||
`engine/render/ocioconf/config.ocio`)。
|
||||
- `NodeGroupAddInputPassthrough` 的上游 bug 原样保留(去Qt化不改行为
|
||||
逻辑,待单独裁决修复)。
|
||||
- track.h 的 `QFontMetrics` 13px 常量:轨道默认高度原依赖
|
||||
`QFontMetrics(qApp->font()).height()`,去Qt后固化为 13px 字面量,
|
||||
换字体/DPI 不再自适应。
|
||||
- `Footage::check_footage()` 的 QTimer 周期回调删除,函数本体保留为
|
||||
public 由 facade 周期调用;`qApp->activeWindow()` 门槛移到 app 层。
|
||||
- 序列化 XML 元素/属性名与读写顺序逐字节不变;`XmlStreamWriter` 输出
|
||||
紧凑 XML(无声明无缩进);OVEC 压缩段保持 qCompress 兼容
|
||||
(zlib + 4 字节大端长度)。
|
||||
- 修复三个上游真实 bug(行为因此与旧版不同,特此记录):
|
||||
`Project::clear()` 现重置 `root_`(clear 后可再次 initialize);
|
||||
clear 不再先 `set_parent(nullptr)` 再 delete(否则 ~Node 的
|
||||
disconnect_edge 触发 parent 不等的 assert);
|
||||
`Sequence` 新增析构删除三个 `TrackList`(原来构造 new 后泄漏)。
|
||||
- `sender()` 的其他替代:原 keyframe signal→Node 的 5 条失效通知链
|
||||
(invalidate_from_keyframe_*)函数本体保留为 public 成员,带
|
||||
`NodeKeyframe *key` 显式参数,待 facade 接线。
|
||||
|
||||
## 信号与槽的处理策略(2026-08-05)
|
||||
|
||||
去Qt化对 Qt 信号槽的统一处理模式,M1–M3 一致执行,后续模块照此:
|
||||
|
||||
1. **模块内通知改显式回调/直调(std::function)**:通知双方都在同一
|
||||
模块内时,去掉 connect,改为 `std::function` 回调注册或成员直调。
|
||||
例:oakundo `UndoStack::index_changed(int)` signal →
|
||||
`set_index_changed_callback(std::function<void(int)>)`;oaknode
|
||||
`Node::current_gizmo()/set_current_gizmo()` 机制替代 gizmo drag
|
||||
回调里的 `sender()`;`DraggableGizmo` 的 handle_start/handle_movement
|
||||
信号改直调 `parent_node()->gizmo_drag_start()/gizmo_drag_move()`;
|
||||
TrackList 原 track_list_changed/length_changed 发射点直调
|
||||
`sequence->update_track_cache()/verify_length()`。
|
||||
2. **跨层/跨界通知删除,由 facade 经 oakengine_event 统一发出**:
|
||||
一切指向 app/UI/其他模块的信号整组删除(Node 28 个、NodeKeyframe
|
||||
5 个、Project/Folder/Sequence/Footage 全部),oaknode 自身不持有
|
||||
任何上层回调;facade 在执行命令后经既有 `oakengine_event` 通道发
|
||||
通知(事件 id 沿用 `oakengine/events.h` 70-95 段,值不变)。依据:
|
||||
oaknode 的所有修改都经命令函数完成,调用方知道影响(M3 §2 特殊
|
||||
约定 2,04 §3)。
|
||||
3. **QObject 父子生命周期改显式所有权**:`setParent`/childEvent 机制
|
||||
删除,所有权用显式容器与注册函数表达。例:Project 持有节点
|
||||
(`add_node`/`remove_node`/`clear()`);`ColorManager` 由
|
||||
`std::unique_ptr` 持有;nodeundo 用 `std::unique_ptr<Node>` 替代
|
||||
`memory_manager_`;keyframe/gizmo 分别经 `add_keyframe()`/
|
||||
`add_gizmo()` 显式注册;UndoCommand 树由 MultiUndoCommand 析构
|
||||
删除 children。
|
||||
4. **sender() 的替代**:两种形态——拖曳场景用
|
||||
`Node::current_gizmo()`(DraggableGizmo 直调回调前后
|
||||
`set_current_gizmo(this/nullptr)` 包一层);通知链场景改显式参数,
|
||||
如 `invalidate_from_keyframe_*(NodeKeyframe *key)`。
|
||||
5. **原 slot 函数本体保留为 public 成员,待 facade 接线**:signal 删了
|
||||
但 slot 承载的业务逻辑不删,改为 public 成员函数由上层直调。清单
|
||||
见 `src/node/DEQT.md` §7(如 `Sequence::update_track_cache()`、
|
||||
`Footage::check_footage()/default_color_space_changed()/
|
||||
proxy_ready()/proxy_finished()`、5 条 invalidate_from_keyframe_*)。
|
||||
|
||||
Reference in New Issue
Block a user