Files
oak-editor/docs/zh/plans/riir/notes.md
T
Mike-Solar 66831a870e refactor(timeline): de-Qt oaktimeline and wrap it in a pure C ABI
- de-Qt all 11 sources: markers/workarea lose QObject/signals (list
  re-sort via direct resort() call, drawing moves to the app layer),
  explicit unique_ptr ownership for marker lists and viewer's
  workarea/markers
- timeline undo command families (split/pointer/ripple/general/track)
  now hold oaknode C handles and do all graph work through the oaknode
  C ABI (no adapter layer); gaps are attached to the project graph
  before insertion, orphan handles tracked and freed
- fix the de-Qt severed Block->Track length-changed chain with a
  direct block_length_changed() call (positions were stale after
  split/trim)
- expand oaknode C API by ~20 functions needed by timeline
  (copy_in_graph, block kind/casts, connect_element, input arrays,
  tracklist family, marker/workarea borrowed outlets)
- pure C ABI in include/timeline (marker/workarea/edit, command
  factories returning OakUndoCommand), oakcommon XML get_native
  accessors
- oaknode<->oaktimeline resolve each other at runtime; standalone
  drivers link both into test binaries
- tests: oaktimeline 117, regressions oakcommon 193 / oaknode 96 /
  oakrender 42 / oakcodec 18 / oakaudio 36 all green
2026-08-07 04:37:24 +08:00

