Files
oak-editor/docs/zh/modularization-plan/10-implementation-roadmap.md
T

346 lines
14 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.
# 10 周小步快跑实施路线图
> 本路线图为**渐进式、可回退、可并行**的实施计划。每一周都有明确的交付物和验收标准。任何一周的任务如果超时或遇到阻碍,都可以独立回退或跳过,不影响其他周的进度。
---
## 关键原则
1. **不改现有代码,只增代码**:每一周的改造都通过新增文件(`c_api/``app/render/renderer_main.cpp` 等)完成,现有源文件尽量不动。只有必须解耦的地方才修改现有头文件。
2. **编译开关控制**:通过 CMake 选项 `-DOLIVE_DYNAMIC_MODULES=ON` 控制是否走动态库路径。默认 OFF,确保主干始终可编译可运行。
3. **每周可独立验证**:每周结束都有一个可运行的版本,即使后续周不开始,当前成果也是有价值的。
4. **先易后难**:从耦合最低的模块(core、codec)开始,积累经验和工具链,最后攻克最难的 node/render。
---
## 人员分工建议(假设 2–3 人)
| 角色 | 负责内容 |
|---|---|
| **基础设施工程师** | ModuleLoader、CMake 改造、CI 适配、C API 规范执行 |
| **编解码工程师** | `libolivecore.so``libolivecodec.so``liboliveaudio.so` |
| **渲染工程师** | `libolivenode.so` 解耦、`liboliverender.so``olive-renderer` 多进程 |
| **UI 工程师** | `liboliveui.so`、主进程集成、ViewerWidget 改造 |
**注意**:初期阶段(Week 1–3)只需 1 人负责基础设施 + 1 人负责编解码即可。渲染和 UI 改造在 Week 4 之后全面展开。
---
## Week 1:基础设施与工具链(1 人主责)
### 目标
建立 C API 基础设施,验证显式加载工具链在所有目标平台正常工作。
### 任务清单
- [ ] **T1.1** 创建目录结构 `c_api/include/olive/`, `c_api/src/`, `c_api/tests/`
- [ ] **T1.2** 编写 `ModuleLoader` 类(`c_api/src/module_loader.h/cpp`),支持 POSIX + Windows。
- [ ] **T1.3** 定义全局 C API 规范文件:`c_api/include/olive/core_api.h` 的基础部分(`OliveResult`, `OliveRational`, 内存管理函数)。
- [ ] **T1.4** 编写最小测试动态库 `c_api/tests/test_module/`(只导出一个 `int test_add(int, int)`),验证 `ModuleLoader` 可以正确加载和调用。
- [ ] **T1.5** 在 CMake 中新增 `OLIVE_DYNAMIC_MODULES` 选项(默认 OFF)。
- [ ] **T1.6** 在 CIGitHub Actions)中增加一个 job:开启 `OLIVE_DYNAMIC_MODULES=ON` 编译,验证 Linux/macOS/Windows 三平台。
### 验收标准
```bash
# 运行测试
./tests/c_api/test_module_loader
# 输出:
# [PASS] Load test_module.so
# [PASS] Call test_add(2, 3) = 5
# [PASS] Unload test_module.so
```
### 回退策略
`ModuleLoader` 在某平台工作异常,该周可仅完成 POSIX 平台,Windows 平台延后处理。
---
## Week 2libolivecore.so1 人主责)
### 目标
`ext/core/` 从静态库改造为显式加载的动态库,建立第一个完整的 C API。
### 任务清单
- [ ] **T2.1** 修改 `ext/core/CMakeLists.txt``add_library(olivecore SHARED ...)`,添加 `CXX_VISIBILITY_PRESET hidden`
- [ ] **T2.2**`ext/core/` 中需要跨库使用的类添加导出宏(或保持 C API 头文件中的 `OLIVE_CORE_API`)。
- [ ] **T2.3** 完成 `c_api/include/olive/core_api.h`Rational, Color, TimeRange, Timecode, PixelFormat, SampleBuffer, VideoParams, AudioParams。
- [ ] **T2.4** 编写 `c_api/src/core_api.cpp`,封装所有上述类型。
- [ ] **T2.5** 编写单元测试 `tests/c_api/test_core_api.cpp`
- [ ] **T2.6** 在主进程 `Core::Start()` 中尝试显式加载 `libolivecore.so`,失败时打印警告但不阻塞启动。
### 验收标准
```cpp
ModuleLoader loader;
loader.Load("core", "./libolivecore.so");
auto make = loader.GetFunction<OliveRational(*)(int64_t,int64_t)>("core", "olive_rational_make");
auto add = loader.GetFunction<OliveRational(*)(OliveRational,OliveRational)>("core", "olive_rational_add");
OliveRational r = add(make(1,2), make(1,3));
assert(r.num == 5 && r.den == 6);
```
### 回退策略
若 C API 封装工作量超预期,本周可只完成 Rational + TimeRange 的最小子集,其余类型后续补充。
---
## Week 3libolivecodec.so1 人主责)
### 目标
`app/codec/` + `app/common/` 封装为显式加载动态库,实现解码器的 C API。
### 任务清单
- [ ] **T3.1**`app/common/` 的源文件并入 `app/codec/CMakeLists.txt`
- [ ] **T3.2** 创建 `libolivecodec.so` 的 SHARED 目标。
- [ ] **T3.3** 编写 `c_api/include/olive/codec_api.h`(最小子集):MediaInfo, Decoder, Frame。
- [ ] **T3.4** 编写 `c_api/src/codec_api.cpp`
- [ ] **T3.5** 编写测试:加载视频文件 → 解码第一帧 → 验证宽高 > 0。
- [ ] **T3.6** 在主进程中显式加载 `libolivecodec.so`
### 验收标准
```cpp
auto decoder = olive_decoder_create(nullptr);
olive_decoder_open(decoder, "test.mp4", 0);
OliveFrame* frame = nullptr;
olive_decoder_decode_video(decoder, olive_rational_make(0,1), &frame);
assert(olive_frame_width(frame) == 1920);
olive_frame_destroy(frame);
olive_decoder_destroy(decoder);
```
### 回退策略
`common/` 中有代码依赖 `node/``render/`,先将这些代码移回主库,再继续。
---
## Week 4libolivenode.so 解耦(2 人并行,最关键的一周)
### 目标
解决 `Node.h``render/` 的头文件依赖,为 `libolivenode.so` 的独立编译扫清障碍。
### 任务清单
- [ ] **T4.1** 创建 `app/node/nodecachecallbacks.h`,定义 `NodeCacheCallbacks` 纯虚接口。
- [ ] **T4.2** 修改 `app/node/node.h`
- 移除 `#include "render/rendercache.h"`
- 添加 `NodeCacheCallbacks* cache_callbacks_``SetCacheCallbacks()`
-`InvalidateCache` 相关逻辑改为调用 `cache_callbacks_->InvalidateCache()`
- [ ] **T4.3** 创建 `app/node/jobtypes.h`,定义 `NodeJobType``NodeJobData`
- [ ] **T4.4** 修改 `Node.h` 中的 `ProcessXxx` 虚函数签名,使用 `NodeJobData`
- [ ] **T4.5** 修改 `RenderProcessor`,适配新的 `NodeCacheCallbacks``NodeJobData`
- [ ] **T4.6** 验证 `app/node/` 目录可以独立编译(写一个临时 CMake 测试)。
### 验收标准
```bash
cd /tmp && cmake /path/to/oak/app/node && make
# 编译成功,不报错
```
### 回退策略
**若解耦工作量超预期**:允许 `node/``render/` 暂时合并为 `libolive-engine.so`。这是最重要的回退策略——宁可合并也不阻塞进度。合并后仍可继续封装 C API,后续再拆分。
---
## Week 5libolivenode.so C API + liboliveplugin.so2 人并行)
### 目标
完成节点图系统的 C API,并将 OFX 插件宿主独立为动态库。
### 任务清单(节点图工程师)
- [ ] **T5.1** 创建 `libolivenode.so`,聚合 `node/`, `timeline/`, `undo/`, `config/`
- [ ] **T5.2** 编写 `c_api/include/olive/node_api.h``c_api/src/node_api.cpp`
- [ ] **T5.3** 实现最小 C APINodeGraph create/destroy, load_xml/save_xml, add_node, connect, param set/get。
- [ ] **T5.4** 编写测试:用 C API 构建一个 Generator -> ViewerOutput 的图,序列化后反序列化验证。
### 任务清单(插件工程师)
- [ ] **T5.5**`pluginSupport/` 从主 OBJECT 库移出,创建 `liboliveplugin.so`
- [ ] **T5.6** 编写 `c_api/include/olive/plugin_api.h`(最小子集):host create/destroy, add_path, rescan, plugin count/get。
- [ ] **T5.7** 验证主进程可以扫描 OFX 插件目录并列出插件名称。
### 验收标准
```cpp
// 节点图测试
OliveNodeGraph* g = olive_node_graph_create();
olive_node_graph_add_node(g, "SolidGenerator", "Solid1");
olive_node_graph_add_node(g, "ViewerOutput", "Viewer1");
OliveNode* solid = olive_node_graph_find_node(g, "Solid1");
OliveNode* viewer = olive_node_graph_find_node(g, "Viewer1");
olive_node_connect(solid, 0, viewer, 0);
size_t len;
char* xml = olive_node_graph_save_xml(g, &len);
assert(len > 0);
olive_core_free(xml);
```
### 回退策略
`node/` C API 工作量过大,优先保证 `load_xml` / `save_xml` / `find_node` 三个函数(这是渲染子进程最需要的),其余延后。
---
## Week 6olive-renderer 单帧端到端(2 人并行)
### 目标
实现第一个可用的 `olive-renderer` 可执行文件,能渲染一帧测试图。
### 任务清单
- [ ] **T6.1** 编写 `app/render/renderer_main.cpp`,实现命令行解析。
- [ ] **T6.2** 实现 `olive_shm_create/open/close/unlink`POSIX + Windows)。
- [ ] **T6.3**`olive-renderer` 中集成:加载 XML → 初始化 OpenGL → 渲染 → 写入 SHM → 输出 JSON。
- [ ] **T6.4** 编写主进程中的 `RenderProcessLauncher::RenderFrameSync`
- [ ] **T6.5** 端到端测试:主进程启动 `olive-renderer` 渲染一帧纯色,验证 SHM 中的像素值正确。
### 验收标准
```bash
# 命令行直接测试
olive-renderer --mode=frame --node-graph=test_solid.xml --time=0/1 \
--video-params='{"width":100,"height":100,"format":"rgba32f"}' \
--output-shm=/olive_test --output-shm-size=160000
# 输出:
# {"status":"ok","width":100,"height":100,"format":"rgba32f",...}
```
### 回退策略
若 OpenGL 离屏上下文初始化在某平台失败,该平台暂时使用 `--backend=dummy`(只测试进程模型,不测试实际渲染)。
---
## Week 7ViewerWidget 集成 + 用完即弃验证(2 人并行)
### 目标
`olive-renderer` 集成到主进程的 Viewer 中,验证"用完即弃"模型在实际场景中的可行性。
### 任务清单
- [ ] **T7.1** 修改 `ViewerWidget`:从 `RenderManager::RenderFrame()` 改为启动 `olive-renderer`
- [ ] **T7.2** 实现异步完成回调:`OnRenderProcessFinished()` 读取 SHM 并更新 Texture。
- [ ] **T7.3** 处理子进程崩溃:崩溃时忽略该帧,保持上一帧显示,记录日志。
- [ ] **T7.4** 测量实际性能:拖动时间线时的帧率、CPU 占用、进程启动耗时。
- [ ] **T7.5** 若性能不达标,实现批处理模式(`--mode=batch`)。
### 验收标准
- 打开一个简单项目(单轨道 + 纯色生成器),拖动时间线,Viewer 实时更新。
- `kill -9` 一个渲染子进程,主进程不崩溃,Viewer 保持显示。
### 回退策略
若"用完即弃"性能完全不可接受(如帧率 < 5fps),立即切换为**批处理模式**或**进程池模式**(预启动 N 个进程,循环使用,每个进程渲染一批后自杀)。
---
## Week 8liboliveaudio.so + liboliveui.so2 人并行)
### 目标
完成音频库和 UI 库的动态库拆分。
### 任务清单(音频工程师)
- [ ] **T8.1** 创建 `liboliveaudio.so`
- [ ] **T8.2** 编写 `audio_api.h/cpp`(最小子集):manager init/play/pause/push_buffer。
- [ ] **T8.3** 验证音频播放通过 C API 正常工作。
### 任务清单(UI 工程师)
- [ ] **T8.1** 聚合所有 UI 源文件,创建 `liboliveui.so`
- [ ] **T8.2** 解耦 `Core` 类中的 UI 逻辑(`StartGUI()` 迁移到 `liboliveui.so` 内部)。
- [ ] **T8.3** 主进程显式加载 `liboliveui.so`,成功启动主窗口。
- [ ] **T8.4** 编写 `ui_api.h`(最小子集):app create/exec, main_window show, open_project。
### 验收标准
- 主程序通过显式加载 `liboliveui.so` 启动,界面正常。
- 音频播放正常(可听到声音)。
---
## Week 9:导出集成 + 稳定性打磨(2 人并行)
### 目标
将导出流程集成到多进程渲染模型,全面稳定性测试。
### 任务清单
- [ ] **T9.1** 修改导出任务(`task/export/`),使用 `olive-renderer` 逐帧/逐批渲染。
- [ ] **T9.2** 导出天然适合批处理:一次性发送 10–50 帧给子进程。
- [ ] **T9.3** 编写压力测试:连续渲染 100 帧,验证无内存泄漏、无共享内存泄漏。
- [ ] **T9.4** 测试 OFX 插件在子进程中的渲染(选择几个免费 OFX 插件测试)。
- [ ] **T9.5** 测试崩溃场景:`kill -9` 随机子进程,验证主进程稳定。
### 验收标准
- 成功导出一个 10 秒视频(300 帧),画面正确。
- 连续启动 100 个渲染子进程,系统无共享内存泄漏(`ls /dev/shm/` 检查)。
---
## Week 10:打包适配 + 文档 + 性能优化(2 人并行)
### 目标
完成打包脚本适配,编写用户文档,进行最终性能优化。
### 任务清单
- [ ] **T10.1** 更新 macOS 打包脚本:确保 `libolive*.dylib``olive-renderer` 被打入 `.app` Bundle`@rpath` 设置正确。
- [ ] **T10.2** 更新 Windows 打包脚本:确保 `.dll``olive-renderer.exe` 在_installer 中。
- [ ] **T10.3** 更新 Linux AppImage 打包:确保动态库在 AppImage 内可加载。
- [ ] **T10.4** 更新 `docs/build.md``docs/build-macos-zh.md`,说明新的运行时依赖。
- [ ] **T10.5** 性能优化:
- 节点图 XML 缓存(相同图只序列化一次)。
- 共享内存预分配池(避免反复创建/销毁)。
- 批处理大小动态调整(根据上一批的渲染时间调整下一批的帧数)。
- [ ] **T10.6** 全面回归测试:导入、编辑、预览、导出、Undo/Redo、保存/加载项目。
### 验收标准
- 在三平台上都能通过 `make install` 或打包脚本生成可分发包。
- 新用户按照 `build.md` 可以成功编译并运行。
- 与 Week 0(改造前)相比,导出速度不差于 90%,预览帧率不差于 70%。
---
## 并行工作流
```
Week 1: [基础设施]
Week 2: [core] (依赖 W1)
Week 3: [codec] (依赖 W2)
Week 4: [node 解耦] (可并行 W3, 但建议 W3 后启动)
Week 5: [node C API] + [plugin] (依赖 W4)
Week 6: [renderer] (依赖 W5)
Week 7: [Viewer 集成] (依赖 W6)
Week 8: [audio] + [ui] (可并行 W7)
Week 9: [导出集成] (依赖 W7)
Week 10: [打包/文档/优化] (依赖 W9)
```
**最大并行度**:Week 8 时可以有 3 人同时工作(1 人 audio1 人 ui1 人优化 renderer)。
---
## 回退总策略
| 场景 | 回退方案 |
|---|---|
| 某周任务无法按时完成 | 将该周剩余任务移到下一周,当前周只交付已完成部分。 |
| `node/` 解耦完全不可行 | 将 `node/` + `render/` 合并为 `libolive-engine.so`,后续再拆分。 |
| "用完即弃"性能完全不可接受 | 切换为"批处理模式"(每进程渲染 N 帧),或"进程池模式"(预启动 N 个进程)。 |
| 动态库在某平台加载失败 | 该平台暂时保持静态链接,其他平台先用动态库。 |
| C API 维护成本过高 | 保留 C API 用于子进程通信,主进程内部恢复直接 C++ 链接(但库仍编译为动态库,由操作系统隐式加载)。 |
| 项目期限紧张 | 优先完成 `olive-renderer` 多进程(核心价值),动态库拆分可以延后。 |