Renderive Scene 与统一 Render DAG
Scene 类型层次
Scene 不再使用维度枚举。运行时类型关系为:
Scene_Base
├── Scene_2D_Base
└── Scene_3D_Base
Scene2D_Context 通过 Triple_State_Strategy<Scene_2D_Base, ...> 继承 Scene_2D_Base,Scene3D_Context 通过 Triple_State_Strategy<Scene_3D_Base, ...> 继承 Scene_3D_Base。调用方通过类型系统、概念或 dynamic_cast 区分 2D 与 3D Scene。
Scene 三缓冲状态
Scene2D_Context 与 Scene3D_Context 都公开类型别名 Scene_State_Strategy。状态访问使用基类名限定,避免多继承时成员歧义:
scene.Scene_State_Strategy::set<&Scene_State::value>(value);
scene.Scene_State_Strategy::publish();
三缓冲分别承担正在渲染、已发布待获取、前台写入三种职责。render() 提交任务前调用 acquire_render_state(),后台帧获得稳定的状态版本。渲染状态不提供公共读取接口,只能在 Renderable 的 paint 调用栈中通过 Render_State_View 读取当前帧的稳定引用。
Scene 观察者
Scene 状态观察者记录状态缓存修改、发布和渲染获取事件。Scene 生命周期观察者记录:
render_submittedrender_startedrender_completedrender_failed
观察器和时间源沿用 Observer_State。
单一 Renderable
Renderable 不再拆分为 Scene 持有缓存和独立持有缓存两类。所有对象统一继承 Renderable_Base:
struct Spectrum : Renderable_Base {
explicit Spectrum(Scene_Base& scene)
: Renderable_Base(scene, {.cache_enabled = true}) {}
};
Renderable_Configuration::cache_enabled 只控制渲染结果是否跨帧复用。默认关闭,以保持每次 Scene 提交都重新渲染的旧语义。Scene2D 仍为每个 Renderable 维护颜色缓存,以支持渲染依赖顺序和显示合成顺序相互独立。
缓存开启后,Prepare Buffer 与 Paint Buffer 分别按自己的 revision 判定有效性。Prepare 失效会使 Paint 失效,Paint-only 变化不会反向污染 Prepare;命中的阶段在编译本帧 Render Plan 时直接裁剪,不生成空任务。缓存关闭时两个阶段每帧执行。configuration() 返回线程安全的配置快照,配置修改统一通过 Scene_Base::set_renderable_configuration() 完成。
Renderable 内部 Render DAG
每个 Renderable 通过 build_prepare_graph(Renderable_Graph_Builder&) 与 build_paint_graph(Renderable_Graph_Builder&) 向同一个内部图写入任务。接口只暴露 Renderive 的任务句柄,不暴露 tf::Taskflow、tf::Task 或任何 Taskflow 头文件:
void build_prepare_graph(Renderable_Graph_Builder& builder) override {
auto prepare = builder.emplace(
"prepare", "Prepare", Render_Node_Kind::prepare,
[this](const Scene_Render_Context&) { prepare_data(); });
auto chunk = builder.emplace(
"chunk:0", "Chunk 0 Prepare", Render_Node_Kind::prepare,
[this](const Scene_Render_Context&) { prepare_chunk(0); });
builder.precede(prepare, chunk);
}
void build_paint_graph(Renderable_Graph_Builder& builder) override {
auto paint = builder.emplace(
"paint", "Paint", Render_Node_Kind::paint,
[this](const Scene_Render_Context&) { paint_buffer(); });
builder.precede(builder.find("chunk:0"), paint);
}
简单 Renderable 的默认 Prepare 节点调用 prepare();需要 Paint 的类型显式提供 Paint 图。内部图快照不是公共 API,Scene 在构图同步边界内获取它。rebuild_render_graph() 与内部快照读取由 Renderable 串行化,已经提交的帧持有自己的不可变执行绑定。
外部依赖与总图组合
依赖树只表达跨 Renderable 的 Prepare 数据依赖;显示树只表达 Composite 顺序。关系修改统一通过 Scene 完成,例如 scene.set_dependency_parent(child, parent),不会绕过 Scene 的渲染同步边界。每次后台渲染会:
- 构造或复用各 Renderable 的内部图描述。
- 根据 Prepare/Paint 缓存有效性裁剪节点。
- 将 dependency 连接为
parent Prepare -> child Prepare。 - 将各 Renderable 的 Paint 连接到自己的 Composite,并按显示树串联 Composite。
- 发布带版本的唯一 Render Plan。
- 按
1 Render Node = 1 Taskflow Task、1 Render Edge = 1 Taskflow dependency编译一个 Taskflow。
Render Plan 只保存拓扑和最小节点元数据;当前帧的执行闭包由 Render_Task 临时持有,因此历史 Plan 不捕获 Renderable 指针。Taskflow 只在 Scene_Base.cpp 中包含。Scene_Base 通过内部执行上下文访问进程内共享的 tf::Executor,Executor 跨 Scene、跨帧复用。Renderive_Kernel 对 Taskflow 使用 PRIVATE 链接,外部头文件不会受到 Taskflow 宏、类型或包含链污染。
显示树与依赖树
显示树只决定 Composite 顺序,依赖树只约束 Prepare。两棵树保持独立,因此轴 Prepare 可以先于曲线 Prepare,而各自 Paint 仍可并行,轴最后 Composite。节点对象不作为公开拓扑读接口;调用方通过 topology_snapshot() 获取线程安全关系快照,父子关系只通过 set_display_parent() 和 set_dependency_parent() 修改。参与显示树或依赖树的 Renderable 必须先 attach 到所属 Scene,避免拓扑引用未被 Scene 持有的对象。
测试入口
RENDERIVE_BUILD_TESTS=ON 时遍历 tests,为每个测试源文件生成独立 target 并注册到 CTest;RENDERIVE_BUILD_TESTS=OFF 时只构建 Renderive_Kernel 库,不编译测试源文件。
线程模型
Scene、Frame Strategy、Real-Time-Data、State Strategy、Render DAG 与 Frame Capture 的允许并发组合、生命周期边界和对应测试统一记录在 threading.md。多线程语义测试作为普通测试套件的一部分构建;支持的平台应额外用 TSan 执行同一套测试。
Taskflow 查找
Kernel/CMakeLists.txt 通过 find_package(Taskflow CONFIG REQUIRED) 查找 Taskflow。Kernel 不提供替代执行器,配置时必须提供真实 Taskflow 包。
Flow 并发队列
Flow_Refresh_Strategy 当前使用 std::pmr::deque<Memory_Resource_Unique_Ptr<Frame>> 保存待渲染帧,不引入 Boost.Lockfree 或其他新增队列依赖。多个 painter 可以并发创建 Frame,入队由 state_mutex_ 串行化;多个 renderer 调用也安全,但实际消费由 render_mutex_ 串行化,因此每个 Frame 最多被消费一次。多 producer 下的全局顺序以实际完成入队的线性化顺序为准。
Flow 的 observer 允许同线程重入 Observer_State;renderer observer 中再次申请同一个 Flow renderer 会立即得到空 lease,不会等待当前 renderer lease 自身释放。完整线程契约和多线程压力测试见 threading.md。
实时数据附件所有权
With_Real_Time_Data 接收 std::shared_ptr<Data>,Attach_Real_Time_Data 在自身生命周期内持有这些实时数据源,避免 Renderable 仍存活时外部提前销毁 Data 产生悬空指针:
auto data = std::make_shared<Latest_Real_Time_Data<int>>();
auto renderable = std::make_shared<Attach_Real_Time_Data<My_Renderable, Latest_Real_Time_Data<int>>>(
With_Real_Time_Data(data), scene);
Scene 内存域
Scene_Base 接受外部 std::pmr::memory_resource 作为上游资源。Scene 内部建立 std::pmr::synchronized_pool_resource,所有可能跨线程访问的持久容器都使用该线程安全内存域:
std::pmr::unsynchronized_pool_resource application_resource;
Scene2D_Context<> scene(application_resource);
外部资源决定最终的上游分配机制。Scene 内部的同步池负责将来自调用线程、后台 Scene 线程和 Renderable 构图路径的分配串行化。外部资源必须比 Scene 以及由 Scene 工厂创建后仍存活的 Renderable 更长寿。
可通过以下接口访问两个层级:
scene.upstream_memory_resource();
scene.memory_resource();
upstream_memory_resource() 返回构造时传入的资源;memory_resource() 返回 Scene 实际使用的线程安全同步池。
单次渲染的 Taskflow、节点索引、执行绑定和任务句柄只由当前 Render_Task 持有,帧完成后释放,不进入 Render Plan 历史。
Renderable 两种构造模式
Scene 工厂模式同时控制 Renderable 对象、shared_ptr 控制块以及 Renderable 内部关系节点分配:
Scene2D_Context<> scene(memory_resource);
auto renderable = scene.make_renderable<Spectrum_Renderable>(configuration);
scene.attach_renderable(renderable);
工厂只构造对象,不自动挂载,attach_renderable() 的原有语义不变。工厂分配器持有 Scene 内存域的共享所有权,因此 Renderable 析构时内存域仍然有效。
独立构造模式继续保留:
auto renderable = std::make_shared<Spectrum_Renderable>(scene, configuration);
Spectrum_Renderable stack_renderable(scene, configuration);
独立模式下,Renderable 对象本身和 shared_ptr 控制块由调用方选择的机制分配;关系节点仍使用所属 Scene 的内存域。Renderable 可以晚于 Scene 析构以完成自身释放;Scene 销毁后实时数据绑定自动失效,继续调用 scene() 会抛出 std::logic_error,不会返回悬空 Scene 引用。
独立 allocator-aware 组件
以下组件可脱离 Scene 独立注入资源:
Multiway_NodeRecording_Color_CacheFlow_Refresh_StrategyHistory_Real_Time_DataProperty_Builder::build_with_resource()Property_Builder::build_unique_with_resource()
History_Real_Time_Data 的时间戳容器始终使用传入资源。历史值容器只有在 Container 本身支持 PMR allocator 时才使用该资源:
History_Real_Time_Data<int, std::pmr::vector<int>> history(memory_resource);
不受 Scene PMR 控制的分配
以下内存不属于 Renderive 自己的 allocator-aware 场景域:
- Taskflow 4.1.0 内部任务节点、执行队列和 executor 工作资源
std::function超出小对象优化后的内部存储std::thread的系统线程对象和线程栈- 异常运行时对象
- 用户自定义 State、Frame、Observer、Renderable 成员内部自行使用的非 PMR 容器
- 第三方渲染后端内部资源
Scene PMR 的语义是控制 Renderive 明确实现为 allocator-aware 的核心对象,不承诺接管第三方库和用户类型的全部动态分配。