From a5cfb4baa97ff31f2deaf2c67cc59ebeb37d67d2 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Thu, 20 Aug 2026 17:03:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B8=E5=BF=83=E7=AC=AC=E4=B8=80=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Project_naming_conventions.md | 8 +- kernel/main.cmake | 3 +- .../{rely.hpp => Dependency_Graph.hpp} | 196 +++--- .../Dependency_Graph_Storage.hpp | 335 ++++++++++ .../double_buffer/{core.hpp => mechanism.hpp} | 211 ++++--- .../double_buffer/{object.hpp => model.hpp} | 216 +++---- .../src/kernel/double_buffer/rely_storage.hpp | 365 ----------- kernel/src/kernel/render.hpp | 583 ------------------ .../kernel/{render.cpp => render_common.cpp} | 92 +-- kernel/src/kernel/render_common.hpp | 103 ++++ kernel/src/kernel/render_common.ipp | 19 + kernel/src/kernel/renderable.cpp | 77 +++ kernel/src/kernel/renderable.hpp | 128 ++++ kernel/src/kernel/renderable.ipp | 128 ++++ kernel/src/kernel/scene.cpp | 7 + kernel/src/kernel/scene.hpp | 44 ++ kernel/src/kernel/scene.ipp | 211 +++++++ ...ely_test.cpp => Dependency_Graph_Test.cpp} | 71 +-- ...{double_buffer_test.cpp => model_test.cpp} | 22 +- kernel/src/test/object_test.cpp | 44 +- kernel/src/test/render_test.cpp | 47 +- 21 files changed, 1510 insertions(+), 1400 deletions(-) rename kernel/src/kernel/double_buffer/{rely.hpp => Dependency_Graph.hpp} (73%) create mode 100644 kernel/src/kernel/double_buffer/Dependency_Graph_Storage.hpp rename kernel/src/kernel/double_buffer/{core.hpp => mechanism.hpp} (72%) rename kernel/src/kernel/double_buffer/{object.hpp => model.hpp} (50%) delete mode 100644 kernel/src/kernel/double_buffer/rely_storage.hpp delete mode 100644 kernel/src/kernel/render.hpp rename kernel/src/kernel/{render.cpp => render_common.cpp} (79%) create mode 100644 kernel/src/kernel/render_common.hpp create mode 100644 kernel/src/kernel/render_common.ipp create mode 100644 kernel/src/kernel/renderable.cpp create mode 100644 kernel/src/kernel/renderable.hpp create mode 100644 kernel/src/kernel/renderable.ipp create mode 100644 kernel/src/kernel/scene.cpp create mode 100644 kernel/src/kernel/scene.hpp create mode 100644 kernel/src/kernel/scene.ipp rename kernel/src/test/{rely_test.cpp => Dependency_Graph_Test.cpp} (51%) rename kernel/src/test/{double_buffer_test.cpp => model_test.cpp} (65%) diff --git a/Project_naming_conventions.md b/Project_naming_conventions.md index c6c047d..986b068 100644 --- a/Project_naming_conventions.md +++ b/Project_naming_conventions.md @@ -16,11 +16,11 @@ frame_count default_capacity ``` -* 成员变量:`lower_snake_case_` +* 成员变量:`lower_snake_case` ```cpp -scene_ -pending_exception_ +scene +pending_exception ``` * 枚举值:`lower_snake_case` @@ -55,4 +55,4 @@ Low_Latency_Strategy.inl * 内部实现统一使用 `detail` 命名空间。 -**总规则:类型大写下划线,值和函数小写下划线,成员变量末尾加 `_`。** +**总规则:类型大写下划线,值和函数,成员变量小写下划线,字段注释保持行尾注释并且保持对齐** diff --git a/kernel/main.cmake b/kernel/main.cmake index 9810ddb..23aa271 100644 --- a/kernel/main.cmake +++ b/kernel/main.cmake @@ -56,7 +56,7 @@ install(TARGETS Aethera_Kernel RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") install(DIRECTORY "${Aethera_Kernel_source_dir}/renderive" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "*.inl") + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "*.ipp" PATTERN "*.inl") if (Aethera_BUILD_TESTS) add_custom_target(Aethera_Kernel_check COMMAND "${CMAKE_CTEST_COMMAND}" --test-dir "${CMAKE_BINARY_DIR}" @@ -64,4 +64,3 @@ if (Aethera_BUILD_TESTS) DEPENDS ${Aethera_Kernel_test_targets} USES_TERMINAL) endif () - diff --git a/kernel/src/kernel/double_buffer/rely.hpp b/kernel/src/kernel/double_buffer/Dependency_Graph.hpp similarity index 73% rename from kernel/src/kernel/double_buffer/rely.hpp rename to kernel/src/kernel/double_buffer/Dependency_Graph.hpp index 9fa9375..1637113 100644 --- a/kernel/src/kernel/double_buffer/rely.hpp +++ b/kernel/src/kernel/double_buffer/Dependency_Graph.hpp @@ -1,9 +1,9 @@ #pragma once -#include "core.hpp" +#include "mechanism.hpp" namespace double_buffer { -// Rely 是有向无环依赖图。Node 保存对象和直接前驱,Edge 额外记录触发 dirty 的来源键,用于状态/Buffer/阶段脏标记传播。 -struct Rely { - using Error = Rely_Error; +// Dependency_Graph 是有向无环依赖图。Node 保存对象和直接前驱,Edge 额外记录触发 dirty 的来源键,用于状态/Buffer/阶段脏标记传播。 +struct Dependency_Graph { + using Error = Dependency_Graph_Error; struct Node { using Bind = void (*)(Node*, Root*); Root* object; @@ -42,45 +42,45 @@ private: public: class View { protected: - const Rely* rely; - explicit View(const Rely& value) : rely(&value) {} - friend struct Rely; + const Dependency_Graph* dependency_graph; + explicit View(const Dependency_Graph& value) : dependency_graph(&value) {} + friend struct Dependency_Graph; public: bool empty() const { - return rely->empty(); + return dependency_graph->empty(); } std::size_t size() const { - return rely->size(); + return dependency_graph->size(); } const Node* find(Root* object) const { - return rely->find(object); + return dependency_graph->find(object); } bool contains(Root* object) const { - return rely->contains(object); + return dependency_graph->contains(object); } bool depends_on(Root* target, Root* source) const { - return rely->depends_on(target, source); + return dependency_graph->depends_on(target, source); } std::vector dependents(Root* source) const { - return rely->dependents(source); + return dependency_graph->dependents(source); } std::vector dependencies(Root* object) const { - return rely->dependencies(object); + return dependency_graph->dependencies(object); } std::vector find_edges(Root* target, Root* source) const { - return rely->find_edges(target, source); + return dependency_graph->find_edges(target, source); } - template requires std::invocable + template Callback> void for_each_edge(Callback&& callback) const { - rely->for_each_edge(std::forward(callback)); + dependency_graph->for_each_edge(std::forward(callback)); } }; - template requires Root_Derived + template class Typed_View : public View { public: using Object_Type = Object; using Private_Type = typename Object::Private; - explicit Typed_View(const Rely& value) : View(value) {} + explicit Typed_View(const Dependency_Graph& value) : View(value) {} [[nodiscard]] bool bound(const Node& node) const noexcept { return node.data != nullptr; } @@ -95,130 +95,120 @@ public: auto* node = this->find(root); return node ? private_data(*node) : nullptr; } - template requires std::invocable + template Callback> void for_each_bound(Callback&& callback) const { - this->rely->for_each( + this->dependency_graph->for_each( [&](const Node& node) { auto* data = private_data(node); if (data) std::invoke(callback, static_cast(node.object), *data); } ); } - template requires std::invocable + template Callback> std::expected for_each_topological_view(Callback&& callback) const { - return this->rely->for_each_topological( + return this->dependency_graph->for_each_topological( [&](const Node& node) { std::invoke(callback, *this, node); } ); } - template requires std::invocable + template Callback> void for_each(Callback&& callback) const { - this->rely->for_each(std::forward(callback)); + this->dependency_graph->for_each(std::forward(callback)); } }; - // Editor 只操作待提交图;结构是否合法由编辑完成后的拓扑验证统一判断。新增节点和依赖源必须满足 Attached,保证进入图的 Root* 一定已由 Attach_Object 绑定运行时 exchange。 - template requires Root_Derived + // Editor 只操作待提交图;结构是否合法由编辑完成后的拓扑验证统一判断,不在每个底层操作重复检查。 + template class Editor : public View { private: - Rely* edit_rely; + Dependency_Graph* edit_dependency_graph; const void* target_tag; const void* target_dirty_key; bool bind_node_data; public: using Object_Type = Bound_Object; - Editor(Rely& value, const void* tag, const void* dirty_key) : Editor(value, tag, dirty_key, true) {} - Editor(Rely& value, const void* tag, const void* dirty_key, bool value_bind_node_data) : View(value), - edit_rely(&value), - target_tag(tag), - target_dirty_key(dirty_key), - bind_node_data(value_bind_node_data) {} + Editor(Dependency_Graph& value, const void* tag, const void* dirty_key) : Editor(value, tag, dirty_key, true) {} + Editor(Dependency_Graph& value, const void* tag, const void* dirty_key, bool value_bind_node_data) : View(value), + edit_dependency_graph(&value), + target_tag(tag), + target_dirty_key(dirty_key), + bind_node_data(value_bind_node_data) {} void clear() { - edit_rely->reset(); + edit_dependency_graph->reset(); } - template requires detail::Bound_Rely_Object && std::derived_from + template Object> Node* add(Object* object) { - return edit_rely->template add_node(object, bind_node_data); + return edit_dependency_graph->template add_node(object, bind_node_data); } - template requires detail::Bound_Rely_Object && std::derived_from + template Object> void add(std::initializer_list objects) { - for (auto* object : objects) edit_rely->template add_node(object, bind_node_data); + for (auto* object : objects) edit_dependency_graph->template add_node(object, bind_node_data); } bool remove(Root* object) { - return edit_rely->remove_node(object); + return edit_dependency_graph->remove_node(object); } - template requires Root_Derived && std::derived_from + template Target> void clear_dependencies(Target* target) { - edit_rely->clear_dependencies_impl(target); + edit_dependency_graph->clear_dependencies_impl(target); } void remove_dependents(Root* source) { - edit_rely->remove_dependents_impl(source); + edit_dependency_graph->remove_dependents_impl(source); } void disconnect(Root* object) { - edit_rely->disconnect_impl(object); + edit_dependency_graph->disconnect_impl(object); } - template requires - detail::Bound_Rely_Object && detail::State_Rely_Source && - std::derived_from + template Target, detail::State_Dependency_Source Source> Node* add_dependency(Target* target, Source* source) { - return edit_rely->template add_dependency_runtime( + return edit_dependency_graph->template add_dependency_runtime( target, source, - detail::rely_id>(), + detail::dependency_id>(), target_tag, target_dirty_key, bind_node_data ); } - template requires - detail::Bound_Rely_Object && detail::Buffer_Rely_Source && - std::derived_from + template Target, detail::Buffer_Dependency_Source Source> Node* add_dependency(Target* target, Source* source) { - return edit_rely->template add_dependency_runtime( + return edit_dependency_graph->template add_dependency_runtime( target, source, - detail::rely_id>(), + detail::dependency_id>(), target_tag, target_dirty_key, bind_node_data ); } - template requires - detail::Bound_Rely_Object && detail::Rely_Object && - std::derived_from + template Target, detail::Dependency_Object Source> Node* add_dirty_dependency(Target* target, Source* source) { - return edit_rely->template add_dependency_runtime( + return edit_dependency_graph->template add_dependency_runtime( target, source, - detail::rely_id>(), + detail::dependency_id>(), target_tag, target_dirty_key, bind_node_data ); } - template requires - detail::Bound_Rely_Object && detail::State_Rely_Source && - std::derived_from + template Target, detail::State_Dependency_Source Source> bool remove_dependency(Target* target, Source* source) { - return edit_rely->remove_edge(target, source, detail::rely_id>(), target_tag); + return edit_dependency_graph->remove_edge(target, source, detail::dependency_id>(), target_tag); } - template requires - detail::Bound_Rely_Object && detail::Buffer_Rely_Source && - std::derived_from + template Target, detail::Buffer_Dependency_Source Source> bool remove_dependency(Target* target, Source* source) { - return edit_rely->remove_edge(target, source, detail::rely_id>(), target_tag); + return edit_dependency_graph->remove_edge(target, source, detail::dependency_id>(), target_tag); } - template requires Root_Derived && std::derived_from + template Target> bool remove_dependency(Target* target, Root* source) { - return edit_rely->remove_dependency_impl(target, source); + return edit_dependency_graph->remove_dependency_impl(target, source); } }; private: friend struct Root; template - friend struct detail::Rely_Storage; + friend struct detail::Dependency_Graph_Storage; template - friend struct detail::Rely_Build_Storage; + friend struct detail::Dependency_Graph_Build_Storage; std::pmr::memory_resource* pmr_resource; std::pmr::list list; std::pmr::unordered_map nodes; @@ -226,7 +216,7 @@ private: std::pmr::unordered_multimap dirty_edges; std::uint64_t structure_revision{}; mutable bool bound{}; - Rely* mirror{}; + Dependency_Graph* mirror{}; Node* emplace_node(Root* object, Node::Bind bind) { auto current = nodes.find(object); if (current != nodes.end()) { @@ -298,10 +288,10 @@ private: } template static Node::Bind node_bind() { - if constexpr (std::derived_from && detail::Bound_Rely_Object) { + if constexpr (std::derived_from && detail::Bound_Dependency_Object) { return [](Node* node, Root* root) { auto* object = static_cast(root); - if constexpr (requires { object->bind_rely_object(object); }) object->bind_rely_object(object); + if constexpr (requires { object->bind_dependency_graph_object(object); }) object->bind_dependency_graph_object(object); node->data = static_cast(&object->d); }; } @@ -334,7 +324,7 @@ private: nodes.clear(); list.clear(); } - void pair_with(Rely& other) noexcept { + void pair_with(Dependency_Graph& other) noexcept { mirror = &other; other.mirror = this; } @@ -347,13 +337,13 @@ private: clear_data(); ++structure_revision; } - void copy_from(const Rely& other) { + void copy_from(const Dependency_Graph& other) { for (const auto& node : other.list) emplace_node(node.object, node.bind)->data = node.data; edges = other.edges; structure_revision = other.structure_revision; rebuild(); } - void swap_data(Rely& other) noexcept { + void swap_data(Dependency_Graph& other) noexcept { list.swap(other.list); nodes.swap(other.nodes); edges.swap(other.edges); @@ -453,34 +443,34 @@ private: rebuild(); return true; } - Rely(const Rely& other, std::pmr::memory_resource* resource) : Rely(resource) { + Dependency_Graph(const Dependency_Graph& other, std::pmr::memory_resource* resource) : Dependency_Graph(resource) { copy_from(other); } public: using allocator_type = std::pmr::polymorphic_allocator; - Rely() : Rely(std::pmr::get_default_resource()) {} - Rely(std::allocator_arg_t, const allocator_type& allocator) : Rely(allocator.resource()) {} - explicit Rely(std::pmr::memory_resource* resource) : pmr_resource(resource), - list(resource), - nodes(resource), - edges(resource), - dirty_edges(resource) {} - Rely(const Rely& other) : Rely(other.pmr_resource) { + Dependency_Graph() : Dependency_Graph(std::pmr::get_default_resource()) {} + Dependency_Graph(std::allocator_arg_t, const allocator_type& allocator) : Dependency_Graph(allocator.resource()) {} + explicit Dependency_Graph(std::pmr::memory_resource* resource) : pmr_resource(resource), + list(resource), + nodes(resource), + edges(resource), + dirty_edges(resource) {} + Dependency_Graph(const Dependency_Graph& other) : Dependency_Graph(other.pmr_resource) { copy_from(other); } - Rely& operator=(const Rely& other) { + Dependency_Graph& operator=(const Dependency_Graph& other) { if (this == &other) return *this; unbind(); clear_data(); copy_from(other); return *this; } - Rely(Rely&&) = delete; - Rely& operator=(Rely&&) = delete; - ~Rely() { + Dependency_Graph(Dependency_Graph&&) = delete; + Dependency_Graph& operator=(Dependency_Graph&&) = delete; + ~Dependency_Graph() { unbind(); } - void swap(Rely& other) { + void swap(Dependency_Graph& other) { bool this_bound = bound; bool other_bound = other.bound; unbind(); @@ -489,8 +479,8 @@ public: swap_data(other); } else { - Rely this_value(*this, other.pmr_resource); - Rely other_value(other, pmr_resource); + Dependency_Graph this_value(*this, other.pmr_resource); + Dependency_Graph other_value(other, pmr_resource); clear_data(); other.clear_data(); copy_from(other_value); @@ -501,12 +491,12 @@ public: } void bind() const { if (bound) return; - for (const auto& node : list) node.object->bind_rely(this); + for (const auto& node : list) node.object->bind_dependency_graph(this); bound = true; } void unbind() const { if (!bound) return; - for (const auto& node : list) node.object->unbind_rely(this); + for (const auto& node : list) node.object->unbind_dependency_graph(this); bound = false; } bool empty() const { @@ -562,7 +552,7 @@ public: } return result; } - template requires std::invocable + template Callback> void for_each_edge(Callback&& callback) const { for (const auto& edge : edges) std::invoke(callback, edge); } @@ -601,18 +591,18 @@ public: } return result; } - template requires std::invocable + template Callback> void for_each(Callback&& callback) const { for (const auto& node : list) std::invoke(callback, node); } - template requires std::invocable + template Callback> std::expected for_each_topological(Callback&& callback) const { auto result = topological_order(); if (!result) return std::unexpected(result.error()); for (const auto* node : *result) std::invoke(callback, *node); return {}; } - template requires std::invocable + template Callback> std::expected for_each_topological_view(Callback&& callback) const { View view(*this); return for_each_topological( @@ -634,10 +624,10 @@ public: } }; inline Root::~Root() { - while (!rely_graphs.empty()) { - auto* rely = rely_graphs.back(); - rely_graphs.pop_back(); - const_cast(rely)->detach_destroyed(this); + while (!dependency_graphs.empty()) { + auto* dependency_graph = dependency_graphs.back(); + dependency_graphs.pop_back(); + const_cast(dependency_graph)->detach_destroyed(this); } } } diff --git a/kernel/src/kernel/double_buffer/Dependency_Graph_Storage.hpp b/kernel/src/kernel/double_buffer/Dependency_Graph_Storage.hpp new file mode 100644 index 0000000..a945aef --- /dev/null +++ b/kernel/src/kernel/double_buffer/Dependency_Graph_Storage.hpp @@ -0,0 +1,335 @@ +#pragma once +#include "Dependency_Graph.hpp" +namespace double_buffer { +namespace detail { +template +concept Dependency_Graph_Edit_Callback_For = Dependency_Graph_List && Unique_Types::value && std::invocable< + Callback, + Dependency_Graph::Editor>&... +>; +template +concept Dependency_Graph_View_Node_Callback = Root_Derived && std::invocable&, const Dependency_Graph::Node&>; +template +concept Dependency_Graph_Current_Callback = std::invocable; +template +concept Dependency_Graph_Access_Callback_For = Dependency_Graph_List && Unique_Types::value && std::invocable&...>; +} +inline bool Root::mark_dirty_id(const void* tag, const void* dirty_key) { + if (std::find(dirty_tags.begin(), dirty_tags.end(), tag) != dirty_tags.end()) return false; + dirty_tags.push_back(tag); + for (const auto* dependency_graph : dependency_graphs) dependency_graph->emit(this, dirty_key); + return true; +} +inline void Root::emit_dependency(const void* key) { + for (const auto* dependency_graph : dependency_graphs) dependency_graph->emit(this, key); +} +namespace detail { +template +struct Dependency_Graph_Storage; +// Dependency_Graph_Storage 为每个 Dependency_Graph_Type 保存 pending/current 两份图;编辑只落到 pending,advance 后 current 成为执行侧并同步新的编辑基线。 +template +struct Dependency_Graph_Storage> { + using allocator_type = std::pmr::polymorphic_allocator; +private: + template + struct Dependency_Graph_Buffer : Commit_Double_Buffer, Mechanism { + using Base = Commit_Double_Buffer; + using allocator_type = typename Base::allocator_type; + Dependency_Graph_Buffer() = default; + Dependency_Graph_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} + void mark_structure_dirty() noexcept { + this->mark_dirty(); + } + }; + std::tuple...> dependency_graphs; + void pair_buffers() { + std::apply( + [](auto&... buffer) { + (buffer.pending->pair_with(*buffer.current), ...); + }, + dependency_graphs + ); + } + template + static consteval std::size_t index() { + return tag_index(); + } + template + std::expected validate_pending_impl() const { + if constexpr (I == sizeof...(Dependency_Graph_Types)) { + return {}; + } + else { + auto check = std::get(dependency_graphs).pending->topological_order(); + if (!check) return std::unexpected(check.error()); + return validate_pending_impl(); + } + } + template + static void advance_one(Dependency_Graph_Buffer& buffer) { + // pending/current revision 相同表示上次推进后没有结构编辑,无需再次深拷贝整张依赖图。 + if (buffer.pending->structure_revision == buffer.current->structure_revision) return; + buffer.current->unbind(); + buffer.advance(); + buffer.current->bind(); + } +public: + Dependency_Graph_Storage() : Dependency_Graph_Storage(std::allocator_arg, allocator_type{std::pmr::get_default_resource()}) {} + Dependency_Graph_Storage(std::allocator_arg_t, const allocator_type& allocator) : dependency_graphs(std::allocator_arg, allocator) { + pair_buffers(); + } + template > Tag> + auto& get() { + return std::get()>(dependency_graphs); + } + template > Tag> + const auto& get() const { + return std::get()>(dependency_graphs); + } + template >... Tags, Dependency_Graph_Access_Callback_For, Tags...> Callback> + void access(Callback&& callback) { + std::invoke( + std::forward(callback), + static_cast>&>(get())... + ); + } + // 编辑采用临时图事务:先复制 pending,回调修改临时图并统一验证,成功后才替换 pending,失败不会污染现有结构。 + template >... Tags, Dependency_Graph_Edit_Callback_For, Tags...> Callback> + std::expected edit(Callback&& callback) { + std::tuple...> next(*get().pending...); + auto editors = [&](std::index_sequence) { + return std::tuple{ + Dependency_Graph::Editor>>( + std::get(next), + dependency_id(), + dependency_id>(), + true + )... + }; + }(std::index_sequence_for{}); + std::apply( + [&](auto&... values) { + std::invoke(std::forward(callback), values...); + }, + editors + ); + std::expected result; + std::apply( + [&](const auto&... value) { + ( + [&] { + if (!result) return; + auto check = value.topological_order(); + if (!check) result = std::unexpected(check.error()); + }(), + ... + ); + }, + next + ); + if (!result) return result; + auto changed = [&](std::index_sequence) { + return std::tuple{ + (std::get(next).structure_revision != get().pending->structure_revision)... + }; + }(std::index_sequence_for{}); + [&](std::index_sequence) { + ( + [&] { + auto& buffer = get(); + buffer.pending->swap(std::get(next)); + if (std::get(changed)) buffer.mark_structure_dirty(); + }(), + ... + ); + }(std::index_sequence_for{}); + return {}; + } + std::expected validate_pending() const { + return validate_pending_impl(); + } + void advance() { + std::apply( + [](auto&... buffer) { + (advance_one(buffer), ...); + }, + dependency_graphs + ); + } + template + void for_each_current(Callback&& callback) const { + std::apply( + [&](const auto&... buffer) { + (std::invoke(callback, std::as_const(*buffer.current)), ...); + }, + dependency_graphs + ); + } +}; +// Builder 使用独立依赖图完成对象构造期编辑;build 成功时一次性提交到对象的正式 Dependency_Graph_Storage。 +template +struct Dependency_Graph_Build_Storage> { + using allocator_type = std::pmr::polymorphic_allocator; +private: + std::tuple...> dependency_graphs; + template + static consteval std::size_t index() { + return tag_index(); + } + template + std::expected validate_impl() const { + if constexpr (I == sizeof...(Dependency_Graph_Types)) { + return {}; + } + else { + auto result = std::get(dependency_graphs).topological_order(); + if (!result) return std::unexpected(result.error()); + return validate_impl(); + } + } + template + void commit_one(Dependency_Graph_Storage>& storage) { + using Tag = typename Graph_Declaration::Tag_Type; + auto& buffer = storage.template get(); + buffer.pending->swap(get()); + buffer.pending->bind_nodes(); + *buffer.current = *buffer.pending; + buffer.current->bind(); + buffer.mark_structure_dirty(); + } +public: + Dependency_Graph_Build_Storage() : Dependency_Graph_Build_Storage(std::pmr::get_default_resource()) {} + explicit Dependency_Graph_Build_Storage(std::pmr::memory_resource* resource) : dependency_graphs(std::allocator_arg, allocator_type{resource}) {} + template > Tag> + Dependency_Graph& get() { + return std::get()>(dependency_graphs); + } + template > Tag> + const Dependency_Graph& get() const { + return std::get()>(dependency_graphs); + } + template >... Tags, Dependency_Graph_Edit_Callback_For, Tags...> Callback> + void edit(Callback&& callback) { + auto editors = std::tuple{ + Dependency_Graph::Editor>>( + get(), + dependency_id(), + dependency_id>(), + false + )... + }; + std::apply( + [&](auto&... values) { + std::invoke(std::forward(callback), values...); + }, + editors + ); + } + std::expected validate() const { + return validate_impl(); + } + void commit(Dependency_Graph_Storage>& storage) { + (commit_one(storage), ...); + } +}; +} +template +struct Root::Builder { + using Prop = typename Object::Prop; + std::unique_ptr object; + detail::Dependency_Graph_Build_Storage dependency_graph_storage; + template requires std::constructible_from + explicit Builder(Args&&... args) : object(std::make_unique(std::forward(args)...)), + dependency_graph_storage(object->memory_resource()) {} + Builder& set_pmr(Pmr pmr) { + object->set_pmr_resource(pmr); + return *this; + } + Builder& set_pmr(Pmr::Resource& resource) { + return set_pmr(Pmr{resource}); + } + Builder& set_pmr(Pmr::Resource* resource) { + return set_pmr(Pmr{resource}); + } + template Value> + Builder& set(Member Owner::* member, Value&& value) { + object->set(member, std::forward(value)); + return *this; + } + template Tag, detail::Buffer_Value_Settable Value> + Builder& set(Value&& value) { + object->template initialize_buffer(std::forward(value)); + return *this; + } + template ... Dependency_Graph_Tags, detail::Dependency_Graph_Edit_Callback_For Callback> + Builder& edit_dependency_graph(Callback&& callback) { + dependency_graph_storage.template edit(std::forward(callback)); + return *this; + } + template Dependency_Graph_Tag, detail::Bound_Dependency_Graph_Target> Node_Object> + Builder& add_dependency_node(Node_Object* node) { + return edit_dependency_graph( + [&](auto& editor) { + editor.add(node); + } + ); + } + template Dependency_Graph_Tag, Root_Derived Node_Object> + Builder& remove_dependency_node(Node_Object* node) { + return edit_dependency_graph( + [&](auto& editor) { + editor.remove(node); + } + ); + } + template Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target> Target, detail::State_Dependency_Source Source> + Builder& add_dependency(Target* target, Source* source) { + return edit_dependency_graph( + [&](auto& editor) { + editor.template add_dependency(target, source); + } + ); + } + template Dependency_Graph_Tag, typename Buffer_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Buffer_Dependency_Source Source> + Builder& add_dependency(Target* target, Source* source) { + return edit_dependency_graph( + [&](auto& editor) { + editor.template add_dependency(target, source); + } + ); + } + template Dependency_Graph_Tag, typename Source_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Dependency_Object Source> + Builder& add_dirty_dependency(Target* target, Source* source) { + return edit_dependency_graph( + [&](auto& editor) { + editor.template add_dirty_dependency(target, source); + } + ); + } + template Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target> Target, detail::State_Dependency_Source Source> + Builder& remove_dependency(Target* target, Source* source) { + return edit_dependency_graph( + [&](auto& editor) { + editor.template remove_dependency(target, source); + } + ); + } + template Dependency_Graph_Tag, typename Buffer_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Buffer_Dependency_Source Source> + Builder& remove_dependency(Target* target, Source* source) { + return edit_dependency_graph( + [&](auto& editor) { + editor.template remove_dependency(target, source); + } + ); + } + std::expected, Dependency_Graph_Error> build() { + auto validate_result = validate(); + if (!validate_result) return std::unexpected(validate_result.error()); + dependency_graph_storage.commit(object->d.dependency_graph_storage); + return std::move(object); + } + std::expected validate() const { + return dependency_graph_storage.validate(); + } +}; +} diff --git a/kernel/src/kernel/double_buffer/core.hpp b/kernel/src/kernel/double_buffer/mechanism.hpp similarity index 72% rename from kernel/src/kernel/double_buffer/core.hpp rename to kernel/src/kernel/double_buffer/mechanism.hpp index d6bc36e..590cd7f 100644 --- a/kernel/src/kernel/double_buffer/core.hpp +++ b/kernel/src/kernel/double_buffer/mechanism.hpp @@ -103,7 +103,7 @@ struct Pinned { Pinned(Pinned&&) = delete; Pinned& operator=(Pinned&&) = delete; }; -// Double_Buffer 只负责交换 pending/current 两个视图,不复制数据;需要交换后同步基线的场景使用 Synchronized_Double_Buffer。 +// Double_Buffer 只负责推进 pending/current 两个视图,不复制数据;需要推进后同步基线时使用 Commit_Double_Buffer 或 Publish_Double_Buffer。 template struct Double_Buffer : Pinned { using allocator_type = std::pmr::polymorphic_allocator; @@ -116,28 +116,32 @@ public: Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : buf(std::allocator_arg, allocator), pending(&std::get<0>(buf)), current(&std::get<1>(buf)) {} - void exchange() { + void advance() { std::swap(pending, current); } }; -enum class Buffer_Direction { - inward, - outward -}; -/* - * 同步双缓冲保留 Double_Buffer 的交换语义,并在交换后把发布值复制回写入侧。 - * inward: pending 是外部写入侧,current 是内部消费侧;outward: current 是内部写入侧,pending 是外部发布侧。 - */ -template -struct Synchronized_Double_Buffer : Double_Buffer { +// Commit_Double_Buffer 用于外部提交到内部:pending 接收外部写入,推进后 current 成为内部读值,再把 current 回写 pending 作为下一次编辑基线。 +template +struct Commit_Double_Buffer : Double_Buffer { using Base = Double_Buffer; using allocator_type = typename Base::allocator_type; - Synchronized_Double_Buffer() = default; - Synchronized_Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} - void exchange() { - Base::exchange(); - if constexpr (Direction == Buffer_Direction::inward) *this->pending = *this->current; - else *this->current = *this->pending; + Commit_Double_Buffer() = default; + Commit_Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} + void advance() { + Base::advance(); + *this->pending = *this->current; + } +}; +// Publish_Double_Buffer 用于内部发布到外部:current 接收内部写入,推进后 pending 成为外部读值,再把 pending 回写 current 作为下一次内部写入基线。 +template +struct Publish_Double_Buffer : Double_Buffer { + using Base = Double_Buffer; + using allocator_type = typename Base::allocator_type; + Publish_Double_Buffer() = default; + Publish_Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} + void advance() { + Base::advance(); + *this->current = *this->pending; } }; namespace detail { @@ -146,23 +150,21 @@ struct State_Root { }; } // State_Type 用 Tag 标记每一层状态,Prev_State 把 CRTP 继承链上的状态按层串起来,供精确回调和类型约束使用。 -template -requires Prop_State +template struct State_Type : Prev { using Tag_Type = Tag; using Prev_State = Prev; bool operator==(const State_Type&) const = default; }; -template -requires Prop_State +template struct Tagged_Buffer { using Tag_Type = Tag; using Value_Type = Value; }; struct Root; -// Rely_Type 描述一类依赖图及其允许绑定的对象类型;dirty 只表示该依赖阶段需要重新处理,不承担图结构所有权。 +// Dependency_Graph_Type 描述一类依赖图及其允许绑定的对象类型;dirty 只表示该依赖阶段需要重新处理,不承担图结构所有权。 template -struct Rely_Type { +struct Dependency_Graph_Type { using Tag_Type = Tag; using Object_Type = Object; private: @@ -197,9 +199,9 @@ struct Is_Tagged_Buffer : std::false_type {}; template struct Is_Tagged_Buffer> : std::true_type {}; template -struct Is_Rely_Type : std::false_type {}; +struct Is_Dependency_Graph_Type : std::false_type {}; template -struct Is_Rely_Type> : std::true_type {}; +struct Is_Dependency_Graph_Type> : std::true_type {}; template struct Is_State_Type : std::false_type {}; template @@ -207,11 +209,11 @@ struct Is_State_Type> : std::true_type {}; template concept Buffer_Type = Is_Tagged_Buffer::value; template -concept Rely_Mechanism = Is_Rely_Type::value; +concept Dependency_Graph_Mechanism = Is_Dependency_Graph_Type::value; template concept State_Mechanism = Is_State_Type::value; template -concept Mechanism_Type = Buffer_Type || Rely_Mechanism || State_Mechanism; +concept Mechanism_Type = Buffer_Type || Dependency_Graph_Mechanism || State_Mechanism; template concept Tagged_State = Prop_State && requires { typename Value::Tag_Type; @@ -254,7 +256,7 @@ concept State_Member_Settable = requires(State& state, Value&& value) { template using Mechanism_Buffer_Tuple = std::conditional_t, std::tuple, std::tuple<>>; template -using Mechanism_Rely_Tuple = std::conditional_t, std::tuple, std::tuple<>>; +using Mechanism_Dependency_Graph_Tuple = std::conditional_t, std::tuple, std::tuple<>>; template using Mechanism_State_Tuple = std::conditional_t, std::tuple, std::tuple<>>; template @@ -277,30 +279,30 @@ struct Is_Buffer_List> : Tagged_List_Check< Buffers... > {}; template -struct Is_Rely_List : std::false_type {}; -template -struct Is_Rely_List> : Tagged_List_Check< - (Rely_Mechanism && ...) && (std::derived_from && ...), - Relies... +struct Is_Dependency_Graph_List : std::false_type {}; +template +struct Is_Dependency_Graph_List> : Tagged_List_Check< + (Dependency_Graph_Mechanism && ...) && (std::derived_from && ...), + Dependency_Graph_Types... > {}; template concept Buffer_List = Is_Buffer_List::value; template -concept Rely_List = Is_Rely_List::value; +concept Dependency_Graph_List = Is_Dependency_Graph_List::value; template concept Buffer_Tag_In = requires { requires Buffer_List; requires Has_Tag::value; }; template -concept Rely_Tag_In = requires { - requires Rely_List; +concept Dependency_Graph_Tag_In = requires { + requires Dependency_Graph_List; requires Has_Tag::value; }; template -struct Rely_Type_By_Tag; -template -struct Rely_Type_By_Tag> { +struct Dependency_Graph_Declaration_By_Tag; +template +struct Dependency_Graph_Declaration_By_Tag> { private: template struct Find { @@ -315,12 +317,12 @@ private: using Type = Last; }; public: - using Type = typename Find::Type; + using Type = typename Find::Type; }; template -using Rely_Declaration = typename Rely_Type_By_Tag::Type; +using Dependency_Graph_Declaration = typename Dependency_Graph_Declaration_By_Tag::Type; template -using Rely_Bound_Object = typename Rely_Declaration::Object_Type; +using Dependency_Graph_Bound_Object = typename Dependency_Graph_Declaration::Object_Type; template struct Is_State_List : std::false_type {}; template @@ -399,17 +401,17 @@ private: return tag_index_v; } public: - template requires State_Tag_In + template Tag, typename Callback> void set(Callback&& callback) { constexpr auto value_index = index(); using Layer = std::tuple_element_t; std::get(callbacks) = std::function{std::forward(callback)}; } - template requires State_Tag_In + template Tag> void clear() { std::get()>(callbacks) = {}; } - template requires State_Tag_In + template Tag> void notify(const State_T& state) { constexpr auto value_index = index(); using Layer = std::tuple_element_t; @@ -435,7 +437,7 @@ concept Buffer_Value_Settable = requires(Buffer_Value& target, Value requires Buffer_Tag_In; target = std::forward(value); }; -// Buffer_Storage 保存普通 Tagged_Buffer 的双缓冲;它只执行基础交换,不做 State/Prop 那种交换后同步。 +// Buffer_Storage 保存普通 Tagged_Buffer 的双缓冲;它只执行基础推进,不做 State/Prop 那种推进后同步。 template struct Buffer_Storage; template @@ -450,39 +452,63 @@ private: public: Buffer_Storage() : Buffer_Storage(std::allocator_arg, allocator_type{std::pmr::get_default_resource()}) {} Buffer_Storage(std::allocator_arg_t, const allocator_type& allocator) : buffers(std::allocator_arg, allocator) {} - template requires Buffer_Tag_In> + template > Tag> auto& get() { return std::get()>(buffers); } - template requires Buffer_Tag_In> + template > Tag> const auto& get() const { return std::get()>(buffers); } - void exchange() { + void advance() { std::apply( [](auto&... buffer) { - (buffer.exchange(), ...); + (buffer.advance(), ...); }, buffers ); } }; } +/* + * State_Access 是 State 继承链的按 Tag 访问视图。 + * State_Type 为 const 时 get() 返回只读层,否则返回可写层;视图不暴露 pending/current 缓冲角色。 + */ +template requires detail::State_Chain> +class State_Access { +private: + using Value_Type = std::remove_const_t; + State_Type* state; + template requires detail::State_Chain> + friend class State_Access; +public: + explicit State_Access(State_Type& value) noexcept : state(&value) {} + template requires std::convertible_to + State_Access(const State_Access& source) noexcept : state(source.state) {} + template requires detail::State_Tag_In> + [[nodiscard]] decltype(auto) get() const noexcept { + using Layer = detail::State_Value; + if constexpr (std::is_const_v) return static_cast(*state); + else return static_cast(*state); + } +}; +template requires detail::State_Chain> +State_Access(State_Type&) -> State_Access; namespace detail { template -struct State_Rely_Key {}; +struct State_Dependency_Key {}; template -struct Buffer_Rely_Key {}; +struct Buffer_Dependency_Key {}; template -struct Dirty_Rely_Key {}; +struct Dirty_Dependency_Key {}; template -inline char rely_token; +inline char dependency_token; template -constexpr const void* rely_id() { - return &rely_token; +constexpr const void* dependency_id() { + return &dependency_token; } } -struct Rely; +struct Dependency_Graph; template concept Root_Derived = std::derived_from; template @@ -494,15 +520,15 @@ concept Object_Core = requires { requires Prop_State; requires detail::State_Chain_Matches; requires detail::Buffer_List; - requires detail::Rely_List; + requires detail::Dependency_Graph_List; }; namespace detail { template -struct Rely_Storage; +struct Dependency_Graph_Storage; template -struct Rely_Build_Storage; +struct Dependency_Graph_Build_Storage; } -enum class Rely_Error { +enum class Dependency_Graph_Error { self_dependency, missing_dependency, cycle @@ -510,7 +536,7 @@ enum class Rely_Error { struct Root_State_Tag {}; struct Root { using Buffers = std::tuple<>; - using Relies = std::tuple<>; + using Dependency_Graph_Types = std::tuple<>; using States = std::tuple>; struct Prop {}; struct State : State_Type { @@ -518,35 +544,35 @@ struct Root { }; private: using Call = void (*)(Root*); - Call exchange_call{}; + Call advance_call{}; detail::Pmr_Resource pmr_resource; - std::pmr::vector rely_graphs; + std::pmr::vector dependency_graphs; std::pmr::vector dirty_tags; void set_pmr_resource(Pmr pmr) noexcept { pmr_resource.set_resource(pmr.resource()); } bool mark_dirty_id(const void* tag, const void* dirty_key); - void emit_rely(const void* key); - void bind_rely(const Rely* rely) { - if (std::find(rely_graphs.begin(), rely_graphs.end(), rely) == rely_graphs.end()) rely_graphs.push_back(rely); + void emit_dependency(const void* key); + void bind_dependency_graph(const Dependency_Graph* dependency_graph) { + if (std::find(dependency_graphs.begin(), dependency_graphs.end(), dependency_graph) == dependency_graphs.end()) dependency_graphs.push_back(dependency_graph); } - void unbind_rely(const Rely* rely) { - std::erase(rely_graphs, rely); + void unbind_dependency_graph(const Dependency_Graph* dependency_graph) { + std::erase(dependency_graphs, dependency_graph); } - friend struct Rely; + friend struct Dependency_Graph; protected: template void bind_object_crtp() { - exchange_call = [](Root* root) { - static_cast(root)->exchange(); + advance_call = [](Root* root) { + static_cast(root)->advance(); }; } - void emit_rely_source(const void* key) { - emit_rely(key); + void emit_dependency_source(const void* key) { + emit_dependency(key); } public: Root() : pmr_resource(), - rely_graphs(&pmr_resource), + dependency_graphs(&pmr_resource), dirty_tags(&pmr_resource) {} ~Root(); [[nodiscard]] std::pmr::memory_resource* memory_resource() const noexcept { @@ -561,21 +587,21 @@ public: } template [[nodiscard]] bool dirty() const { - return std::find(dirty_tags.begin(), dirty_tags.end(), detail::rely_id()) != dirty_tags.end(); + return std::find(dirty_tags.begin(), dirty_tags.end(), detail::dependency_id()) != dirty_tags.end(); } template void mark_dirty() { - mark_dirty_id(detail::rely_id(), detail::rely_id>()); + mark_dirty_id(detail::dependency_id(), detail::dependency_id>()); } template bool take_dirty() { - auto current = std::find(dirty_tags.begin(), dirty_tags.end(), detail::rely_id()); + auto current = std::find(dirty_tags.begin(), dirty_tags.end(), detail::dependency_id()); if (current == dirty_tags.end()) return false; dirty_tags.erase(current); return true; } - void exchange_object() { - exchange_call(this); + void advance_object() { + advance_call(this); } template struct Builder; @@ -586,36 +612,39 @@ concept Object_Root = requires { requires Object_Core; typename T::template Builder; }; -// Attached 约束真正进入运行时依赖图的对象必须来自 Attach_Object。Rely_Type 只声明允许绑定的基类型,实际节点在加入图时必须满足该概念。 +// Attached 是实际运行时对象的编译期标记:只有经过 Attach_Object 包装的对象才拥有 Attached_Object,并允许进入会触发 advance_object 的 Dependency_Graph 图。 template -concept Attached = requires { - requires Object_Core; +concept Attached = Object_Core && requires { typename T::Attached_Object; requires Object_Root; requires std::derived_from; }; namespace detail { -// Rely_Object 表示已经附着运行时数据、可以安全进入依赖图的实际对象类型。图内保存 Root* 后静态类型会被擦除,因此该约束必须在入图边界完成。 template -concept Rely_Object = Attached && requires { +concept Dependency_Object = Attached && requires { + requires Root_Derived; typename T::State; typename T::Buffers; requires Prop_State; requires Buffer_List; }; template -concept Bound_Rely_Object = Rely_Object && requires(T* object) { +concept Bound_Dependency_Object = Dependency_Object && requires(T* object) { typename T::Private; object->d; }; +template +concept Bound_Dependency_Graph_Target = Bound_Dependency_Object && std::derived_from; +template +concept Dependency_Graph_Target = Root_Derived && std::derived_from; template -concept State_Rely_Source = requires { - requires Rely_Object; +concept State_Dependency_Source = requires { + requires Dependency_Object; requires State_Member; }; template -concept Buffer_Rely_Source = requires { - requires Rely_Object; +concept Buffer_Dependency_Source = requires { + requires Dependency_Object; requires Buffer_Tag_In; }; } diff --git a/kernel/src/kernel/double_buffer/object.hpp b/kernel/src/kernel/double_buffer/model.hpp similarity index 50% rename from kernel/src/kernel/double_buffer/object.hpp rename to kernel/src/kernel/double_buffer/model.hpp index 445e336..6f82435 100644 --- a/kernel/src/kernel/double_buffer/object.hpp +++ b/kernel/src/kernel/double_buffer/model.hpp @@ -1,5 +1,5 @@ #pragma once -#include "rely_storage.hpp" +#include "Dependency_Graph_Storage.hpp" namespace double_buffer { template concept Object = requires { @@ -29,9 +29,9 @@ using Impl_Buffers = decltype(std::tuple_cat( std::declval>()... )); template -using Impl_Relies = decltype(std::tuple_cat( - std::declval(), - std::declval>()... +using Impl_Dependency_Graph_Types = decltype(std::tuple_cat( + std::declval(), + std::declval>()... )); template using Local_States = decltype(std::tuple_cat( @@ -48,19 +48,21 @@ using Impl_State_Base = Rebind_State_T< typename Base::State >; template -concept Impl_Mechanisms = requires { - requires Object_Root; - requires (Mechanism_Type && ...); +concept Impl_Mechanisms = Object_Root && (Mechanism_Type && ...) && requires { requires std::tuple_size_v> == 1; requires Buffer_List>; - requires Rely_List>; + requires Dependency_Graph_List>; requires State_List>; }; +template +concept Process_Callback_For = requires(Private& private_data, Object_T* object, Callback&& callback) { + private_data.process(object, std::forward(callback)); +}; } -// Impl 在编译期把 Buffer/Rely/State 三类机制叠加到继承链;每一层只声明自己的机制,最终类型汇总完整能力。 -template requires +// Impl 在编译期把 Buffer/Dependency_Graph/State 三类机制叠加到继承链;每一层只声明自己的机制,最终类型汇总完整能力。 +template requires detail::Impl_Mechanisms -struct Impl : Base { +struct Def : Base { using This_Object = Self; using Prev_Object = Base; using Prev_Prop = typename Base::Prop; @@ -68,21 +70,33 @@ struct Impl : Base { using Prev_Builder = typename Base::template Builder; using State_Declaration = std::tuple_element_t<0, detail::Local_States>; using State_Tag = typename State_Declaration::Tag_Type; - template requires std::same_as + template Tag> using Prev_State = detail::Rebind_State_T; using Base_Private = typename Base::Private; using Buffers = detail::Impl_Buffers; - using Relies = detail::Impl_Relies; + using Dependency_Graph_Types = detail::Impl_Dependency_Graph_Types; using States = detail::Impl_States; struct Private : Base_Private { + /* CRTP 可覆盖:写入 State 成员前按基类到派生类顺序调用;pending_states.get() 返回对应可写状态层。 */ template - void before_state_set(Self*, Member Owner::*, State_T*) {} + void before_state_set(Self* object, Member Owner::* member, State_Access pending_states) {} + /* CRTP 可覆盖:写入 State 成员后按派生类到基类顺序调用;member 是本次写入的成员指针。 */ template - void after_state_set(Self*, Member Owner::*, State_T*) {} + void after_state_set(Self* object, Member Owner::* member, State_Access pending_states) {} + /* CRTP 可覆盖:缓冲推进前按基类到派生类顺序调用;pending_states 可写,current_states 只读。 */ template - void before_exchange(Self*, Prop_T*, State_T*, const Prop_T*, const State_T*) {} + void before_advance(Self* object, + Prop_T* pending_prop, + State_Access pending_states, + const Prop_T* current_prop, + State_Access current_states) {} + /* CRTP 可覆盖:所有缓冲完成推进后按派生类到基类顺序调用;两侧 State 均通过 Tag 选择状态层。 */ template - void after_exchange(Self*, Prop_T*, State_T*, const Prop_T*, const State_T*) {} + void after_advance(Self* object, + Prop_T* pending_prop, + State_Access pending_states, + const Prop_T* current_prop, + State_Access current_states) {} }; using Prev_Private = Private; template @@ -94,30 +108,30 @@ struct Impl : Base { } } }; -// Attach_Object 是对象运行时唯一数据入口:Prop 向外发布,State 向内提交,普通 Buffer 只交换,Rely 负责依赖图同步。 -template requires Object_Root && Lockable -struct Attach_Object : Obj { +// Attach_Object 是对象运行时唯一数据入口:Prop 向外发布,State 向内提交,普通 Buffer 只推进,Dependency_Graph 负责依赖图同步。 +template +struct Impl : Obj { using Prop = typename Obj::Prop; using State = typename Obj::State; using Buffers = typename Obj::Buffers; - using Relies = typename Obj::Relies; + using Dependency_Graph_Types = typename Obj::Dependency_Graph_Types; using States = typename Obj::States; - using Builder = typename Obj::template Builder; + using Builder = typename Obj::template Builder; using Attached_Object = Obj; - struct Private : Obj::Private, Synchronized_Double_Buffer { + struct Private : Obj::Private, Publish_Double_Buffer { using Allocator = std::pmr::polymorphic_allocator; - Synchronized_Double_Buffer state; + Commit_Double_Buffer state; detail::State_Callback_Storage state_callbacks; detail::Buffer_Storage buffer_storage; - detail::Rely_Storage rely_storage; - explicit Private(std::pmr::memory_resource* resource) : Synchronized_Double_Buffer(std::allocator_arg, Allocator{resource}), + detail::Dependency_Graph_Storage dependency_graph_storage; + explicit Private(std::pmr::memory_resource* resource) : Publish_Double_Buffer(std::allocator_arg, Allocator{resource}), state(std::allocator_arg, Allocator{resource}), buffer_storage(std::allocator_arg, Allocator{resource}), - rely_storage(std::allocator_arg, Allocator{resource}) {} + dependency_graph_storage(std::allocator_arg, Allocator{resource}) {} } d; template requires std::constructible_from - explicit Attach_Object(Args&&... args) : Obj(std::forward(args)...), d(this->memory_resource()) { - this->template bind_object_crtp(); + explicit Impl(Args&&... args) : Obj(std::forward(args)...), d(this->memory_resource()) { + this->template bind_object_crtp(); } private: friend struct Root; @@ -132,161 +146,157 @@ private: } template void before_state_set(Member Owner::* member) { + State_Access pending_states{*d.state.pending}; auto callback = [&](auto& private_data) { - private_data.before_state_set(this, member, d.state.pending); + private_data.before_state_set(this, member, pending_states); }; walk_private(callback); } template void after_state_set(Member Owner::* member) { + State_Access pending_states{*d.state.pending}; auto callback = [&](auto& private_data) { - private_data.after_state_set(this, member, d.state.pending); + private_data.after_state_set(this, member, pending_states); }; walk_private(callback); } - void before_exchange() { + void before_advance() { + State_Access pending_states{*d.state.pending}; + State_Access current_states{std::as_const(*d.state.current)}; auto callback = [&](auto& private_data) { - private_data.before_exchange( + private_data.before_advance( this, d.pending, - d.state.pending, + pending_states, d.current, - d.state.current + current_states ); }; walk_private(callback); } - void after_exchange() { + void after_advance() { + State_Access pending_states{*d.state.pending}; + State_Access current_states{*d.state.current}; auto callback = [&](auto& private_data) { - private_data.after_exchange( + private_data.after_advance( this, d.pending, - d.state.pending, + pending_states, d.current, - d.state.current + current_states ); }; walk_private(callback); } - void exchange_unlocked() { - before_exchange(); - // State 从 pending 向内部 current 提交;Prop 从内部 current 向 pending 发布,两者交换后的写入侧都同步为最新基线。 - static_cast&>(d).exchange(); - d.state.exchange(); - d.buffer_storage.exchange(); - d.rely_storage.exchange(); - after_exchange(); + void advance_unlocked() { + before_advance(); + // State 从 pending 向内部 current 提交;Prop 从内部 current 向 pending 发布,两者推进后的写入侧都同步为最新基线。 + static_cast&>(d).advance(); + d.state.advance(); + d.buffer_storage.advance(); + d.dependency_graph_storage.advance(); + after_advance(); + // after_advance 只修改已提交的 current;由状态缓冲统一刷新下一次编辑基线,具体机制不得自行同步两份状态。 + *d.state.pending = *d.state.current; } public: - template requires detail::Buffer_Tag_In - auto& buffer() { - this->emit_rely_source(detail::rely_id>()); + template Tag> + auto& pending_buffer() { + this->emit_dependency_source(detail::dependency_id>()); return *d.buffer_storage.template get().pending; } - template requires detail::Buffer_Tag_In + template Tag> const auto& current_buffer() const { return *d.buffer_storage.template get().current; } - template requires - detail::Buffer_Tag_In && detail::Buffer_Value_Settable - void set_initial_buffer(Value&& value) { + template Tag, detail::Buffer_Value_Settable Value> + void initialize_buffer(Value&& value) { auto& buffer = d.buffer_storage.template get(); *buffer.pending = std::forward(value); *buffer.current = *buffer.pending; } - template requires detail::Rely_Tag_In - [[nodiscard]] auto rely() const { - using Bound_Object = detail::Rely_Bound_Object; - const Rely* graph = d.rely_storage.template get().pending; + template Tag> + [[nodiscard]] auto pending_dependency_graph() const { + using Bound_Object = detail::Dependency_Graph_Bound_Object; + const Dependency_Graph* graph = d.dependency_graph_storage.template get().pending; return graph->typed_view(); } - template requires detail::Rely_Tag_In - [[nodiscard]] auto current_rely() const { - using Bound_Object = detail::Rely_Bound_Object; - const Rely* graph = d.rely_storage.template get().current; + template Tag> + [[nodiscard]] auto current_dependency_graph() const { + using Bound_Object = detail::Dependency_Graph_Bound_Object; + const Dependency_Graph* graph = d.dependency_graph_storage.template get().current; return graph->typed_view(); } - template requires - (detail::Rely_Tag_In && ...) && detail::Unique_Types::value && - detail::Rely_Access_Callback_For - void access_rely(Callback&& callback) { - d.rely_storage.template access(std::forward(callback)); + template ... Tags, detail::Dependency_Graph_Access_Callback_For Callback> + void access_pending_dependency_graph(Callback&& callback) { + d.dependency_graph_storage.template access(std::forward(callback)); } // 依赖图只允许在编辑侧修改;回调完成后先验证 DAG,再把新结构提交到 pending。 - template requires - (detail::Rely_Tag_In && ...) && detail::Unique_Types::value && - detail::Rely_Edit_Callback_For - std::expected edit_rely(Callback&& callback) { + template ... Tags, detail::Dependency_Graph_Edit_Callback_For Callback> + std::expected edit_dependency_graph(Callback&& callback) { std::lock_guard guard(lock); - return d.rely_storage.template edit(std::forward(callback)); + return d.dependency_graph_storage.template edit(std::forward(callback)); } - std::expected validate_rely() const { - return d.rely_storage.validate_pending(); + std::expected validate_dependency_graph() const { + return d.dependency_graph_storage.validate_pending(); } - template requires - detail::Rely_Tag_In && - detail::Rely_View_Node_Callback> - std::expected for_each_rely_topological(Callback&& callback) const { - return current_rely().for_each_topological_view(std::forward(callback)); + template Tag, detail::Dependency_Graph_View_Node_Callback> Callback> + std::expected for_each_dependency_graph_topological(Callback&& callback) const { + return current_dependency_graph().for_each_topological_view(std::forward(callback)); } - template requires detail::Rely_Current_Callback - void for_each_current_rely(Callback&& callback) const { - d.rely_storage.for_each_current(std::forward(callback)); + template + void for_each_current_dependency_graph(Callback&& callback) const { + d.dependency_graph_storage.for_each_current(std::forward(callback)); } - template requires - detail::Prop_Member_Settable - Attach_Object& set(Member Owner::* member, Value&& value) { + template Value> + Impl& set(Member Owner::* member, Value&& value) { std::lock_guard guard(lock); d.current->*member = std::forward(value); return *this; } - template requires - detail::State_Callback_For + template Tag, detail::State_Callback_For Callback> void set_state_callback(Callback&& callback) { std::lock_guard guard(lock); d.state_callbacks.template set(std::forward(callback)); } - template requires detail::State_Tag_In + template Tag> void clear_state_callback() { std::lock_guard guard(lock); d.state_callbacks.template clear(); } - template requires - detail::State_Callback_For + template Tag, detail::State_Callback_For Callback> void access_state(Callback&& callback) const { std::lock_guard guard(lock); using Layer = detail::State_Value; std::invoke(std::forward(callback), static_cast(*d.state.current)); } - template requires detail::State_Tag_In + template Tag> void notify_state() { d.state_callbacks.template notify(*d.state.current); } - // State 写入 pending,随后向依赖图发出对应成员的 dirty 信号;真正进入 current 发生在 exchange/process 边界。 - template requires detail::State_Member_Settable + // State 写入 pending,随后向依赖图发出对应成员的 dirty 信号;真正进入 current 发生在 advance/process 边界。 + template Value> void update_state(Value&& value) { std::lock_guard guard(lock); before_state_set(Member); d.state.pending->*Member = std::forward(value); after_state_set(Member); - this->emit_rely_source(detail::rely_id>()); - } - template requires requires(Private& private_data, Attach_Object* object, Callback&& callback) { - private_data.process(object, std::forward(callback)); + this->emit_dependency_source(detail::dependency_id>()); } + template Callback> void process(Callback&& callback) { std::lock_guard guard(lock); - exchange_unlocked(); + advance_unlocked(); d.process(this, std::forward(callback)); } - void exchange() { + void advance() { std::lock_guard guard(lock); - exchange_unlocked(); + advance_unlocked(); } - template requires std::invocable - void exchange(Callback&& callback) { + template Callback> + void advance(Callback&& callback) { std::lock_guard guard(lock); - exchange_unlocked(); + advance_unlocked(); std::invoke(std::forward(callback), std::as_const(*d.state.current)); } }; diff --git a/kernel/src/kernel/double_buffer/rely_storage.hpp b/kernel/src/kernel/double_buffer/rely_storage.hpp deleted file mode 100644 index b4f56fe..0000000 --- a/kernel/src/kernel/double_buffer/rely_storage.hpp +++ /dev/null @@ -1,365 +0,0 @@ -#pragma once -#include "rely.hpp" -namespace double_buffer { -namespace detail { -template -concept Rely_Edit_Callback_For = Rely_List && std::invocable< - Callback, - Rely::Editor>&... ->; -template -concept Rely_View_Node_Callback = Root_Derived && std::invocable&, const Rely::Node&>; -template -concept Rely_Current_Callback = std::invocable; -template -concept Rely_Access_Callback_For = Rely_List && std::invocable&...>; -} -inline bool Root::mark_dirty_id(const void* tag, const void* dirty_key) { - if (std::find(dirty_tags.begin(), dirty_tags.end(), tag) != dirty_tags.end()) return false; - dirty_tags.push_back(tag); - for (const auto* rely : rely_graphs) rely->emit(this, dirty_key); - return true; -} -inline void Root::emit_rely(const void* key) { - for (const auto* rely : rely_graphs) rely->emit(this, key); -} -namespace detail { -template -struct Rely_Storage; -// Rely_Storage 为每个 Rely_Type 保存 pending/current 两份图;编辑只落到 pending,exchange 后 current 成为执行侧并同步新的编辑基线。 -template -struct Rely_Storage> { - using allocator_type = std::pmr::polymorphic_allocator; -private: - template - struct Rely_Buffer : Synchronized_Double_Buffer, Mechanism { - using Base = Synchronized_Double_Buffer; - using allocator_type = typename Base::allocator_type; - Rely_Buffer() = default; - Rely_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} - void mark_structure_dirty() noexcept { - this->mark_dirty(); - } - }; - std::tuple...> relies; - void pair_buffers() { - std::apply( - [](auto&... buffer) { - (buffer.pending->pair_with(*buffer.current), ...); - }, - relies - ); - } - template - static consteval std::size_t index() { - return tag_index(); - } - template - std::expected validate_pending_impl() const { - if constexpr (I == sizeof...(Relies)) { - return {}; - } - else { - auto check = std::get(relies).pending->topological_order(); - if (!check) return std::unexpected(check.error()); - return validate_pending_impl(); - } - } - template - static void exchange_one(Rely_Buffer& buffer) { - // pending/current revision 相同表示上次交换后没有结构编辑,无需再次深拷贝整张依赖图。 - if (buffer.pending->structure_revision == buffer.current->structure_revision) return; - buffer.current->unbind(); - buffer.exchange(); - buffer.current->bind(); - } -public: - Rely_Storage() : Rely_Storage(std::allocator_arg, allocator_type{std::pmr::get_default_resource()}) {} - Rely_Storage(std::allocator_arg_t, const allocator_type& allocator) : relies(std::allocator_arg, allocator) { - pair_buffers(); - } - template requires Rely_Tag_In> - auto& get() { - return std::get()>(relies); - } - template requires Rely_Tag_In> - const auto& get() const { - return std::get()>(relies); - } - template requires - (Rely_Tag_In> && ...) && Unique_Types::value && - Rely_Access_Callback_For, Tags...> - void access(Callback&& callback) { - std::invoke( - std::forward(callback), - static_cast>&>(get())... - ); - } - // 编辑采用临时图事务:先复制 pending,回调修改临时图并统一验证,成功后才替换 pending,失败不会污染现有结构。 - template requires - (Rely_Tag_In> && ...) && Unique_Types::value && - Rely_Edit_Callback_For, Tags...> - std::expected edit(Callback&& callback) { - std::tuple...> next(*get().pending...); - auto editors = [&](std::index_sequence) { - return std::tuple{ - Rely::Editor>>( - std::get(next), - rely_id(), - rely_id>(), - true - )... - }; - }(std::index_sequence_for{}); - std::apply( - [&](auto&... values) { - std::invoke(std::forward(callback), values...); - }, - editors - ); - std::expected result; - std::apply( - [&](const auto&... value) { - ( - [&] { - if (!result) return; - auto check = value.topological_order(); - if (!check) result = std::unexpected(check.error()); - }(), - ... - ); - }, - next - ); - if (!result) return result; - auto changed = [&](std::index_sequence) { - return std::tuple{ - (std::get(next).structure_revision != get().pending->structure_revision)... - }; - }(std::index_sequence_for{}); - [&](std::index_sequence) { - ( - [&] { - auto& buffer = get(); - buffer.pending->swap(std::get(next)); - if (std::get(changed)) buffer.mark_structure_dirty(); - }(), - ... - ); - }(std::index_sequence_for{}); - return {}; - } - std::expected validate_pending() const { - return validate_pending_impl(); - } - void exchange() { - std::apply( - [](auto&... buffer) { - (exchange_one(buffer), ...); - }, - relies - ); - } - template - void for_each_current(Callback&& callback) const { - std::apply( - [&](const auto&... buffer) { - (std::invoke(callback, std::as_const(*buffer.current)), ...); - }, - relies - ); - } -}; -// Builder 使用独立依赖图完成对象构造期编辑;build 成功时一次性提交到对象的正式 Rely_Storage。 -template -struct Rely_Build_Storage> { - using allocator_type = std::pmr::polymorphic_allocator; -private: - std::tuple...> relies; - template - static consteval std::size_t index() { - return tag_index(); - } - template - std::expected validate_impl() const { - if constexpr (I == sizeof...(Relies)) { - return {}; - } - else { - auto result = std::get(relies).topological_order(); - if (!result) return std::unexpected(result.error()); - return validate_impl(); - } - } - template - void commit_one(Rely_Storage>& storage) { - using Tag = typename Rely_Type::Tag_Type; - auto& buffer = storage.template get(); - buffer.pending->swap(get()); - buffer.pending->bind_nodes(); - *buffer.current = *buffer.pending; - buffer.current->bind(); - buffer.mark_structure_dirty(); - } -public: - Rely_Build_Storage() : Rely_Build_Storage(std::pmr::get_default_resource()) {} - explicit Rely_Build_Storage(std::pmr::memory_resource* resource) : relies(std::allocator_arg, allocator_type{resource}) {} - template requires Rely_Tag_In> - Rely& get() { - return std::get()>(relies); - } - template requires Rely_Tag_In> - const Rely& get() const { - return std::get()>(relies); - } - template requires - (Rely_Tag_In> && ...) && Unique_Types::value && - Rely_Edit_Callback_For, Tags...> - void edit(Callback&& callback) { - auto editors = std::tuple{ - Rely::Editor>>( - get(), - rely_id(), - rely_id>(), - false - )... - }; - std::apply( - [&](auto&... values) { - std::invoke(std::forward(callback), values...); - }, - editors - ); - } - std::expected validate() const { - return validate_impl(); - } - void commit(Rely_Storage>& storage) { - (commit_one(storage), ...); - } -}; -} -template -struct Root::Builder { - using Prop = typename Object::Prop; - std::unique_ptr object; - detail::Rely_Build_Storage rely_storage; - template requires std::constructible_from - explicit Builder(Args&&... args) : object(std::make_unique(std::forward(args)...)), - rely_storage(object->memory_resource()) {} - Builder& set_pmr(Pmr pmr) { - object->set_pmr_resource(pmr); - return *this; - } - Builder& set_pmr(Pmr::Resource& resource) { - return set_pmr(Pmr{resource}); - } - Builder& set_pmr(Pmr::Resource* resource) { - return set_pmr(Pmr{resource}); - } - template requires - detail::Prop_Member_Settable - Builder& set(Member Owner::* member, Value&& value) { - object->set(member, std::forward(value)); - return *this; - } - template requires - detail::Buffer_Tag_In && - detail::Buffer_Value_Settable - Builder& set(Value&& value) { - object->template set_initial_buffer(std::forward(value)); - return *this; - } - template requires - (detail::Rely_Tag_In && ...) && - detail::Unique_Types::value && - detail::Rely_Edit_Callback_For - Builder& edit_rely(Callback&& callback) { - rely_storage.template edit(std::forward(callback)); - return *this; - } - template requires - detail::Rely_Tag_In && detail::Bound_Rely_Object && - std::derived_from> - Builder& add_dependency_node(Node_Object* node) { - return edit_rely( - [&](auto& editor) { - editor.add(node); - } - ); - } - template requires - detail::Rely_Tag_In && Root_Derived - Builder& remove_dependency_node(Node_Object* node) { - return edit_rely( - [&](auto& editor) { - editor.remove(node); - } - ); - } - template requires - detail::Rely_Tag_In && detail::Rely_Object && - detail::State_Rely_Source && detail::Bound_Rely_Object && - std::derived_from> - Builder& add_dependency(Target* target, Source* source) { - return edit_rely( - [&](auto& editor) { - editor.template add_dependency(target, source); - } - ); - } - template requires - detail::Rely_Tag_In && detail::Rely_Object && - detail::Buffer_Rely_Source && detail::Bound_Rely_Object && - std::derived_from> - Builder& add_dependency(Target* target, Source* source) { - return edit_rely( - [&](auto& editor) { - editor.template add_dependency(target, source); - } - ); - } - template requires - detail::Rely_Tag_In && Root_Derived && Root_Derived && - detail::Bound_Rely_Object && - std::derived_from> - Builder& add_dirty_dependency(Target* target, Source* source) { - return edit_rely( - [&](auto& editor) { - editor.template add_dirty_dependency(target, source); - } - ); - } - template requires - detail::Rely_Tag_In && detail::Rely_Object && - detail::State_Rely_Source && detail::Bound_Rely_Object && - std::derived_from> - Builder& remove_dependency(Target* target, Source* source) { - return edit_rely( - [&](auto& editor) { - editor.template remove_dependency(target, source); - } - ); - } - template requires - detail::Rely_Tag_In && detail::Rely_Object && - detail::Buffer_Rely_Source && detail::Bound_Rely_Object && - std::derived_from> - Builder& remove_dependency(Target* target, Source* source) { - return edit_rely( - [&](auto& editor) { - editor.template remove_dependency(target, source); - } - ); - } - std::expected, Rely_Error> build() { - auto validate_result = validate(); - if (!validate_result) return std::unexpected(validate_result.error()); - rely_storage.commit(object->d.rely_storage); - return std::move(object); - } - std::expected validate() const { - return rely_storage.validate(); - } -}; -} diff --git a/kernel/src/kernel/render.hpp b/kernel/src/kernel/render.hpp deleted file mode 100644 index 1335205..0000000 --- a/kernel/src/kernel/render.hpp +++ /dev/null @@ -1,583 +0,0 @@ -#pragma once -#include "double_buffer/object.hpp" -#include "double_buffer/core.hpp" -#include -#include -#include -namespace aethera { -struct Prepare_Data_Tag {}; -struct Paint_Tag {}; -struct Color_Cache {}; -struct Task_Runtime_State_Tag {}; -struct Scene_State_Tag {}; -struct Renderable_State_Tag {}; -struct Task_Type_State { - std::size_t count{}; - std::uint64_t total_time_ns{}; - std::uint64_t min_time_ns{}; - std::uint64_t max_time_ns{}; - bool operator==(const Task_Type_State&) const = default; -}; -struct Task_Worker_State { - std::size_t id{}; - std::size_t task_count{}; - std::size_t peak_observed_queue_size{}; - std::size_t max_observed_queue_capacity{}; - std::uint64_t task_time_ns{}; - std::uint64_t busy_time_ns{}; - std::uint64_t idle_time_ns{}; - std::uint64_t min_task_time_ns{}; - std::uint64_t max_task_time_ns{}; - double utilization{}; - bool operator==(const Task_Worker_State&) const = default; -}; -struct Task_Runtime_State : ::double_buffer::State_Type { - std::size_t worker_count{}; - std::size_t active_topology_count{}; - std::size_t active_taskflow_count{}; - std::size_t peak_active_taskflow_count{}; - std::size_t completed_taskflow_count{}; - std::size_t failed_taskflow_count{}; - std::size_t active_task_count{}; - std::size_t peak_active_task_count{}; - std::size_t active_worker_count{}; - std::size_t peak_active_worker_count{}; - std::size_t observed_task_count{}; - std::size_t named_task_count{}; - std::size_t peak_observed_worker_queue_size{}; - std::size_t max_observed_worker_queue_capacity{}; - std::size_t max_predecessors{}; - std::size_t max_successors{}; - std::size_t max_strong_dependencies{}; - std::size_t max_weak_dependencies{}; - std::size_t longest_task_hash{}; - std::string longest_task_name; - tf::TaskType longest_task_type{tf::TaskType::UNDEFINED}; - std::uint64_t longest_task_time_ns{}; - std::uint64_t total_task_time_ns{}; - std::uint64_t worker_busy_time_ns{}; - std::uint64_t observed_wall_time_ns{}; - double worker_utilization{}; - std::array task_types{}; - std::vector workers; - bool operator==(const Task_Runtime_State&) const = default; -}; -void initialize_runtime(std::size_t workers = std::thread::hardware_concurrency(), - std::shared_ptr worker_interface = nullptr, - ::double_buffer::Pmr pmr = {}); -namespace detail { -void set_runtime_state_callback(std::function callback); -void clear_runtime_state_callback(); -std::uint64_t run_taskflow(tf::Taskflow& taskflow); -std::pmr::memory_resource* task_memory_resource() noexcept; -} -template requires - std::same_as && std::invocable -void set_runtime_state_callback(Callback&& callback) { - detail::set_runtime_state_callback(std::function{std::forward(callback)}); -} -template requires std::same_as -void clear_runtime_state_callback() { - detail::clear_runtime_state_callback(); -} -struct Renderable; -/* - * Prepare 数据模式定制点。 - * 子类的 Private 可定义: - * void prepare_data(T* object); - * 当未定义 build_prepare_graph(...) 时必须提供该函数。 - * 如果 prepare_data(...) 与 build_prepare_graph(...) 同时存在,Prepare 阶段选择子图模式, - * prepare_data(...) 不再作为该阶段的执行函数。 - */ -template -concept Prepare_Data_Renderable = ::double_buffer::Attached && requires(typename T::Private& private_data, T* object) { - { private_data.prepare_data(object) } -> std::same_as; -}; -/* - * Prepare 子图模式定制点。 - * 子类的 Private 可定义: - * tf::Taskflow build_prepare_graph(T* object, const T::State& state); - * 只要该函数存在且签名满足此概念,Prepare 阶段就进入子图模式。 - * 子图首次执行前一定会构建;之后仅在 should_rebuild_prepare_graph(...) 返回 true 时重建。 - * 子图构建需要 PMR 时直接使用 detail::task_memory_resource() 获取运行时全局资源, - * 不把 std::pmr::memory_resource 作为 Renderable 子类接口参数传递。 - * 不需要子图模式时不要定义一个仅返回空 Taskflow 的函数,应直接省略该定制点。 - */ -template -concept Prepare_Graph_Renderable = ::double_buffer::Attached && requires(typename T::Private& private_data, T* object, const typename T::State& state) { - { private_data.build_prepare_graph(object, state) } -> std::same_as; -}; -/* - * Paint 数据模式定制点。 - * 子类的 Private 可定义: - * void paint(T* object); - * 当未定义 build_paint_graph(...) 时必须提供该函数。 - * 如果 paint(...) 与 build_paint_graph(...) 同时存在,Paint 阶段选择子图模式, - * paint(...) 不再作为该阶段的执行函数。 - */ -template -concept Paint_Data_Renderable = ::double_buffer::Attached && requires(typename T::Private& private_data, T* object) { - { private_data.paint(object) } -> std::same_as; -}; -/* - * Paint 子图模式定制点。 - * 子类的 Private 可定义: - * tf::Taskflow build_paint_graph(T* object, const T::State& state); - * 只要该函数存在且签名满足此概念,Paint 阶段就进入子图模式。 - * 子图首次执行前一定会构建;之后仅在 should_rebuild_paint_graph(...) 返回 true 时重建。 - * 子图构建需要 PMR 时直接使用 detail::task_memory_resource() 获取运行时全局资源, - * 不把 std::pmr::memory_resource 作为 Renderable 子类接口参数传递。 - * 不需要子图模式时不要定义一个仅返回空 Taskflow 的函数,应直接省略该定制点。 - */ -template -concept Paint_Graph_Renderable = ::double_buffer::Attached && requires(typename T::Private& private_data, T* object, const typename T::State& state) { - { private_data.build_paint_graph(object, state) } -> std::same_as; -}; -/* - * Renderable 子类完整契约。 - * Prepare 与 Paint 两个阶段都必须至少提供一种实现:普通函数或子图构建函数。 - * should_prepare(...)、should_paint(...)、should_rebuild_prepare_graph(...)、 - * should_rebuild_paint_graph(...) 在 Renderable::Private 中提供默认行为,子类可按需重新定义。 - */ -template -concept Renderable_Object = ::double_buffer::Attached && std::derived_from && requires(typename T::Private& private_data, T* object, const typename T::State& state, bool dirty) { - { private_data.should_prepare(object, state, dirty) } -> std::same_as; - { private_data.should_paint(object, state, dirty) } -> std::same_as; - { private_data.should_rebuild_prepare_graph(object, state) } -> std::same_as; - { private_data.should_rebuild_paint_graph(object, state) } -> std::same_as; -} && (Prepare_Data_Renderable || Prepare_Graph_Renderable) && (Paint_Data_Renderable || Paint_Graph_Renderable); -struct Renderable : ::double_buffer::Impl, ::double_buffer::Tagged_Buffer> { - struct Prop : Prev_Prop {}; - struct State : Prev_State { - bool prepare_dirty{}; - bool paint_dirty{}; - bool prepare_executed{}; - bool paint_executed{}; - bool prepare_graph_rebuilt{}; - bool paint_graph_rebuilt{}; - std::size_t prepare_task_count{}; - std::size_t paint_task_count{}; - std::uint64_t prepare_execution_time_ns{}; - std::uint64_t paint_execution_time_ns{}; - bool operator==(const State&) const = default; - }; -protected: - template - static void run_prepare_data(Object* object) { - auto& private_data = static_cast(object->d); - private_data.prepare_data(object); - auto callback = [&](auto& value) { - if constexpr (requires { value.after_prepare_data(object); }) value.after_prepare_data(object); - }; - walk_private(object, callback); - } -public: - struct Private : Prev_Private { - using Run_Predicate = bool (*)(::double_buffer::Root*, bool); - using Rebuild_Predicate = bool (*)(::double_buffer::Root*); - using Stage_Run = void (*)(::double_buffer::Root*); - using Graph_Builder = tf::Taskflow (*)(::double_buffer::Root*); - using State_Access = State* (*)(::double_buffer::Root*); - using State_Notify = void (*)(::double_buffer::Root*); - struct Stage_Dispatch { - Run_Predicate predicate; - Rebuild_Predicate rebuild_predicate; - Stage_Run run; - Graph_Builder builder; - }; - struct State_Dispatch { - State_Access access; - State_Notify notify; - }; - struct Dispatch { - Stage_Dispatch prepare; - Stage_Dispatch paint; - State_Dispatch state; - }; - const Dispatch* dispatch{}; - std::unique_ptr prepare_graph; - std::unique_ptr paint_graph; - bool prepare_graph_built{}; - bool paint_graph_built{}; - /* - * Prepare 子图重建判定定制点。 - * 子类的 Private 可定义: - * bool should_rebuild_prepare_graph(T* object, const T::State& state); - * 仅在定义了 build_prepare_graph(...) 的子图模式下使用。 - * 首次子图构建不依赖该返回值;默认 false 表示首次构建后不主动重建。 - */ - bool should_rebuild_prepare_graph(::double_buffer::Attached auto*, const State&) { - return false; - } - /* - * Paint 子图重建判定定制点。 - * 子类的 Private 可定义: - * bool should_rebuild_paint_graph(T* object, const T::State& state); - * 仅在定义了 build_paint_graph(...) 的子图模式下使用。 - * 首次子图构建不依赖该返回值;默认 false 表示首次构建后不主动重建。 - */ - bool should_rebuild_paint_graph(::double_buffer::Attached auto*, const State&) { - return false; - } - /* - * Prepare 阶段执行判定定制点。 - * 子类的 Private 可定义: - * bool should_prepare(T* object, const T::State& state, bool dirty); - * dirty 是运行时在完成必要的子图重建处理后读取到的 Prepare 脏状态。 - * 返回 true 执行 Prepare 阶段,返回 false 跳过;默认仅在 dirty 为 true 时执行。 - */ - bool should_prepare(::double_buffer::Attached auto*, const State&, bool dirty) { - return dirty; - } - /* - * Paint 阶段执行判定定制点。 - * 子类的 Private 可定义: - * bool should_paint(T* object, const T::State& state, bool dirty); - * dirty 是运行时在完成必要的子图重建处理后读取到的 Paint 脏状态。 - * 返回 true 执行 Paint 阶段,返回 false 跳过;默认仅在 dirty 为 true 时执行。 - */ - bool should_paint(::double_buffer::Attached auto*, const State&, bool dirty) { - return dirty; - } - /* - * Prepare 数据模式完成后的可选后置钩子。 - * 子类的 Private 可定义: - * void after_prepare_data(T* object); - * 仅在 prepare_data(...) 数据模式执行完成后调用,Prepare 子图模式不会调用该钩子。 - */ - void after_prepare_data(::double_buffer::Attached auto*) {} - }; -private: - void bind_rely_object(::double_buffer::Attached auto* object) { - using Object = std::remove_pointer_t; - static_assert(Renderable_Object); - auto& data = static_cast(object->d); - static const Private::Dispatch dispatch{ - { - [](::double_buffer::Root* root, bool dirty) { - auto* value = static_cast(root); - auto& private_data = static_cast(value->d); - return private_data.should_prepare(value, *value->d.state.current, dirty); - }, - [](::double_buffer::Root* root) { - auto* value = static_cast(root); - auto& private_data = static_cast(value->d); - return private_data.should_rebuild_prepare_graph(value, *value->d.state.current); - }, - []() -> Private::Stage_Run { - if constexpr (Prepare_Data_Renderable) { - return [](::double_buffer::Root* root) { - auto* value = static_cast(root); - run_prepare_data(value); - }; - } - else { - return nullptr; - } - }(), - []() -> Private::Graph_Builder { - if constexpr (Prepare_Graph_Renderable) { - return [](::double_buffer::Root* root) { - auto* value = static_cast(root); - auto& private_data = static_cast(value->d); - return private_data.build_prepare_graph(value, *value->d.state.current); - }; - } - else { - return nullptr; - } - }() - }, - { - [](::double_buffer::Root* root, bool dirty) { - auto* value = static_cast(root); - auto& private_data = static_cast(value->d); - return private_data.should_paint(value, *value->d.state.current, dirty); - }, - [](::double_buffer::Root* root) { - auto* value = static_cast(root); - auto& private_data = static_cast(value->d); - return private_data.should_rebuild_paint_graph(value, *value->d.state.current); - }, - []() -> Private::Stage_Run { - if constexpr (Paint_Data_Renderable) { - return [](::double_buffer::Root* root) { - auto* value = static_cast(root); - auto& private_data = static_cast(value->d); - private_data.paint(value); - }; - } - else { - return nullptr; - } - }(), - []() -> Private::Graph_Builder { - if constexpr (Paint_Graph_Renderable) { - return [](::double_buffer::Root* root) { - auto* value = static_cast(root); - auto& private_data = static_cast(value->d); - return private_data.build_paint_graph(value, *value->d.state.current); - }; - } - else { - return nullptr; - } - }() - }, - { - [](::double_buffer::Root* root) { - auto* value = static_cast(root); - return static_cast(value->d.state.current); - }, - [](::double_buffer::Root* root) { - auto* value = static_cast(root); - value->template notify_state(); - } - } - }; - data.dispatch = &dispatch; - object->template mark_dirty(); - } - friend struct ::double_buffer::Rely; -}; -namespace detail { -enum class Renderable_Stage { - Prepare, - Paint -}; -enum class Stage_Observer_Point { - Begin, - End -}; -void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, ::double_buffer::Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point); -void clear_stage_observers(const tf::Taskflow* owner); -} -struct Scene : ::double_buffer::Impl, ::double_buffer::Rely_Type, ::double_buffer::Rely_Type> { - struct Prop : Prev_Prop {}; - struct State : Prev_State { - bool taskflow_rebuilt{}; - std::size_t renderable_count{}; - std::size_t taskflow_task_count{}; - std::size_t taskflow_dependency_count{}; - std::size_t taskflow_max_predecessors{}; - std::size_t taskflow_max_successors{}; - std::uint64_t taskflow_execution_time_ns{}; - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private { - struct Result {}; - private: - tf::Taskflow taskflow; - std::size_t renderable_count{}; - std::size_t taskflow_dependency_count{}; - std::size_t taskflow_max_predecessors{}; - std::size_t taskflow_max_successors{}; - bool taskflow_built{}; - bool taskflow_rebuilt{}; - public: - ~Private() { - detail::clear_stage_observers(&taskflow); - } - void after_exchange(::double_buffer::Attached auto* object, Prop*, State*, const Prop*, const State*); - template Callback> - void process(::double_buffer::Attached auto* object, Callback&& callback) { - auto& state = static_cast(*object->d.state.current); - state.taskflow_rebuilt = taskflow_rebuilt; - state.renderable_count = renderable_count; - state.taskflow_task_count = taskflow.num_tasks(); - state.taskflow_dependency_count = taskflow_dependency_count; - state.taskflow_max_predecessors = taskflow_max_predecessors; - state.taskflow_max_successors = taskflow_max_successors; - state.taskflow_execution_time_ns = 0; - if (taskflow_built && !taskflow.empty()) state.taskflow_execution_time_ns = detail::run_taskflow(taskflow); - object->template notify_state(); - Result result; - std::invoke(std::forward(callback), std::as_const(result)); - } - }; -}; -void Scene::Private::after_exchange(::double_buffer::Attached auto* object, Prop*, State*, const Prop*, const State*) { - auto* resource = detail::task_memory_resource(); - taskflow_rebuilt = false; - std::pmr::unordered_set<::double_buffer::Root*> exchanged_objects{resource}; - exchanged_objects.insert(object); - object->for_each_current_rely( - [&](const ::double_buffer::Rely& rely) { - rely.for_each( - [&](const ::double_buffer::Rely::Node& node) { - if (exchanged_objects.insert(node.object).second) node.object->exchange_object(); - } - ); - } - ); - bool taskflow_dirty = !taskflow_built; - object->template access_rely( - [&](auto& prepare_state, auto& paint_state) { - taskflow_dirty = taskflow_dirty || prepare_state.dirty() || paint_state.dirty(); - } - ); - if (!taskflow_dirty) return; - auto prepare_dependencies = object->template current_rely(); - auto paint_dependencies = object->template current_rely(); - std::pmr::unordered_set renderables{resource}; - prepare_dependencies.for_each_bound( - [&](Renderable* renderable, Renderable::Private&) { - renderables.insert(renderable); - } - ); - paint_dependencies.for_each_bound( - [&](Renderable* renderable, Renderable::Private&) { - renderables.insert(renderable); - } - ); - renderable_count = renderables.size(); - detail::clear_stage_observers(&taskflow); - taskflow.clear(); - struct Stage_Tasks { - tf::Task prepare_entry; - tf::Task prepare_exit; - tf::Task paint_entry; - tf::Task paint_exit; - }; - std::pmr::unordered_map<::double_buffer::Root*, Stage_Tasks> stage_tasks{resource}; - for (auto* renderable : renderables) { - ::double_buffer::Root* root = renderable; - auto* data = prepare_dependencies.private_data(root); - if (!data) data = paint_dependencies.private_data(root); - if (!data) continue; - auto* dispatch = data->dispatch; - auto prepare_if = taskflow.emplace([data, dispatch, root] { - auto& state = *dispatch->state.access(root); - state.prepare_graph_rebuilt = false; - state.prepare_execution_time_ns = 0; - if (dispatch->prepare.builder) { - bool rebuild = dispatch->prepare.rebuild_predicate(root); - if (!data->prepare_graph_built || rebuild) { - *data->prepare_graph = dispatch->prepare.builder(root); - data->prepare_graph_built = true; - state.prepare_graph_rebuilt = true; - root->template mark_dirty(); - } - state.prepare_task_count = data->prepare_graph->num_tasks(); - } - else { - state.prepare_task_count = 1; - } - bool dirty = root->template dirty(); - state.prepare_dirty = dirty; - state.prepare_executed = dispatch->prepare.predicate(root, dirty); - return state.prepare_executed ? 0 : 1; - }).name("renderable.prepare.condition"); - tf::Task prepare_run; - if (dispatch->prepare.builder) { - if (!data->prepare_graph) data->prepare_graph = std::make_unique(); - prepare_run = taskflow.composed_of(*data->prepare_graph).name("renderable.prepare.graph"); - } - else { - prepare_run = taskflow.emplace([dispatch, root] { - dispatch->prepare.run(root); - }).name("renderable.prepare.data"); - } - auto prepare_done = taskflow.emplace([dispatch, root] { - auto& state = *dispatch->state.access(root); - if (!state.prepare_executed) return; - root->template take_dirty(); - root->template mark_dirty(); - }).name("renderable.prepare.complete"); - prepare_if.precede(prepare_run, prepare_done); - prepare_run.precede(prepare_done); - auto paint_if = taskflow.emplace([data, dispatch, root] { - auto& state = *dispatch->state.access(root); - state.paint_graph_rebuilt = false; - state.paint_execution_time_ns = 0; - if (dispatch->paint.builder) { - bool rebuild = dispatch->paint.rebuild_predicate(root); - if (!data->paint_graph_built || rebuild) { - *data->paint_graph = dispatch->paint.builder(root); - data->paint_graph_built = true; - state.paint_graph_rebuilt = true; - root->template mark_dirty(); - } - state.paint_task_count = data->paint_graph->num_tasks(); - } - else { - state.paint_task_count = 1; - } - bool dirty = root->template dirty(); - state.paint_dirty = dirty; - state.paint_executed = dispatch->paint.predicate(root, dirty); - return state.paint_executed ? 0 : 1; - }).name("renderable.paint.condition"); - tf::Task paint_run; - if (dispatch->paint.builder) { - if (!data->paint_graph) data->paint_graph = std::make_unique(); - paint_run = taskflow.composed_of(*data->paint_graph).name("renderable.paint.graph"); - } - else { - paint_run = taskflow.emplace([dispatch, root] { - dispatch->paint.run(root); - }).name("renderable.paint.data"); - } - auto paint_done = taskflow.emplace([dispatch, root] { - auto& state = *dispatch->state.access(root); - if (state.paint_executed) root->template take_dirty(); - dispatch->state.notify(root); - }).name("renderable.paint.complete"); - paint_if.precede(paint_run, paint_done); - paint_run.precede(paint_done); - prepare_done.precede(paint_if); - detail::bind_stage_observer(&taskflow, prepare_if.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::Begin); - detail::bind_stage_observer(&taskflow, prepare_done.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::End); - detail::bind_stage_observer(&taskflow, paint_if.hash_value(), root, data, detail::Renderable_Stage::Paint, detail::Stage_Observer_Point::Begin); - detail::bind_stage_observer(&taskflow, paint_done.hash_value(), root, data, detail::Renderable_Stage::Paint, detail::Stage_Observer_Point::End); - stage_tasks.emplace(root, Stage_Tasks{prepare_if, prepare_done, paint_if, paint_done}); - } - auto connect_dependencies = [&](const auto& rely, bool prepare) { - rely.for_each( - [&](const ::double_buffer::Rely::Node& rely_node) { - if (!rely.private_data(rely_node)) return; - auto target = stage_tasks.find(rely_node.object); - if (target == stage_tasks.end()) return; - std::pmr::unordered_set<::double_buffer::Root*> visited{resource}; - std::pmr::vector pending{resource}; - for (const auto* dependency : rely_node.dependencies) pending.push_back(dependency); - while (!pending.empty()) { - const auto* dependency = pending.back(); - pending.pop_back(); - if (!visited.insert(dependency->object).second) continue; - if (rely.private_data(*dependency)) { - auto source = stage_tasks.find(dependency->object); - if (source != stage_tasks.end()) { - auto source_task = prepare ? source->second.prepare_exit : source->second.paint_exit; - auto target_task = prepare ? target->second.prepare_entry : target->second.paint_entry; - source_task.precede(target_task); - } - continue; - } - for (const auto* next : dependency->dependencies) pending.push_back(next); - } - } - ); - }; - connect_dependencies(prepare_dependencies, true); - connect_dependencies(paint_dependencies, false); - taskflow_dependency_count = 0; - taskflow_max_predecessors = 0; - taskflow_max_successors = 0; - taskflow.for_each_task( - [&](tf::Task task) { - taskflow_dependency_count += task.num_successors(); - taskflow_max_predecessors = std::max(taskflow_max_predecessors, task.num_predecessors()); - taskflow_max_successors = std::max(taskflow_max_successors, task.num_successors()); - } - ); - object->template access_rely( - [](auto& prepare_state, auto& paint_state) { - if (prepare_state.dirty()) prepare_state.take_dirty(); - if (paint_state.dirty()) paint_state.take_dirty(); - } - ); - taskflow_built = true; - taskflow_rebuilt = true; -} -} diff --git a/kernel/src/kernel/render.cpp b/kernel/src/kernel/render_common.cpp similarity index 79% rename from kernel/src/kernel/render.cpp rename to kernel/src/kernel/render_common.cpp index f40cecb..9adaa40 100644 --- a/kernel/src/kernel/render.cpp +++ b/kernel/src/kernel/render_common.cpp @@ -1,66 +1,14 @@ -#include "render.hpp" +#include "render_common.hpp" +#include #include #include +#include #include -#include "double_buffer/core.hpp" namespace aethera { namespace { -class Stage_Registry : ::double_buffer::Pinned { -private: - struct Binding { - const tf::Taskflow* owner; - ::double_buffer::Root* object; - Renderable::Private* data; - detail::Renderable_Stage stage; - detail::Stage_Observer_Point point; - }; - std::unordered_map bindings; - std::mutex mutex; -public: - void bind(const tf::Taskflow* owner, std::size_t task_hash, ::double_buffer::Root* object, Renderable::Private* data, detail::Renderable_Stage stage, detail::Stage_Observer_Point point) { - std::lock_guard guard(mutex); - bindings.insert_or_assign(task_hash, Binding{owner, object, data, stage, point}); - } - void clear(const tf::Taskflow* owner) { - std::lock_guard guard(mutex); - std::erase_if( - bindings, - [owner](const auto& value) { - return value.second.owner == owner; - } - ); - } - void on_entry(std::size_t task_hash, std::uint64_t now) { - std::lock_guard guard(mutex); - auto current = bindings.find(task_hash); - if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::End) return; - auto& binding = current->second; - auto& state = *binding.data->dispatch->state.access(binding.object); - if (binding.stage == detail::Renderable_Stage::Prepare) { - if (state.prepare_executed) state.prepare_execution_time_ns = now - state.prepare_execution_time_ns; - } - else if (state.paint_executed) { - state.paint_execution_time_ns = now - state.paint_execution_time_ns; - } - } - void on_exit(std::size_t task_hash, std::uint64_t now) { - std::lock_guard guard(mutex); - auto current = bindings.find(task_hash); - if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::Begin) return; - auto& binding = current->second; - auto& state = *binding.data->dispatch->state.access(binding.object); - if (binding.stage == detail::Renderable_Stage::Prepare) { - state.prepare_execution_time_ns = state.prepare_executed ? now : 0; - } - else { - state.paint_execution_time_ns = state.paint_executed ? now : 0; - } - } -}; class Task_Observer : public tf::ObserverInterface { private: using Clock = std::chrono::steady_clock; - Stage_Registry* stage_registry; struct Task_Statistics { std::atomic_size_t count{}; std::atomic_uint64_t total_time_ns{}; @@ -122,7 +70,6 @@ private: return static_cast(type); } public: - explicit Task_Observer(Stage_Registry* registry) : stage_registry(registry) {} void set_up(std::size_t workers) override { starts.resize(workers); worker_busy_starts.resize(workers); @@ -132,7 +79,7 @@ public: } void on_entry(tf::WorkerView worker, tf::TaskView task) override { auto now = Clock::now(); - stage_registry->on_entry(task.hash_value(), clock_ns(now)); + detail::observe_stage_entry(task.hash_value(), clock_ns(now)); auto& worker_starts = starts[worker.id()]; if (worker_starts.empty()) { worker_busy_starts[worker.id()] = now; @@ -156,7 +103,7 @@ public: } void on_exit(tf::WorkerView worker, tf::TaskView task) override { auto now = Clock::now(); - stage_registry->on_exit(task.hash_value(), clock_ns(now)); + detail::observe_stage_exit(task.hash_value(), clock_ns(now)); auto& worker_starts = starts[worker.id()]; auto start = worker_starts.back(); worker_starts.pop_back(); @@ -247,29 +194,28 @@ public: state.longest_task_type = longest_task_type; } }; -class Task_Resource : ::double_buffer::Pinned { +class Task_Resource : Pinned { private: std::unique_ptr executor; std::shared_ptr observer; - Stage_Registry stage_registry; std::pmr::memory_resource* memory; std::atomic_size_t active_taskflows{}; std::atomic_size_t peak_active_taskflows{}; std::atomic_size_t completed_taskflows{}; std::atomic_size_t failed_taskflows{}; Task_Runtime_State state; - ::double_buffer::detail::State_Callback_Storage state_callbacks; + double_buffer::detail::State_Callback_Storage state_callbacks; std::recursive_mutex state_mutex; void create_executor(std::size_t workers, std::shared_ptr worker_interface) { executor = std::make_unique(workers, std::move(worker_interface)); - observer = executor->make_observer(&stage_registry); + observer = executor->make_observer(); state = {}; } void ensure_executor() { std::lock_guard guard(state_mutex); if (executor) return; executor = std::make_unique(); - observer = executor->make_observer(&stage_registry); + observer = executor->make_observer(); } void publish_state() { std::lock_guard guard(state_mutex); @@ -286,7 +232,7 @@ public: static Task_Resource value; return value; } - void initialize(std::size_t workers, std::shared_ptr worker_interface, ::double_buffer::Pmr pmr) { + void initialize(std::size_t workers, std::shared_ptr worker_interface, Pmr pmr) { std::lock_guard guard(state_mutex); memory = pmr.resource(); create_executor(workers, std::move(worker_interface)); @@ -316,12 +262,6 @@ public: publish_state(); return elapsed; } - void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, ::double_buffer::Root* object, Renderable::Private* data, detail::Renderable_Stage stage, detail::Stage_Observer_Point point) { - stage_registry.bind(owner, task_hash, object, data, stage, point); - } - void clear_stage_observers(const tf::Taskflow* owner) { - stage_registry.clear(owner); - } std::pmr::memory_resource* memory_resource() const noexcept { return memory; } @@ -335,25 +275,19 @@ public: } }; } -void initialize_runtime(std::size_t workers, std::shared_ptr worker_interface, ::double_buffer::Pmr pmr) { +void initialize_runtime(std::size_t workers, std::shared_ptr worker_interface, Pmr pmr) { Task_Resource::instance().initialize(workers, std::move(worker_interface), pmr); } namespace detail { -void set_runtime_state_callback(std::function callback) { +void set_runtime_state_callback_impl(std::function callback) { Task_Resource::instance().set_state_callback(std::move(callback)); } -void clear_runtime_state_callback() { +void clear_runtime_state_callback_impl() { Task_Resource::instance().clear_state_callback(); } std::uint64_t run_taskflow(tf::Taskflow& taskflow) { return Task_Resource::instance().run(taskflow); } -void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, ::double_buffer::Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point) { - Task_Resource::instance().bind_stage_observer(owner, task_hash, object, data, stage, point); -} -void clear_stage_observers(const tf::Taskflow* owner) { - Task_Resource::instance().clear_stage_observers(owner); -} std::pmr::memory_resource* task_memory_resource() noexcept { return Task_Resource::instance().memory_resource(); } diff --git a/kernel/src/kernel/render_common.hpp b/kernel/src/kernel/render_common.hpp new file mode 100644 index 0000000..e23718a --- /dev/null +++ b/kernel/src/kernel/render_common.hpp @@ -0,0 +1,103 @@ +#pragma once +#include "double_buffer/model.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace aethera { +using double_buffer::Pinned; +using double_buffer::Def; +using double_buffer::Impl; +using double_buffer::State_Type; +using double_buffer::State_Access; +using double_buffer::Pmr; +using double_buffer::Root; +using double_buffer::Tagged_Buffer; +using double_buffer::Attached; +using double_buffer::Dependency_Graph_Type; +using double_buffer::Dependency_Graph; +/* Prepare 数据阶段在 Dependency_Graph 图中的标签。 */ +struct Prepare_Data_Tag {}; +/* Paint 阶段在 Dependency_Graph 图中的标签。 */ +struct Paint_Tag {}; +/* 全局 Taskflow 运行时状态标签,用于注册状态回调。 */ +struct Task_Runtime_State_Tag {}; +/* 单一 Taskflow 任务类型的累计统计。 */ +struct Task_Type_State { + std::size_t count{}; + std::uint64_t total_time_ns{}; + std::uint64_t min_time_ns{}; + std::uint64_t max_time_ns{}; + bool operator==(const Task_Type_State&) const = default; +}; +/* 单一 Taskflow Worker 的累计统计。 */ +struct Task_Worker_State { + std::size_t id{}; + std::size_t task_count{}; + std::size_t peak_observed_queue_size{}; + std::size_t max_observed_queue_capacity{}; + std::uint64_t task_time_ns{}; + std::uint64_t busy_time_ns{}; + std::uint64_t idle_time_ns{}; + std::uint64_t min_task_time_ns{}; + std::uint64_t max_task_time_ns{}; + double utilization{}; + bool operator==(const Task_Worker_State&) const = default; +}; +/* + * 全局 Taskflow Executor 的累计运行状态。 + * 状态在每次 taskflow 执行完成或异常结束后发布,适合用于性能统计和调试面板。 + */ +struct Task_Runtime_State : State_Type { + std::size_t worker_count{}; + std::size_t active_topology_count{}; + std::size_t active_taskflow_count{}; + std::size_t peak_active_taskflow_count{}; + std::size_t completed_taskflow_count{}; + std::size_t failed_taskflow_count{}; + std::size_t active_task_count{}; + std::size_t peak_active_task_count{}; + std::size_t active_worker_count{}; + std::size_t peak_active_worker_count{}; + std::size_t observed_task_count{}; + std::size_t named_task_count{}; + std::size_t peak_observed_worker_queue_size{}; + std::size_t max_observed_worker_queue_capacity{}; + std::size_t max_predecessors{}; + std::size_t max_successors{}; + std::size_t max_strong_dependencies{}; + std::size_t max_weak_dependencies{}; + std::size_t longest_task_hash{}; + std::string longest_task_name; + tf::TaskType longest_task_type{tf::TaskType::UNDEFINED}; + std::uint64_t longest_task_time_ns{}; + std::uint64_t total_task_time_ns{}; + std::uint64_t worker_busy_time_ns{}; + std::uint64_t observed_wall_time_ns{}; + double worker_utilization{}; + std::array task_types{}; + std::vector workers; + bool operator==(const Task_Runtime_State&) const = default; +}; +/* + * 初始化全局 Taskflow 运行时。 + * workers 为 Executor Worker 数;worker_interface 可自定义 Worker;pmr 作为渲染内核临时图结构的统一内存资源。 + * 未主动调用时运行时会在第一次执行 Taskflow 时按 Taskflow 默认配置惰性创建 Executor。 + */ +void initialize_runtime(std::size_t workers = std::thread::hardware_concurrency(), + std::shared_ptr worker_interface = nullptr, + Pmr pmr = {}); +/* 为全局 Taskflow 运行时状态注册回调;Tag 目前只接受 Task_Runtime_State_Tag。 */ +template Tag, std::invocable Callback> +void set_runtime_state_callback(Callback&& callback); +/* 清除全局 Taskflow 运行时状态回调;Tag 目前只接受 Task_Runtime_State_Tag。 */ +template Tag> +void clear_runtime_state_callback(); +} +#include "render_common.ipp" diff --git a/kernel/src/kernel/render_common.ipp b/kernel/src/kernel/render_common.ipp new file mode 100644 index 0000000..a18cc9d --- /dev/null +++ b/kernel/src/kernel/render_common.ipp @@ -0,0 +1,19 @@ +#pragma once +namespace aethera::detail { +void set_runtime_state_callback_impl(std::function callback); +void clear_runtime_state_callback_impl(); +std::uint64_t run_taskflow(tf::Taskflow& taskflow); +std::pmr::memory_resource* task_memory_resource() noexcept; +void observe_stage_entry(std::size_t task_hash, std::uint64_t now); +void observe_stage_exit(std::size_t task_hash, std::uint64_t now); +} +namespace aethera { +template Tag, std::invocable Callback> +void set_runtime_state_callback(Callback&& callback) { + detail::set_runtime_state_callback_impl(std::function{std::forward(callback)}); +} +template Tag> +void clear_runtime_state_callback() { + detail::clear_runtime_state_callback_impl(); +} +} diff --git a/kernel/src/kernel/renderable.cpp b/kernel/src/kernel/renderable.cpp new file mode 100644 index 0000000..18c1175 --- /dev/null +++ b/kernel/src/kernel/renderable.cpp @@ -0,0 +1,77 @@ +#include "renderable.hpp" +#include +#include +namespace aethera { +namespace { +class Stage_Registry : Pinned { +private: + struct Binding { + const tf::Taskflow* owner; + Root* object; + Renderable::Private* data; + detail::Renderable_Stage stage; + detail::Stage_Observer_Point point; + }; + std::unordered_map bindings; + std::mutex mutex; +public: + void bind(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, detail::Renderable_Stage stage, detail::Stage_Observer_Point point) { + std::lock_guard guard(mutex); + bindings.insert_or_assign(task_hash, Binding{owner, object, data, stage, point}); + } + void clear(const tf::Taskflow* owner) { + std::lock_guard guard(mutex); + std::erase_if( + bindings, + [owner](const auto& value) { + return value.second.owner == owner; + } + ); + } + void on_entry(std::size_t task_hash, std::uint64_t now) { + std::lock_guard guard(mutex); + auto current = bindings.find(task_hash); + if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::End) return; + auto& binding = current->second; + auto& state = *binding.data->dispatch->state.get(binding.object); + if (binding.stage == detail::Renderable_Stage::Prepare) { + if (state.prepare_executed) state.prepare_execution_time_ns = now - state.prepare_execution_time_ns; + } + else if (state.paint_executed) { + state.paint_execution_time_ns = now - state.paint_execution_time_ns; + } + } + void on_exit(std::size_t task_hash, std::uint64_t now) { + std::lock_guard guard(mutex); + auto current = bindings.find(task_hash); + if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::Begin) return; + auto& binding = current->second; + auto& state = *binding.data->dispatch->state.get(binding.object); + if (binding.stage == detail::Renderable_Stage::Prepare) { + state.prepare_execution_time_ns = state.prepare_executed ? now : 0; + } + else { + state.paint_execution_time_ns = state.paint_executed ? now : 0; + } + } +}; +Stage_Registry& stage_registry() { + static Stage_Registry value; + return value; +} +} +namespace detail { +void observe_stage_entry(std::size_t task_hash, std::uint64_t now) { + stage_registry().on_entry(task_hash, now); +} +void observe_stage_exit(std::size_t task_hash, std::uint64_t now) { + stage_registry().on_exit(task_hash, now); +} +void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point) { + stage_registry().bind(owner, task_hash, object, data, stage, point); +} +void clear_stage_observers(const tf::Taskflow* owner) { + stage_registry().clear(owner); +} +} +} diff --git a/kernel/src/kernel/renderable.hpp b/kernel/src/kernel/renderable.hpp new file mode 100644 index 0000000..c218293 --- /dev/null +++ b/kernel/src/kernel/renderable.hpp @@ -0,0 +1,128 @@ +#pragma once +#include "render_common.hpp" +namespace aethera { +/* Renderable 对外发布的颜色缓存 Buffer 类型。 */ +struct Color_Cache {}; +/* Renderable 状态标签,用于访问和订阅 Renderable::State。 */ +struct Renderable_State_Tag {}; +struct Renderable; +/* + * Prepare 数据模式定制点。 + * 最终对象的 Private 提供 void prepare_data(T* object) 即满足;若同时存在 build_prepare_graph(...),子图模式优先。 + */ +template +concept Prepare_Data_Renderable = Attached && requires(typename T::Private& private_data, T* object) { + { private_data.prepare_data(object) } -> std::same_as; +}; +/* + * Prepare 子图模式定制点。 + * 最终对象的 Private 提供 tf::Taskflow build_prepare_graph(T* object, const T::State& state) 即满足。 + * 子图首次执行前一定构建,之后由 should_rebuild_prepare_graph(...) 决定是否重建。 + */ +template +concept Prepare_Graph_Renderable = Attached && requires(typename T::Private& private_data, T* object, const typename T::State& state) { + { private_data.build_prepare_graph(object, state) } -> std::same_as; +}; +/* + * Paint 数据模式定制点。 + * 最终对象的 Private 提供 void paint(T* object) 即满足;若同时存在 build_paint_graph(...),子图模式优先。 + */ +template +concept Paint_Data_Renderable = Attached && requires(typename T::Private& private_data, T* object) { + { private_data.paint(object) } -> std::same_as; +}; +/* + * Paint 子图模式定制点。 + * 最终对象的 Private 提供 tf::Taskflow build_paint_graph(T* object, const T::State& state) 即满足。 + * 子图首次执行前一定构建,之后由 should_rebuild_paint_graph(...) 决定是否重建。 + */ +template +concept Paint_Graph_Renderable = Attached && requires(typename T::Private& private_data, T* object, const typename T::State& state) { + { private_data.build_paint_graph(object, state) } -> std::same_as; +}; +/* + * 最终可交给 Scene 执行的 Renderable 契约。 + * Prepare 与 Paint 各自至少提供数据模式或子图模式之一,并继承 Renderable 提供的默认阶段策略。 + */ +template +concept Renderable_Object = Attached && std::derived_from && requires(typename T::Private& private_data, T* object, const typename T::State& state, bool dirty) { + { private_data.should_prepare(object, state, dirty) } -> std::same_as; + { private_data.should_paint(object, state, dirty) } -> std::same_as; + { private_data.should_rebuild_prepare_graph(object, state) } -> std::same_as; + { private_data.should_rebuild_paint_graph(object, state) } -> std::same_as; +} && (Prepare_Data_Renderable || Prepare_Graph_Renderable) && (Paint_Data_Renderable || Paint_Graph_Renderable); +/* + * Renderable 定义 Scene 可调度对象的公共机制。 + * 用户继续派生该定义,在派生类型的 Private 中提供 Prepare/Paint 定制点,并最终使用 Impl 创建可运行实例。 + */ +struct Renderable : Def, Tagged_Buffer> { + /* Renderable 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */ + struct Prop : Prev_Prop {}; + /* Renderable 每次 Scene 执行后发布的阶段状态与统计。 */ + struct State : Prev_State { + bool prepare_dirty{}; /* Prepare 条件判断时观察到的 Prepare_Data_Tag dirty 状态。 */ + bool paint_dirty{}; /* Paint 条件判断时观察到的 Paint_Tag 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 operator==(const State&) const = default; /* 支持测试、快照比较和变更检测的逐字段相等比较。 */ + }; + struct Private : Prev_Private { + /* + * 派生 Renderable 的 Private 必须继承 Prev_Private,并为 Prepare/Paint 各提供一种能力: + * void prepare_data(T* object) 或 tf::Taskflow build_prepare_graph(T* object, const T::State& state); + * void paint(T* object) 或 tf::Taskflow build_paint_graph(T* object, const T::State& state)。 + */ + using Run_Predicate = bool (*)(Root*, bool); + using Rebuild_Predicate = bool (*)(Root*); + using Stage_Run = void (*)(Root*); + using Graph_Builder = tf::Taskflow (*)(Root*); + using State_Get = State* (*)(Root*); + using State_Notify = void (*)(Root*); + struct Stage_Dispatch { + Run_Predicate predicate; /* 判断该阶段本次是否执行。 */ + Rebuild_Predicate rebuild_predicate; /* 判断已有阶段子图是否重建。 */ + Stage_Run run; /* 数据模式执行入口;子图模式为空。 */ + Graph_Builder builder; /* 子图模式构建入口;数据模式为空。 */ + }; + struct State_Dispatch { + State_Get get; /* 获取最终对象的 Renderable 状态层。 */ + State_Notify notify; /* 发布最终对象的 Renderable 状态。 */ + }; + struct Dispatch { + Stage_Dispatch prepare; /* Prepare 阶段分派。 */ + Stage_Dispatch paint; /* Paint 阶段分派。 */ + State_Dispatch state; /* Renderable 状态访问与发布分派。 */ + }; + const Dispatch* dispatch{}; /* 绑定最终对象类型后指向其静态分派表。 */ + std::unique_ptr prepare_graph; /* Prepare 子图模式的当前构建产物。 */ + std::unique_ptr paint_graph; /* Paint 子图模式的当前构建产物。 */ + bool prepare_graph_built{}; /* Prepare 子图是否至少成功构建过一次。 */ + bool paint_graph_built{}; /* Paint 子图是否至少成功构建过一次。 */ + /* CRTP 可覆盖:决定已构建的 Prepare 子图是否重建;默认返回 false。 */ + bool should_rebuild_prepare_graph(Attached auto* object, const State& state); + /* CRTP 可覆盖:决定已构建的 Paint 子图是否重建;默认返回 false。 */ + bool should_rebuild_paint_graph(Attached auto* object, const State& state); + /* CRTP 可覆盖:决定本次是否执行 Prepare;默认返回 dirty。 */ + bool should_prepare(Attached auto* object, const State& state, bool dirty); + /* CRTP 可覆盖:决定本次是否执行 Paint;默认返回 dirty。 */ + bool should_paint(Attached auto* object, const State& state, bool dirty); + /* CRTP 可覆盖:prepare_data(...) 完成后按派生类到基类顺序调用;默认不处理。 */ + void after_prepare_data(Attached auto* object); + /* Def::Private 的四个 State 生命周期 hook 同样可在派生 Private 中覆盖,并通过 State_Access::get() 访问状态层。 */ + }; +private: + /* 数据模式的内部调度入口:执行最终对象 prepare_data(...),再触发各 CRTP 层 after_prepare_data(...)。 */ + template + static void run_prepare_data(Object* object); + /* 依赖图绑定时为最终对象建立无虚函数分派表,并标记首次 Prepare。 */ + void bind_dependency_graph_object(Attached auto* object); + friend struct Dependency_Graph; +}; +} +#include "renderable.ipp" diff --git a/kernel/src/kernel/renderable.ipp b/kernel/src/kernel/renderable.ipp new file mode 100644 index 0000000..a6b0e8e --- /dev/null +++ b/kernel/src/kernel/renderable.ipp @@ -0,0 +1,128 @@ +#pragma once +#include +namespace aethera { +template +void Renderable::run_prepare_data(Object* object) { + auto& private_data = static_cast(object->d); + private_data.prepare_data(object); + auto callback = [&](auto& value) { + if constexpr (requires { value.after_prepare_data(object); }) value.after_prepare_data(object); + }; + walk_private(object, callback); +} +inline bool Renderable::Private::should_rebuild_prepare_graph(Attached auto*, const State&) { + return false; +} +inline bool Renderable::Private::should_rebuild_paint_graph(Attached auto*, const State&) { + 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*) {} +inline void Renderable::bind_dependency_graph_object(Attached auto* object) { + using Object = std::remove_pointer_t; + static_assert(Renderable_Object); + auto& data = static_cast(object->d); + static const Private::Dispatch dispatch{ + { + [](Root* root, bool dirty) { + auto* value = static_cast(root); + auto& private_data = static_cast(value->d); + return private_data.should_prepare(value, *value->d.state.current, dirty); + }, + [](Root* root) { + auto* value = static_cast(root); + auto& private_data = static_cast(value->d); + return private_data.should_rebuild_prepare_graph(value, *value->d.state.current); + }, + []() -> Private::Stage_Run { + if constexpr (Prepare_Data_Renderable) { + return [](Root* root) { + auto* value = static_cast(root); + run_prepare_data(value); + }; + } + else { + return nullptr; + } + }(), + []() -> Private::Graph_Builder { + if constexpr (Prepare_Graph_Renderable) { + return [](Root* root) { + auto* value = static_cast(root); + auto& private_data = static_cast(value->d); + return private_data.build_prepare_graph(value, *value->d.state.current); + }; + } + else { + return nullptr; + } + }() + }, + { + [](Root* root, bool dirty) { + auto* value = static_cast(root); + auto& private_data = static_cast(value->d); + return private_data.should_paint(value, *value->d.state.current, dirty); + }, + [](Root* root) { + auto* value = static_cast(root); + auto& private_data = static_cast(value->d); + return private_data.should_rebuild_paint_graph(value, *value->d.state.current); + }, + []() -> Private::Stage_Run { + if constexpr (Paint_Data_Renderable) { + return [](Root* root) { + auto* value = static_cast(root); + auto& private_data = static_cast(value->d); + private_data.paint(value); + }; + } + else { + return nullptr; + } + }(), + []() -> Private::Graph_Builder { + if constexpr (Paint_Graph_Renderable) { + return [](Root* root) { + auto* value = static_cast(root); + auto& private_data = static_cast(value->d); + return private_data.build_paint_graph(value, *value->d.state.current); + }; + } + else { + return nullptr; + } + }() + }, + { + [](Root* root) { + auto* value = static_cast(root); + return static_cast(value->d.state.current); + }, + [](Root* root) { + auto* value = static_cast(root); + value->template notify_state(); + } + } + }; + data.dispatch = &dispatch; + object->template mark_dirty(); +} +namespace detail { +enum class Renderable_Stage { + Prepare, + Paint +}; +enum class Stage_Observer_Point { + Begin, + End +}; +void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point); +void clear_stage_observers(const tf::Taskflow* owner); +} +} diff --git a/kernel/src/kernel/scene.cpp b/kernel/src/kernel/scene.cpp new file mode 100644 index 0000000..64bdc7b --- /dev/null +++ b/kernel/src/kernel/scene.cpp @@ -0,0 +1,7 @@ +#include "scene.hpp" +namespace aethera { +Scene::Private::Private() : runtime(std::make_unique()) {} +Scene::Private::~Private() { + if (runtime->taskflow) detail::clear_stage_observers(runtime->taskflow.get()); +} +} diff --git a/kernel/src/kernel/scene.hpp b/kernel/src/kernel/scene.hpp new file mode 100644 index 0000000..81b28f7 --- /dev/null +++ b/kernel/src/kernel/scene.hpp @@ -0,0 +1,44 @@ +#pragma once +#include "renderable.hpp" +namespace aethera { +/* Scene 状态标签,用于访问和订阅 Scene::State。 */ +struct Scene_State_Tag {}; +/* + * Scene 汇总 Prepare/Paint 两张 Dependency_Graph 图并构建总 Taskflow。 + * 用户最终通过 Impl 创建可使用实例;编辑 Dependency_Graph 后调用 advance() 提交结构变化,再调用 process(...) 执行当前场景。 + */ +struct Scene : Def, Dependency_Graph_Type, Dependency_Graph_Type> { + /* Scene 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */ + struct Prop : Prev_Prop {}; + /* Scene 每次 process(...) 后发布的总图结构与执行统计。 */ + struct State : Prev_State { + bool taskflow_rebuilt{}; /* 本次 process(...) 前的 advance 是否重新构建了总 Taskflow。 */ + std::size_t renderable_count{}; /* Prepare/Paint 两张依赖图中去重后的 Renderable 数量。 */ + std::size_t taskflow_task_count{}; /* 当前总 Taskflow 中的任务节点数量。 */ + std::size_t taskflow_dependency_count{}; /* 当前总 Taskflow 中的直接依赖边数量。 */ + std::size_t taskflow_max_predecessors{}; /* 当前总 Taskflow 中单个任务的最大直接前驱数量。 */ + std::size_t taskflow_max_successors{}; /* 当前总 Taskflow 中单个任务的最大直接后继数量。 */ + std::uint64_t taskflow_execution_time_ns{}; /* 本次 process(...) 执行总 Taskflow 的耗时,单位为纳秒;没有任务时为 0。 */ + bool operator==(const State&) const = default; /* 支持测试、快照比较和变更检测的逐字段相等比较。 */ + }; + struct Private : Prev_Private { + struct Result {}; /* process(...) 完成回调的结果类型;当前仅表示完成。 */ + struct Runtime; /* Scene 的 Taskflow 构建产物,定义保留在实现文件。 */ + std::unique_ptr runtime; /* Scene 唯一运行时构建产物的所有权。 */ + Private(); + ~Private(); + /* Def CRTP hook:所有缓冲推进后重建必要的总 Taskflow,并更新 Scene_State_Tag 状态层。 */ + template + void after_advance(Object* object, + Prop* pending_prop, + State_Access pending_states, + const Prop* current_prop, + State_Access current_states); + /* Impl CRTP 入口:同步执行当前总 Taskflow;派生 Private 只有在替换完整 Scene 处理语义时才应覆盖。 */ + template + void process(Object* object, Callback&& callback) requires std::invocable; + /* 派生 Scene 的 Private 还可覆盖 Def::Private 的四个 State 生命周期 hook,并通过 State_Access::get() 访问状态层。 */ + }; +}; +} +#include "scene.ipp" diff --git a/kernel/src/kernel/scene.ipp b/kernel/src/kernel/scene.ipp new file mode 100644 index 0000000..d7a03c7 --- /dev/null +++ b/kernel/src/kernel/scene.ipp @@ -0,0 +1,211 @@ +#pragma once +#include +#include +#include +#include +namespace aethera { +struct Scene::Private::Runtime { + std::unique_ptr taskflow; +}; +template +void Scene::Private::process(Object* object, Callback&& callback) requires std::invocable { + auto& state = static_cast(*object->d.state.current); + state.taskflow_execution_time_ns = 0; + if (runtime->taskflow && !runtime->taskflow->empty()) state.taskflow_execution_time_ns = detail::run_taskflow(*runtime->taskflow); + object->template notify_state(); + Result result; + std::invoke(std::forward(callback), std::as_const(result)); +} +template +void Scene::Private::after_advance(Object* object, + Prop* pending_prop, + State_Access pending_states, + const Prop* current_prop, + State_Access current_states) { + auto* resource = detail::task_memory_resource(); + auto& scene_state = current_states.get(); + scene_state.taskflow_rebuilt = false; + std::pmr::unordered_set advanced_objects{resource}; + advanced_objects.insert(object); + object->for_each_current_dependency_graph( + [&](const Dependency_Graph& dependency_graph) { + dependency_graph.for_each( + [&](const Dependency_Graph::Node& node) { + if (advanced_objects.insert(node.object).second) node.object->advance_object(); + } + ); + } + ); + bool taskflow_dirty = !runtime->taskflow; + object->template access_pending_dependency_graph( + [&](auto& prepare_state, auto& paint_state) { + taskflow_dirty = taskflow_dirty || prepare_state.dirty() || paint_state.dirty(); + } + ); + if (!taskflow_dirty) return; + if (!runtime->taskflow) runtime->taskflow = std::make_unique(); + auto& taskflow = *runtime->taskflow; + auto prepare_dependencies = object->template current_dependency_graph(); + auto paint_dependencies = object->template current_dependency_graph(); + std::pmr::unordered_set renderables{resource}; + prepare_dependencies.for_each_bound( + [&](Renderable* renderable, Renderable::Private&) { + renderables.insert(renderable); + } + ); + paint_dependencies.for_each_bound( + [&](Renderable* renderable, Renderable::Private&) { + renderables.insert(renderable); + } + ); + scene_state.renderable_count = renderables.size(); + detail::clear_stage_observers(&taskflow); + taskflow.clear(); + struct Stage_Tasks { + tf::Task prepare_entry; + tf::Task prepare_exit; + tf::Task paint_entry; + tf::Task paint_exit; + }; + std::pmr::unordered_map stage_tasks{resource}; + for (auto* renderable : renderables) { + Root* root = renderable; + auto* data = prepare_dependencies.private_data(root); + if (!data) data = paint_dependencies.private_data(root); + if (!data) continue; + auto* dispatch = data->dispatch; + auto prepare_if = taskflow.emplace([data, dispatch, root] { + auto& state = *dispatch->state.get(root); + state.prepare_graph_rebuilt = false; + state.prepare_execution_time_ns = 0; + if (dispatch->prepare.builder) { + bool rebuild = dispatch->prepare.rebuild_predicate(root); + if (!data->prepare_graph_built || rebuild) { + *data->prepare_graph = dispatch->prepare.builder(root); + data->prepare_graph_built = true; + state.prepare_graph_rebuilt = true; + root->template mark_dirty(); + } + state.prepare_task_count = data->prepare_graph->num_tasks(); + } + else { + state.prepare_task_count = 1; + } + bool dirty = root->template dirty(); + state.prepare_dirty = dirty; + state.prepare_executed = dispatch->prepare.predicate(root, dirty); + return state.prepare_executed ? 0 : 1; + }).name("renderable.prepare.condition"); + tf::Task prepare_run; + if (dispatch->prepare.builder) { + if (!data->prepare_graph) data->prepare_graph = std::make_unique(); + prepare_run = taskflow.composed_of(*data->prepare_graph).name("renderable.prepare.graph"); + } + else { + prepare_run = taskflow.emplace([dispatch, root] { + dispatch->prepare.run(root); + }).name("renderable.prepare.data"); + } + auto prepare_done = taskflow.emplace([dispatch, root] { + auto& state = *dispatch->state.get(root); + if (!state.prepare_executed) return; + root->template take_dirty(); + root->template mark_dirty(); + }).name("renderable.prepare.complete"); + prepare_if.precede(prepare_run, prepare_done); + prepare_run.precede(prepare_done); + auto paint_if = taskflow.emplace([data, dispatch, root] { + auto& state = *dispatch->state.get(root); + state.paint_graph_rebuilt = false; + state.paint_execution_time_ns = 0; + if (dispatch->paint.builder) { + bool rebuild = dispatch->paint.rebuild_predicate(root); + if (!data->paint_graph_built || rebuild) { + *data->paint_graph = dispatch->paint.builder(root); + data->paint_graph_built = true; + state.paint_graph_rebuilt = true; + root->template mark_dirty(); + } + state.paint_task_count = data->paint_graph->num_tasks(); + } + else { + state.paint_task_count = 1; + } + bool dirty = root->template dirty(); + state.paint_dirty = dirty; + state.paint_executed = dispatch->paint.predicate(root, dirty); + return state.paint_executed ? 0 : 1; + }).name("renderable.paint.condition"); + tf::Task paint_run; + if (dispatch->paint.builder) { + if (!data->paint_graph) data->paint_graph = std::make_unique(); + paint_run = taskflow.composed_of(*data->paint_graph).name("renderable.paint.graph"); + } + else { + paint_run = taskflow.emplace([dispatch, root] { + dispatch->paint.run(root); + }).name("renderable.paint.data"); + } + auto paint_done = taskflow.emplace([dispatch, root] { + auto& state = *dispatch->state.get(root); + if (state.paint_executed) root->template take_dirty(); + dispatch->state.notify(root); + }).name("renderable.paint.complete"); + paint_if.precede(paint_run, paint_done); + paint_run.precede(paint_done); + prepare_done.precede(paint_if); + detail::bind_stage_observer(&taskflow, prepare_if.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::Begin); + detail::bind_stage_observer(&taskflow, prepare_done.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::End); + detail::bind_stage_observer(&taskflow, paint_if.hash_value(), root, data, detail::Renderable_Stage::Paint, detail::Stage_Observer_Point::Begin); + detail::bind_stage_observer(&taskflow, paint_done.hash_value(), root, data, detail::Renderable_Stage::Paint, detail::Stage_Observer_Point::End); + stage_tasks.emplace(root, Stage_Tasks{prepare_if, prepare_done, paint_if, paint_done}); + } + auto connect_dependencies = [&](const auto& dependency_graph, bool prepare) { + 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; + std::pmr::unordered_set visited{resource}; + std::pmr::vector pending{resource}; + for (const auto* dependency : dependency_node.dependencies) pending.push_back(dependency); + while (!pending.empty()) { + const auto* dependency = pending.back(); + 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()) { + auto source_task = prepare ? source->second.prepare_exit : source->second.paint_exit; + auto target_task = prepare ? target->second.prepare_entry : target->second.paint_entry; + source_task.precede(target_task); + } + continue; + } + for (const auto* next : dependency->dependencies) pending.push_back(next); + } + } + ); + }; + connect_dependencies(prepare_dependencies, true); + connect_dependencies(paint_dependencies, false); + scene_state.taskflow_task_count = taskflow.num_tasks(); + scene_state.taskflow_dependency_count = 0; + scene_state.taskflow_max_predecessors = 0; + scene_state.taskflow_max_successors = 0; + taskflow.for_each_task( + [&](tf::Task task) { + scene_state.taskflow_dependency_count += task.num_successors(); + scene_state.taskflow_max_predecessors = std::max(scene_state.taskflow_max_predecessors, task.num_predecessors()); + scene_state.taskflow_max_successors = std::max(scene_state.taskflow_max_successors, task.num_successors()); + } + ); + object->template access_pending_dependency_graph( + [](auto& prepare_state, auto& paint_state) { + if (prepare_state.dirty()) prepare_state.take_dirty(); + if (paint_state.dirty()) paint_state.take_dirty(); + } + ); + scene_state.taskflow_rebuilt = true; +} +} diff --git a/kernel/src/test/rely_test.cpp b/kernel/src/test/Dependency_Graph_Test.cpp similarity index 51% rename from kernel/src/test/rely_test.cpp rename to kernel/src/test/Dependency_Graph_Test.cpp index 96b49b8..1c2617c 100644 --- a/kernel/src/test/rely_test.cpp +++ b/kernel/src/test/Dependency_Graph_Test.cpp @@ -1,10 +1,10 @@ -#include "double_buffer/object.hpp" +#include "double_buffer/model.hpp" #include namespace { struct Node_State_Tag {}; struct Graph_State_Tag {}; struct Graph_Tag {}; -struct Node_Object : double_buffer::Impl> { +struct Node_Object : double_buffer::Def> { struct Prop : Prev_Prop {}; struct State : Prev_State { int value{}; @@ -12,27 +12,24 @@ struct Node_Object : double_buffer::Impl; -struct Graph_Object : double_buffer::Impl, double_buffer::Rely_Type> { +using Node = double_buffer::Impl; +struct Graph_Object : double_buffer::Def, double_buffer::Dependency_Graph_Type> { struct Prop : Prev_Prop {}; struct State : Prev_State { bool operator==(const State&) const = default; }; struct Private : Prev_Private {}; }; -using Graph = double_buffer::Attach_Object; -static_assert(!double_buffer::Attached); -static_assert(double_buffer::Attached); -static_assert(!double_buffer::detail::Rely_Object); -static_assert(double_buffer::detail::Rely_Object); -static_assert(!double_buffer::detail::State_Rely_Source); -static_assert(double_buffer::detail::State_Rely_Source); +using Graph = double_buffer::Impl; +static_assert(!double_buffer::detail::Dependency_Object); +static_assert(double_buffer::detail::Dependency_Object); +static_assert(!double_buffer::detail::State_Dependency_Source); } -TEST(rely_storage, edited_graph_moves_inward_and_stays_synchronized) { +TEST(dependency_graph_storage, edited_graph_commits_and_stays_synchronized) { Node first; Node second; Graph graph; - auto result = graph.edit_rely( + auto result = graph.edit_dependency_graph( [&](auto& editor) { editor.add(&first); editor.add(&second); @@ -40,10 +37,10 @@ TEST(rely_storage, edited_graph_moves_inward_and_stays_synchronized) { } ); ASSERT_TRUE(result.has_value()); - auto& buffer = graph.d.rely_storage.get(); + auto& buffer = graph.d.dependency_graph_storage.get(); auto* pending_before = buffer.pending; auto* current_before = buffer.current; - graph.exchange(); + graph.advance(); EXPECT_EQ(buffer.current, pending_before); EXPECT_EQ(buffer.pending, current_before); EXPECT_EQ(buffer.current->size(), 2u); @@ -51,31 +48,31 @@ TEST(rely_storage, edited_graph_moves_inward_and_stays_synchronized) { EXPECT_TRUE(buffer.current->depends_on(&second, &first)); EXPECT_TRUE(buffer.pending->depends_on(&second, &first)); } -TEST(rely_storage, unchanged_graph_does_not_exchange_again) { +TEST(dependency_graph_storage, unchanged_graph_does_not_advance_again) { Node node; Graph graph; - ASSERT_TRUE(graph.edit_rely([&](auto& editor) { editor.add(&node); }).has_value()); - graph.exchange(); - auto& buffer = graph.d.rely_storage.get(); + ASSERT_TRUE(graph.edit_dependency_graph([&](auto& editor) { editor.add(&node); }).has_value()); + graph.advance(); + auto& buffer = graph.d.dependency_graph_storage.get(); auto* pending = buffer.pending; auto* current = buffer.current; - graph.exchange(); + graph.advance(); EXPECT_EQ(buffer.pending, pending); EXPECT_EQ(buffer.current, current); } -TEST(rely, topological_order_and_state_dirty_propagation) { +TEST(dependency_graph, topological_order_and_state_dirty_propagation) { Node source; Node target; Graph graph; - ASSERT_TRUE(graph.edit_rely( + ASSERT_TRUE(graph.edit_dependency_graph( [&](auto& editor) { - editor.add(&source); - editor.add(&target); - editor.template add_dependency<&Node_Object::State::value>(&target, &source); + editor.add(&source); + editor.add(&target); + editor.template add_dependency<&Node_Object::State::value>(&target, &source); } ).has_value()); - graph.exchange(); - auto view = graph.current_rely(); + graph.advance(); + auto view = graph.current_dependency_graph(); std::vector order; auto result = view.for_each_topological_view( [&](const auto&, const auto& node) { @@ -89,11 +86,11 @@ TEST(rely, topological_order_and_state_dirty_propagation) { source.update_state<&Node_Object::State::value>(31); EXPECT_TRUE(target.dirty()); } -TEST(rely, cycle_is_rejected_before_pending_graph_commit) { +TEST(dependency_graph, cycle_is_rejected_before_pending_graph_commit) { Node first; Node second; Graph graph; - auto result = graph.edit_rely( + auto result = graph.edit_dependency_graph( [&](auto& editor) { editor.add(&first); editor.add(&second); @@ -102,16 +99,16 @@ TEST(rely, cycle_is_rejected_before_pending_graph_commit) { } ); ASSERT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), double_buffer::Rely_Error::cycle); - EXPECT_TRUE(graph.rely().empty()); + EXPECT_EQ(result.error(), double_buffer::Dependency_Graph_Error::cycle); + EXPECT_TRUE(graph.pending_dependency_graph().empty()); } -TEST(rely_lifetime, referenced_object_can_be_destroyed_before_graph_owner) { +TEST(dependency_graph_lifetime, referenced_object_can_be_destroyed_before_graph_owner) { Graph graph; auto node = std::make_unique(); - ASSERT_TRUE(graph.edit_rely([&](auto& editor) { editor.add(node.get()); }).has_value()); - graph.exchange(); - ASSERT_EQ(graph.current_rely().size(), 1u); + ASSERT_TRUE(graph.edit_dependency_graph([&](auto& editor) { editor.add(node.get()); }).has_value()); + graph.advance(); + ASSERT_EQ(graph.current_dependency_graph().size(), 1u); node.reset(); - EXPECT_TRUE(graph.current_rely().empty()); - EXPECT_TRUE(graph.rely().empty()); + EXPECT_TRUE(graph.current_dependency_graph().empty()); + EXPECT_TRUE(graph.pending_dependency_graph().empty()); } diff --git a/kernel/src/test/double_buffer_test.cpp b/kernel/src/test/model_test.cpp similarity index 65% rename from kernel/src/test/double_buffer_test.cpp rename to kernel/src/test/model_test.cpp index 186885d..357265d 100644 --- a/kernel/src/test/double_buffer_test.cpp +++ b/kernel/src/test/model_test.cpp @@ -1,4 +1,4 @@ -#include "double_buffer/core.hpp" +#include "double_buffer/mechanism.hpp" #include namespace { struct Value { @@ -7,38 +7,38 @@ struct Value { bool operator==(const Value&) const = default; }; } -TEST(double_buffer, exchange_only_swaps_sides) { +TEST(double_buffer, advance_only_swaps_sides) { double_buffer::Double_Buffer buffer; buffer.pending->first = 1; buffer.current->first = 2; auto* pending = buffer.pending; auto* current = buffer.current; - buffer.exchange(); + buffer.advance(); EXPECT_EQ(buffer.pending, current); EXPECT_EQ(buffer.current, pending); EXPECT_EQ(buffer.pending->first, 2); EXPECT_EQ(buffer.current->first, 1); } -TEST(synchronized_double_buffer, inward_preserves_incremental_pending_state) { - double_buffer::Synchronized_Double_Buffer buffer; +TEST(commit_publish_double_buffer, commit_preserves_incremental_pending_state) { + double_buffer::Commit_Double_Buffer buffer; buffer.pending->first = 7; - buffer.exchange(); + buffer.advance(); EXPECT_EQ(buffer.current->first, 7); EXPECT_EQ(buffer.pending->first, 7); buffer.pending->second = 9; - buffer.exchange(); + buffer.advance(); EXPECT_EQ(buffer.current->first, 7); EXPECT_EQ(buffer.current->second, 9); EXPECT_EQ(*buffer.pending, *buffer.current); } -TEST(synchronized_double_buffer, outward_preserves_incremental_current_prop) { - double_buffer::Synchronized_Double_Buffer buffer; +TEST(commit_publish_double_buffer, publish_preserves_incremental_current_prop) { + double_buffer::Publish_Double_Buffer buffer; buffer.current->first = 11; - buffer.exchange(); + buffer.advance(); EXPECT_EQ(buffer.pending->first, 11); EXPECT_EQ(buffer.current->first, 11); buffer.current->second = 13; - buffer.exchange(); + buffer.advance(); EXPECT_EQ(buffer.pending->first, 11); EXPECT_EQ(buffer.pending->second, 13); EXPECT_EQ(*buffer.pending, *buffer.current); diff --git a/kernel/src/test/object_test.cpp b/kernel/src/test/object_test.cpp index 456e0f4..7be52da 100644 --- a/kernel/src/test/object_test.cpp +++ b/kernel/src/test/object_test.cpp @@ -1,8 +1,8 @@ -#include "double_buffer/object.hpp" +#include "double_buffer/model.hpp" #include namespace { struct Object_State_Tag {}; -struct Test_Object : double_buffer::Impl> { +struct Test_Object : double_buffer::Def> { struct Prop : Prev_Prop { int first{}; int second{}; @@ -14,9 +14,9 @@ struct Test_Object : double_buffer::Impl; +using Object = double_buffer::Impl; struct Derived_State_Tag {}; -struct Derived_Object : double_buffer::Impl> { +struct Derived_Object : double_buffer::Def> { struct Prop : Prev_Prop {}; struct State : Prev_State { int derived{}; @@ -24,29 +24,29 @@ struct Derived_Object : double_buffer::Impl; +using Derived = double_buffer::Impl; } -TEST(object_buffer, state_moves_inward_and_keeps_incremental_baseline) { +TEST(object_buffer, state_commits_and_keeps_incremental_baseline) { Object object; object.update_state<&Test_Object::State::first>(3); - object.exchange(); + object.advance(); EXPECT_EQ(object.d.state.current->first, 3); EXPECT_EQ(object.d.state.pending->first, 3); object.update_state<&Test_Object::State::second>(5); - object.exchange(); + object.advance(); EXPECT_EQ(object.d.state.current->first, 3); EXPECT_EQ(object.d.state.current->second, 5); } -TEST(object_buffer, prop_moves_outward_and_keeps_incremental_baseline) { +TEST(object_buffer, prop_publishes_and_keeps_incremental_baseline) { Object object; object.set(&Test_Object::Prop::first, 17); EXPECT_EQ(object.d.current->first, 17); EXPECT_EQ(object.d.pending->first, 0); - object.exchange(); + object.advance(); EXPECT_EQ(object.d.pending->first, 17); EXPECT_EQ(object.d.current->first, 17); object.set(&Test_Object::Prop::second, 19); - object.exchange(); + object.advance(); EXPECT_EQ(object.d.pending->first, 17); EXPECT_EQ(object.d.pending->second, 19); } @@ -60,7 +60,7 @@ TEST(state_tag, callback_publishes_only_requested_layer) { } ); object.update_state<&Test_Object::State::first>(23); - object.exchange(); + object.advance(); EXPECT_EQ(calls, 0); object.notify_state(); EXPECT_EQ(calls, 1); @@ -86,6 +86,26 @@ TEST(state_tag, inherited_tags_remain_independently_addressable) { static_assert(double_buffer::detail::State_Tag_In); static_assert(double_buffer::detail::State_Tag_In); static_assert(double_buffer::detail::State_Tag_In); +static_assert(std::same_as< + decltype(std::declval>().template get()), + Test_Object::State& +>); +static_assert(std::same_as< + decltype(std::declval>().template get()), + const Derived_Object::State& +>); +TEST(state_tag, state_access_selects_mutable_and_const_layers_by_tag) { + Derived::State state; + double_buffer::State_Access states{state}; + states.get().first = 31; + states.get().derived = 47; + double_buffer::State_Access base_states = states; + EXPECT_EQ(base_states.get().first, 31); + const auto& const_state = state; + double_buffer::State_Access current_states{const_state}; + EXPECT_EQ(current_states.get().first, 31); + EXPECT_EQ(current_states.get().derived, 47); +} TEST(state_tag, state_chain_keeps_default_equality_usable) { Test_Object::State first; Test_Object::State second; diff --git a/kernel/src/test/render_test.cpp b/kernel/src/test/render_test.cpp index 9a09703..8683f78 100644 --- a/kernel/src/test/render_test.cpp +++ b/kernel/src/test/render_test.cpp @@ -1,9 +1,9 @@ -#include "render.hpp" +#include "scene.hpp" #include namespace { struct Direct_State_Tag {}; struct Graph_State_Tag {}; -struct Direct_Renderable : double_buffer::Impl> { +struct Direct_Renderable : double_buffer::Def> { struct Prop : Prev_Prop {}; struct State : Prev_State { bool operator==(const State&) const = default; @@ -19,7 +19,7 @@ struct Direct_Renderable : double_buffer::Impl> { +struct Graph_Renderable : double_buffer::Def> { struct Prop : Prev_Prop {}; struct State : Prev_State { bool operator==(const State&) const = default; @@ -43,11 +43,11 @@ struct Graph_Renderable : double_buffer::Impl; -using Graph = double_buffer::Attach_Object; -using Scene = double_buffer::Attach_Object; +using Direct = double_buffer::Impl; +using Graph = double_buffer::Impl; +using Scene = double_buffer::Impl; struct Dependency_State_Tag {}; -struct Dependency_Renderable : double_buffer::Impl> { +struct Dependency_Renderable : double_buffer::Def> { struct Prop : Prev_Prop {}; struct State : Prev_State { int revision{}; @@ -66,10 +66,10 @@ struct Dependency_Renderable : double_buffer::Impl; +using Dependency = double_buffer::Impl; template void add_renderable(Scene& scene, Renderable* renderable) { - ASSERT_TRUE((scene.edit_rely( + ASSERT_TRUE((scene.edit_dependency_graph( [&](auto& prepare, auto& paint) { prepare.add(renderable); paint.add(renderable); @@ -142,12 +142,39 @@ TEST(renderable_state, scene_and_renderable_callbacks_publish_at_stage_boundarie EXPECT_EQ(runtime_updates, 1); aethera::clear_runtime_state_callback(); } +TEST(scene_state, structural_statistics_survive_a_process_without_rebuild) { + aethera::initialize_runtime(2); + Direct renderable; + Scene scene; + add_renderable(scene, &renderable); + std::size_t task_count{}; + std::size_t dependency_count{}; + int updates{}; + scene.set_state_callback([&](const auto& state) { + EXPECT_EQ(state.renderable_count, 1u); + EXPECT_GT(state.taskflow_task_count, 0u); + if (updates == 0) { + EXPECT_TRUE(state.taskflow_rebuilt); + task_count = state.taskflow_task_count; + dependency_count = state.taskflow_dependency_count; + } + else { + EXPECT_FALSE(state.taskflow_rebuilt); + EXPECT_EQ(state.taskflow_task_count, task_count); + EXPECT_EQ(state.taskflow_dependency_count, dependency_count); + } + ++updates; + }); + scene.process([](const auto&) {}); + scene.process([](const auto&) {}); + EXPECT_EQ(updates, 2); +} TEST(scene_condition, upstream_change_makes_downstream_run_in_same_taskflow) { aethera::initialize_runtime(2); Dependency source; Dependency target; Scene scene; - ASSERT_TRUE((scene.edit_rely( + ASSERT_TRUE((scene.edit_dependency_graph( [&](auto& prepare, auto& paint) { prepare.add(&source); prepare.add(&target);