This commit is contained in:
2026-08-27 14:54:06 +08:00
parent ecf24cbe50
commit 63bb406a27
34 changed files with 690 additions and 1296 deletions
+1 -98
View File
@@ -199,105 +199,8 @@ struct Def : Base, detail::Public_Access<Self> {
};
// Attach_Object 是对象运行时唯一数据入口:Prop 向外发布,State 向内提交,普通 Buffer 只推进,Dependency_Graph 负责依赖图同步。
namespace detail {
template <typename Obj, typename Lock, typename Base = typename Obj::Private>
struct Renderable_Runtime_Private : Base {};
template <typename Obj, typename Lock, typename Base>
requires requires { typename Base::Renderable_Runtime_Interface; }
struct Renderable_Runtime_Private<Obj, Lock, Base> : Base {
private:
[[nodiscard]] Attached_Private<Obj, Lock>& runtime() noexcept {
return static_cast<Attached_Private<Obj, Lock>&>(*this);
}
[[nodiscard]] const Attached_Private<Obj, Lock>& runtime() const noexcept {
return static_cast<const Attached_Private<Obj, Lock>&>(*this);
}
public:
[[nodiscard]] std::string_view business_name() const override {
static const std::string value = [] {
std::string name = typeid(Obj).name();
for (const std::string_view prefix : {"struct ", "class "})
if (name.starts_with(prefix)) name.erase(0, prefix.size());
if (const auto separator = name.rfind("::"); separator != std::string::npos)
name.erase(0, separator + 2);
return name;
}();
return value;
}
[[nodiscard]] bool prepare_predicate(Root*, bool dirty) override {
if constexpr (requires { typename Base::No_Prepare; }) return false;
else if constexpr (requires { this->should_prepare(runtime().object, *runtime().state.current, dirty); })
return this->should_prepare(runtime().object, *runtime().state.current, dirty);
else return false;
}
[[nodiscard]] bool prepare_rebuild_predicate(Root*) override {
if constexpr (requires { this->should_rebuild_prepare_graph(runtime().object, *runtime().current); })
return this->should_rebuild_prepare_graph(runtime().object, *runtime().current);
else return false;
}
[[nodiscard]] bool has_prepare_graph() const override {
return requires(Renderable_Runtime_Private& value, Obj* object) {
static_cast<Base&>(value).build_prepare_graph(object, *value.runtime().current);
};
}
[[nodiscard]] bool has_prepare_run() const override {
return !requires { typename Base::No_Prepare; } &&
requires(Renderable_Runtime_Private& value, Obj* object) { value.prepare_data(object); };
}
void run_prepare(Root*) override {
if constexpr (!requires { typename Base::No_Prepare; } &&
requires { this->prepare_data(runtime().object); }) {
this->prepare_data(runtime().object);
auto callback = [&](auto& value) {
if constexpr (requires { value.after_prepare_data(runtime().object); })
value.after_prepare_data(runtime().object);
};
Obj::template walk_private<true, Obj>(runtime().object, callback);
}
}
[[nodiscard]] typename Base::Task_Graph_Type build_prepare_graph(Root*) override {
using Result = typename Base::Task_Graph_Type;
if constexpr (requires { static_cast<Base&>(*this).build_prepare_graph(runtime().object, *runtime().current); })
return static_cast<Base&>(*this).build_prepare_graph(runtime().object, *runtime().current);
else return Result{};
}
[[nodiscard]] bool paint_predicate(Root*, bool dirty) override {
if constexpr (requires { this->should_paint(runtime().object, *runtime().state.current, dirty); })
return this->should_paint(runtime().object, *runtime().state.current, dirty);
else return false;
}
[[nodiscard]] bool paint_rebuild_predicate(Root*) override {
if constexpr (requires { this->should_rebuild_paint_graph(runtime().object, *runtime().current); })
return this->should_rebuild_paint_graph(runtime().object, *runtime().current);
else return false;
}
[[nodiscard]] bool has_paint_graph() const override {
return requires(Renderable_Runtime_Private& value, Obj* object) {
static_cast<Base&>(value).build_paint_graph(object, *value.runtime().current);
};
}
[[nodiscard]] bool has_paint_run() const override {
return requires(Renderable_Runtime_Private& value, Obj* object) { value.paint(object); };
}
void run_paint(Root*) override {
if constexpr (requires { this->paint(runtime().object); }) this->paint(runtime().object);
}
[[nodiscard]] typename Base::Task_Graph_Type build_paint_graph(Root*) override {
using Result = typename Base::Task_Graph_Type;
if constexpr (requires { static_cast<Base&>(*this).build_paint_graph(runtime().object, *runtime().current); })
return static_cast<Base&>(*this).build_paint_graph(runtime().object, *runtime().current);
else return Result{};
}
[[nodiscard]] void* pending_renderable_state(Root*) override {
return runtime().state.pending;
}
void publish_renderable_state(Root*) override {
runtime().template publish_state_snapshot<typename Base::Renderable_State_Tag>();
}
};
template <typename Obj, typename Lock,
typename Base = Renderable_Runtime_Private<Obj, Lock>>
typename Base = typename Obj::Private>
struct Axis_Runtime_Private : Base {};
template <typename Obj, typename Lock, typename Base>
+2 -2
View File
@@ -26,8 +26,8 @@ using double_buffer::Attached;
using double_buffer::Dependency_Graph_Type;
using double_buffer::Dependency_Graph;
using double_buffer::Dependency_Graph_Error;
/* Prepare 数据阶段在 Dependency_Graph 图中的标签。 */
struct Prepare_Data_Tag {};
/* Renderable 业务图在 Scene 依赖图中的唯一标签。 */
struct Render_Graph_Tag {};
/* 全局 Taskflow 运行时状态标签,用于注册状态回调。 */
struct Task_Runtime_State_Tag {};
/* 单一 Taskflow 任务类型的累计统计。 */
+2 -5
View File
@@ -4,10 +4,7 @@ std::optional<double> Renderable::event_routing_distance(const Event& event) con
const auto& data = static_cast<const Private&>(*d);
return data.event_routing_distance_run ? data.event_routing_distance_run(this, event) : std::nullopt;
}
Task_Graph& Renderable::prepare_taskflow() {
return static_cast<Private&>(*d).prepare_extension;
}
Task_Graph& Renderable::paint_taskflow() {
return static_cast<Private&>(*d).paint_extension;
Task_Graph& Renderable::taskflow() {
return static_cast<Private&>(*d).extension;
}
}
+16 -67
View File
@@ -14,73 +14,30 @@ template <typename T>
concept Event_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const Event& event) {
{ private_data.handle_event(object, event) } -> std::same_as<void>;
};
/*
* Prepare 数据模式定制点。
* 最终对象的 Private 提供 void prepare_data(T* object) 即满足;若同时存在 build_prepare_graph(...),子图模式优先。
*/
/* 最终 Private 提供 build_graph(T*, const Prop&) 时使用其自定义业务图。 */
template <typename T>
concept Prepare_Data_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object) {
{ private_data.prepare_data(object) } -> std::same_as<void>;
concept Graph_Renderable = Attached<T> && requires(typename T::Private& private_data,
T* object,
const typename T::Prop& prop) {
{ private_data.build_graph(object, prop) } -> std::same_as<Task_Graph>;
};
/*
* Prepare 子图模式定制点。
* 最终对象的 Private 提供 Task_Graph build_prepare_graph(T* object, const T::State& state) 即满足。
* 子图首次执行前一定构建,之后由 should_rebuild_prepare_graph(...) 决定是否重建。
*/
/* 最终可交给 Scene 执行的 Renderable 契约。 */
template <typename T>
concept Prepare_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::Prop& prop) {
{ private_data.build_prepare_graph(object, prop) } -> std::same_as<Task_Graph>;
};
/* 最终 Private 声明 No_Prepare 时,该 Renderable 不参与 Prepare 阶段。 */
template <typename T>
concept No_Prepare_Renderable = Attached<T> && requires { typename T::Private::No_Prepare; };
/*
* Paint 数据模式定制点。
* 最终对象的 Private 提供 void paint(T* object) 即满足;若同时存在 build_paint_graph(...),子图模式优先。
*/
template <typename T>
concept Paint_Data_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object) {
{ private_data.paint(object) } -> std::same_as<void>;
};
/*
* Paint 子图模式定制点。
* 最终对象的 Private 提供 Task_Graph build_paint_graph(T* object, const T::State& state) 即满足。
* 子图首次执行前一定构建,之后由 should_rebuild_paint_graph(...) 决定是否重建。
*/
template <typename T>
concept Paint_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::Prop& prop) {
{ private_data.build_paint_graph(object, prop) } -> std::same_as<Task_Graph>;
};
/*
* 最终可交给 Scene 执行的 Renderable 契约。
* Prepare 与 Paint 各自至少提供数据模式或子图模式之一,并继承 Renderable 提供的默认阶段策略。
*/
template <typename T>
concept Renderable_Object = Attached<T> && std::derived_from<T, Renderable> && requires(typename T::Private& private_data, T* object, const typename T::State& state, const typename T::Prop& prop, bool dirty) {
{ private_data.should_prepare(object, state, dirty) } -> std::same_as<bool>;
{ private_data.should_paint(object, state, dirty) } -> std::same_as<bool>;
{ private_data.should_rebuild_prepare_graph(object, prop) } -> std::same_as<bool>;
{ private_data.should_rebuild_paint_graph(object, prop) } -> std::same_as<bool>;
} && (No_Prepare_Renderable<T> || Prepare_Data_Renderable<T> || Prepare_Graph_Renderable<T>) && (Paint_Data_Renderable<T> || Paint_Graph_Renderable<T>);
concept Renderable_Object = Attached<T> && std::derived_from<T, Renderable>;
/*
* Renderable 定义 Scene 可调度对象的公共机制。
* 用户继续派生该定义,在最终具体类型的 Private 中提供 Prepare/Paint 定制点并直接创建该具体对象
* 用户继续派生该定义,在最终具体类型的 Private 中定义自己的业务图
*/
struct Renderable : Def<Renderable, Root> {
/* Renderable 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */
struct Prop : Prev_Prop {};
/* Renderable 每次 Scene 执行后发布的阶段状态与统计。 */
/* Renderable 每次 Scene 执行后发布的业务图状态与统计。 */
struct State : Prev_State {
bool prepare_dirty{}; /* Prepare 条件判断时观察到的 Prepare_Data_Tag dirty 状态。 */
bool paint_dirty{}; /* 专用渲染阶段条件判断时观察到的 dirty 状态。 */
bool prepare_executed{}; /* 本次 Scene 执行是否运行了 Prepare 数据函数或子图。 */
bool paint_executed{}; /* 本次 Scene 执行是否运行了 Paint 数据函数或子图。 */
bool prepare_graph_rebuilt{}; /* 本次 Prepare 条件判断是否重建了 Prepare 子图。 */
bool paint_graph_rebuilt{}; /* 本次 Paint 条件判断是否重建了 Paint 子图。 */
std::size_t prepare_task_count{}; /* Prepare 子图的任务数;数据模式固定为 1。 */
std::size_t paint_task_count{}; /* Paint 子图的任务数;数据模式固定为 1。 */
std::uint64_t prepare_execution_time_ns{}; /* 本次 Prepare 阶段实际执行耗时,单位为纳秒;未执行时为 0。 */
std::uint64_t paint_execution_time_ns{}; /* 本次 Paint 阶段实际执行耗时,单位为纳秒;未执行时为 0。 */
bool render_dirty{}; /* 条件判断时观察到的 Render_Graph_Tag dirty 状态。 */
bool render_executed{}; /* 本次 Scene 执行是否运行了业务图。 */
bool graph_rebuilt{}; /* 本次 advance 是否重建了业务图。 */
std::size_t task_count{}; /* 当前业务图的任务数。 */
std::uint64_t execution_time_ns{}; /* 本次业务图实际执行耗时,单位为纳秒。 */
/* 支持测试、快照比较和变更检测的逐字段相等比较。 */
bool operator==(const State&) const = default;
};
@@ -92,17 +49,9 @@ struct Renderable : Def<Renderable, Root> {
* 返回 Prepare 阶段完成后执行的业务 DAG 扩展端口。
* Task_Graph 只能在该 Renderable 所属 Scene 没有运行时修改。
*/
[[nodiscard]] Task_Graph& prepare_taskflow();
/*
* 返回 Paint 阶段完成后执行的业务 DAG 扩展端口。
* Task_Graph 只能在该 Renderable 所属 Scene 没有运行时修改。
*/
[[nodiscard]] Task_Graph& paint_taskflow();
[[nodiscard]] Task_Graph& taskflow();
private:
/* 数据模式的内部调度入口:执行最终对象 prepare_data(...),再触发各 CRTP 层 after_prepare_data(...)。 */
template <Prepare_Data_Renderable Object>
static void run_prepare_data(Object* object);
/* 依赖图绑定时为最终对象建立无虚函数分派表,并标记首次 Prepare。 */
/* 依赖图绑定时为最终对象建立 CRTP 类型擦除入口,并标记首次执行。 */
void bind_dependency_graph_object(Attached auto* object);
friend struct Dependency_Graph;
};
+100 -72
View File
@@ -1,89 +1,115 @@
#pragma once
#include <memory>
#include <string>
#include <string_view>
#include <typeinfo>
namespace aethera {
struct Renderable::Private : Prev_Private {
using Renderable_Runtime_Interface = void;
using Task_Graph_Type = Task_Graph;
using Renderable_State_Tag = Renderable::Base_Tag;
/*
* 派生 Renderable 的 Private 必须继承 Prev_Private,并为 Prepare/Paint 各提供一种执行能力。
* Prepare 在编译期按能力选择:存在 build_prepare_graph(...) 时使用子图模式,否则使用 prepare_data(...);两者同时存在时子图优先。
* Paint 在编译期按能力选择:存在 build_paint_graph(...) 时使用子图模式,否则使用 paint(...);两者同时存在时子图优先。
* should_rebuild_prepare_graph(...) 和 should_rebuild_paint_graph(...) 只决定已选中子图模式后的重建时机,不负责选择模式。
*/
using Event_Run = void (*)(Root*, const Event&);
using Event_Routing_Distance_Run = std::optional<double> (*)(const Root*, const Event&);
using Color_Cache_Visitor = void (*)(void*, const Color_Cache&);
using Color_Cache_Visit = void (*)(Root*, void*, Color_Cache_Visitor);
[[nodiscard]] virtual std::string_view business_name() const = 0;
[[nodiscard]] virtual bool prepare_predicate(Root* object, bool dirty) = 0;
[[nodiscard]] virtual bool prepare_rebuild_predicate(Root* object) = 0;
[[nodiscard]] virtual bool has_prepare_graph() const = 0;
[[nodiscard]] virtual bool has_prepare_run() const = 0;
virtual void run_prepare(Root* object) = 0;
[[nodiscard]] virtual Task_Graph build_prepare_graph(Root* object) = 0;
[[nodiscard]] virtual bool paint_predicate(Root* object, bool dirty) = 0;
[[nodiscard]] virtual bool paint_rebuild_predicate(Root* object) = 0;
[[nodiscard]] virtual bool has_paint_graph() const = 0;
[[nodiscard]] virtual bool has_paint_run() const = 0;
virtual void run_paint(Root* object) = 0;
[[nodiscard]] virtual Task_Graph build_paint_graph(Root* object) = 0;
[[nodiscard]] virtual void* pending_renderable_state(Root* object) = 0;
virtual void publish_renderable_state(Root* object) = 0;
Event_Run event_run{}; /* 最终 Private 具备事件能力时的无虚函数入口。 */
Event_Routing_Distance_Run event_routing_distance_run{}; /* 可选事件候选距离;Scene 路由规则按需查询。 */
Color_Cache_Visit color_cache_visit{}; /* 最终对象存在 Color_Cache Buffer 时访问本轮写入结果。 */
std::unique_ptr<Task_Graph> prepare_graph; /* Prepare 子图模式的当前构建产物。 */
std::unique_ptr<Task_Graph> paint_graph; /* Paint 子图模式的当前构建产物。 */
Task_Graph prepare_extension{"renderable.prepare.extension"}; /* 外部直接续写的 Prepare 完成图。 */
Task_Graph paint_extension{"renderable.paint.extension"}; /* 外部直接续写的 Paint 完成图。 */
bool prepare_graph_built{}; /* Prepare 子图是否至少成功构建过一次。 */
bool paint_graph_built{}; /* Paint 子图是否至少成功构建过一次。 */
/* CRTP 可覆盖:决定已选中子图模式的 Prepare 子图是否重建;object 为最终对象,state 为当前发布状态;默认返回 false。 */
bool should_rebuild_prepare_graph(Attached auto* object, const Prop& prop);
/* CRTP 可覆盖:决定已选中子图模式的 Paint 子图是否重建;object 为最终对象,state 为当前发布状态;默认返回 false。 */
bool should_rebuild_paint_graph(Attached auto* object, const Prop& prop);
/* CRTP 可覆盖:决定本次是否执行 Prepare;object 为最终对象,state 为当前发布状态,dirty 为 Prepare dirty;默认返回 dirty。 */
bool should_prepare(Attached auto* object, const State& state, bool dirty);
/* CRTP 可覆盖:决定本次是否执行 Paint;object 为最终对象,state 为当前发布状态,dirty 为 Paint dirty;默认返回 dirty。 */
bool should_paint(Attached auto* object, const State& state, bool dirty);
/* CRTP 可覆盖:prepare_data(...) 完成后按派生类到基类顺序调用;object 为最终对象;默认不处理。 */
void after_prepare_data(Attached auto* object);
/* CRTP 覆盖:Builder 挂接最终 Private 后绑定事件、颜色缓存和阶段分派;派生 Private 必须先调用此实现。 */
using Build_Graph_Run = std::optional<Task_Graph> (*)(Root*);
using Graph_Rebuild_Run = bool (*)(Root*);
using Render_Predicate_Run = bool (*)(Root*, bool);
using Pending_State_Run = void* (*)(Root*);
using Publish_State_Run = void (*)(Root*);
std::string business_name_value{};
Build_Graph_Run build_graph_run{};
Graph_Rebuild_Run graph_rebuild_run{};
Render_Predicate_Run render_predicate_run{};
Pending_State_Run pending_state_run{};
Publish_State_Run publish_state_run{};
Event_Run event_run{};
Event_Routing_Distance_Run event_routing_distance_run{};
Color_Cache_Visit color_cache_visit{};
std::optional<Task_Graph> graph{};
Task_Graph extension{"renderable.extension"};
[[nodiscard]] std::string_view business_name() const noexcept {
return business_name_value;
}
[[nodiscard]] std::optional<Task_Graph> build_runtime_graph(Root* object) const {
return build_graph_run ? build_graph_run(object) : std::nullopt;
}
[[nodiscard]] bool should_rebuild_runtime_graph(Root* object) const {
return graph_rebuild_run && graph_rebuild_run(object);
}
[[nodiscard]] bool should_render(Root* object, bool dirty) const {
return render_predicate_run ? render_predicate_run(object, dirty) : dirty;
}
[[nodiscard]] void* pending_renderable_state(Root* object) const {
return pending_state_run(object);
}
void publish_renderable_state(Root* object) const {
publish_state_run(object);
}
template <Attached Object>
void bind_private_crtp(Object* object);
/* Def::Private 的四个 State 生命周期 hook 同样可在派生 Private 中覆盖,并通过 State_Access::get<Tag>() 访问状态层。 */
};
template <Prepare_Data_Renderable Object>
void Renderable::run_prepare_data(Object* object) {
auto& private_data = double_buffer::detail::Internal_Access::get(object);
private_data.prepare_data(object);
auto callback = [&](auto& value) {
if constexpr (requires { value.after_prepare_data(object); }) value.after_prepare_data(object);
};
walk_private<true, typename Object::Attached_Object>(object, callback);
}
inline bool Renderable::Private::should_rebuild_prepare_graph(Attached auto*, const Prop&) {
return false;
}
inline bool Renderable::Private::should_rebuild_paint_graph(Attached auto*, const Prop&) {
return false;
}
inline bool Renderable::Private::should_prepare(Attached auto*, const State&, bool dirty) {
return dirty;
}
inline bool Renderable::Private::should_paint(Attached auto*, const State&, bool dirty) {
return dirty;
}
inline void Renderable::Private::after_prepare_data(Attached auto*) {}
template <Attached Object>
void Renderable::Private::bind_private_crtp(Object* object) {
Prev_Private::bind_private_crtp(object);
if constexpr (!No_Prepare_Renderable<Object>)
double_buffer::detail::Internal_Access::mark_dirty<Prepare_Data_Tag>(object);
auto& data = static_cast<Private&>(*this);
data.business_name_value = [] {
std::string name = typeid(Object).name();
for (const std::string_view prefix : {"struct ", "class "})
if (name.starts_with(prefix)) name.erase(0, prefix.size());
if (const auto separator = name.rfind("::"); separator != std::string::npos)
name.erase(0, separator + 2);
return name;
}();
data.build_graph_run = [](Root* root) -> std::optional<Task_Graph> {
auto* value = static_cast<Object*>(root);
auto& private_data = double_buffer::detail::Internal_Access::get(value);
const auto& prop = double_buffer::detail::Internal_Access::current_prop(value);
if constexpr (requires { private_data.build_graph(value, prop); }) {
return private_data.build_graph(value, prop);
}
else if constexpr (requires { private_data.prepare_data(value); } ||
requires { private_data.paint(value); }) {
Task_Graph graph{"renderable.graph"};
graph.add("render", [value] {
auto& runtime_data = double_buffer::detail::Internal_Access::get(value);
if constexpr (requires { runtime_data.prepare_data(value); })
runtime_data.prepare_data(value);
if constexpr (requires { runtime_data.paint(value); })
runtime_data.paint(value);
});
return graph;
}
else {
return std::nullopt;
}
};
data.graph_rebuild_run = [](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = double_buffer::detail::Internal_Access::get(value);
const auto& prop = double_buffer::detail::Internal_Access::current_prop(value);
if constexpr (requires { private_data.should_rebuild_graph(value, prop); })
return private_data.should_rebuild_graph(value, prop);
return false;
};
data.render_predicate_run = [](Root* root, bool dirty) {
auto* value = static_cast<Object*>(root);
auto& private_data = double_buffer::detail::Internal_Access::get(value);
const auto& state = static_cast<const State&>(
double_buffer::detail::Internal_Access::current_state(value));
if constexpr (requires { private_data.should_render(value, state, dirty); })
return private_data.should_render(value, state, dirty);
return dirty;
};
data.pending_state_run = [](Root* root) -> void* {
auto* value = static_cast<Object*>(root);
return &double_buffer::detail::Internal_Access::pending_state(value);
};
data.publish_state_run = [](Root* root) {
auto* value = static_cast<Object*>(root);
double_buffer::detail::Internal_Access::publish_state<Renderable::Base_Tag>(value);
};
if constexpr (Event_Renderable<Object>) {
data.event_run = [](Root* root, const Event& event) {
auto* value = static_cast<Object*>(root);
@@ -97,21 +123,23 @@ void Renderable::Private::bind_private_crtp(Object* object) {
using Cache = std::remove_reference_t<decltype(
double_buffer::detail::Internal_Access::pending_buffer<Color_Cache>(object))>;
if constexpr (std::derived_from<Cache, Color_Cache>) {
data.color_cache_visit = [](Root* root, void* context, Private::Color_Cache_Visitor visitor) {
data.color_cache_visit = [](Root* root, void* context, Color_Cache_Visitor visitor) {
auto* value = static_cast<Object*>(root);
const auto& render_state = static_cast<const State&>(
double_buffer::detail::Internal_Access::current_state(value));
if (render_state.paint_executed)
if (render_state.render_executed)
visitor(context, double_buffer::detail::Internal_Access::pending_buffer<Color_Cache>(value));
else
visitor(context, double_buffer::detail::Internal_Access::current_buffer<Color_Cache>(value));
};
}
}
double_buffer::detail::Internal_Access::mark_dirty<Render_Graph_Tag>(object);
}
inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
using Object = std::remove_pointer_t<decltype(object)>;
static_assert(Renderable_Object<Object>);
double_buffer::detail::Internal_Access::mark_dirty<Prepare_Data_Tag>(object);
double_buffer::detail::Internal_Access::mark_dirty<Render_Graph_Tag>(object);
}
}
+3
View File
@@ -2,4 +2,7 @@
namespace aethera {
Scene::Private::Private() = default;
Scene::Private::~Private() = default;
Task_Graph& Scene::completion_taskflow() {
return static_cast<Private&>(*d).completion_graph;
}
}
+2 -1
View File
@@ -12,7 +12,7 @@ struct Scene_Event_Stream_Tag {};
* 最终具体 Scene 直接持有其 Private;编辑 Dependency_Graph 后提交结构变化,再执行当前场景。
*/
struct Scene : Def<Scene, Root,
Dependency_Graph_Type<Prepare_Data_Tag, Renderable>,
Dependency_Graph_Type<Render_Graph_Tag, Renderable>,
Mpmc_Triple_Buffer<Scene_Event_Stream_Tag, std::shared_ptr<Event>>> {
using Event_Pointer = std::shared_ptr<Event>;
using Event_Batch = std::pmr::vector<Event_Pointer>;
@@ -37,6 +37,7 @@ struct Scene : Def<Scene, Root,
struct Private;
template <Event_Object Event_Object_Type, typename... Arguments>
[[nodiscard]] std::shared_ptr<Event_Object_Type> make_event(Arguments&&... arguments);
[[nodiscard]] Task_Graph& completion_taskflow();
};
}
#include "scene.ipp"
+63 -70
View File
@@ -11,6 +11,7 @@ struct Scene::Private : Prev_Private {
struct Result {}; /* process(...) 完成回调的结果类型;当前仅表示完成。 */
struct Runtime; /* Scene 的 Taskflow 构建产物;完整定义位于本文件下方。 */
std::unique_ptr<Runtime> runtime; /* Scene 唯一运行时构建产物的所有权。 */
Task_Graph completion_graph{"scene.completion"};
Event_Statistics_Accumulator event_statistics{}; /* Scene 单帧域内按事件类型增量计算。 */
Private();
~Private();
@@ -70,10 +71,10 @@ void Scene::Private::process(Object* object, Render_Frame* frame, Callback&& cal
if (frame) frame->mark(Frame_Trace_Marker::prepare_started);
if (runtime->taskflow && !runtime->taskflow->empty())
state.taskflow_execution_time_ns = frame && frame->taskflow_trace_requested()
? detail::run_taskflow(*runtime->taskflow, *frame, "scene.prepare")
? detail::run_taskflow(*runtime->taskflow, *frame, "scene.renderables")
: detail::run_taskflow(*runtime->taskflow);
if (frame) frame->mark(Frame_Trace_Marker::prepare_finished);
double_buffer::detail::Internal_Access::current_dependency_graph<Prepare_Data_Tag>(object).for_each_bound(
double_buffer::detail::Internal_Access::current_dependency_graph<Render_Graph_Tag>(object).for_each_bound(
[](Renderable* renderable, Renderable::Private& data) {
data.publish_renderable_state(renderable);
});
@@ -104,103 +105,95 @@ void Scene::Private::after_advance(Object* object,
);
}
);
auto prepare_dependencies =
double_buffer::detail::Internal_Access::current_dependency_graph<Prepare_Data_Tag>(object);
auto render_dependencies =
double_buffer::detail::Internal_Access::current_dependency_graph<Render_Graph_Tag>(object);
bool taskflow_dirty = !runtime->taskflow;
double_buffer::detail::Internal_Access::access_pending_dependency_graph<Prepare_Data_Tag>(object,
[&](auto& prepare_state) { taskflow_dirty = taskflow_dirty || prepare_state.dirty(); });
double_buffer::detail::Internal_Access::access_pending_dependency_graph<Render_Graph_Tag>(object,
[&](auto& graph_state) { taskflow_dirty = taskflow_dirty || graph_state.dirty(); });
render_dependencies.for_each_bound(
[&](Renderable* renderable, Renderable::Private& data) {
auto& state = *static_cast<Renderable::State*>(
data.pending_renderable_state(renderable));
state.graph_rebuilt = false;
if (!data.graph || data.should_rebuild_runtime_graph(renderable)) {
data.graph = data.build_runtime_graph(renderable);
state.graph_rebuilt = true;
double_buffer::detail::Internal_Access::mark_dirty<Render_Graph_Tag>(
renderable);
}
state.task_count = data.graph ? data.graph->size() : 0;
});
if (!taskflow_dirty) return;
if (!runtime->taskflow) runtime->taskflow = std::make_unique<Task_Graph>("scene.prepare");
if (!runtime->taskflow) runtime->taskflow = std::make_unique<Task_Graph>("scene.render");
auto& taskflow = *runtime->taskflow;
std::pmr::unordered_set<Renderable*> renderables{resource};
prepare_dependencies.for_each_bound(
render_dependencies.for_each_bound(
[&](Renderable* renderable, Renderable::Private&) {
renderables.insert(renderable);
}
);
scene_state.renderable_count = renderables.size();
taskflow.clear();
struct Stage_Tasks {
Task_Node prepare_entry; /* Prepare 条件任务,作为该阶段依赖入口。 */
Task_Node prepare_exit; /* Prepare 完成任务,作为该阶段依赖出口。 */
struct Render_Tasks {
Task_Node entry;
Task_Node exit;
};
std::pmr::unordered_map<Root*, Stage_Tasks> stage_tasks{resource};
std::pmr::unordered_map<Root*, Render_Tasks> render_tasks{resource};
/*
* Prepare 的并行关系只有一个权威来源:Prepare_Data_Tag。
* 每个 Renderable 先建立独立的条件、执行和完成节点;下面只为显式
* 依赖连边,不得按遍历顺序增加 previous 链或其他隐式串行关系。
* Render_Graph_Tag 是对象间执行顺序的唯一权威来源。对象内部节点和
* 对其他 Private 图的直接访问都属于具体实现,公共层不定义阶段端口。
*/
for (auto* renderable : renderables) {
Root* root = renderable;
auto* data = prepare_dependencies.private_data(root);
auto* data = render_dependencies.private_data(root);
if (!data) continue;
const auto task_prefix = std::string(data->business_name()) + ".prepare";
auto prepare_if = taskflow.add_condition(task_prefix + ".condition", [data, root] {
const auto task_prefix = std::string(data->business_name()) + ".render";
auto render_if = taskflow.add_condition(task_prefix + ".condition", [data, root] {
auto& state = *static_cast<Renderable::State*>(data->pending_renderable_state(root));
state.prepare_graph_rebuilt = false;
state.prepare_execution_time_ns = 0;
if (data->has_prepare_graph()) {
bool rebuild = data->prepare_rebuild_predicate(root);
if (!data->prepare_graph_built || rebuild) {
*data->prepare_graph = data->build_prepare_graph(root);
data->prepare_graph_built = true;
state.prepare_graph_rebuilt = true;
double_buffer::detail::Internal_Access::mark_dirty<Prepare_Data_Tag>(root);
}
state.prepare_task_count = data->prepare_graph->size();
}
else {
state.prepare_task_count = data->has_prepare_run() ? 1 : 0;
}
bool dirty = double_buffer::detail::Internal_Access::dirty<Prepare_Data_Tag>(root);
state.prepare_dirty = dirty;
state.prepare_executed = data->prepare_predicate(root, dirty);
if (state.prepare_executed) {
state.prepare_execution_time_ns = static_cast<std::uint64_t>(
state.execution_time_ns = 0;
const bool dirty =
double_buffer::detail::Internal_Access::dirty<Render_Graph_Tag>(root);
state.render_dirty = dirty;
state.render_executed = data->should_render(root, dirty);
if (state.render_executed) {
state.execution_time_ns = static_cast<std::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count());
}
return state.prepare_executed ? 0 : 1;
return state.render_executed ? 0 : 1;
});
Task_Node prepare_run;
if (data->has_prepare_graph()) {
if (!data->prepare_graph) data->prepare_graph = std::make_unique<Task_Graph>(task_prefix + ".graph");
prepare_run = taskflow.compose(task_prefix + ".graph", *data->prepare_graph);
}
else {
prepare_run = taskflow.add(task_prefix + ".data", [data, root] {
if (data->has_prepare_run()) data->run_prepare(root);
});
}
auto prepare_done = taskflow.add(task_prefix + ".complete", [data, root] {
Task_Node render_run = data->graph
? taskflow.compose(task_prefix + ".graph", *data->graph)
: taskflow.add(task_prefix + ".empty", [] {});
auto render_done = taskflow.add(task_prefix + ".complete", [data, root] {
auto& state = *static_cast<Renderable::State*>(data->pending_renderable_state(root));
if (state.prepare_executed) {
(void)double_buffer::detail::Internal_Access::take_dirty<Prepare_Data_Tag>(root);
if (state.render_executed) {
(void)double_buffer::detail::Internal_Access::take_dirty<Render_Graph_Tag>(root);
const auto finished = static_cast<std::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count());
state.prepare_execution_time_ns = finished - state.prepare_execution_time_ns;
state.execution_time_ns = finished - state.execution_time_ns;
}
});
prepare_if.precede(prepare_run);
prepare_if.precede(prepare_done);
if (data->prepare_extension.empty()) {
prepare_run.precede(prepare_done);
render_if.precede(render_run);
render_if.precede(render_done);
if (data->extension.empty()) {
render_run.precede(render_done);
}
else {
auto prepare_extension = taskflow.compose(
task_prefix + ".extension", data->prepare_extension);
prepare_run.precede(prepare_extension);
prepare_extension.precede(prepare_done);
auto extension = taskflow.compose(
task_prefix + ".extension", data->extension);
render_run.precede(extension);
extension.precede(render_done);
}
stage_tasks.emplace(root, Stage_Tasks{prepare_if, prepare_done});
render_tasks.emplace(root, Render_Tasks{render_if, render_done});
}
auto connect_dependencies = [&](const auto& dependency_graph) {
dependency_graph.for_each(
[&](const Dependency_Graph::Node& dependency_node) {
if (!dependency_graph.private_data(dependency_node)) return;
auto target = stage_tasks.find(dependency_node.object);
if (target == stage_tasks.end()) return;
auto target = render_tasks.find(dependency_node.object);
if (target == render_tasks.end()) return;
std::pmr::unordered_set<Root*> visited{resource};
std::pmr::vector<const Dependency_Graph::Node*> pending{resource};
for (const auto* dependency : dependency_node.dependencies) pending.push_back(dependency);
@@ -209,9 +202,9 @@ void Scene::Private::after_advance(Object* object,
pending.pop_back();
if (!visited.insert(dependency->object).second) continue;
if (dependency_graph.private_data(*dependency)) {
auto source = stage_tasks.find(dependency->object);
if (source != stage_tasks.end()) {
source->second.prepare_exit.precede(target->second.prepare_entry);
auto source = render_tasks.find(dependency->object);
if (source != render_tasks.end()) {
source->second.exit.precede(target->second.entry);
}
continue;
}
@@ -220,7 +213,7 @@ void Scene::Private::after_advance(Object* object,
}
);
};
connect_dependencies(prepare_dependencies);
connect_dependencies(render_dependencies);
scene_state.taskflow_task_count = taskflow.size();
scene_state.taskflow_dependency_count = 0;
scene_state.taskflow_max_predecessors = 0;
@@ -232,8 +225,8 @@ void Scene::Private::after_advance(Object* object,
scene_state.taskflow_max_successors = std::max(
scene_state.taskflow_max_successors, node.successors.size());
}
double_buffer::detail::Internal_Access::access_pending_dependency_graph<Prepare_Data_Tag>(object,
[](auto& prepare_state) { if (prepare_state.dirty()) prepare_state.take_dirty(); });
double_buffer::detail::Internal_Access::access_pending_dependency_graph<Render_Graph_Tag>(object,
[](auto& graph_state) { if (graph_state.dirty()) graph_state.take_dirty(); });
scene_state.taskflow_rebuilt = true;
}
}
+35 -66
View File
@@ -30,18 +30,17 @@ struct Graph_Renderable : double_buffer::Def<Graph_Renderable, aethera::Renderab
int paint_calls{};
int prepare_builds{};
bool rebuild_prepare{};
aethera::Task_Graph build_prepare_graph(double_buffer::Attached auto*, const Prop&) {
aethera::Task_Graph build_graph(double_buffer::Attached auto*, const Prop&) {
++prepare_builds;
aethera::Task_Graph graph{"test.prepare.graph"};
graph.add("task", [this] { ++prepare_calls; });
aethera::Task_Graph graph{"test.render.graph"};
auto prepare = graph.add("prepare", [this] { ++prepare_calls; });
auto paint = graph.add("paint", [this] { ++paint_calls; });
prepare.precede(paint);
return graph;
}
bool should_rebuild_prepare_graph(double_buffer::Attached auto*, const Prop&) {
bool should_rebuild_graph(double_buffer::Attached auto*, const Prop&) {
return std::exchange(rebuild_prepare, false);
}
void paint(double_buffer::Attached auto*) {
++paint_calls;
}
};
Private& data_for_test();
};
@@ -54,33 +53,6 @@ inline Direct_Renderable::Private& Direct_Renderable::data_for_test() {
inline Graph_Renderable::Private& Graph_Renderable::data_for_test() {
return static_cast<Private&>(*d);
}
struct Dependency_Renderable : double_buffer::Def<Dependency_Renderable, aethera::Renderable> {
struct Prop : Prev_Prop {};
struct State : Prev_State {
int revision{};
bool operator==(const State&) const = default;
};
struct Private : Prev_Private {
int prepare_calls{};
int paint_calls{};
bool publish_revision{};
void prepare_data(double_buffer::Attached auto* object) {
++prepare_calls;
using Object = std::remove_pointer_t<decltype(object)>;
auto& private_data = static_cast<double_buffer::detail::Attached_Private<Object, std::mutex>&>(*this);
if (std::exchange(publish_revision, false))
private_data.template update_state<&State::revision>(private_data.state.pending->revision + 1);
}
void paint(double_buffer::Attached auto*) {
++paint_calls;
}
};
Private& data_for_test();
};
using Dependency = Dependency_Renderable;
inline Dependency_Renderable::Private& Dependency_Renderable::data_for_test() {
return static_cast<Private&>(*d);
}
template <typename Object_Type>
std::unique_ptr<Object_Type> build_object() {
auto result = typename Object_Type::template Builder<Object_Type>{}.build();
@@ -89,7 +61,7 @@ std::unique_ptr<Object_Type> build_object() {
}
template <typename Renderable>
void add_renderable(Scene& scene, Renderable* renderable) {
ASSERT_TRUE(scene.edit_dependency_graph<aethera::Prepare_Data_Tag>([&](auto& prepare) { prepare.add(renderable); }).has_value());
ASSERT_TRUE(scene.edit_dependency_graph<aethera::Render_Graph_Tag>([&](auto& prepare) { prepare.add(renderable); }).has_value());
}
}
TEST(renderable_capability, direct_stages_do_not_allocate_subgraphs) {
@@ -98,19 +70,18 @@ TEST(renderable_capability, direct_stages_do_not_allocate_subgraphs) {
auto scene = build_object<Scene>();
add_renderable(*scene, renderable.get());
int prepare_extension_calls{};
renderable->prepare_taskflow().add(
"test.prepare.extension", [&] { ++prepare_extension_calls; });
renderable->taskflow().add(
"test.render.extension", [&] { ++prepare_extension_calls; });
scene->process([](const auto&) {});
auto& data = renderable->data_for_test();
auto& base = static_cast<aethera::Renderable::Private&>(data);
EXPECT_EQ(data.prepare_calls, 1);
EXPECT_EQ(data.paint_calls, 0);
EXPECT_EQ(data.paint_calls, 1);
EXPECT_EQ(prepare_extension_calls, 1);
EXPECT_EQ(base.prepare_graph, nullptr);
EXPECT_EQ(base.paint_graph, nullptr);
EXPECT_TRUE(base.graph.has_value());
scene->process([](const auto&) {});
EXPECT_EQ(data.prepare_calls, 1);
EXPECT_EQ(data.paint_calls, 0);
EXPECT_EQ(data.paint_calls, 1);
EXPECT_EQ(prepare_extension_calls, 1);
}
TEST(renderable_capability, graph_stage_builds_lazily_and_rebuilds_inside_condition) {
@@ -120,16 +91,15 @@ TEST(renderable_capability, graph_stage_builds_lazily_and_rebuilds_inside_condit
add_renderable(*scene, renderable.get());
auto& data = renderable->data_for_test();
auto& base = static_cast<aethera::Renderable::Private&>(data);
EXPECT_EQ(base.prepare_graph, nullptr);
scene->process([](const auto&) {});
ASSERT_NE(base.prepare_graph, nullptr);
ASSERT_TRUE(base.graph.has_value());
EXPECT_EQ(data.prepare_builds, 1);
EXPECT_EQ(data.prepare_calls, 1);
scene->process([](const auto&) {});
EXPECT_EQ(data.prepare_builds, 1);
EXPECT_EQ(data.prepare_calls, 1);
data.rebuild_prepare = true;
renderable->mark_dirty<aethera::Prepare_Data_Tag>();
renderable->mark_dirty<aethera::Render_Graph_Tag>();
scene->process([](const auto&) {});
EXPECT_EQ(data.prepare_builds, 2);
EXPECT_EQ(data.prepare_calls, 2);
@@ -144,8 +114,7 @@ TEST(renderable_state, scene_and_renderable_callbacks_publish_at_stage_boundarie
int runtime_updates = 0;
renderable->set_state_callback<aethera::Renderable::Base_Tag>([&](const auto& state) {
++renderable_updates;
EXPECT_TRUE(state.prepare_executed);
EXPECT_FALSE(state.paint_executed);
EXPECT_TRUE(state.render_executed);
});
scene->set_state_callback<aethera::Scene::Base_Tag>([&](const auto& state) {
++scene_updates;
@@ -189,31 +158,31 @@ TEST(scene_state, structural_statistics_survive_a_process_without_rebuild) {
scene->process([](const auto&) {});
EXPECT_EQ(updates, 2);
}
TEST(scene_condition, upstream_change_makes_downstream_run_in_same_taskflow) {
TEST(scene_condition, render_order_does_not_create_a_false_data_dependency) {
aethera::initialize_runtime({.workers = 2});
auto source = build_object<Dependency>();
auto target = build_object<Dependency>();
auto source = build_object<Direct_Renderable>();
auto target = build_object<Direct_Renderable>();
auto scene = build_object<Scene>();
ASSERT_TRUE((scene->edit_dependency_graph<aethera::Prepare_Data_Tag>(
[&](auto& prepare) {
prepare.add(source.get());
prepare.add(target.get());
prepare.template add_dependency<&Dependency_Renderable::State::revision>(target.get(), source.get());
}
).has_value()));
ASSERT_TRUE((scene->edit_dependency_graph<aethera::Render_Graph_Tag>(
[&](auto& graph) {
graph.add(source.get());
graph.add(target.get());
graph.add_dependency(target.get(), source.get());
}).has_value()));
scene->process([](const auto&) {});
auto& source_data = source->data_for_test();
auto& target_data = target->data_for_test();
EXPECT_EQ(source_data.prepare_calls, 1);
EXPECT_EQ(target_data.prepare_calls, 1);
EXPECT_EQ(source->data_for_test().prepare_calls, 1);
EXPECT_EQ(target->data_for_test().prepare_calls, 1);
scene->process([](const auto&) {});
EXPECT_EQ(source_data.prepare_calls, 1);
EXPECT_EQ(target_data.prepare_calls, 1);
source_data.publish_revision = true;
source->mark_dirty<aethera::Prepare_Data_Tag>();
EXPECT_EQ(source->data_for_test().prepare_calls, 1);
EXPECT_EQ(target->data_for_test().prepare_calls, 1);
source->mark_dirty<aethera::Render_Graph_Tag>();
scene->process([](const auto&) {});
EXPECT_EQ(source_data.prepare_calls, 2);
EXPECT_EQ(target_data.prepare_calls, 2);
EXPECT_EQ(source->data_for_test().prepare_calls, 2);
EXPECT_EQ(target->data_for_test().prepare_calls, 1);
}
TEST(task_graph_observer, business_dag_and_native_execution_share_node_identity) {