Files
Aethera/render_2D/render_2D/scene/Render_Scene_2D.ipp
T
2026-08-21 17:06:38 +08:00

321 lines
18 KiB
C++

#pragma once
#include "../event/Event_Routing_Rules.hpp"
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace aethera::render_2d {
template <typename Object>
Render_Scene_2D::Builder<Object>::Builder() : Base() {}
template <typename Object>
template <Attached Renderable_Object>
requires std::derived_from<Renderable_Object, Renderable_2D_Base>
Render_Scene_2D::Builder<Object>& Render_Scene_2D::Builder<Object>::add_renderable(Renderable_Object* renderable) {
attachments.emplace_back([renderable](Object* scene) {
auto& data = Base::private_access(renderable).template get<Renderable_2D_Base::Base_Tag>();
if (!data.scene_attach)
throw std::logic_error("2D renderable has no scene attachment contract");
auto attach = std::move(data.scene_attach);
return attach(scene);
});
return *this;
}
template <typename Object>
std::expected<std::unique_ptr<Object>, Dependency_Graph_Error> Render_Scene_2D::Builder<Object>::build() {
auto result = Base::build();
if (!result) return std::unexpected(result.error());
auto scene = std::move(result).value();
for (auto& attach : attachments) {
auto attach_result = attach(scene.get());
if (!attach_result) return std::unexpected(attach_result.error());
}
return scene;
}
struct Render_Scene_2D::Private : Prev_Private {
using Render_Run = void (*)(Root*);
using Callback_Run = void (*)(Root*, Frame_Callback);
using Event_Run = void (*)(Root*, const Event&);
using Active_Run = void (*)(Root*, bool);
using Frame_View_Run = Image_View (*)(const Root*);
struct Paint_Node {
Root* object{}; /* Paint 拓扑位置对应的最终对象;Scene 不拥有。 */
Renderable_2D_Base::Private* private_data{}; /* 对象的二维能力层;对象存活期间有效。 */
Root* cache_owner{}; /* 所属缓存根;空值表示直接绘制最终帧。 */
bool cache_group_last{}; /* 是否为所属缓存组在拓扑序中的最后节点。 */
bool paint_requested{}; /* Scene 按本轮组级失效结果计算的执行许可。 */
};
struct Cache_Group {
Root* owner{}; /* 显式选择缓存的二维 Renderable 根。 */
std::vector<Root*> members{}; /* 本缓存覆盖的根及无冲突后继节点。 */
bool rebuild{}; /* 本轮是否因任一成员失效而整体重绘。 */
};
struct Dispatch {
Render_Run render; /* 执行最终 Scene 并合成颜色层。 */
Callback_Run set_frame_callback; /* 安装最终完成帧回调。 */
Event_Run dispatch_event; /* 向最终 Scene 当前 Paint 图派发事件。 */
Active_Run set_active; /* 修改最终 Scene 的视图活动状态。 */
Frame_View_Run frame_view; /* 访问最终 Scene 已合成的写侧帧。 */
};
const Dispatch* dispatch{}; /* Builder 绑定最终 Scene 类型后的静态分派表。 */
Frame_Callback frame_callback{}; /* 合成完成后的唯一像素发布出口。 */
std::unique_ptr<tf::Taskflow> paint_taskflow{}; /* 仅由二维 Paint 图构建的执行图。 */
bool rendering{}; /* 是否正在同步合成当前帧。 */
bool render_pending{}; /* 合成期间是否又收到 render();多个调用合并。 */
/* Impl CRTP 实现:在对象锁内执行 Kernel Scene,再按 Paint 图拓扑顺序合成颜色层。 */
std::vector<Paint_Node> paint_order{}; /* Paint 图当前拓扑序及 Scene 缓存分组结果。 */
std::vector<Cache_Group> cache_groups{}; /* 仅保存显式缓存根对应的执行分组。 */
template <Attached Object, typename Callback>
void process(Object* object, Callback&& callback) requires std::invocable<Callback>;
/* Def CRTP hook:二维 Paint 图结构变化后重建其独立 Taskflow。 */
template <Attached Object>
void after_advance(Object* object, Prop* pending_prop, State_Access<State> pending_states, const Prop* current_prop, State_Access<State> current_states);
/* 每帧在 Prepare 完成后计算组级 dirty,并为所有二维节点指定唯一绘制目标。 */
template <Attached Object> void prepare_paint_targets(Object* object, Blend2D_Cache& frame, Size viewport);
template <Attached Object> void render(Object* object);
/* CRTP 业务实现:按 Paint 图拓扑逆序派发事件。 */
template <Attached Object>
void dispatch_event(Object* object, const Event& event);
template <Attached Object>
[[nodiscard]] static const Dispatch& dispatch_for();
/* CRTP 覆盖:Builder 挂接最终 Private 后安装二维 Scene 的无虚函数业务分派。 */
template <Attached Object>
void bind_private_crtp(Object* object);
};
template <Attached Object>
void Render_Scene_2D::Private::after_advance(Object* object, Prop*, State_Access<State>, const Prop*, State_Access<State>) {
bool rebuild = !paint_taskflow;
object->template access_pending_dependency_graph<Paint_Tag>([&](auto& paint_state) { rebuild = rebuild || paint_state.dirty(); });
if (!rebuild) return;
if (!paint_taskflow) paint_taskflow = std::make_unique<tf::Taskflow>();
auto& taskflow = *paint_taskflow;
aethera::detail::clear_stage_observers(&taskflow);
taskflow.clear();
const auto dependencies = object->template current_dependency_graph<Paint_Tag>();
paint_order.clear();
cache_groups.clear();
std::unordered_map<Root*, Root*> cache_owner_by_object;
std::unordered_map<Root*, std::size_t> cache_group_by_owner;
const auto order_result = dependencies.for_each_topological_view([&](const auto& view, const Dependency_Graph::Node& node) {
auto* data = view.private_data(node);
if (!data) return;
Root* cache_owner{};
if (data->pending_cache) cache_owner = node.object;
else {
bool conflict{};
for (const auto* dependency : node.dependencies) {
if (!view.private_data(*dependency)) continue;
auto current = cache_owner_by_object.find(dependency->object);
const Root* dependency_owner = current == cache_owner_by_object.end() ? nullptr : current->second;
if (!dependency_owner || (cache_owner && cache_owner != dependency_owner)) { conflict = true; break; }
cache_owner = const_cast<Root*>(dependency_owner);
}
if (conflict) cache_owner = nullptr;
}
cache_owner_by_object.emplace(node.object, cache_owner);
paint_order.push_back(Paint_Node{node.object, data, cache_owner, false, false});
if (!cache_owner) return;
auto [group, inserted] = cache_group_by_owner.emplace(cache_owner, cache_groups.size());
if (inserted) cache_groups.push_back(Cache_Group{cache_owner});
cache_groups[group->second].members.push_back(node.object);
});
if (!order_result) throw std::logic_error("render scene paint graph became invalid while building cache groups");
std::unordered_set<Root*> closed_groups;
for (auto current = paint_order.rbegin(); current != paint_order.rend(); ++current)
if (current->cache_owner && closed_groups.insert(current->cache_owner).second) current->cache_group_last = true;
tf::Task previous;
bool has_previous{};
for (std::size_t index = 0; index < paint_order.size(); ++index) {
auto& paint_node = paint_order[index];
Root* root = paint_node.object;
auto& data = *paint_node.private_data;
auto* dispatch = data.dispatch;
auto paint_if = taskflow.emplace([this, &data, dispatch, root, index] {
auto& state = *dispatch->state.get(root);
state.paint_graph_rebuilt = false;
state.paint_execution_time_ns = 0;
if (dispatch->paint.builder) {
const bool rebuild_graph = dispatch->paint.rebuild_predicate(root);
if (!data.paint_graph_built || rebuild_graph) {
*data.paint_graph = dispatch->paint.builder(root);
data.paint_graph_built = true;
state.paint_graph_rebuilt = true;
root->template mark_dirty<Paint_Tag>();
}
state.paint_task_count = data.paint_graph->num_tasks();
} else state.paint_task_count = dispatch->paint.run ? 1 : 0;
const bool dirty = root->template dirty<Paint_Tag>();
state.paint_dirty = dirty;
state.paint_executed = paint_order[index].paint_requested && dispatch->paint.predicate(root, dirty);
return state.paint_executed ? 0 : 1;
}).name("render_2d.paint.condition");
tf::Task paint_run;
if (dispatch->paint.builder) {
if (!data.paint_graph) data.paint_graph = std::make_unique<tf::Taskflow>();
paint_run = taskflow.composed_of(*data.paint_graph).name("render_2d.paint.graph");
} else paint_run = taskflow.emplace([dispatch, root] { if (dispatch->paint.run) dispatch->paint.run(root); }).name("render_2d.paint.data");
auto paint_done = taskflow.emplace([dispatch, root] {
auto& state = *dispatch->state.get(root);
if (state.paint_executed) root->template take_dirty<Paint_Tag>();
dispatch->state.notify(root);
}).name("render_2d.paint.complete");
paint_if.precede(paint_run, paint_done);
paint_run.precede(paint_done);
if (has_previous) previous.precede(paint_if);
aethera::detail::bind_stage_observer(&taskflow, paint_if.hash_value(), root, &data, aethera::detail::Renderable_Stage::Paint, aethera::detail::Stage_Observer_Point::Begin);
aethera::detail::bind_stage_observer(&taskflow, paint_done.hash_value(), root, &data, aethera::detail::Renderable_Stage::Paint, aethera::detail::Stage_Observer_Point::End);
previous = paint_done;
if (paint_node.cache_group_last) {
Root* cache_owner = paint_node.cache_owner;
auto composite = taskflow.emplace([this, object, cache_owner] {
const auto group = std::find_if(cache_groups.begin(), cache_groups.end(), [cache_owner](const Cache_Group& value) { return value.owner == cache_owner; });
const auto owner_node = std::find_if(paint_order.begin(), paint_order.end(), [cache_owner](const Paint_Node& value) { return value.object == cache_owner; });
if (group == cache_groups.end() || owner_node == paint_order.end()) return;
if (group->rebuild) owner_node->private_data->valid_cache = owner_node->private_data->paint_target;
if (owner_node->private_data->valid_cache)
object->template pending_buffer<Scene_Color_Cache_Tag>().composite(*owner_node->private_data->valid_cache);
}).name("render_2d.paint.cache.composite");
paint_done.precede(composite);
previous = composite;
}
has_previous = true;
}
object->template access_pending_dependency_graph<Paint_Tag>([](auto& paint_state) { if (paint_state.dirty()) paint_state.take_dirty(); });
}
template <Attached Object>
void Render_Scene_2D::Private::prepare_paint_targets(Object*, Blend2D_Cache& frame, Size viewport) {
for (auto& group : cache_groups) {
const auto owner_node = std::find_if(paint_order.begin(), paint_order.end(), [&](const Paint_Node& value) { return value.object == group.owner; });
if (owner_node == paint_order.end() || !owner_node->private_data->pending_cache)
throw std::logic_error("2D cache group lost its cache owner capability");
group.rebuild = owner_node->private_data->valid_cache == nullptr;
for (Root* member : group.members) {
const auto node = std::find_if(paint_order.begin(), paint_order.end(), [member](const Paint_Node& value) { return value.object == member; });
if (node == paint_order.end()) continue;
group.rebuild = group.rebuild || member->template dirty<Paint_Tag>();
if (node->private_data->dispatch->paint.builder) {
group.rebuild = group.rebuild || !node->private_data->paint_graph_built || node->private_data->dispatch->paint.rebuild_predicate(member);
}
}
if (group.rebuild) {
auto* target = owner_node->private_data->pending_cache(group.owner);
target->ensure_size(viewport);
target->clear();
for (Root* member : group.members) {
member->template mark_dirty<Paint_Tag>();
const auto node = std::find_if(paint_order.begin(), paint_order.end(), [member](const Paint_Node& value) { return value.object == member; });
if (node != paint_order.end()) { node->private_data->paint_target = target; node->paint_requested = true; }
}
} else {
for (Root* member : group.members) {
const auto node = std::find_if(paint_order.begin(), paint_order.end(), [member](const Paint_Node& value) { return value.object == member; });
if (node != paint_order.end()) {
node->private_data->paint_target = owner_node->private_data->valid_cache;
node->paint_requested = false;
}
}
}
}
for (auto& node : paint_order) {
if (node.cache_owner) continue;
node.private_data->paint_target = &frame;
node.paint_requested = true;
node.object->template mark_dirty<Paint_Tag>();
}
}
template <Attached Object, typename Callback>
void Render_Scene_2D::Private::process(Object* object, Callback&& callback)
requires std::invocable<Callback> {
const auto& state = static_cast<const Prop&>(*static_cast<typename Object::Private&>(*this).current);
if (!state.view_active) {
return;
}
if (state.viewport.empty()) {
return;
}
object->template current_dependency_graph<Paint_Cache_Tag>().for_each([](const Dependency_Graph::Node& node) {
if (!node.object->template take_dirty<Paint_Cache_Tag>()) return;
node.object->template mark_dirty<Prepare_Data_Tag>();
node.object->template mark_dirty<Paint_Tag>();
});
std::vector<Renderable*> prepare_executions;
object->template current_dependency_graph<Prepare_Data_Tag>().for_each_bound([&](Renderable* renderable, Renderable::Private& data) {
bool execute = renderable->template dirty<Prepare_Data_Tag>();
if (data.dispatch->prepare.builder)
execute = execute || !data.prepare_graph_built || data.dispatch->prepare.rebuild_predicate(renderable);
if (execute) prepare_executions.push_back(renderable);
});
Prev_Private::process(object, [&](const Scene::Private::Result&) {
for (auto* renderable : prepare_executions) renderable->template mark_dirty<Paint_Tag>();
auto& frame = object->template pending_buffer<Scene_Color_Cache_Tag>();
frame.ensure_size(state.viewport);
frame.clear();
{
detail::Painter painter(frame, state.viewport);
painter.rect({0.0, 0.0, static_cast<double>(state.viewport.width),
static_cast<double>(state.viewport.height)},
Pen{.style = Line_Style::none}, Brush{state.background, Brush_Style::solid});
}
prepare_paint_targets(object, frame, state.viewport);
if (paint_taskflow && !paint_taskflow->empty()) aethera::detail::run_taskflow(*paint_taskflow);
});
std::invoke(std::forward<Callback>(callback));
}
template <Attached Object>
void Render_Scene_2D::Private::render(Object* object) {
if (rendering) { render_pending = true; return; }
rendering = true;
do {
render_pending = false;
object->process([&] { if (frame_callback) frame_callback(object->template pending_buffer<Scene_Color_Cache_Tag>().view()); });
} while (render_pending);
rendering = false;
}
template <Attached Object>
void Render_Scene_2D::Private::dispatch_event(Object* object, const Event& event) {
std::vector<Renderable*> order;
const auto graph = object->template current_dependency_graph<Paint_Tag>();
const auto result = graph.for_each_topological_view([&](const auto& view, const Dependency_Graph::Node& node) {
if (auto* renderable = view.object(node)) order.push_back(renderable);
});
if (!result) throw std::logic_error("render scene paint graph became invalid during event dispatch");
std::vector<Renderable*> delivery_order(order.rbegin(), order.rend());
detail::Nearest_Event_Target_Rule::apply(delivery_order,
[&](const Renderable* target) { return target->event_routing_distance(event); });
for (auto* target : delivery_order) {
if (event.is_accepted()) break;
target->dispatch_event(event);
}
}
template <Attached Object>
const Render_Scene_2D::Private::Dispatch& Render_Scene_2D::Private::dispatch_for() {
static const Dispatch value{
[](Root* root) {
auto* object = static_cast<Object*>(root);
static_cast<typename Object::Private&>(*object->d).render(object);
},
[](Root* root, Frame_Callback callback) {
auto* object = static_cast<Object*>(root);
static_cast<typename Object::Private&>(*object->d).frame_callback = std::move(callback);
},
[](Root* root, const Event& event) {
auto* object = static_cast<Object*>(root);
static_cast<typename Object::Private&>(*object->d).dispatch_event(object, event);
},
[](Root* root, bool active) {
static_cast<Object*>(root)->template set<&Prop::view_active>(active);
},
[](const Root* root) {
const auto* object = static_cast<const Object*>(root);
const auto& private_data = static_cast<const typename Object::Private&>(*object->d);
return private_data.buffer_storage.template get<Scene_Color_Cache_Tag>().pending->view();
}
};
return value;
}
template <Attached Object>
void Render_Scene_2D::Private::bind_private_crtp(Object* object) {
Prev_Private::bind_private_crtp(object);
dispatch = &Private::dispatch_for<Object>();
}
}