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