Files
2026-08-14 06:22:14 +08:00

937 lines
13 KiB
Markdown
Raw Permalink 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.
可以,下面这份就按 **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 状态。