288 lines
18 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.
# 记录项
## common
CancelableObject只在render和task处有🚰,移入对应模块。
## 从 oakcommon(src/common)移除的类(2026-08-05)
判据:不严重依赖 common 其他类,且只有一个非 common 模块使用它
(tests/gtest 不计入使用方)。以下类已从 `src/common/` 移除,
拆分时应放到对应模块:
| 类 | 唯一使用方 | 应放到 |
|---|---|---|
| `Html`(html.h/.cpp) | engine/node | oaknode(M3)。注意它依赖 common 的 xmlutils,迁移时需连同 XML 辅助或改为调用 oakcommon C API |
| `JobTime`(jobtime.h/.cpp) | engine/render | oakrender(M7) |
| `OTIOUtils`(otioutils.h) | engine/task | oaktask(M8) |
| `PlaybackAudioClock`(playbackaudioclock.h) | engine/audio | oakaudio(M6) |
| `to_hex`(tohex.h) | engine/node | oaknode(M3) |
| `mid()`(util.h) | engine/node | oaknode(M3) |
| `AVFramePtr`(avframeptr.h) | engine/render | oakrender(M7) |
| `CrashpadInterface`(crashpadinterface.h/.cpp) | app/main.cpp | app(crashhandler 相关) |
| crashpadutils.h | app/crashhandler | app/crashhandler |
| `AutoScroll`(autoscroll.h,oakutil shim) | app/dialog、app/widget | app |
| `digit.h`(oakutil shim) | app/dialog | app |
| `range.h`(oakutil shim) | app/widget | app |
另:`power.h`、`memorypool.h`、`threadsafemap.h` 当前没有任何
common 模块外的使用方(零用户),不满足移除判据,暂保留在
oakcommon;后续若确认无用途可直接删除。
## oakcommon 去Qt化的删除与语义变更(2026-08-05)
去Qt化过程中以下函数被删除或语义变化,迁移调用方时需注意:
- `CommandLineParser::print_help()`:不再自动读取
`QCoreApplication::applicationName()/applicationVersion()`,需先调
`set_app_info()`(C API:`oakcommon_commandlineparser_set_app_info`),
否则打印默认 `"oak"` + 空版本。
- `FileFunctions::get_unique_file_identifier`:哈希由 SHA-1 改为
FNV-1a 64-bit,旧缓存 key 全部失效(需一次重建)。
`get_configuration_location`/`get_application_path`/
`get_temp_file_path`/`get_auto_recovery_root` 的标准路径改为手写
平台实现,路径与原 Qt 版不同。`read_file_as_string(":/...")` 的
qrc 资源路径不再可用,shader 等资源调用方需改为磁盘路径或内嵌。
- xmlutils:`xml_read_next_start_element(reader, CancelAtom*)`
重载与 cancel_atom 参数删除(依赖 render/cancelatom.h),取消语义
由调用方在循环外自行检查;`XMLAttributeLoop` 宏删除,由
`XmlStreamReader::attributes()` + range-for 替代;
`QXmlStreamReader/Writer` 由基于 expat 的
`olive::XmlStreamReader/Writer` 替代(不支持 XML 命名空间)。
- `OIIOUtils::frame_to_buffer/buffer_to_frame`:未进 C API,C++ 侧签名
拍平为 `(const void *data, int64_t linesize_bytes, OIIO::ImageBuf *)`,
engine/codec/frame.cpp 迁移时传成员即可。`OakCommonPixelFormat` 枚举
目前定义在 include/common/ocioutils.h,oiioutils 复用,后续可抽成
独立的 include/common/pixelformat.h。
- `MemoryPool`:删除 QTimer 每 5 秒自动回收空 arena,改为公有
`clear_empty_arenas()` 由调用方周期调用;不再继承 QObject。
memorypool/threadsafemap 零使用方且为模板/多态基类,未包 C API。
- `debug_handler`:丢失 `QMessageLogContext`(文件/行号)参数(原实现
本就未用);`qInstallMessageHandler` 无对应物。
- `Current::set_current_video_params/audio_params`:由按值拷贝改为持有
`std::shared_ptr<void>`(类型擦除),调用方需自行管理生命周期并用
`static_pointer_cast` 还原(调用点:olivehost.cpp、
timebasedwidget.cpp、plugin.cpp 等 6 处)。
- qtutils:engine 未使用的纯 UI 函数(`q_font_metrics_width`、
`create_horizontal/vertical_line`、`set_combo_box_data`、
`word_wrap_string`、`flip_control_and_shift_modifiers`、
`get_formatted_date_time`、`to_q_color`、`core::qHash`、
`Q_DECLARE_METATYPE` 宏组)未迁入 oakcommon,app 层继续用 Qt 版。
`ptr_to_value/value_to_ptr` 载体由 QVariant 改为 `uintptr_t`,
迁移时调用点需同步改(renderprocessor.cpp、rendermanager.cpp、
previewautocacher.cpp、src/capi/worker.cpp)。`get_parent_of_type`
依赖 C++ 模板+RTTI,未进 C API。
- define.h:删除 `MACRO_NAME_AS_STR`/`MACRO_VAL_AS_STR`/
`OLIVE_NS_CONST_ARG`/`OLIVE_NS_ARG`/`OLIVE_NS_RETURN_ARG`
(依赖 Qt QArgument,全仓无使用)。
- decibel.h:删除 `ALLOW_RETURNING_INFINITY` 编译开关分支(代码库
未定义该宏,行为不变)。
- `FFmpegUtils`:新增类内常量 `k_rgb_channel_count/k_rgba_channel_count`
(3/4)取代对 `VideoParams` 同名常量的引用,消除 common→render
反向依赖。
## oakundo 去Qt化的删除与语义变更(2026-08-05)
`src/undo/`(olive::UndoCommand / MultiUndoCommand / UndoStack)去Qt化
过程中以下删除与语义变化,迁移调用方时需注意:
- `UndoStack` 不再继承 `QAbstractItemModel`(删除 `Q_OBJECT`、
`columnCount/data/index/parent/rowCount/headerData/hasChildren` 全部
model overrides 及 `begin/end*Rows` 通知)。历史面板(QTreeView 等)
属 app UI 层,迁移时应以 facade 的行式查询
(command_count/done_count/command_name/command_is_done)自行实现
model。
- `UndoStack::GetUndoAction/GetRedoAction/update_actions`(QAction 成员
及其文本/使能维护)删除——QAction 属 app 菜单层,app 应自行创建
action 并连接 undo/redo;`index_changed(int)` signal 改为 C++ 侧
`std::function<void(int)>`(`set_index_changed_callback`),C ABI
不暴露事件,调用方在变更命令后读 `oakundo_undostack_index`。
- `UndoCommand` 与 `olive::Project` 解耦:删除纯虚
`get_relevant_project()`(含 MultiUndoCommand/EmptyCommand 的
override)与 `project_` 成员;`redo_and_set_modified/
undo_and_set_modified` 的修改标记语义改为可选回调对
`set_modified_callbacks(is_modified, set_modified)`:redo 时记录当前
修改标记并置 true,undo 时恢复记录值;不挂回调则退化为纯
redo/undo。读写顺序与 Qt 版一致(redo_now() 之后读取并置位),
行为对齐。Project 侧迁移时在适配层把 `Project::is_modified/
set_modified` 绑到这对回调即可。
- `UndoStack` 析构不再调用 `clear()`(Qt 版在析构里 clear 会再 push
一个 EmptyCommand 造成泄漏),改为直接删除所有持有命令。
- 顺带修复三处 engine 原版就存在的缺陷(迁移调用方无需适配,但
行为与旧版不同,特此记录):
- `jump(0)` 死循环:底部 EmptyCommand 不可 undo,`jump` 现以
`can_undo()/can_redo()` 为守卫,跳到 0 时停在 index 1;
- `MultiUndoCommand` 原来不拥有子命令(析构泄漏),现析构删除
全部 children;
- `push_pre_executed` 原来不置 `done_` 标记,导致入栈后 undo
为空操作(facade undo-group 的组撤销实际不生效),现入栈前
`set_done(true)`(UndoCommand 新增 `set_done()`)。
- `clear()` 推入的占位命令名固定为字面量 "New/Open Project"(原
`tr()` 翻译移除,翻译由 app 层负责)。
- `push/push_pre_executed` 的命令名由 `QString` 改为 `std::string`;
C ABI 中 NULL name 视同空串。
- C ABI 句柄语义:`oakundo_command_init/init_multi` 返回的句柄拥有
底层命令;push 或 `oakundo_command_multi_add_child` 成功后所有权
转移、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_*)。
## oakrender 去Qt化的删除与语义变更(2026-08-05)
- `RenderTicket::finished` 不再跨线程补发:回调在调用 finish() 的
线程上触发;未决状态由 facade 自查 `is_running()`。
- PreviewAutoCacher 延迟重排队改显式标志:单次 QTimer 重排队删除,
`try_render()` 置 `delayed_requeue_pending_`,facade 在
`requeue_delay_ms()` 后重调 `try_render()`。
- DiskCacheFolder 周期存盘由 GUI 线程 QTimer 改后台线程,数据保护
用 `std::recursive_mutex`(原靠 QObject 线程亲和串行化)。
- PlaybackCache 的 Qt 信号改单回调注册:`set_invalidated_callback`/
`set_validated_callback`/`set_requested_callback`/
`set_cancel_all_callback`;跨层 invalidated/validated 通知由 facade
在命令后重发(M7 §2.2:oakrender 不持有上层回调)。
- `DiskManager::instance()` 惰性自建(原 app 启动时显式
create_instance;库消费者不经 facade 也能用)。
- FrameHashCache 帧缓存 JPEG 读写 QImage→OIIO/OpenEXR。
- AudioWaveformCache/PlaybackCache 的 QPainter 绘制归 app 层;
缓存指示条高度固化为常量 4(`oakrender_cache_indicator_height()`,
原 `QFontMetrics(QFont()).height()/4`)。
- 离屏 GL 上下文共享组需 app 显式传入(原 QOpenGLContext
globalShareContext 隐式共享)。
- OpenGLContext/Vulkan 上下文抽象层(OpenGLContextProvider 等)替代
QOpenGLContext 直用。
- WorkerProcess 仅 POSIX 实现(Windows 占位未做)。
- worker IPC 的 NDJSON 协议未变,workerjson 为本地(去 Qt)实现。
- `:/shaders` qrc 资源缺口:着色器源码的 Qt 资源路径在库形态下
不可用,需 app 侧提供文件映射。
- 删除 ipc/ipcmessage.cpp 等 3 个过期 .cpp(对应头已自包含 inline,
.cpp 内容失效)。
- displayinternal.h 分层违规(render→src/capi)并入 texturehandle.h。
- Renderer 线程亲和由 QObject::thread() 改显式 owner thread
(`set_owner_thread_to_current()`/`called_on_owner_thread()`)。
## oaktimeline 去Qt化的删除与语义变更(2026-08-05)
- TimelineMarker/TimelineWorkArea 的信号(time_changed/name_changed/
color_changed/enabled_changed/range_changed/marker_added/removed/
modified)全部删除,变更通知由 facade 层在命令后发出;list 的重排由
TimelineMarker::set_time 直调 parent_->resort(this)。
- TimelineMarker::draw()/get_marker_height()(QPainter/QFontMetrics)
删除,归 app 层。
- TimelineMarkerList 的 QObject childEvent 自动注册改为显式
add_marker(std::unique_ptr)/remove_marker 所有权转移;marker 构造
不再带 parent 参数(oaknode 的 4 个 serializer 调用点已适配)。
- ViewerOutput 的 workarea_/markers_ 由 QObject 父子管理改为
std::unique_ptr 持有。
- timeline 全部 undo 命令类改为持有 oaknode C 句柄
(OakNodeTrack/OakNodeBlock/OakNodeSequence),图操作全部经
oaknode C ABI(01 §0 铁律 6);为此 oaknode C API 新增:
copy_in_graph、clip_add_cache_passthrough_from、block_get_kind、
block_as_node/from_node、track_as_node、nearest_block_before/after_or_at、
command_create_remove_node/add_node/set_position_recursive、
node_get_project、input_array_insert/remove、connect/disconnect_element、
tracklist_get_sequence/track_input_id/array_append/array_remove_last/
get_array_index_from_cache_index、sequence_as_node、node_get_markers/
get_work_area(后两个为 oaktimeline 的借用出口)。
- UndoCommand 跨模块继承(oaktimeline 命令类继承 oakundo 的
olive::UndoCommand)保留——undo 架构的既有跨模块模式,记录为
01 §5 的明确例外(oaknode 的 nodeundo 同例)。
- **修复 de-Qt 断链**:Block 长度变化不再经信号通知 Track,改为
Block::InputValueChangedEvent 里对 track_->block_length_changed(this)
直调(原 Qt 版 connect 链,wave-2 遗留的 facade 接线项提前兑现)。
- 命令创建的 GapBlock 在插入 track 前必须先进项目图
(block_add_to_graph/block_remove_from_graph helper,对齐原版
setParent(graph)/setParent(memory) 语义);孤立句柄用 orphan 标志
跟踪,析构时 oaknode_block_free。
- oaknode↔oaktimeline 运行期互相解析(add_default_nodes →
TimelineAddTrackCommand),双方 dylib dynamic_lookup,测试二进制
必须同时链两个库(各 standalone 驱动已接线)。
- oakcommon xml C API 新增 get_native 借用访问器(C++ only),
oaktimeline 的 load/save 经它取回 XmlStreamReader/Writer。