115 lines
9.5 KiB
Markdown
115 lines
9.5 KiB
Markdown
# Olive/Oak 模块化与多进程渲染:显式加载 + 纯 C 接口方案
|
||
|
||
> **状态**:详细实施计划
|
||
> **范围**:仅制定方案,不涉及代码变更。
|
||
> **核心目标**:
|
||
> 1. 所有业务模块编译为动态库,主进程**显式加载**(`dlopen`/`LoadLibrary`),通过**纯 C 接口**交互。
|
||
> 2. 渲染引擎拆分为独立可执行文件 `olive-renderer`,采用**"用完即弃"**的进程模型:每帧(或每几帧)启动一个新进程,渲染完成后立即退出,最大限度避免锁和状态同步问题。
|
||
|
||
---
|
||
|
||
## 目录索引
|
||
|
||
| 文件 | 内容 |
|
||
|---|---|
|
||
| `README.md` | 本文档:总体架构、设计哲学、目录索引 |
|
||
| `09-c-api-design.md` | **先读此文件**:C API 设计规范、显式加载器、内存管理、错误处理、类型映射总纲。所有其他库的 C API 都遵循此规范。 |
|
||
| `01-olivecore.md` | `libolivecore.so`:基础数据类型库(Rational, Color, TimeRange, PixelFormat, SampleBuffer 等) |
|
||
| `02-olivecodec.md` | `libolivecodec.so`:编解码库(Decoder, Encoder, Frame, Stream) |
|
||
| `03-oliveplugin.md` | `liboliveplugin.so`:OFX 插件宿主支持 |
|
||
| `04-oliveaudio.md` | `liboliveaudio.so`:音频播放与处理 |
|
||
| `05-olivenode.md` | `libolivenode.so`:节点图系统(Node, NodeGraph, Project, Param, Keyframe, Timeline, Undo) |
|
||
| `06-oliverender.md` | `liboliverender.so`:渲染引擎抽象(RenderContext, RenderJob, RenderResult) |
|
||
| `07-oliveui.md` | `liboliveui.so`:UI 层(Widget, Panel, Window, Dialog) |
|
||
| `08-olive-renderer.md` | `olive-renderer` 可执行文件:多进程渲染的"用完即弃"模型详细设计 |
|
||
| `10-implementation-roadmap.md` | 10 周小步快跑实施路线图,含每周任务、验收标准、回退策略 |
|
||
| `11-ipc-protocol.md` | 渲染子进程的 IPC 协议(命令行参数、stdin JSON、stdout NDJSON、共享内存) |
|
||
|
||
---
|
||
|
||
## 总体架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ olive-editor(主进程,GUI) │
|
||
│ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ ModuleLoader(显式加载管理器) │ │
|
||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
||
│ │ │dlopen │ │dlopen │ │dlopen │ │dlopen │ │dlopen │ │ │
|
||
│ │ │olivecore │ │olivecodec│ │olivenode │ │oliverender││oliveui │ │ │
|
||
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
|
||
│ │ │ │ │ │ │ │ │
|
||
│ │ └────────────┴────────────┴────────────┘ │ │ │
|
||
│ │ 纯 C 接口交互 │ │ │
|
||
│ │ ▼ │ │
|
||
│ │ ┌──────────────┐ │ │
|
||
│ │ │ QProcess │ │ │
|
||
│ │ │ 启动/等待/回收 │ │ │
|
||
│ │ └──────┬───────┘ │ │
|
||
│ └───────────────────────────────────────────────────────────┼───────────┘ │
|
||
│ │ │
|
||
├──────────────────────────────────────────────────────────────┼───────────────┤
|
||
│ │ │
|
||
│ ┌───────────────────────────────────────────────────────────┘ │
|
||
│ │ olive-renderer(子进程,无 GUI,"用完即弃") │
|
||
│ │ │
|
||
│ │ 启动参数:--node-graph=/tmp/g.xml --time=1001/30000 --output-shm=/o_123 │
|
||
│ │ │
|
||
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ │ 1. 初始化 Qt Core │ │
|
||
│ │ │ 2. 初始化 OpenGL 离屏上下文 │ │
|
||
│ │ │ 3. 加载节点图 XML │ │
|
||
│ │ │ 4. 执行渲染(RenderProcessor + OpenGLRenderer) │ │
|
||
│ │ │ 5. 将帧数据写入共享内存 │ │
|
||
│ │ │ 6. 输出 JSON 结果到 stdout │ │
|
||
│ │ │ 7. 清理并退出(return 0) │ │
|
||
│ │ └─────────────────────────────────────────────────────────────────────┘ │
|
||
│ └────────────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
│ 所有动态库共享的基础依赖:QtCore, FFmpeg, OpenColorIO 等(由操作系统加载器解析)
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 核心设计哲学
|
||
|
||
### 1. 显式加载 + 纯 C 接口
|
||
|
||
- **主进程不链接任何业务动态库**。`olive-editor` 的 `main.cpp` 和 `core.cpp` 中没有任何对业务模块的 `#include`(除 C API 头文件外)。
|
||
- 所有跨模块边界的交互通过**纯 C 函数**完成,使用**不透明指针(Opaque Pointer)**封装 C++ 对象。
|
||
- 动态库内部可以继续使用 C++、Qt、STL、虚函数、模板等任意特性,但对外仅暴露 C 接口。
|
||
|
||
**为什么用 C 接口而非 C++ 类?**
|
||
- C++ 的 ABI(虚表布局、name mangling、异常传播)在不同编译器/版本间不兼容。
|
||
- C 接口的符号名干净(无 mangling),`dlsym` 可直接查找。
|
||
- 未来如果需要,C 接口可被 Python、Rust、C# 等语言直接绑定。
|
||
|
||
### 2. "用完即弃"的渲染进程
|
||
|
||
- **每一帧(或每 N 帧)渲染任务 = 一个独立的操作系统进程**。
|
||
- 进程启动时接收完整的渲染参数和节点图,渲染完成后立即 `exit(0)`。
|
||
- **不存在常驻渲染进程**,因此不需要:心跳检测、崩溃恢复、状态同步、读写锁、graph_ref 缓存、复杂的取消机制。
|
||
- 主进程如果需要取消渲染,直接 `QProcess::kill()` 即可。
|
||
|
||
**可能的问题与回退**:
|
||
- 若进程启动开销(`QProcess::start()` + OpenGL 上下文初始化)导致实时预览帧率不足,可回退为**"批处理模式"**:每 3–5 帧共享一个进程,或预先启动一个进程池(但进程池内的进程仍不共享状态,每个进程只处理一个批次后自杀)。详见 `08-olive-renderer.md`。
|
||
|
||
### 3. 小步快跑
|
||
|
||
- 每个模块的改造都是**独立、可回退、可并行**的。
|
||
- 不改现有 C++ 类定义,只在其上层**新增 C 封装层**(`api/<module>_api.cpp`)。
|
||
- 保留原有 OBJECT 库编译方式,通过 CMake 选项 `-DOLIVE_DYNAMIC_MODULES=ON` 切换。
|
||
- 每完成一个模块,就通过该模块的单元测试验证,再进入下一个模块。
|
||
|
||
---
|
||
|
||
## 快速开始(阅读顺序)
|
||
|
||
1. **先读 `09-c-api-design.md`**:理解 C API 的约定、显式加载器、内存管理规则。
|
||
2. **再读 `08-olive-renderer.md`**:理解最核心的架构变革——多进程渲染。
|
||
3. **然后按任意顺序阅读 `01-` 到 `07-`**:各业务模块的具体 C API 设计和实施步骤。
|
||
4. **最后读 `10-implementation-roadmap.md`**:10 周实施计划,了解如何排期和验收。
|
||
5. **参考 `11-ipc-protocol.md`**:渲染子进程的通信协议细节。
|