2D 绘图缓存节点

This commit is contained in:
2026-08-21 10:16:16 +08:00
parent 26a89335b4
commit 855e25981d
55 changed files with 772 additions and 488 deletions
@@ -1,4 +1,4 @@
#include "Render_Scene_3D.hpp"
#include "Render_Scene_3D.hpp" /* 三维异步提交阶段实现。 */
namespace aethera::render_3d {
bool Render_Scene_3D::Prop::operator==(const Prop&) const = default;
bool Render_Scene_3D::State::operator==(const State&) const = default;
@@ -6,8 +6,10 @@
#include <functional>
#include <memory>
namespace aethera::render_3d {
/* 执行 3D Renderable 图,并把 Paint 阶段无等待地提交给 Datoviz 渲染域。 */
struct Render_Scene_3D : Def<Render_Scene_3D, Scene> {
/* 三维 Prepare 完成后的异步后端提交阶段标签。 */
struct Submit_Tag {};
/* 执行共有 Prepare 图,并把三维 Submit 阶段无等待地提交给 Datoviz 渲染域。 */
struct Render_Scene_3D : Def<Render_Scene_3D, Scene, Dependency_Graph_Type<Submit_Tag, Renderable>> {
struct Prop : Prev_Prop {
Extent viewport{560, 320}; /* 离屏渲染目标尺寸,单位为像素。 */
Linear_Color clear_color{}; /* 每帧开始时写入的线性背景色。 */
@@ -29,7 +31,7 @@ struct Render_Scene_3D : Def<Render_Scene_3D, Scene> {
Root* visual{}; /* 不拥有的唯一 Visual;生命周期必须覆盖 Scene。 */
Visual_Family visual_family{Visual_Family::point}; /* Visual 编译期 Spec 对应的后端 family。 */
void (*bind_visual)(Root*, std::shared_ptr<void>){}; /* 把 Scene 拥有的弱提交上下文绑定到最终 Visual Private。 */
Attach_Visual attach_visual{}; /* 以具体 Visual 类型安装 Prepare、Paint 和 Prop 依赖。 */
Attach_Visual attach_visual{}; /* 以具体 Visual 类型安装 Prepare 与三维 Submit 节点。 */
std::uint32_t gpu_index{}; /* Datoviz 使用的物理 GPU 下标。 */
bool validation_enabled{}; /* 是否启用 Vulkan 验证。 */
};
@@ -27,19 +27,19 @@ struct Render_Scene_3D::Private : Prev_Private {
template <Attached Object> void initialize_backend(Object* object, std::uint32_t gpu_index, bool validation_enabled, Visual_Family visual_family);
/* CRTP 覆盖:绑定 Scene 机制和 Render_Scene_3D 公开薄壳。 */
template <Attached Object> void bind_private_crtp(Object* object);
/* CRTP 覆盖:执行 Kernel Scene Taskflow;其 Paint 子图只负责异步入队。 */
/* CRTP 覆盖:执行 Kernel Prepare Taskflow,再运行只负责异步入队的三维 Submit 阶段。 */
template <Attached Object, typename Callback> void process(Object* object, Callback&& callback) requires std::invocable<Callback>;
template <Attached Object> [[nodiscard]] static const Dispatch& dispatch_for();
};
template <typename Object> template <Attached Visual_Object>
Render_Scene_3D::Builder<Object>::Builder(Visual_Object* visual_value, std::uint32_t gpu_index_value, bool validation_enabled_value) : Base(), visual(visual_value), visual_family(Visual_Object::Attached_Object::Specification::family), gpu_index(gpu_index_value), validation_enabled(validation_enabled_value) {
bind_visual = [](Root* root, std::shared_ptr<void> context) { auto* object = static_cast<Visual_Object*>(root); Base::private_access(object).template get<typename Visual_Object::Attached_Object::Base_Tag>().bind_paint_target(std::move(context), [](void* raw_context, const detail::Prepared_Visual& prepared) { auto& submission = *static_cast<detail::Scene_Paint_Context<Object>*>(raw_context); const auto& prop = submission.scene->template read_prop<Render_Scene_3D::Base_Tag>(); submission.backend->render(prepared, detail::Scene_3D_Parameters{prop.viewport, prop.clear_color}); }); };
attach_visual = [](Object* scene, Root* root) { auto* object = static_cast<Visual_Object*>(root); return scene->template edit_dependency_graph<Prepare_Data_Tag, Paint_Tag>([&](auto& prepare, auto& paint) { prepare.add(object); paint.add(object); prepare.template add_prop_dependency<Render_Scene_3D::Base_Tag>(object, scene); }); };
attach_visual = [](Object* scene, Root* root) { auto* object = static_cast<Visual_Object*>(root); return scene->template edit_dependency_graph<Prepare_Data_Tag, Submit_Tag>([&](auto& prepare, auto& submit) { prepare.add(object); submit.add(object); }); };
}
template <typename Object>
std::expected<std::unique_ptr<Object>, Dependency_Graph_Error> Render_Scene_3D::Builder<Object>::build() { if (!visual || !bind_visual || !attach_visual) throw std::invalid_argument("Render_Scene_3D requires one Visual"); auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto scene = std::move(result).value(); auto& private_data = Base::private_access(scene.get()).template get<Render_Scene_3D::Base_Tag>(); private_data.initialize_backend(scene.get(), gpu_index, validation_enabled, visual_family); bind_visual(visual, private_data.paint_context); auto graph_result = attach_visual(scene.get(), visual); if (!graph_result) return std::unexpected(graph_result.error()); return std::move(scene); }
template <Attached Object> void Render_Scene_3D::Private::initialize_backend(Object* object, std::uint32_t gpu_index, bool validation_enabled, Visual_Family visual_family) { const auto& prop = object->template read_prop<Render_Scene_3D::Base_Tag>(); backend = std::make_shared<detail::Async_Render_Backend>(gpu_index, validation_enabled, visual_family, detail::Scene_3D_Parameters{prop.viewport, prop.clear_color}); paint_context = std::make_shared<detail::Scene_Paint_Context<Object>>(detail::Scene_Paint_Context<Object>{backend, object}); }
template <Attached Object, typename Callback> void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requires std::invocable<Callback> { const auto& prop = object->template read_prop<Render_Scene_3D::Base_Tag>(); if (!prop.view_active || prop.viewport.empty() || !backend || !backend->available()) return; Prev_Private::process(object, [](const Scene::Private::Result&) {}); std::invoke(std::forward<Callback>(callback)); }
template <Attached Object, typename Callback> void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requires std::invocable<Callback> { const auto& prop = object->template read_prop<Render_Scene_3D::Base_Tag>(); if (!prop.view_active || prop.viewport.empty() || !backend || !backend->available()) return; Prev_Private::process(object, [&](const Scene::Private::Result&) { const auto prepare = object->template current_dependency_graph<Prepare_Data_Tag>(); prepare.for_each_bound([](Renderable* renderable, Renderable::Private& data) { if (data.dispatch->state.get(renderable)->prepare_executed) renderable->template mark_dirty<Submit_Tag>(); }); const auto submit = object->template current_dependency_graph<Submit_Tag>(); const auto result = submit.for_each_topological_view([&](const auto& view, const Dependency_Graph::Node& node) { auto* data = view.private_data(node); if (!data) return; Root* root = node.object; auto* dispatch = data->dispatch; auto& state = *dispatch->state.get(root); state.paint_graph_rebuilt = false; state.paint_execution_time_ns = 0; const bool dirty = root->template dirty<Submit_Tag>(); state.paint_dirty = dirty; state.paint_executed = dispatch->paint.predicate(root, dirty); if (!state.paint_executed) return; if (dispatch->paint.builder) { if (!data->paint_graph) data->paint_graph = std::make_unique<tf::Taskflow>(); const bool rebuild = dispatch->paint.rebuild_predicate(root); if (!data->paint_graph_built || rebuild) { *data->paint_graph = dispatch->paint.builder(root); data->paint_graph_built = true; state.paint_graph_rebuilt = true; } state.paint_task_count = data->paint_graph->num_tasks(); if (!data->paint_graph->empty()) aethera::detail::run_taskflow(*data->paint_graph); } else { state.paint_task_count = dispatch->paint.run ? 1 : 0; if (dispatch->paint.run) dispatch->paint.run(root); } root->template take_dirty<Submit_Tag>(); dispatch->state.notify(root); }); if (!result) throw std::logic_error("render scene submit graph became invalid during submission"); }); std::invoke(std::forward<Callback>(callback)); }
template <Attached Object> const Render_Scene_3D::Private::Dispatch& Render_Scene_3D::Private::dispatch_for() { static const Dispatch value{[](Root* root) { auto* object = static_cast<Object*>(root); object->process([] {}); }, [](Root* root, Frame_Callback callback) { auto* object = static_cast<Object*>(root); auto& data = static_cast<typename Object::Private&>(*object->d); if (data.backend) data.backend->set_frame_callback(std::move(callback)); }, [](Root* root, const Event& event) { auto* object = static_cast<Object*>(root); auto& data = static_cast<typename Object::Private&>(*object->d); if (!data.backend) return Dispatch_Event_Result::backend_unavailable; const auto viewport = object->template read_prop<Render_Scene_3D::Base_Tag>().viewport; switch (data.backend->dispatch_event(event, viewport)) { case detail::Async_Render_Backend::Dispatch_Event_Result::dispatched: return Dispatch_Event_Result::dispatched; case detail::Async_Render_Backend::Dispatch_Event_Result::ignored: return Dispatch_Event_Result::ignored; case detail::Async_Render_Backend::Dispatch_Event_Result::invalid_event: return Dispatch_Event_Result::invalid_event; case detail::Async_Render_Backend::Dispatch_Event_Result::backend_unavailable: return Dispatch_Event_Result::backend_unavailable; } return Dispatch_Event_Result::backend_unavailable; }, [](Root* root, bool active) { static_cast<Object*>(root)->template set<&Prop::view_active>(active); }}; return value; }
template <Attached Object> void Render_Scene_3D::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for<Object>(); }
}