优化
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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 任务类型的累计统计。 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user