937 lines
13 KiB
Markdown
937 lines
13 KiB
Markdown
可以,下面这份就按 **Codex 执行计划** 来做。基于你当前代码,重点不是再动 Datoviz,而是把
|
||
`Kernel → Scene3D → Datoviz backend` 这条链建立起来。
|
||
|
||
## 1. 先扩展 Kernel 帧策略
|
||
|
||
修改:
|
||
|
||
```text
|
||
Kernel/src/renderive/frame_control/strategy/low_latency/
|
||
Kernel/src/renderive/frame_control/strategy/manual/
|
||
Kernel/tests/renderive/frame_control/strategy/
|
||
```
|
||
|
||
### Low_Latency_Strategy
|
||
|
||
给:
|
||
|
||
```cpp
|
||
struct Configuration
|
||
```
|
||
|
||
增加:
|
||
|
||
```cpp
|
||
bool replace_pending_frame{true};
|
||
```
|
||
|
||
语义:
|
||
|
||
```text
|
||
true
|
||
= 保持现在行为
|
||
= pending frame 可以被新帧替换
|
||
= abandoned_frame_count 正常增长
|
||
|
||
false
|
||
= 已发布 pending frame 禁止自动替换
|
||
= Painter 在 pending frame 被 renderer 取得前产生背压
|
||
```
|
||
|
||
默认必须是 `true`,不能破坏 Render2D 当前语义。
|
||
|
||
禁止 busy wait。使用等待/通知机制;等待仅发生在 pending slot 争用阶段。
|
||
|
||
Renderer 完成:
|
||
|
||
```text
|
||
cache → render
|
||
```
|
||
|
||
交换后立即释放状态锁并唤醒 Painter。
|
||
|
||
### Manual_Refresh_Strategy
|
||
|
||
这个也必须一起处理。
|
||
|
||
当前 Manual 同样存在:
|
||
|
||
```text
|
||
prepared A
|
||
↓
|
||
prepare B
|
||
↓
|
||
A 被 prepared_replaced
|
||
```
|
||
|
||
所以增加同语义配置:
|
||
|
||
```cpp
|
||
bool replace_pending_frame{true};
|
||
```
|
||
|
||
Scene3D 固定 `false`。
|
||
|
||
`Flow_Refresh_Strategy` 不改,它本身就是队列,不覆盖。
|
||
|
||
### 注释必须明确
|
||
|
||
Kernel 注释说明:
|
||
|
||
```text
|
||
replace_pending_frame=true
|
||
保持 latest-frame-wins / prepared-frame-replacement 旧语义。
|
||
|
||
replace_pending_frame=false
|
||
保持单 pending frame,但通过 producer backpressure 保证已发布帧不会被后续帧自动替换。
|
||
```
|
||
|
||
并明确:
|
||
|
||
```text
|
||
这个选项只控制自动 replacement,
|
||
不改变显式 discard API 的语义。
|
||
```
|
||
|
||
### Kernel 测试
|
||
|
||
必须新增:
|
||
|
||
```text
|
||
low latency 默认仍覆盖
|
||
low latency false 不覆盖
|
||
low latency false producer 会等待
|
||
renderer 获取 pending 后 producer 被唤醒
|
||
显式 discard 后 producer 被唤醒
|
||
abandoned_frame_count 在 false 模式不增加
|
||
|
||
manual 默认仍 replacement
|
||
manual false 不 replacement
|
||
refresh 后 painter 才继续
|
||
```
|
||
|
||
全量 Kernel 测试通过后再进入 Render3D。
|
||
|
||
---
|
||
|
||
# 2. Render3D 正式链接 Kernel
|
||
|
||
当前:
|
||
|
||
```cmake
|
||
Renderive_render_3D
|
||
```
|
||
|
||
只检查:
|
||
|
||
```cmake
|
||
TARGET
|
||
Renderive_Kernel
|
||
```
|
||
|
||
但没有像 Render2D 那样真正链接。
|
||
|
||
改成:
|
||
|
||
```cmake
|
||
target_link_libraries(Renderive_render_3D
|
||
PUBLIC
|
||
Renderive_Kernel
|
||
...
|
||
)
|
||
```
|
||
|
||
除此之外不要调整现有 Datoviz CMake 布局。
|
||
|
||
---
|
||
|
||
# 3. 不使用现有 Kernel Scene3D_Context
|
||
|
||
当前:
|
||
|
||
```text
|
||
Kernel/Scene3D_Context
|
||
↓
|
||
Triple_State_Strategy
|
||
↓
|
||
Scene_Base
|
||
↓
|
||
Renderable DAG
|
||
↓
|
||
Frame_Render_Snapshot
|
||
```
|
||
|
||
Render3D 不走这条链。
|
||
|
||
原因:
|
||
|
||
```text
|
||
Datoviz Scene/Visual
|
||
不是 Kernel Renderable
|
||
|
||
3D 不需要再套 Renderable DAG
|
||
|
||
当前 Scene3D_Context 使用 Triple_State,
|
||
也不符合这次要求的 Double_State 模型
|
||
```
|
||
|
||
只复用 Kernel 的基础机制:
|
||
|
||
```text
|
||
Double_State_Strategy
|
||
Low_Latency_Strategy
|
||
Manual_Refresh_Strategy
|
||
Flow_Refresh_Strategy
|
||
Property_Builder
|
||
Validated_Value / Validator
|
||
renderive_Owner
|
||
Atomic_Spin_Mutex
|
||
Observer
|
||
```
|
||
|
||
不要改 `Scene3D_Context` 做兼容层。
|
||
|
||
---
|
||
|
||
# 4. 建 Render3D 自己的对象层
|
||
|
||
目录:
|
||
|
||
```text
|
||
render_3D/render_3D/
|
||
├── base/
|
||
│ ├── Object_Id.h
|
||
│ └── Object_Id.cpp
|
||
├── camera/
|
||
│ ├── Camera.h
|
||
│ ├── Camera.cpp
|
||
│ └── Camera_p.h
|
||
├── material/
|
||
│ ├── Material.h
|
||
│ ├── Material.cpp
|
||
│ └── Material_p.h
|
||
├── texture/
|
||
│ ├── Texture.h
|
||
│ ├── Texture.cpp
|
||
│ └── Texture_p.h
|
||
├── mesh/
|
||
│ ├── Mesh.h
|
||
│ ├── Mesh.cpp
|
||
│ └── Mesh_p.h
|
||
└── scene/
|
||
├── Scene.h
|
||
├── Scene.cpp
|
||
├── Scene_p.h
|
||
└── Scene_Frame.h
|
||
```
|
||
|
||
第一批只做:
|
||
|
||
```text
|
||
Scene
|
||
Camera
|
||
Mesh
|
||
Material
|
||
Texture
|
||
```
|
||
|
||
不要一次把 Datoviz 所有 Visual 包出去。
|
||
|
||
---
|
||
|
||
# 5. PImpl 规则
|
||
|
||
风格参考 Render2D。
|
||
|
||
例如:
|
||
|
||
```text
|
||
Mesh
|
||
└── unique_ptr<Mesh::Impl>
|
||
|
||
Material
|
||
└── unique_ptr<Material::Impl>
|
||
```
|
||
|
||
严格规定:
|
||
|
||
```text
|
||
public Mesh → Mesh::Impl 允许
|
||
|
||
Scene::Impl → Mesh::Impl 允许
|
||
Mesh::Impl → Material::Impl 允许
|
||
Material::Impl → Texture::Impl 允许
|
||
|
||
Scene::Impl → Mesh public API 禁止
|
||
Mesh::Impl → Material public API 禁止
|
||
backend → 任意 public API 禁止
|
||
```
|
||
|
||
内部需要访问别的对象时通过:
|
||
|
||
```text
|
||
friend
|
||
+
|
||
d_func<Impl>()
|
||
```
|
||
|
||
完成。
|
||
|
||
不要为了访问内部状态绕回公开 getter。
|
||
|
||
---
|
||
|
||
# 6. 每个对象使用 Double_State_Strategy
|
||
|
||
例如逻辑:
|
||
|
||
```text
|
||
Mesh::Impl
|
||
↓
|
||
Double_State_Strategy<Mesh_State>
|
||
```
|
||
|
||
状态分成:
|
||
|
||
```text
|
||
cache
|
||
render
|
||
scratch
|
||
```
|
||
|
||
setter:
|
||
|
||
```text
|
||
短锁
|
||
↓
|
||
修改 cache
|
||
```
|
||
|
||
prepare:
|
||
|
||
```text
|
||
publish
|
||
↓
|
||
cache → scratch
|
||
↓
|
||
交换 render
|
||
```
|
||
|
||
交换后不再持锁。
|
||
|
||
Camera、Material、Texture 同样处理。
|
||
|
||
---
|
||
|
||
# 7. 大数据禁止跟着 Double State 整体复制
|
||
|
||
例如:
|
||
|
||
```text
|
||
Mesh_State
|
||
├── transform
|
||
├── visible
|
||
├── material_id
|
||
└── shared_ptr<const Geometry_Data>
|
||
```
|
||
|
||
`Geometry_Data`:
|
||
|
||
```text
|
||
vertices
|
||
indices
|
||
normals
|
||
uv
|
||
```
|
||
|
||
Texture 同样:
|
||
|
||
```text
|
||
Texture_State
|
||
└── shared_ptr<const Image_Data>
|
||
```
|
||
|
||
所以 Double State publish 只复制:
|
||
|
||
```text
|
||
matrix
|
||
enum
|
||
id
|
||
shared_ptr
|
||
```
|
||
|
||
禁止每帧复制整个 vertex/image 数据。
|
||
|
||
---
|
||
|
||
# 8. Scene 结构不要做 Snapshot
|
||
|
||
本阶段 **不引入 Snapshot**。
|
||
|
||
Scene 内部维护:
|
||
|
||
```text
|
||
attached meshes
|
||
active camera
|
||
pending structure changes
|
||
```
|
||
|
||
结构修改统一通过一个内部 mutation/edit 对象完成,不增加大量:
|
||
|
||
```text
|
||
apply_attach_xxx
|
||
apply_detach_xxx
|
||
apply_set_xxx
|
||
```
|
||
|
||
这种函数。
|
||
|
||
例如语义:
|
||
|
||
```cpp
|
||
scene.update([](Scene_Edit& edit) {
|
||
edit.attach(mesh);
|
||
edit.detach(old_mesh);
|
||
edit.set_camera(camera);
|
||
});
|
||
```
|
||
|
||
`Scene_Edit` 直接操作 `Scene::Impl` 的结构状态。
|
||
|
||
---
|
||
|
||
# 9. Scene 所有权
|
||
|
||
使用:
|
||
|
||
```text
|
||
renderive_Owner
|
||
```
|
||
|
||
规则:
|
||
|
||
```text
|
||
外部 owner
|
||
+
|
||
Scene attach 后持有自己的 owner
|
||
```
|
||
|
||
所以:
|
||
|
||
```text
|
||
external mesh.reset()
|
||
```
|
||
|
||
但 Mesh 仍 attach:
|
||
|
||
```text
|
||
Mesh 继续存活
|
||
```
|
||
|
||
detach 后:
|
||
|
||
```text
|
||
Scene 释放自己的 owner
|
||
```
|
||
|
||
Datoviz backend **绝不持有 public Mesh/Material/Texture 指针**。
|
||
|
||
---
|
||
|
||
# 10. 定义 Scene3D_Frame_Data
|
||
|
||
放:
|
||
|
||
```text
|
||
render_3D/render_3D/scene/Scene_Frame.h
|
||
```
|
||
|
||
它是 Kernel frame strategy 传递的数据。
|
||
|
||
内容只包括:
|
||
|
||
```text
|
||
scene state
|
||
object updates
|
||
structure updates
|
||
immutable resource handles
|
||
```
|
||
|
||
例如逻辑:
|
||
|
||
```text
|
||
Scene3D_Frame_Data
|
||
├── camera
|
||
├── mesh_updates
|
||
├── material_updates
|
||
├── texture_updates
|
||
├── attach_updates
|
||
└── detach_updates
|
||
```
|
||
|
||
不要命名成 Snapshot。
|
||
|
||
它是:
|
||
|
||
> Kernel 帧槽中的稳定交接数据。
|
||
|
||
---
|
||
|
||
# 11. 帧生成边界
|
||
|
||
`prepare_frame()` 固定流程:
|
||
|
||
```text
|
||
acquire_painter()
|
||
↓
|
||
publish Scene Double State
|
||
↓
|
||
publish Camera/Mesh/Material/Texture Double State
|
||
↓
|
||
比较 revision
|
||
↓
|
||
把发生变化的稳定状态写入 Scene3D_Frame_Data
|
||
↓
|
||
写入 attach/detach 等结构变化
|
||
↓
|
||
Painter_Lease 结束
|
||
↓
|
||
frame publish
|
||
```
|
||
|
||
一旦 Painter Lease 结束:
|
||
|
||
```text
|
||
Scene3D_Frame_Data
|
||
```
|
||
|
||
必须完全独立于:
|
||
|
||
```text
|
||
public object
|
||
Double_State 内部地址
|
||
Scene::Impl 可变结构
|
||
```
|
||
|
||
---
|
||
|
||
# 12. Scene3D 固定关闭帧覆盖
|
||
|
||
定义类似 Render2D:
|
||
|
||
```text
|
||
Manual_Frame_Control
|
||
Low_Latency_Frame_Control
|
||
Playback_Frame_Control
|
||
```
|
||
|
||
但是创建 3D LowLatency 时:
|
||
|
||
```cpp
|
||
configuration.replace_pending_frame = false;
|
||
```
|
||
|
||
Manual 同样:
|
||
|
||
```cpp
|
||
configuration.replace_pending_frame = false;
|
||
```
|
||
|
||
代码旁必须写注释:
|
||
|
||
```text
|
||
Scene3D 的 published frame 可能包含 Datoviz retained backend
|
||
的有序资源/状态转换,因此发布后的帧禁止被后续帧自动替换。
|
||
```
|
||
|
||
这个值不提供 public setter。
|
||
|
||
Flow 不需要特殊配置。
|
||
|
||
---
|
||
|
||
# 13. Scene3D 不暴露 discard
|
||
|
||
Kernel generic strategy 保留:
|
||
|
||
```text
|
||
discard_pending_frame()
|
||
```
|
||
|
||
但 Scene3D public API 第一版不要暴露它。
|
||
|
||
否则:
|
||
|
||
```text
|
||
attach/update frame
|
||
↓
|
||
显式 discard
|
||
↓
|
||
Datoviz backend 没收到
|
||
```
|
||
|
||
仍然会破坏 retained state 顺序。
|
||
|
||
---
|
||
|
||
# 14. 建 Datoviz adapter/backend
|
||
|
||
新目录:
|
||
|
||
```text
|
||
render_3D/render_3D/backend/datoviz/
|
||
├── Datoviz_Renderer.h
|
||
├── Datoviz_Renderer.cpp
|
||
├── Datoviz_Resources.h
|
||
└── Datoviz_Frame.h
|
||
```
|
||
|
||
这里才写 Render3D ↔ Datoviz 的 C++ 适配。
|
||
|
||
不要继续修改:
|
||
|
||
```text
|
||
render_3D/datoviz/
|
||
```
|
||
|
||
除非发现真正的 Datoviz 移植 bug。
|
||
|
||
---
|
||
|
||
# 15. Datoviz backend 所有权
|
||
|
||
统一:
|
||
|
||
```text
|
||
Datoviz_Renderer
|
||
├── DvzScene
|
||
├── DvzFigure
|
||
├── DvzPanel
|
||
├── DvzDrp2Runtime
|
||
├── GPU/VMA context
|
||
├── object binding map
|
||
└── GPU frame slots
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
Mesh Object_Id
|
||
↓
|
||
Backend_Mesh
|
||
↓
|
||
DvzVisual*
|
||
```
|
||
|
||
这个映射属于:
|
||
|
||
```text
|
||
Datoviz_Renderer
|
||
```
|
||
|
||
不属于:
|
||
|
||
```text
|
||
Mesh::Impl
|
||
```
|
||
|
||
因此:
|
||
|
||
```text
|
||
Mesh::~Mesh()
|
||
```
|
||
|
||
禁止直接调用 Datoviz/Vulkan destroy。
|
||
|
||
---
|
||
|
||
# 16. Renderer 热路径完全脱离对象层
|
||
|
||
固定:
|
||
|
||
```text
|
||
acquire_renderer()
|
||
↓
|
||
拿到 Scene3D_Frame_Data
|
||
↓
|
||
从这里开始禁止访问 public object
|
||
↓
|
||
也禁止访问 Mesh::Impl / Material::Impl
|
||
↓
|
||
Datoviz_Renderer::render(frame)
|
||
```
|
||
|
||
也就是:
|
||
|
||
```text
|
||
双缓冲 publish
|
||
↓
|
||
Frame_Data 固化
|
||
====================
|
||
下面开始无模型锁区域
|
||
====================
|
||
Datoviz state update
|
||
DRP2 emit
|
||
command recording
|
||
submit
|
||
```
|
||
|
||
这条边界必须写测试。
|
||
|
||
---
|
||
|
||
# 17. Datoviz retained state 只在 Renderer 线程修改
|
||
|
||
禁止:
|
||
|
||
```text
|
||
应用线程 → dvz_visual_xxx
|
||
Painter → dvz_scene_xxx
|
||
Mesh::Impl → dvz_buffer_xxx
|
||
```
|
||
|
||
只有:
|
||
|
||
```text
|
||
Datoviz_Renderer thread
|
||
```
|
||
|
||
允许修改:
|
||
|
||
```text
|
||
DvzScene
|
||
DvzVisual
|
||
DvzFramePlanEmitter
|
||
DvzDrp2Runtime
|
||
Vulkan resource
|
||
```
|
||
|
||
这样 Datoviz 内部不用额外补大量锁。
|
||
|
||
---
|
||
|
||
# 18. GPU 帧槽独立于 Kernel 帧槽
|
||
|
||
不要做:
|
||
|
||
```text
|
||
Kernel frame[0] == GPU frame[0]
|
||
```
|
||
|
||
单独建立:
|
||
|
||
```text
|
||
GPU_Frame_Slot
|
||
├── command buffer
|
||
├── fence
|
||
├── render image
|
||
└── staging/readback resources
|
||
```
|
||
|
||
Renderer:
|
||
|
||
```text
|
||
选择下一 GPU slot
|
||
↓
|
||
仅等待该 slot fence
|
||
↓
|
||
reset
|
||
↓
|
||
record
|
||
↓
|
||
submit
|
||
```
|
||
|
||
禁止主渲染路径每帧:
|
||
|
||
```text
|
||
vkDeviceWaitIdle()
|
||
dvz_device_wait()
|
||
queue-wide wait
|
||
```
|
||
|
||
---
|
||
|
||
# 19. 第一条真实功能只打通 Mesh
|
||
|
||
不要同时做所有 Datoviz Visual。
|
||
|
||
第一条完整链:
|
||
|
||
```text
|
||
Scene
|
||
↓
|
||
Camera
|
||
↓
|
||
Mesh
|
||
↓
|
||
Material
|
||
↓
|
||
Geometry_Data
|
||
↓
|
||
Scene3D_Frame_Data
|
||
↓
|
||
Datoviz_Renderer
|
||
↓
|
||
Dvz mesh visual
|
||
↓
|
||
DRP2
|
||
↓
|
||
Vulkan offscreen image
|
||
```
|
||
|
||
这条跑通以后再加:
|
||
|
||
```text
|
||
Texture
|
||
Text
|
||
Point
|
||
Sphere
|
||
Primitive
|
||
Volume
|
||
Transparency
|
||
Picking
|
||
```
|
||
|
||
---
|
||
|
||
# 20. Builder
|
||
|
||
对象 Builder 复用 Kernel:
|
||
|
||
```text
|
||
Property_Builder
|
||
Validator
|
||
Validated_Value
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
Mesh_Properties
|
||
Mesh_Builder
|
||
|
||
Material_Properties
|
||
Material_Builder
|
||
```
|
||
|
||
Builder 只负责:
|
||
|
||
```text
|
||
验证 properties
|
||
↓
|
||
构造 public object + Impl
|
||
```
|
||
|
||
禁止 Builder:
|
||
|
||
```text
|
||
创建 DvzVisual
|
||
创建 VkBuffer
|
||
访问 Datoviz_Renderer
|
||
attach GPU resource
|
||
```
|
||
|
||
GPU 创建只能等 frame 到 renderer。
|
||
|
||
---
|
||
|
||
# 21. 必须补的 Render3D 测试
|
||
|
||
CPU 测试:
|
||
|
||
```text
|
||
Mesh setter 只修改 cache
|
||
|
||
publish 后 render state 稳定
|
||
|
||
publish 后应用线程继续修改 cache
|
||
旧 Frame_Data 不变化
|
||
|
||
Impl → Impl 读取正确
|
||
|
||
Scene attach 持有 owner
|
||
|
||
external owner reset 后 attached object 存活
|
||
|
||
detach 后 Scene owner 释放
|
||
|
||
Frame_Data 不保存 public object 指针
|
||
|
||
Frame_Data 不保存 Double_State 内部引用
|
||
```
|
||
|
||
帧测试:
|
||
|
||
```text
|
||
LowLatency Scene3D 不出现 abandoned frame
|
||
|
||
A published 未消费
|
||
→ B 不能覆盖 A
|
||
|
||
renderer acquire A
|
||
→ painter 才允许继续 B
|
||
```
|
||
|
||
并发测试:
|
||
|
||
```text
|
||
Renderer 正在处理 Frame A
|
||
同时应用线程修改 Mesh cache
|
||
→ 不互相持有长期锁
|
||
```
|
||
|
||
Backend 测试:
|
||
|
||
```text
|
||
attach Mesh
|
||
→ 创建一次 DvzVisual
|
||
|
||
修改 Mesh
|
||
→ 更新同一个 backend object
|
||
|
||
detach Mesh
|
||
→ 销毁一次
|
||
|
||
public Mesh 销毁
|
||
→ 不直接触发 Datoviz GPU 操作
|
||
```
|
||
|
||
---
|
||
|
||
# 22. 完成标准
|
||
|
||
这一阶段完成时必须满足:
|
||
|
||
```text
|
||
Datoviz 源码保持独立移植目录
|
||
Kernel 不知道 Datoviz
|
||
public Render3D API 不出现 Dvz*
|
||
Scene3D 不使用 Renderable DAG
|
||
Scene3D 不使用 Frame_Render_Snapshot
|
||
对象状态使用 Double_State
|
||
LowLatency/Manual 3D 帧不自动覆盖
|
||
Frame publish 后 renderer 不访问 public/Impl
|
||
Datoviz 全部由 Renderer 线程拥有
|
||
CPU 修改下一帧与 GPU 渲染上一帧可以并行
|
||
主路径不存在 device-wide wait
|
||
```
|
||
|
||
执行顺序严格按:
|
||
|
||
```text
|
||
1 Kernel replacement 配置 + 测试
|
||
2 Render3D 链接 Kernel
|
||
3 PImpl + Object State
|
||
4 Scene ownership / mutation
|
||
5 Scene3D_Frame_Data
|
||
6 Basic_Scene3D + 三种 Frame_Control
|
||
7 Datoviz_Renderer
|
||
8 Mesh 完整链路
|
||
9 并发/所有权测试
|
||
10 再扩展其他 Datoviz visual
|
||
```
|
||
|
||
**本轮不引入任何 Snapshot。** 当前稳定帧交接只需要轻量 `Scene3D_Frame_Data + immutable shared payload`,没有理由复制整个
|
||
Scene 或 Datoviz backend 状态。
|