核心第一版

This commit is contained in:
2026-08-20 17:03:24 +08:00
parent b8a8470ced
commit a5cfb4baa9
21 changed files with 1510 additions and 1400 deletions
+1 -2
View File
@@ -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 ()
@@ -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<const Node*> dependents(Root* source) const {
return rely->dependents(source);
return dependency_graph->dependents(source);
}
std::vector<const Node*> dependencies(Root* object) const {
return rely->dependencies(object);
return dependency_graph->dependencies(object);
}
std::vector<Edge> find_edges(Root* target, Root* source) const {
return rely->find_edges(target, source);
return dependency_graph->find_edges(target, source);
}
template <typename Callback> requires std::invocable<Callback, const Edge&>
template <std::invocable<const Edge&> Callback>
void for_each_edge(Callback&& callback) const {
rely->for_each_edge(std::forward<Callback>(callback));
dependency_graph->for_each_edge(std::forward<Callback>(callback));
}
};
template <typename Object> requires Root_Derived<Object>
template <Root_Derived Object>
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 <typename Callback> requires std::invocable<Callback, Object*, Private_Type&>
template <std::invocable<Object*, Private_Type&> 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<Object*>(node.object), *data);
}
);
}
template <typename Callback> requires std::invocable<Callback, const Typed_View&, const Node&>
template <std::invocable<const Typed_View&, const Node&> Callback>
std::expected<void, Error> 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 <typename Callback> requires std::invocable<Callback, const Node&>
template <std::invocable<const Node&> Callback>
void for_each(Callback&& callback) const {
this->rely->for_each(std::forward<Callback>(callback));
this->dependency_graph->for_each(std::forward<Callback>(callback));
}
};
// Editor 只操作待提交图;结构是否合法由编辑完成后的拓扑验证统一判断。新增节点和依赖源必须满足 Attached,保证进入图的 Root* 一定已由 Attach_Object 绑定运行时 exchange
template <typename Bound_Object> requires Root_Derived<Bound_Object>
// Editor 只操作待提交图;结构是否合法由编辑完成后的拓扑验证统一判断,不在每个底层操作重复检查
template <Root_Derived Bound_Object>
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 <typename Object> requires detail::Bound_Rely_Object<Object> && std::derived_from<Object, Bound_Object>
template <detail::Bound_Dependency_Graph_Target<Bound_Object> Object>
Node* add(Object* object) {
return edit_rely->template add_node<Bound_Object>(object, bind_node_data);
return edit_dependency_graph->template add_node<Bound_Object>(object, bind_node_data);
}
template <typename Object> requires detail::Bound_Rely_Object<Object> && std::derived_from<Object, Bound_Object>
template <detail::Bound_Dependency_Graph_Target<Bound_Object> Object>
void add(std::initializer_list<Object*> objects) {
for (auto* object : objects) edit_rely->template add_node<Bound_Object>(object, bind_node_data);
for (auto* object : objects) edit_dependency_graph->template add_node<Bound_Object>(object, bind_node_data);
}
bool remove(Root* object) {
return edit_rely->remove_node(object);
return edit_dependency_graph->remove_node(object);
}
template <typename Target> requires Root_Derived<Target> && std::derived_from<Target, Bound_Object>
template <detail::Dependency_Graph_Target<Bound_Object> 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 <auto Member, typename Target, typename Source> requires
detail::Bound_Rely_Object<Target> && detail::State_Rely_Source<Source, Member> &&
std::derived_from<Target, Bound_Object>
template <auto Member, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::State_Dependency_Source<Member> Source>
Node* add_dependency(Target* target, Source* source) {
return edit_rely->template add_dependency_runtime<Bound_Object, Target, Source>(
return edit_dependency_graph->template add_dependency_runtime<Bound_Object, Target, Source>(
target,
source,
detail::rely_id<detail::State_Rely_Key<Member>>(),
detail::dependency_id<detail::State_Dependency_Key<Member>>(),
target_tag,
target_dirty_key,
bind_node_data
);
}
template <typename Buffer_Tag, typename Target, typename Source> requires
detail::Bound_Rely_Object<Target> && detail::Buffer_Rely_Source<Source, Buffer_Tag> &&
std::derived_from<Target, Bound_Object>
template <typename Buffer_Tag, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::Buffer_Dependency_Source<Buffer_Tag> Source>
Node* add_dependency(Target* target, Source* source) {
return edit_rely->template add_dependency_runtime<Bound_Object, Target, Source>(
return edit_dependency_graph->template add_dependency_runtime<Bound_Object, Target, Source>(
target,
source,
detail::rely_id<detail::Buffer_Rely_Key<Buffer_Tag>>(),
detail::dependency_id<detail::Buffer_Dependency_Key<Buffer_Tag>>(),
target_tag,
target_dirty_key,
bind_node_data
);
}
template <typename Source_Tag, typename Target, typename Source> requires
detail::Bound_Rely_Object<Target> && detail::Rely_Object<Source> &&
std::derived_from<Target, Bound_Object>
template <typename Source_Tag, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::Dependency_Object Source>
Node* add_dirty_dependency(Target* target, Source* source) {
return edit_rely->template add_dependency_runtime<Bound_Object, Target, Source>(
return edit_dependency_graph->template add_dependency_runtime<Bound_Object, Target, Source>(
target,
source,
detail::rely_id<detail::Dirty_Rely_Key<Source_Tag>>(),
detail::dependency_id<detail::Dirty_Dependency_Key<Source_Tag>>(),
target_tag,
target_dirty_key,
bind_node_data
);
}
template <auto Member, typename Target, typename Source> requires
detail::Bound_Rely_Object<Target> && detail::State_Rely_Source<Source, Member> &&
std::derived_from<Target, Bound_Object>
template <auto Member, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::State_Dependency_Source<Member> Source>
bool remove_dependency(Target* target, Source* source) {
return edit_rely->remove_edge(target, source, detail::rely_id<detail::State_Rely_Key<Member>>(), target_tag);
return edit_dependency_graph->remove_edge(target, source, detail::dependency_id<detail::State_Dependency_Key<Member>>(), target_tag);
}
template <typename Buffer_Tag, typename Target, typename Source> requires
detail::Bound_Rely_Object<Target> && detail::Buffer_Rely_Source<Source, Buffer_Tag> &&
std::derived_from<Target, Bound_Object>
template <typename Buffer_Tag, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::Buffer_Dependency_Source<Buffer_Tag> Source>
bool remove_dependency(Target* target, Source* source) {
return edit_rely->remove_edge(target, source, detail::rely_id<detail::Buffer_Rely_Key<Buffer_Tag>>(), target_tag);
return edit_dependency_graph->remove_edge(target, source, detail::dependency_id<detail::Buffer_Dependency_Key<Buffer_Tag>>(), target_tag);
}
template <typename Target> requires Root_Derived<Target> && std::derived_from<Target, Bound_Object>
template <detail::Dependency_Graph_Target<Bound_Object> 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 <typename Tuple>
friend struct detail::Rely_Storage;
friend struct detail::Dependency_Graph_Storage;
template <typename Tuple>
friend struct detail::Rely_Build_Storage;
friend struct detail::Dependency_Graph_Build_Storage;
std::pmr::memory_resource* pmr_resource;
std::pmr::list<Node> list;
std::pmr::unordered_map<Root*, Node*> nodes;
@@ -226,7 +216,7 @@ private:
std::pmr::unordered_multimap<Dirty_Source, Dirty_Target, Dirty_Source_Hash> 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 <Root_Derived Bound_Object, Root_Derived Object>
static Node::Bind node_bind() {
if constexpr (std::derived_from<Object, Bound_Object> && detail::Bound_Rely_Object<Object>) {
if constexpr (std::derived_from<Object, Bound_Object> && detail::Bound_Dependency_Object<Object>) {
return [](Node* node, Root* root) {
auto* object = static_cast<Object*>(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<typename Bound_Object::Private*>(&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<std::byte>;
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 <typename Callback> requires std::invocable<Callback, const Edge&>
template <std::invocable<const Edge&> 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 <typename Callback> requires std::invocable<Callback, const Node&>
template <std::invocable<const Node&> Callback>
void for_each(Callback&& callback) const {
for (const auto& node : list) std::invoke(callback, node);
}
template <typename Callback> requires std::invocable<Callback, const Node&>
template <std::invocable<const Node&> Callback>
std::expected<void, Error> 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 <typename Callback> requires std::invocable<Callback, const View&, const Node&>
template <std::invocable<const View&, const Node&> Callback>
std::expected<void, Error> 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*>(rely)->detach_destroyed(this);
while (!dependency_graphs.empty()) {
auto* dependency_graph = dependency_graphs.back();
dependency_graphs.pop_back();
const_cast<Dependency_Graph*>(dependency_graph)->detach_destroyed(this);
}
}
}
@@ -0,0 +1,335 @@
#pragma once
#include "Dependency_Graph.hpp"
namespace double_buffer {
namespace detail {
template <typename Callback, typename Tuple, typename... Tags>
concept Dependency_Graph_Edit_Callback_For = Dependency_Graph_List<Tuple> && Unique_Types<Tags...>::value && std::invocable<
Callback,
Dependency_Graph::Editor<Dependency_Graph_Bound_Object<Tags, Tuple>>&...
>;
template <typename Callback, typename Object>
concept Dependency_Graph_View_Node_Callback = Root_Derived<Object> && std::invocable<Callback, const Dependency_Graph::Typed_View<Object>&, const Dependency_Graph::Node&>;
template <typename Callback>
concept Dependency_Graph_Current_Callback = std::invocable<Callback, const Dependency_Graph&>;
template <typename Callback, typename Tuple, typename... Tags>
concept Dependency_Graph_Access_Callback_For = Dependency_Graph_List<Tuple> && Unique_Types<Tags...>::value && std::invocable<Callback, Dependency_Graph_Declaration<Tags, Tuple>&...>;
}
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 <typename Tuple>
struct Dependency_Graph_Storage;
// Dependency_Graph_Storage 为每个 Dependency_Graph_Type 保存 pending/current 两份图;编辑只落到 pendingadvance 后 current 成为执行侧并同步新的编辑基线。
template <typename... Dependency_Graph_Types>
struct Dependency_Graph_Storage<std::tuple<Dependency_Graph_Types...>> {
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
private:
template <typename Mechanism>
struct Dependency_Graph_Buffer : Commit_Double_Buffer<Dependency_Graph>, Mechanism {
using Base = Commit_Double_Buffer<Dependency_Graph>;
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_Graph_Buffer<Dependency_Graph_Types>...> dependency_graphs;
void pair_buffers() {
std::apply(
[](auto&... buffer) {
(buffer.pending->pair_with(*buffer.current), ...);
},
dependency_graphs
);
}
template <typename Tag>
static consteval std::size_t index() {
return tag_index<Tag, Dependency_Graph_Types...>();
}
template <std::size_t I = 0>
std::expected<void, Dependency_Graph_Error> validate_pending_impl() const {
if constexpr (I == sizeof...(Dependency_Graph_Types)) {
return {};
}
else {
auto check = std::get<I>(dependency_graphs).pending->topological_order();
if (!check) return std::unexpected(check.error());
return validate_pending_impl<I + 1>();
}
}
template <typename Mechanism>
static void advance_one(Dependency_Graph_Buffer<Mechanism>& 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 <Dependency_Graph_Tag_In<std::tuple<Dependency_Graph_Types...>> Tag>
auto& get() {
return std::get<index<Tag>()>(dependency_graphs);
}
template <Dependency_Graph_Tag_In<std::tuple<Dependency_Graph_Types...>> Tag>
const auto& get() const {
return std::get<index<Tag>()>(dependency_graphs);
}
template <Dependency_Graph_Tag_In<std::tuple<Dependency_Graph_Types...>>... Tags, Dependency_Graph_Access_Callback_For<std::tuple<Dependency_Graph_Types...>, Tags...> Callback>
void access(Callback&& callback) {
std::invoke(
std::forward<Callback>(callback),
static_cast<Dependency_Graph_Declaration<Tags, std::tuple<Dependency_Graph_Types...>>&>(get<Tags>())...
);
}
// 编辑采用临时图事务:先复制 pending,回调修改临时图并统一验证,成功后才替换 pending,失败不会污染现有结构。
template <Dependency_Graph_Tag_In<std::tuple<Dependency_Graph_Types...>>... Tags, Dependency_Graph_Edit_Callback_For<std::tuple<Dependency_Graph_Types...>, Tags...> Callback>
std::expected<void, Dependency_Graph_Error> edit(Callback&& callback) {
std::tuple<std::conditional_t<true, Dependency_Graph, Tags>...> next(*get<Tags>().pending...);
auto editors = [&]<std::size_t... I>(std::index_sequence<I...>) {
return std::tuple{
Dependency_Graph::Editor<Dependency_Graph_Bound_Object<Tags, std::tuple<Dependency_Graph_Types...>>>(
std::get<I>(next),
dependency_id<Tags>(),
dependency_id<Dirty_Dependency_Key<Tags>>(),
true
)...
};
}(std::index_sequence_for<Tags...>{});
std::apply(
[&](auto&... values) {
std::invoke(std::forward<Callback>(callback), values...);
},
editors
);
std::expected<void, Dependency_Graph_Error> 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::size_t... I>(std::index_sequence<I...>) {
return std::tuple{
(std::get<I>(next).structure_revision != get<Tags>().pending->structure_revision)...
};
}(std::index_sequence_for<Tags...>{});
[&]<std::size_t... I>(std::index_sequence<I...>) {
(
[&] {
auto& buffer = get<Tags>();
buffer.pending->swap(std::get<I>(next));
if (std::get<I>(changed)) buffer.mark_structure_dirty();
}(),
...
);
}(std::index_sequence_for<Tags...>{});
return {};
}
std::expected<void, Dependency_Graph_Error> validate_pending() const {
return validate_pending_impl();
}
void advance() {
std::apply(
[](auto&... buffer) {
(advance_one(buffer), ...);
},
dependency_graphs
);
}
template <Dependency_Graph_Current_Callback Callback>
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 <typename... Dependency_Graph_Types>
struct Dependency_Graph_Build_Storage<std::tuple<Dependency_Graph_Types...>> {
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
private:
std::tuple<std::conditional_t<true, Dependency_Graph, Dependency_Graph_Types>...> dependency_graphs;
template <typename Tag>
static consteval std::size_t index() {
return tag_index<Tag, Dependency_Graph_Types...>();
}
template <std::size_t I = 0>
std::expected<void, Dependency_Graph_Error> validate_impl() const {
if constexpr (I == sizeof...(Dependency_Graph_Types)) {
return {};
}
else {
auto result = std::get<I>(dependency_graphs).topological_order();
if (!result) return std::unexpected(result.error());
return validate_impl<I + 1>();
}
}
template <typename Graph_Declaration>
void commit_one(Dependency_Graph_Storage<std::tuple<Dependency_Graph_Types...>>& storage) {
using Tag = typename Graph_Declaration::Tag_Type;
auto& buffer = storage.template get<Tag>();
buffer.pending->swap(get<Tag>());
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 <Dependency_Graph_Tag_In<std::tuple<Dependency_Graph_Types...>> Tag>
Dependency_Graph& get() {
return std::get<index<Tag>()>(dependency_graphs);
}
template <Dependency_Graph_Tag_In<std::tuple<Dependency_Graph_Types...>> Tag>
const Dependency_Graph& get() const {
return std::get<index<Tag>()>(dependency_graphs);
}
template <Dependency_Graph_Tag_In<std::tuple<Dependency_Graph_Types...>>... Tags, Dependency_Graph_Edit_Callback_For<std::tuple<Dependency_Graph_Types...>, Tags...> Callback>
void edit(Callback&& callback) {
auto editors = std::tuple{
Dependency_Graph::Editor<Dependency_Graph_Bound_Object<Tags, std::tuple<Dependency_Graph_Types...>>>(
get<Tags>(),
dependency_id<Tags>(),
dependency_id<Dirty_Dependency_Key<Tags>>(),
false
)...
};
std::apply(
[&](auto&... values) {
std::invoke(std::forward<Callback>(callback), values...);
},
editors
);
}
std::expected<void, Dependency_Graph_Error> validate() const {
return validate_impl();
}
void commit(Dependency_Graph_Storage<std::tuple<Dependency_Graph_Types...>>& storage) {
(commit_one<Dependency_Graph_Types>(storage), ...);
}
};
}
template <typename Object>
struct Root::Builder {
using Prop = typename Object::Prop;
std::unique_ptr<Object> object;
detail::Dependency_Graph_Build_Storage<typename Object::Dependency_Graph_Types> dependency_graph_storage;
template <typename... Args> requires std::constructible_from<Object, Args...>
explicit Builder(Args&&... args) : object(std::make_unique<Object>(std::forward<Args>(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 <typename Owner, typename Member, detail::Prop_Member_Settable<Prop, Owner, Member> Value>
Builder& set(Member Owner::* member, Value&& value) {
object->set(member, std::forward<Value>(value));
return *this;
}
template <detail::Buffer_Tag_In<typename Object::Buffers> Tag, detail::Buffer_Value_Settable<Tag, typename Object::Buffers> Value>
Builder& set(Value&& value) {
object->template initialize_buffer<Tag>(std::forward<Value>(value));
return *this;
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types>... Dependency_Graph_Tags, detail::Dependency_Graph_Edit_Callback_For<typename Object::Dependency_Graph_Types, Dependency_Graph_Tags...> Callback>
Builder& edit_dependency_graph(Callback&& callback) {
dependency_graph_storage.template edit<Dependency_Graph_Tags...>(std::forward<Callback>(callback));
return *this;
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Node_Object>
Builder& add_dependency_node(Node_Object* node) {
return edit_dependency_graph<Dependency_Graph_Tag>(
[&](auto& editor) {
editor.add(node);
}
);
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, Root_Derived Node_Object>
Builder& remove_dependency_node(Node_Object* node) {
return edit_dependency_graph<Dependency_Graph_Tag>(
[&](auto& editor) {
editor.remove(node);
}
);
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::State_Dependency_Source<Member> Source>
Builder& add_dependency(Target* target, Source* source) {
return edit_dependency_graph<Dependency_Graph_Tag>(
[&](auto& editor) {
editor.template add_dependency<Member>(target, source);
}
);
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, typename Buffer_Tag, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::Buffer_Dependency_Source<Buffer_Tag> Source>
Builder& add_dependency(Target* target, Source* source) {
return edit_dependency_graph<Dependency_Graph_Tag>(
[&](auto& editor) {
editor.template add_dependency<Buffer_Tag>(target, source);
}
);
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, typename Source_Tag, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::Dependency_Object Source>
Builder& add_dirty_dependency(Target* target, Source* source) {
return edit_dependency_graph<Dependency_Graph_Tag>(
[&](auto& editor) {
editor.template add_dirty_dependency<Source_Tag>(target, source);
}
);
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::State_Dependency_Source<Member> Source>
Builder& remove_dependency(Target* target, Source* source) {
return edit_dependency_graph<Dependency_Graph_Tag>(
[&](auto& editor) {
editor.template remove_dependency<Member>(target, source);
}
);
}
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, typename Buffer_Tag, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::Buffer_Dependency_Source<Buffer_Tag> Source>
Builder& remove_dependency(Target* target, Source* source) {
return edit_dependency_graph<Dependency_Graph_Tag>(
[&](auto& editor) {
editor.template remove_dependency<Buffer_Tag>(target, source);
}
);
}
std::expected<std::unique_ptr<Object>, 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<void, Dependency_Graph_Error> validate() const {
return dependency_graph_storage.validate();
}
};
}
@@ -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 <typename Value>
struct Double_Buffer : Pinned {
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
@@ -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 <typename Value, Buffer_Direction Direction>
struct Synchronized_Double_Buffer : Double_Buffer<Value> {
// Commit_Double_Buffer 用于外部提交到内部:pending 接收外部写入,推进后 current 成为内部读值,再把 current 回写 pending 作为下一次编辑基线。
template <typename Value>
struct Commit_Double_Buffer : Double_Buffer<Value> {
using Base = Double_Buffer<Value>;
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 <typename Value>
struct Publish_Double_Buffer : Double_Buffer<Value> {
using Base = Double_Buffer<Value>;
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 <typename Tag, typename Prev = detail::State_Root>
requires Prop_State<Prev>
template <typename Tag, Prop_State Prev = detail::State_Root>
struct State_Type : Prev {
using Tag_Type = Tag;
using Prev_State = Prev;
bool operator==(const State_Type&) const = default;
};
template <typename Tag, typename Value = Tag>
requires Prop_State<Value>
template <typename Tag, Prop_State Value = Tag>
struct Tagged_Buffer {
using Tag_Type = Tag;
using Value_Type = Value;
};
struct Root;
// Rely_Type 描述一类依赖图及其允许绑定的对象类型;dirty 只表示该依赖阶段需要重新处理,不承担图结构所有权。
// Dependency_Graph_Type 描述一类依赖图及其允许绑定的对象类型;dirty 只表示该依赖阶段需要重新处理,不承担图结构所有权。
template <typename Tag, typename Object>
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 <typename Tag, typename Value>
struct Is_Tagged_Buffer<Tagged_Buffer<Tag, Value>> : std::true_type {};
template <typename Value>
struct Is_Rely_Type : std::false_type {};
struct Is_Dependency_Graph_Type : std::false_type {};
template <typename Tag, typename Object>
struct Is_Rely_Type<Rely_Type<Tag, Object>> : std::true_type {};
struct Is_Dependency_Graph_Type<Dependency_Graph_Type<Tag, Object>> : std::true_type {};
template <typename Value>
struct Is_State_Type : std::false_type {};
template <typename Tag, typename Prev>
@@ -207,11 +209,11 @@ struct Is_State_Type<State_Type<Tag, Prev>> : std::true_type {};
template <typename Value>
concept Buffer_Type = Is_Tagged_Buffer<Value>::value;
template <typename Value>
concept Rely_Mechanism = Is_Rely_Type<Value>::value;
concept Dependency_Graph_Mechanism = Is_Dependency_Graph_Type<Value>::value;
template <typename Value>
concept State_Mechanism = Is_State_Type<Value>::value;
template <typename Value>
concept Mechanism_Type = Buffer_Type<Value> || Rely_Mechanism<Value> || State_Mechanism<Value>;
concept Mechanism_Type = Buffer_Type<Value> || Dependency_Graph_Mechanism<Value> || State_Mechanism<Value>;
template <typename Value>
concept Tagged_State = Prop_State<Value> && requires {
typename Value::Tag_Type;
@@ -254,7 +256,7 @@ concept State_Member_Settable = requires(State& state, Value&& value) {
template <typename T>
using Mechanism_Buffer_Tuple = std::conditional_t<Buffer_Type<T>, std::tuple<T>, std::tuple<>>;
template <typename T>
using Mechanism_Rely_Tuple = std::conditional_t<Rely_Mechanism<T>, std::tuple<T>, std::tuple<>>;
using Mechanism_Dependency_Graph_Tuple = std::conditional_t<Dependency_Graph_Mechanism<T>, std::tuple<T>, std::tuple<>>;
template <typename T>
using Mechanism_State_Tuple = std::conditional_t<State_Mechanism<T>, std::tuple<T>, std::tuple<>>;
template <typename Tag, typename Tuple>
@@ -277,30 +279,30 @@ struct Is_Buffer_List<std::tuple<Buffers...>> : Tagged_List_Check<
Buffers...
> {};
template <typename Tuple>
struct Is_Rely_List : std::false_type {};
template <typename... Relies>
struct Is_Rely_List<std::tuple<Relies...>> : Tagged_List_Check<
(Rely_Mechanism<Relies> && ...) && (std::derived_from<typename Relies::Object_Type, Root> && ...),
Relies...
struct Is_Dependency_Graph_List : std::false_type {};
template <typename... Dependency_Graph_Types>
struct Is_Dependency_Graph_List<std::tuple<Dependency_Graph_Types...>> : Tagged_List_Check<
(Dependency_Graph_Mechanism<Dependency_Graph_Types> && ...) && (std::derived_from<typename Dependency_Graph_Types::Object_Type, Root> && ...),
Dependency_Graph_Types...
> {};
template <typename Tuple>
concept Buffer_List = Is_Buffer_List<Tuple>::value;
template <typename Tuple>
concept Rely_List = Is_Rely_List<Tuple>::value;
concept Dependency_Graph_List = Is_Dependency_Graph_List<Tuple>::value;
template <typename Tag, typename Tuple>
concept Buffer_Tag_In = requires {
requires Buffer_List<Tuple>;
requires Has_Tag<Tag, Tuple>::value;
};
template <typename Tag, typename Tuple>
concept Rely_Tag_In = requires {
requires Rely_List<Tuple>;
concept Dependency_Graph_Tag_In = requires {
requires Dependency_Graph_List<Tuple>;
requires Has_Tag<Tag, Tuple>::value;
};
template <typename Tag, typename Tuple>
struct Rely_Type_By_Tag;
template <typename Tag, typename... Relies>
struct Rely_Type_By_Tag<Tag, std::tuple<Relies...>> {
struct Dependency_Graph_Declaration_By_Tag;
template <typename Tag, typename... Dependency_Graph_Types>
struct Dependency_Graph_Declaration_By_Tag<Tag, std::tuple<Dependency_Graph_Types...>> {
private:
template <typename First, typename... Rest>
struct Find {
@@ -315,12 +317,12 @@ private:
using Type = Last;
};
public:
using Type = typename Find<Relies...>::Type;
using Type = typename Find<Dependency_Graph_Types...>::Type;
};
template <typename Tag, typename Tuple>
using Rely_Declaration = typename Rely_Type_By_Tag<Tag, Tuple>::Type;
using Dependency_Graph_Declaration = typename Dependency_Graph_Declaration_By_Tag<Tag, Tuple>::Type;
template <typename Tag, typename Tuple>
using Rely_Bound_Object = typename Rely_Declaration<Tag, Tuple>::Object_Type;
using Dependency_Graph_Bound_Object = typename Dependency_Graph_Declaration<Tag, Tuple>::Object_Type;
template <typename Tuple>
struct Is_State_List : std::false_type {};
template <typename... States>
@@ -399,17 +401,17 @@ private:
return tag_index_v<Tag, Layers>;
}
public:
template <typename Tag, typename Callback> requires State_Tag_In<Tag, Layers>
template <State_Tag_In<Layers> Tag, typename Callback>
void set(Callback&& callback) {
constexpr auto value_index = index<Tag>();
using Layer = std::tuple_element_t<value_index, Layers>;
std::get<value_index>(callbacks) = std::function<void(const Layer&)>{std::forward<Callback>(callback)};
}
template <typename Tag> requires State_Tag_In<Tag, Layers>
template <State_Tag_In<Layers> Tag>
void clear() {
std::get<index<Tag>()>(callbacks) = {};
}
template <typename Tag> requires State_Tag_In<Tag, Layers>
template <State_Tag_In<Layers> Tag>
void notify(const State_T& state) {
constexpr auto value_index = index<Tag>();
using Layer = std::tuple_element_t<value_index, Layers>;
@@ -435,7 +437,7 @@ concept Buffer_Value_Settable = requires(Buffer_Value<Tag, Tuple>& target, Value
requires Buffer_Tag_In<Tag, Tuple>;
target = std::forward<Value>(value);
};
// Buffer_Storage 保存普通 Tagged_Buffer 的双缓冲;它只执行基础交换,不做 State/Prop 那种交换后同步。
// Buffer_Storage 保存普通 Tagged_Buffer 的双缓冲;它只执行基础推进,不做 State/Prop 那种推进后同步。
template <typename Tuple>
struct Buffer_Storage;
template <typename... Buffers>
@@ -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 <typename Tag> requires Buffer_Tag_In<Tag, std::tuple<Buffers...>>
template <Buffer_Tag_In<std::tuple<Buffers...>> Tag>
auto& get() {
return std::get<index<Tag>()>(buffers);
}
template <typename Tag> requires Buffer_Tag_In<Tag, std::tuple<Buffers...>>
template <Buffer_Tag_In<std::tuple<Buffers...>> Tag>
const auto& get() const {
return std::get<index<Tag>()>(buffers);
}
void exchange() {
void advance() {
std::apply(
[](auto&... buffer) {
(buffer.exchange(), ...);
(buffer.advance(), ...);
},
buffers
);
}
};
}
/*
* State_Access State Tag 访
* State_Type const get<Tag>() pending/current
*/
template <typename State_Type> requires detail::State_Chain<std::remove_const_t<State_Type>>
class State_Access {
private:
using Value_Type = std::remove_const_t<State_Type>;
State_Type* state;
template <typename Other_State_Type> requires detail::State_Chain<std::remove_const_t<Other_State_Type>>
friend class State_Access;
public:
explicit State_Access(State_Type& value) noexcept : state(&value) {}
template <typename Source_State_Type> requires std::convertible_to<Source_State_Type*, State_Type*>
State_Access(const State_Access<Source_State_Type>& source) noexcept : state(source.state) {}
template <typename Tag> requires detail::State_Tag_In<Tag, detail::State_Layers_T<Value_Type>>
[[nodiscard]] decltype(auto) get() const noexcept {
using Layer = detail::State_Value<Tag, Value_Type>;
if constexpr (std::is_const_v<State_Type>) return static_cast<const Layer&>(*state);
else return static_cast<Layer&>(*state);
}
};
template <typename State_Type> requires detail::State_Chain<std::remove_const_t<State_Type>>
State_Access(State_Type&) -> State_Access<State_Type>;
namespace detail {
template <auto Member>
struct State_Rely_Key {};
struct State_Dependency_Key {};
template <typename Tag>
struct Buffer_Rely_Key {};
struct Buffer_Dependency_Key {};
template <typename Tag>
struct Dirty_Rely_Key {};
struct Dirty_Dependency_Key {};
template <typename Key>
inline char rely_token;
inline char dependency_token;
template <typename Key>
constexpr const void* rely_id() {
return &rely_token<Key>;
constexpr const void* dependency_id() {
return &dependency_token<Key>;
}
}
struct Rely;
struct Dependency_Graph;
template <typename T>
concept Root_Derived = std::derived_from<T, Root>;
template <typename T>
@@ -494,15 +520,15 @@ concept Object_Core = requires {
requires Prop_State<typename T::State>;
requires detail::State_Chain_Matches<typename T::State, typename T::States>;
requires detail::Buffer_List<typename T::Buffers>;
requires detail::Rely_List<typename T::Relies>;
requires detail::Dependency_Graph_List<typename T::Dependency_Graph_Types>;
};
namespace detail {
template <typename Tuple>
struct Rely_Storage;
struct Dependency_Graph_Storage;
template <typename Tuple>
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<State_Type<Root_State_Tag>>;
struct Prop {};
struct State : State_Type<Root_State_Tag> {
@@ -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<const Rely*> rely_graphs;
std::pmr::vector<const Dependency_Graph*> dependency_graphs;
std::pmr::vector<const void*> 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 <Object_Core Object>
void bind_object_crtp() {
exchange_call = [](Root* root) {
static_cast<Object*>(root)->exchange();
advance_call = [](Root* root) {
static_cast<Object*>(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 <typename Tag>
[[nodiscard]] bool dirty() const {
return std::find(dirty_tags.begin(), dirty_tags.end(), detail::rely_id<Tag>()) != dirty_tags.end();
return std::find(dirty_tags.begin(), dirty_tags.end(), detail::dependency_id<Tag>()) != dirty_tags.end();
}
template <typename Tag>
void mark_dirty() {
mark_dirty_id(detail::rely_id<Tag>(), detail::rely_id<detail::Dirty_Rely_Key<Tag>>());
mark_dirty_id(detail::dependency_id<Tag>(), detail::dependency_id<detail::Dirty_Dependency_Key<Tag>>());
}
template <typename Tag>
bool take_dirty() {
auto current = std::find(dirty_tags.begin(), dirty_tags.end(), detail::rely_id<Tag>());
auto current = std::find(dirty_tags.begin(), dirty_tags.end(), detail::dependency_id<Tag>());
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 <typename Object>
struct Builder;
@@ -586,36 +612,39 @@ concept Object_Root = requires {
requires Object_Core<T>;
typename T::template Builder<T>;
};
// Attached 约束真正进入运行时依赖图的对象必须来自 Attach_Object。Rely_Type 只声明允许绑定的基类型,实际节点在加入图时必须满足该概念
// Attached 是实际运行时对象的编译期标记:只有经过 Attach_Object 包装的对象才拥有 Attached_Object,并允许进入会触发 advance_object 的 Dependency_Graph 图
template <typename T>
concept Attached = requires {
requires Object_Core<T>;
concept Attached = Object_Core<T> && requires {
typename T::Attached_Object;
requires Object_Root<typename T::Attached_Object>;
requires std::derived_from<T, typename T::Attached_Object>;
};
namespace detail {
// Rely_Object 表示已经附着运行时数据、可以安全进入依赖图的实际对象类型。图内保存 Root* 后静态类型会被擦除,因此该约束必须在入图边界完成。
template <typename T>
concept Rely_Object = Attached<T> && requires {
concept Dependency_Object = Attached<T> && requires {
requires Root_Derived<T>;
typename T::State;
typename T::Buffers;
requires Prop_State<typename T::State>;
requires Buffer_List<typename T::Buffers>;
};
template <typename T>
concept Bound_Rely_Object = Rely_Object<T> && requires(T* object) {
concept Bound_Dependency_Object = Dependency_Object<T> && requires(T* object) {
typename T::Private;
object->d;
};
template <typename T, typename Bound>
concept Bound_Dependency_Graph_Target = Bound_Dependency_Object<T> && std::derived_from<T, Bound>;
template <typename T, typename Bound>
concept Dependency_Graph_Target = Root_Derived<T> && std::derived_from<T, Bound>;
template <typename Source, auto Member>
concept State_Rely_Source = requires {
requires Rely_Object<Source>;
concept State_Dependency_Source = requires {
requires Dependency_Object<Source>;
requires State_Member<Member, typename Source::State>;
};
template <typename Source, typename Tag>
concept Buffer_Rely_Source = requires {
requires Rely_Object<Source>;
concept Buffer_Dependency_Source = requires {
requires Dependency_Object<Source>;
requires Buffer_Tag_In<Tag, typename Source::Buffers>;
};
}
@@ -1,5 +1,5 @@
#pragma once
#include "rely_storage.hpp"
#include "Dependency_Graph_Storage.hpp"
namespace double_buffer {
template <typename T>
concept Object = requires {
@@ -29,9 +29,9 @@ using Impl_Buffers = decltype(std::tuple_cat(
std::declval<Mechanism_Buffer_Tuple<Local_Mechanisms>>()...
));
template <typename Base, typename... Local_Mechanisms>
using Impl_Relies = decltype(std::tuple_cat(
std::declval<typename Base::Relies>(),
std::declval<Mechanism_Rely_Tuple<Local_Mechanisms>>()...
using Impl_Dependency_Graph_Types = decltype(std::tuple_cat(
std::declval<typename Base::Dependency_Graph_Types>(),
std::declval<Mechanism_Dependency_Graph_Tuple<Local_Mechanisms>>()...
));
template <typename... Local_Mechanisms>
using Local_States = decltype(std::tuple_cat(
@@ -48,19 +48,21 @@ using Impl_State_Base = Rebind_State_T<
typename Base::State
>;
template <typename Base, typename... Local_Mechanisms>
concept Impl_Mechanisms = requires {
requires Object_Root<Base>;
requires (Mechanism_Type<Local_Mechanisms> && ...);
concept Impl_Mechanisms = Object_Root<Base> && (Mechanism_Type<Local_Mechanisms> && ...) && requires {
requires std::tuple_size_v<Local_States<Local_Mechanisms...>> == 1;
requires Buffer_List<Impl_Buffers<Base, Local_Mechanisms...>>;
requires Rely_List<Impl_Relies<Base, Local_Mechanisms...>>;
requires Dependency_Graph_List<Impl_Dependency_Graph_Types<Base, Local_Mechanisms...>>;
requires State_List<Impl_States<Base, Local_Mechanisms...>>;
};
template <typename Callback, typename Private, typename Object_T>
concept Process_Callback_For = requires(Private& private_data, Object_T* object, Callback&& callback) {
private_data.process(object, std::forward<Callback>(callback));
};
}
// Impl 在编译期把 Buffer/Rely/State 三类机制叠加到继承链;每一层只声明自己的机制,最终类型汇总完整能力。
template <typename Self, typename Base, typename... Local_Mechanisms> requires
// Impl 在编译期把 Buffer/Dependency_Graph/State 三类机制叠加到继承链;每一层只声明自己的机制,最终类型汇总完整能力。
template <typename Self, Object_Root Base, detail::Mechanism_Type... Local_Mechanisms> requires
detail::Impl_Mechanisms<Base, Local_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<Object>;
using State_Declaration = std::tuple_element_t<0, detail::Local_States<Local_Mechanisms...>>;
using State_Tag = typename State_Declaration::Tag_Type;
template <typename Tag> requires std::same_as<Tag, State_Tag>
template <std::same_as<State_Tag> Tag>
using Prev_State = detail::Rebind_State_T<State_Declaration, typename Base::State>;
using Base_Private = typename Base::Private;
using Buffers = detail::Impl_Buffers<Base, Local_Mechanisms...>;
using Relies = detail::Impl_Relies<Base, Local_Mechanisms...>;
using Dependency_Graph_Types = detail::Impl_Dependency_Graph_Types<Base, Local_Mechanisms...>;
using States = detail::Impl_States<Base, Local_Mechanisms...>;
struct Private : Base_Private {
/* CRTP 可覆盖:写入 State 成员前按基类到派生类顺序调用;pending_states.get<Tag>() 返回对应可写状态层。 */
template <typename Owner, typename Member, typename State_T>
void before_state_set(Self*, Member Owner::*, State_T*) {}
void before_state_set(Self* object, Member Owner::* member, State_Access<State_T> pending_states) {}
/* CRTP 可覆盖:写入 State 成员后按派生类到基类顺序调用;member 是本次写入的成员指针。 */
template <typename Owner, typename Member, typename State_T>
void after_state_set(Self*, Member Owner::*, State_T*) {}
void after_state_set(Self* object, Member Owner::* member, State_Access<State_T> pending_states) {}
/* CRTP 可覆盖:缓冲推进前按基类到派生类顺序调用;pending_states 可写,current_states 只读。 */
template <typename Prop_T, typename State_T>
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<State_T> pending_states,
const Prop_T* current_prop,
State_Access<const State_T> current_states) {}
/* CRTP 可覆盖:所有缓冲完成推进后按派生类到基类顺序调用;两侧 State 均通过 Tag 选择状态层。 */
template <typename Prop_T, typename State_T>
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<State_T> pending_states,
const Prop_T* current_prop,
State_Access<State_T> current_states) {}
};
using Prev_Private = Private;
template <bool Reverse, typename Layer, typename Object_T, typename Callback>
@@ -94,30 +108,30 @@ struct Impl : Base {
}
}
};
// Attach_Object 是对象运行时唯一数据入口:Prop 向外发布,State 向内提交,普通 Buffer 只交换,Rely 负责依赖图同步。
template <typename Obj, typename Lock = std::mutex> requires Object_Root<Obj> && Lockable<Lock>
struct Attach_Object : Obj {
// Attach_Object 是对象运行时唯一数据入口:Prop 向外发布,State 向内提交,普通 Buffer 只推进,Dependency_Graph 负责依赖图同步。
template <Object_Root Obj, Lockable Lock = std::mutex>
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<Attach_Object>;
using Builder = typename Obj::template Builder<Impl>;
using Attached_Object = Obj;
struct Private : Obj::Private, Synchronized_Double_Buffer<Prop, Buffer_Direction::outward> {
struct Private : Obj::Private, Publish_Double_Buffer<Prop> {
using Allocator = std::pmr::polymorphic_allocator<std::byte>;
Synchronized_Double_Buffer<typename Obj::State, Buffer_Direction::inward> state;
Commit_Double_Buffer<typename Obj::State> state;
detail::State_Callback_Storage<typename Obj::State> state_callbacks;
detail::Buffer_Storage<Buffers> buffer_storage;
detail::Rely_Storage<Relies> rely_storage;
explicit Private(std::pmr::memory_resource* resource) : Synchronized_Double_Buffer<Prop, Buffer_Direction::outward>(std::allocator_arg, Allocator{resource}),
detail::Dependency_Graph_Storage<Dependency_Graph_Types> dependency_graph_storage;
explicit Private(std::pmr::memory_resource* resource) : Publish_Double_Buffer<Prop>(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 <typename... Args> requires std::constructible_from<Obj, Args...>
explicit Attach_Object(Args&&... args) : Obj(std::forward<Args>(args)...), d(this->memory_resource()) {
this->template bind_object_crtp<Attach_Object>();
explicit Impl(Args&&... args) : Obj(std::forward<Args>(args)...), d(this->memory_resource()) {
this->template bind_object_crtp<Impl>();
}
private:
friend struct Root;
@@ -132,161 +146,157 @@ private:
}
template <typename Owner, typename Member>
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<false, Obj>(callback);
}
template <typename Owner, typename Member>
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<true, Obj>(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<false, Obj>(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<true, Obj>(callback);
}
void exchange_unlocked() {
before_exchange();
// State 从 pending 向内部 current 提交;Prop 从内部 current 向 pending 发布,两者交换后的写入侧都同步为最新基线。
static_cast<Synchronized_Double_Buffer<Prop, Buffer_Direction::outward>&>(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<Publish_Double_Buffer<Prop>&>(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 <typename Tag> requires detail::Buffer_Tag_In<Tag, Buffers>
auto& buffer() {
this->emit_rely_source(detail::rely_id<detail::Buffer_Rely_Key<Tag>>());
template <detail::Buffer_Tag_In<Buffers> Tag>
auto& pending_buffer() {
this->emit_dependency_source(detail::dependency_id<detail::Buffer_Dependency_Key<Tag>>());
return *d.buffer_storage.template get<Tag>().pending;
}
template <typename Tag> requires detail::Buffer_Tag_In<Tag, Buffers>
template <detail::Buffer_Tag_In<Buffers> Tag>
const auto& current_buffer() const {
return *d.buffer_storage.template get<Tag>().current;
}
template <typename Tag, typename Value> requires
detail::Buffer_Tag_In<Tag, Buffers> && detail::Buffer_Value_Settable<Value, Tag, Buffers>
void set_initial_buffer(Value&& value) {
template <detail::Buffer_Tag_In<Buffers> Tag, detail::Buffer_Value_Settable<Tag, Buffers> Value>
void initialize_buffer(Value&& value) {
auto& buffer = d.buffer_storage.template get<Tag>();
*buffer.pending = std::forward<Value>(value);
*buffer.current = *buffer.pending;
}
template <typename Tag> requires detail::Rely_Tag_In<Tag, Relies>
[[nodiscard]] auto rely() const {
using Bound_Object = detail::Rely_Bound_Object<Tag, Relies>;
const Rely* graph = d.rely_storage.template get<Tag>().pending;
template <detail::Dependency_Graph_Tag_In<Dependency_Graph_Types> Tag>
[[nodiscard]] auto pending_dependency_graph() const {
using Bound_Object = detail::Dependency_Graph_Bound_Object<Tag, Dependency_Graph_Types>;
const Dependency_Graph* graph = d.dependency_graph_storage.template get<Tag>().pending;
return graph->typed_view<Bound_Object>();
}
template <typename Tag> requires detail::Rely_Tag_In<Tag, Relies>
[[nodiscard]] auto current_rely() const {
using Bound_Object = detail::Rely_Bound_Object<Tag, Relies>;
const Rely* graph = d.rely_storage.template get<Tag>().current;
template <detail::Dependency_Graph_Tag_In<Dependency_Graph_Types> Tag>
[[nodiscard]] auto current_dependency_graph() const {
using Bound_Object = detail::Dependency_Graph_Bound_Object<Tag, Dependency_Graph_Types>;
const Dependency_Graph* graph = d.dependency_graph_storage.template get<Tag>().current;
return graph->typed_view<Bound_Object>();
}
template <typename... Tags, typename Callback> requires
(detail::Rely_Tag_In<Tags, Relies> && ...) && detail::Unique_Types<Tags...>::value &&
detail::Rely_Access_Callback_For<Callback, Relies, Tags...>
void access_rely(Callback&& callback) {
d.rely_storage.template access<Tags...>(std::forward<Callback>(callback));
template <detail::Dependency_Graph_Tag_In<Dependency_Graph_Types>... Tags, detail::Dependency_Graph_Access_Callback_For<Dependency_Graph_Types, Tags...> Callback>
void access_pending_dependency_graph(Callback&& callback) {
d.dependency_graph_storage.template access<Tags...>(std::forward<Callback>(callback));
}
// 依赖图只允许在编辑侧修改;回调完成后先验证 DAG,再把新结构提交到 pending。
template <typename... Tags, typename Callback> requires
(detail::Rely_Tag_In<Tags, Relies> && ...) && detail::Unique_Types<Tags...>::value &&
detail::Rely_Edit_Callback_For<Callback, Relies, Tags...>
std::expected<void, Rely_Error> edit_rely(Callback&& callback) {
template <detail::Dependency_Graph_Tag_In<Dependency_Graph_Types>... Tags, detail::Dependency_Graph_Edit_Callback_For<Dependency_Graph_Types, Tags...> Callback>
std::expected<void, Dependency_Graph_Error> edit_dependency_graph(Callback&& callback) {
std::lock_guard<Lock> guard(lock);
return d.rely_storage.template edit<Tags...>(std::forward<Callback>(callback));
return d.dependency_graph_storage.template edit<Tags...>(std::forward<Callback>(callback));
}
std::expected<void, Rely_Error> validate_rely() const {
return d.rely_storage.validate_pending();
std::expected<void, Dependency_Graph_Error> validate_dependency_graph() const {
return d.dependency_graph_storage.validate_pending();
}
template <typename Tag, typename Callback> requires
detail::Rely_Tag_In<Tag, Relies> &&
detail::Rely_View_Node_Callback<Callback, detail::Rely_Bound_Object<Tag, Relies>>
std::expected<void, Rely_Error> for_each_rely_topological(Callback&& callback) const {
return current_rely<Tag>().for_each_topological_view(std::forward<Callback>(callback));
template <detail::Dependency_Graph_Tag_In<Dependency_Graph_Types> Tag, detail::Dependency_Graph_View_Node_Callback<detail::Dependency_Graph_Bound_Object<Tag, Dependency_Graph_Types>> Callback>
std::expected<void, Dependency_Graph_Error> for_each_dependency_graph_topological(Callback&& callback) const {
return current_dependency_graph<Tag>().for_each_topological_view(std::forward<Callback>(callback));
}
template <typename Callback> requires detail::Rely_Current_Callback<Callback>
void for_each_current_rely(Callback&& callback) const {
d.rely_storage.for_each_current(std::forward<Callback>(callback));
template <detail::Dependency_Graph_Current_Callback Callback>
void for_each_current_dependency_graph(Callback&& callback) const {
d.dependency_graph_storage.for_each_current(std::forward<Callback>(callback));
}
template <typename Owner, typename Member, typename Value> requires
detail::Prop_Member_Settable<Value, Prop, Owner, Member>
Attach_Object& set(Member Owner::* member, Value&& value) {
template <typename Owner, typename Member, detail::Prop_Member_Settable<Prop, Owner, Member> Value>
Impl& set(Member Owner::* member, Value&& value) {
std::lock_guard<Lock> guard(lock);
d.current->*member = std::forward<Value>(value);
return *this;
}
template <typename Tag, typename Callback> requires
detail::State_Callback_For<Callback, Tag, State, States>
template <detail::State_Tag_In<States> Tag, detail::State_Callback_For<Tag, State, States> Callback>
void set_state_callback(Callback&& callback) {
std::lock_guard<Lock> guard(lock);
d.state_callbacks.template set<Tag>(std::forward<Callback>(callback));
}
template <typename Tag> requires detail::State_Tag_In<Tag, States>
template <detail::State_Tag_In<States> Tag>
void clear_state_callback() {
std::lock_guard<Lock> guard(lock);
d.state_callbacks.template clear<Tag>();
}
template <typename Tag, typename Callback> requires
detail::State_Callback_For<Callback, Tag, State, States>
template <detail::State_Tag_In<States> Tag, detail::State_Callback_For<Tag, State, States> Callback>
void access_state(Callback&& callback) const {
std::lock_guard<Lock> guard(lock);
using Layer = detail::State_Value<Tag, State>;
std::invoke(std::forward<Callback>(callback), static_cast<const Layer&>(*d.state.current));
}
template <typename Tag> requires detail::State_Tag_In<Tag, States>
template <detail::State_Tag_In<States> Tag>
void notify_state() {
d.state_callbacks.template notify<Tag>(*d.state.current);
}
// State 写入 pending,随后向依赖图发出对应成员的 dirty 信号;真正进入 current 发生在 exchange/process 边界。
template <auto Member, typename Value> requires detail::State_Member_Settable<Value, Member, State>
// State 写入 pending,随后向依赖图发出对应成员的 dirty 信号;真正进入 current 发生在 advance/process 边界。
template <auto Member, detail::State_Member_Settable<Member, State> Value>
void update_state(Value&& value) {
std::lock_guard<Lock> guard(lock);
before_state_set(Member);
d.state.pending->*Member = std::forward<Value>(value);
after_state_set(Member);
this->emit_rely_source(detail::rely_id<detail::State_Rely_Key<Member>>());
}
template <typename Callback> requires requires(Private& private_data, Attach_Object* object, Callback&& callback) {
private_data.process(object, std::forward<Callback>(callback));
this->emit_dependency_source(detail::dependency_id<detail::State_Dependency_Key<Member>>());
}
template <detail::Process_Callback_For<Private, Impl> Callback>
void process(Callback&& callback) {
std::lock_guard<Lock> guard(lock);
exchange_unlocked();
advance_unlocked();
d.process(this, std::forward<Callback>(callback));
}
void exchange() {
void advance() {
std::lock_guard<Lock> guard(lock);
exchange_unlocked();
advance_unlocked();
}
template <typename Callback> requires std::invocable<Callback, const State&>
void exchange(Callback&& callback) {
template <std::invocable<const State&> Callback>
void advance(Callback&& callback) {
std::lock_guard<Lock> guard(lock);
exchange_unlocked();
advance_unlocked();
std::invoke(std::forward<Callback>(callback), std::as_const(*d.state.current));
}
};
@@ -1,365 +0,0 @@
#pragma once
#include "rely.hpp"
namespace double_buffer {
namespace detail {
template <typename Callback, typename Tuple, typename... Tags>
concept Rely_Edit_Callback_For = Rely_List<Tuple> && std::invocable<
Callback,
Rely::Editor<Rely_Bound_Object<Tags, Tuple>>&...
>;
template <typename Callback, typename Object>
concept Rely_View_Node_Callback = Root_Derived<Object> && std::invocable<Callback, const Rely::Typed_View<Object>&, const Rely::Node&>;
template <typename Callback>
concept Rely_Current_Callback = std::invocable<Callback, const Rely&>;
template <typename Callback, typename Tuple, typename... Tags>
concept Rely_Access_Callback_For = Rely_List<Tuple> && std::invocable<Callback, Rely_Declaration<Tags, Tuple>&...>;
}
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 <typename Tuple>
struct Rely_Storage;
// Rely_Storage 为每个 Rely_Type 保存 pending/current 两份图;编辑只落到 pendingexchange 后 current 成为执行侧并同步新的编辑基线。
template <typename... Relies>
struct Rely_Storage<std::tuple<Relies...>> {
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
private:
template <typename Mechanism>
struct Rely_Buffer : Synchronized_Double_Buffer<Rely, Buffer_Direction::inward>, Mechanism {
using Base = Synchronized_Double_Buffer<Rely, Buffer_Direction::inward>;
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<Rely_Buffer<Relies>...> relies;
void pair_buffers() {
std::apply(
[](auto&... buffer) {
(buffer.pending->pair_with(*buffer.current), ...);
},
relies
);
}
template <typename Tag>
static consteval std::size_t index() {
return tag_index<Tag, Relies...>();
}
template <std::size_t I = 0>
std::expected<void, Rely_Error> validate_pending_impl() const {
if constexpr (I == sizeof...(Relies)) {
return {};
}
else {
auto check = std::get<I>(relies).pending->topological_order();
if (!check) return std::unexpected(check.error());
return validate_pending_impl<I + 1>();
}
}
template <typename Mechanism>
static void exchange_one(Rely_Buffer<Mechanism>& 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 <typename Tag> requires Rely_Tag_In<Tag, std::tuple<Relies...>>
auto& get() {
return std::get<index<Tag>()>(relies);
}
template <typename Tag> requires Rely_Tag_In<Tag, std::tuple<Relies...>>
const auto& get() const {
return std::get<index<Tag>()>(relies);
}
template <typename... Tags, typename Callback> requires
(Rely_Tag_In<Tags, std::tuple<Relies...>> && ...) && Unique_Types<Tags...>::value &&
Rely_Access_Callback_For<Callback, std::tuple<Relies...>, Tags...>
void access(Callback&& callback) {
std::invoke(
std::forward<Callback>(callback),
static_cast<Rely_Declaration<Tags, std::tuple<Relies...>>&>(get<Tags>())...
);
}
// 编辑采用临时图事务:先复制 pending,回调修改临时图并统一验证,成功后才替换 pending,失败不会污染现有结构。
template <typename... Tags, typename Callback> requires
(Rely_Tag_In<Tags, std::tuple<Relies...>> && ...) && Unique_Types<Tags...>::value &&
Rely_Edit_Callback_For<Callback, std::tuple<Relies...>, Tags...>
std::expected<void, Rely_Error> edit(Callback&& callback) {
std::tuple<std::conditional_t<true, Rely, Tags>...> next(*get<Tags>().pending...);
auto editors = [&]<std::size_t... I>(std::index_sequence<I...>) {
return std::tuple{
Rely::Editor<Rely_Bound_Object<Tags, std::tuple<Relies...>>>(
std::get<I>(next),
rely_id<Tags>(),
rely_id<Dirty_Rely_Key<Tags>>(),
true
)...
};
}(std::index_sequence_for<Tags...>{});
std::apply(
[&](auto&... values) {
std::invoke(std::forward<Callback>(callback), values...);
},
editors
);
std::expected<void, Rely_Error> 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::size_t... I>(std::index_sequence<I...>) {
return std::tuple{
(std::get<I>(next).structure_revision != get<Tags>().pending->structure_revision)...
};
}(std::index_sequence_for<Tags...>{});
[&]<std::size_t... I>(std::index_sequence<I...>) {
(
[&] {
auto& buffer = get<Tags>();
buffer.pending->swap(std::get<I>(next));
if (std::get<I>(changed)) buffer.mark_structure_dirty();
}(),
...
);
}(std::index_sequence_for<Tags...>{});
return {};
}
std::expected<void, Rely_Error> validate_pending() const {
return validate_pending_impl();
}
void exchange() {
std::apply(
[](auto&... buffer) {
(exchange_one(buffer), ...);
},
relies
);
}
template <Rely_Current_Callback Callback>
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 <typename... Relies>
struct Rely_Build_Storage<std::tuple<Relies...>> {
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
private:
std::tuple<std::conditional_t<true, Rely, Relies>...> relies;
template <typename Tag>
static consteval std::size_t index() {
return tag_index<Tag, Relies...>();
}
template <std::size_t I = 0>
std::expected<void, Rely_Error> validate_impl() const {
if constexpr (I == sizeof...(Relies)) {
return {};
}
else {
auto result = std::get<I>(relies).topological_order();
if (!result) return std::unexpected(result.error());
return validate_impl<I + 1>();
}
}
template <typename Rely_Type>
void commit_one(Rely_Storage<std::tuple<Relies...>>& storage) {
using Tag = typename Rely_Type::Tag_Type;
auto& buffer = storage.template get<Tag>();
buffer.pending->swap(get<Tag>());
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 <typename Tag> requires Rely_Tag_In<Tag, std::tuple<Relies...>>
Rely& get() {
return std::get<index<Tag>()>(relies);
}
template <typename Tag> requires Rely_Tag_In<Tag, std::tuple<Relies...>>
const Rely& get() const {
return std::get<index<Tag>()>(relies);
}
template <typename... Tags, typename Callback> requires
(Rely_Tag_In<Tags, std::tuple<Relies...>> && ...) && Unique_Types<Tags...>::value &&
Rely_Edit_Callback_For<Callback, std::tuple<Relies...>, Tags...>
void edit(Callback&& callback) {
auto editors = std::tuple{
Rely::Editor<Rely_Bound_Object<Tags, std::tuple<Relies...>>>(
get<Tags>(),
rely_id<Tags>(),
rely_id<Dirty_Rely_Key<Tags>>(),
false
)...
};
std::apply(
[&](auto&... values) {
std::invoke(std::forward<Callback>(callback), values...);
},
editors
);
}
std::expected<void, Rely_Error> validate() const {
return validate_impl();
}
void commit(Rely_Storage<std::tuple<Relies...>>& storage) {
(commit_one<Relies>(storage), ...);
}
};
}
template <typename Object>
struct Root::Builder {
using Prop = typename Object::Prop;
std::unique_ptr<Object> object;
detail::Rely_Build_Storage<typename Object::Relies> rely_storage;
template <typename... Args> requires std::constructible_from<Object, Args...>
explicit Builder(Args&&... args) : object(std::make_unique<Object>(std::forward<Args>(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 <typename Owner, typename Member, typename Value> requires
detail::Prop_Member_Settable<Value, Prop, Owner, Member>
Builder& set(Member Owner::* member, Value&& value) {
object->set(member, std::forward<Value>(value));
return *this;
}
template <typename Tag, typename Value> requires
detail::Buffer_Tag_In<Tag, typename Object::Buffers> &&
detail::Buffer_Value_Settable<Value, Tag, typename Object::Buffers>
Builder& set(Value&& value) {
object->template set_initial_buffer<Tag>(std::forward<Value>(value));
return *this;
}
template <typename... Rely_Tags, typename Callback> requires
(detail::Rely_Tag_In<Rely_Tags, typename Object::Relies> && ...) &&
detail::Unique_Types<Rely_Tags...>::value &&
detail::Rely_Edit_Callback_For<Callback, typename Object::Relies, Rely_Tags...>
Builder& edit_rely(Callback&& callback) {
rely_storage.template edit<Rely_Tags...>(std::forward<Callback>(callback));
return *this;
}
template <typename Rely_Tag, typename Node_Object> requires
detail::Rely_Tag_In<Rely_Tag, typename Object::Relies> && detail::Bound_Rely_Object<Node_Object> &&
std::derived_from<Node_Object, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>
Builder& add_dependency_node(Node_Object* node) {
return edit_rely<Rely_Tag>(
[&](auto& editor) {
editor.add(node);
}
);
}
template <typename Rely_Tag, typename Node_Object> requires
detail::Rely_Tag_In<Rely_Tag, typename Object::Relies> && Root_Derived<Node_Object>
Builder& remove_dependency_node(Node_Object* node) {
return edit_rely<Rely_Tag>(
[&](auto& editor) {
editor.remove(node);
}
);
}
template <typename Rely_Tag, auto Member, typename Target, typename Source> requires
detail::Rely_Tag_In<Rely_Tag, typename Object::Relies> && detail::Rely_Object<Target> &&
detail::State_Rely_Source<Source, Member> && detail::Bound_Rely_Object<Target> &&
std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>
Builder& add_dependency(Target* target, Source* source) {
return edit_rely<Rely_Tag>(
[&](auto& editor) {
editor.template add_dependency<Member>(target, source);
}
);
}
template <typename Rely_Tag, typename Buffer_Tag, typename Target, typename Source> requires
detail::Rely_Tag_In<Rely_Tag, typename Object::Relies> && detail::Rely_Object<Target> &&
detail::Buffer_Rely_Source<Source, Buffer_Tag> && detail::Bound_Rely_Object<Target> &&
std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>
Builder& add_dependency(Target* target, Source* source) {
return edit_rely<Rely_Tag>(
[&](auto& editor) {
editor.template add_dependency<Buffer_Tag>(target, source);
}
);
}
template <typename Rely_Tag, typename Source_Tag, typename Target, typename Source> requires
detail::Rely_Tag_In<Rely_Tag, typename Object::Relies> && Root_Derived<Target> && Root_Derived<Source> &&
detail::Bound_Rely_Object<Target> &&
std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>
Builder& add_dirty_dependency(Target* target, Source* source) {
return edit_rely<Rely_Tag>(
[&](auto& editor) {
editor.template add_dirty_dependency<Source_Tag>(target, source);
}
);
}
template <typename Rely_Tag, auto Member, typename Target, typename Source> requires
detail::Rely_Tag_In<Rely_Tag, typename Object::Relies> && detail::Rely_Object<Target> &&
detail::State_Rely_Source<Source, Member> && detail::Bound_Rely_Object<Target> &&
std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>
Builder& remove_dependency(Target* target, Source* source) {
return edit_rely<Rely_Tag>(
[&](auto& editor) {
editor.template remove_dependency<Member>(target, source);
}
);
}
template <typename Rely_Tag, typename Buffer_Tag, typename Target, typename Source> requires
detail::Rely_Tag_In<Rely_Tag, typename Object::Relies> && detail::Rely_Object<Target> &&
detail::Buffer_Rely_Source<Source, Buffer_Tag> && detail::Bound_Rely_Object<Target> &&
std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>
Builder& remove_dependency(Target* target, Source* source) {
return edit_rely<Rely_Tag>(
[&](auto& editor) {
editor.template remove_dependency<Buffer_Tag>(target, source);
}
);
}
std::expected<std::unique_ptr<Object>, 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<void, Rely_Error> validate() const {
return rely_storage.validate();
}
};
}
-583
View File
@@ -1,583 +0,0 @@
#pragma once
#include "double_buffer/object.hpp"
#include "double_buffer/core.hpp"
#include <array>
#include <string>
#include <taskflow/taskflow.hpp>
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<Task_Runtime_State_Tag> {
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_Type_State, tf::TASK_TYPES.size()> task_types{};
std::vector<Task_Worker_State> workers;
bool operator==(const Task_Runtime_State&) const = default;
};
void initialize_runtime(std::size_t workers = std::thread::hardware_concurrency(),
std::shared_ptr<tf::WorkerInterface> worker_interface = nullptr,
::double_buffer::Pmr pmr = {});
namespace detail {
void set_runtime_state_callback(std::function<void(const Task_Runtime_State&)> callback);
void clear_runtime_state_callback();
std::uint64_t run_taskflow(tf::Taskflow& taskflow);
std::pmr::memory_resource* task_memory_resource() noexcept;
}
template <typename Tag, typename Callback> requires
std::same_as<Tag, Task_Runtime_State_Tag> && std::invocable<Callback, const Task_Runtime_State&>
void set_runtime_state_callback(Callback&& callback) {
detail::set_runtime_state_callback(std::function<void(const Task_Runtime_State&)>{std::forward<Callback>(callback)});
}
template <typename Tag> requires std::same_as<Tag, Task_Runtime_State_Tag>
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 <typename T>
concept Prepare_Data_Renderable = ::double_buffer::Attached<T> && requires(typename T::Private& private_data, T* object) {
{ private_data.prepare_data(object) } -> std::same_as<void>;
};
/*
* 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 <typename T>
concept Prepare_Graph_Renderable = ::double_buffer::Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::State& state) {
{ private_data.build_prepare_graph(object, state) } -> std::same_as<tf::Taskflow>;
};
/*
* Paint 数据模式定制点。
* 子类的 Private 可定义:
* void paint(T* object);
* 当未定义 build_paint_graph(...) 时必须提供该函数。
* 如果 paint(...) 与 build_paint_graph(...) 同时存在,Paint 阶段选择子图模式,
* paint(...) 不再作为该阶段的执行函数。
*/
template <typename T>
concept Paint_Data_Renderable = ::double_buffer::Attached<T> && requires(typename T::Private& private_data, T* object) {
{ private_data.paint(object) } -> std::same_as<void>;
};
/*
* 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 <typename T>
concept Paint_Graph_Renderable = ::double_buffer::Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::State& state) {
{ private_data.build_paint_graph(object, state) } -> std::same_as<tf::Taskflow>;
};
/*
* Renderable 子类完整契约。
* Prepare 与 Paint 两个阶段都必须至少提供一种实现:普通函数或子图构建函数。
* should_prepare(...)、should_paint(...)、should_rebuild_prepare_graph(...)、
* should_rebuild_paint_graph(...) 在 Renderable::Private 中提供默认行为,子类可按需重新定义。
*/
template <typename T>
concept Renderable_Object = ::double_buffer::Attached<T> && std::derived_from<T, Renderable> && 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<bool>;
{ private_data.should_paint(object, state, dirty) } -> std::same_as<bool>;
{ private_data.should_rebuild_prepare_graph(object, state) } -> std::same_as<bool>;
{ private_data.should_rebuild_paint_graph(object, state) } -> std::same_as<bool>;
} && (Prepare_Data_Renderable<T> || Prepare_Graph_Renderable<T>) && (Paint_Data_Renderable<T> || Paint_Graph_Renderable<T>);
struct Renderable : ::double_buffer::Impl<Renderable, ::double_buffer::Root, ::double_buffer::State_Type<Renderable_State_Tag>, ::double_buffer::Tagged_Buffer<Color_Cache>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Renderable_State_Tag> {
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 <Prepare_Data_Renderable Object>
static void run_prepare_data(Object* object) {
auto& private_data = static_cast<typename Object::Private&>(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<true, typename Object::Attached_Object>(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<tf::Taskflow> prepare_graph;
std::unique_ptr<tf::Taskflow> 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<decltype(object)>;
static_assert(Renderable_Object<Object>);
auto& data = static_cast<Private&>(object->d);
static const Private::Dispatch dispatch{
{
[](::double_buffer::Root* root, bool dirty) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_prepare(value, *value->d.state.current, dirty);
},
[](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_rebuild_prepare_graph(value, *value->d.state.current);
},
[]() -> Private::Stage_Run {
if constexpr (Prepare_Data_Renderable<Object>) {
return [](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
run_prepare_data(value);
};
}
else {
return nullptr;
}
}(),
[]() -> Private::Graph_Builder {
if constexpr (Prepare_Graph_Renderable<Object>) {
return [](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(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<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_paint(value, *value->d.state.current, dirty);
},
[](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_rebuild_paint_graph(value, *value->d.state.current);
},
[]() -> Private::Stage_Run {
if constexpr (Paint_Data_Renderable<Object>) {
return [](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
private_data.paint(value);
};
}
else {
return nullptr;
}
}(),
[]() -> Private::Graph_Builder {
if constexpr (Paint_Graph_Renderable<Object>) {
return [](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.build_paint_graph(value, *value->d.state.current);
};
}
else {
return nullptr;
}
}()
},
{
[](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
return static_cast<State*>(value->d.state.current);
},
[](::double_buffer::Root* root) {
auto* value = static_cast<Object*>(root);
value->template notify_state<Renderable_State_Tag>();
}
}
};
data.dispatch = &dispatch;
object->template mark_dirty<Prepare_Data_Tag>();
}
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<Scene, ::double_buffer::Root, ::double_buffer::State_Type<Scene_State_Tag>, ::double_buffer::Rely_Type<Prepare_Data_Tag, Renderable>, ::double_buffer::Rely_Type<Paint_Tag, Renderable>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Scene_State_Tag> {
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 <std::invocable<const Result&> Callback>
void process(::double_buffer::Attached auto* object, Callback&& callback) {
auto& state = static_cast<State&>(*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<Scene_State_Tag>();
Result result;
std::invoke(std::forward<Callback>(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<Prepare_Data_Tag, Paint_Tag>(
[&](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<Prepare_Data_Tag>();
auto paint_dependencies = object->template current_rely<Paint_Tag>();
std::pmr::unordered_set<Renderable*> 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<Prepare_Data_Tag>();
}
state.prepare_task_count = data->prepare_graph->num_tasks();
}
else {
state.prepare_task_count = 1;
}
bool dirty = root->template dirty<Prepare_Data_Tag>();
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<tf::Taskflow>();
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<Prepare_Data_Tag>();
root->template mark_dirty<Paint_Tag>();
}).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<Paint_Tag>();
}
state.paint_task_count = data->paint_graph->num_tasks();
}
else {
state.paint_task_count = 1;
}
bool dirty = root->template dirty<Paint_Tag>();
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<tf::Taskflow>();
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<Paint_Tag>();
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<const ::double_buffer::Rely::Node*> 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<Prepare_Data_Tag, Paint_Tag>(
[](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;
}
}
@@ -1,66 +1,14 @@
#include "render.hpp"
#include "render_common.hpp"
#include <atomic>
#include <chrono>
#include <limits>
#include <mutex>
#include <taskflow/observer/interface.hpp>
#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<std::size_t, Binding> 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<std::size_t>(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<tf::Executor> executor;
std::shared_ptr<Task_Observer> 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<Task_Runtime_State> state_callbacks;
double_buffer::detail::State_Callback_Storage<Task_Runtime_State> state_callbacks;
std::recursive_mutex state_mutex;
void create_executor(std::size_t workers, std::shared_ptr<tf::WorkerInterface> worker_interface) {
executor = std::make_unique<tf::Executor>(workers, std::move(worker_interface));
observer = executor->make_observer<Task_Observer>(&stage_registry);
observer = executor->make_observer<Task_Observer>();
state = {};
}
void ensure_executor() {
std::lock_guard guard(state_mutex);
if (executor) return;
executor = std::make_unique<tf::Executor>();
observer = executor->make_observer<Task_Observer>(&stage_registry);
observer = executor->make_observer<Task_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<tf::WorkerInterface> worker_interface, ::double_buffer::Pmr pmr) {
void initialize(std::size_t workers, std::shared_ptr<tf::WorkerInterface> 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<tf::WorkerInterface> worker_interface, ::double_buffer::Pmr pmr) {
void initialize_runtime(std::size_t workers, std::shared_ptr<tf::WorkerInterface> worker_interface, Pmr pmr) {
Task_Resource::instance().initialize(workers, std::move(worker_interface), pmr);
}
namespace detail {
void set_runtime_state_callback(std::function<void(const Task_Runtime_State&)> callback) {
void set_runtime_state_callback_impl(std::function<void(const Task_Runtime_State&)> 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();
}
+103
View File
@@ -0,0 +1,103 @@
#pragma once
#include "double_buffer/model.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <taskflow/taskflow.hpp>
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<Task_Runtime_State_Tag> {
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_Type_State, tf::TASK_TYPES.size()> task_types{};
std::vector<Task_Worker_State> 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<tf::WorkerInterface> worker_interface = nullptr,
Pmr pmr = {});
/* 为全局 Taskflow 运行时状态注册回调;Tag 目前只接受 Task_Runtime_State_Tag。 */
template <std::same_as<Task_Runtime_State_Tag> Tag, std::invocable<const Task_Runtime_State&> Callback>
void set_runtime_state_callback(Callback&& callback);
/* 清除全局 Taskflow 运行时状态回调;Tag 目前只接受 Task_Runtime_State_Tag。 */
template <std::same_as<Task_Runtime_State_Tag> Tag>
void clear_runtime_state_callback();
}
#include "render_common.ipp"
+19
View File
@@ -0,0 +1,19 @@
#pragma once
namespace aethera::detail {
void set_runtime_state_callback_impl(std::function<void(const Task_Runtime_State&)> 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 <std::same_as<Task_Runtime_State_Tag> Tag, std::invocable<const Task_Runtime_State&> Callback>
void set_runtime_state_callback(Callback&& callback) {
detail::set_runtime_state_callback_impl(std::function<void(const Task_Runtime_State&)>{std::forward<Callback>(callback)});
}
template <std::same_as<Task_Runtime_State_Tag> Tag>
void clear_runtime_state_callback() {
detail::clear_runtime_state_callback_impl();
}
}
+77
View File
@@ -0,0 +1,77 @@
#include "renderable.hpp"
#include <mutex>
#include <unordered_map>
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<std::size_t, Binding> 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);
}
}
}
+128
View File
@@ -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 <typename T>
concept Prepare_Data_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object) {
{ private_data.prepare_data(object) } -> std::same_as<void>;
};
/*
* Prepare 子图模式定制点。
* 最终对象的 Private 提供 tf::Taskflow build_prepare_graph(T* object, const T::State& state) 即满足。
* 子图首次执行前一定构建,之后由 should_rebuild_prepare_graph(...) 决定是否重建。
*/
template <typename T>
concept Prepare_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::State& state) {
{ private_data.build_prepare_graph(object, state) } -> std::same_as<tf::Taskflow>;
};
/*
* Paint 数据模式定制点。
* 最终对象的 Private 提供 void paint(T* object) 即满足;若同时存在 build_paint_graph(...),子图模式优先。
*/
template <typename T>
concept Paint_Data_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object) {
{ private_data.paint(object) } -> std::same_as<void>;
};
/*
* Paint 子图模式定制点。
* 最终对象的 Private 提供 tf::Taskflow build_paint_graph(T* object, const T::State& state) 即满足。
* 子图首次执行前一定构建,之后由 should_rebuild_paint_graph(...) 决定是否重建。
*/
template <typename T>
concept Paint_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::State& state) {
{ private_data.build_paint_graph(object, state) } -> std::same_as<tf::Taskflow>;
};
/*
* 最终可交给 Scene 执行的 Renderable 契约。
* Prepare 与 Paint 各自至少提供数据模式或子图模式之一,并继承 Renderable 提供的默认阶段策略。
*/
template <typename T>
concept Renderable_Object = Attached<T> && std::derived_from<T, Renderable> && requires(typename T::Private& private_data, T* object, const typename T::State& state, bool dirty) {
{ private_data.should_prepare(object, state, dirty) } -> std::same_as<bool>;
{ private_data.should_paint(object, state, dirty) } -> std::same_as<bool>;
{ private_data.should_rebuild_prepare_graph(object, state) } -> std::same_as<bool>;
{ private_data.should_rebuild_paint_graph(object, state) } -> std::same_as<bool>;
} && (Prepare_Data_Renderable<T> || Prepare_Graph_Renderable<T>) && (Paint_Data_Renderable<T> || Paint_Graph_Renderable<T>);
/*
* Renderable 定义 Scene 可调度对象的公共机制。
* 用户继续派生该定义,在派生类型的 Private 中提供 Prepare/Paint 定制点,并最终使用 Impl<T> 创建可运行实例。
*/
struct Renderable : Def<Renderable, Root, State_Type<Renderable_State_Tag>, Tagged_Buffer<Color_Cache>> {
/* Renderable 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */
struct Prop : Prev_Prop {};
/* Renderable 每次 Scene 执行后发布的阶段状态与统计。 */
struct State : Prev_State<Renderable_State_Tag> {
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<tf::Taskflow> prepare_graph; /* Prepare 子图模式的当前构建产物。 */
std::unique_ptr<tf::Taskflow> 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<Tag>() 访问状态层。 */
};
private:
/* 数据模式的内部调度入口:执行最终对象 prepare_data(...),再触发各 CRTP 层 after_prepare_data(...)。 */
template <Prepare_Data_Renderable Object>
static void run_prepare_data(Object* object);
/* 依赖图绑定时为最终对象建立无虚函数分派表,并标记首次 Prepare。 */
void bind_dependency_graph_object(Attached auto* object);
friend struct Dependency_Graph;
};
}
#include "renderable.ipp"
+128
View File
@@ -0,0 +1,128 @@
#pragma once
#include <memory>
namespace aethera {
template <Prepare_Data_Renderable Object>
void Renderable::run_prepare_data(Object* object) {
auto& private_data = static_cast<typename Object::Private&>(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<true, typename Object::Attached_Object>(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<decltype(object)>;
static_assert(Renderable_Object<Object>);
auto& data = static_cast<Private&>(object->d);
static const Private::Dispatch dispatch{
{
[](Root* root, bool dirty) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_prepare(value, *value->d.state.current, dirty);
},
[](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_rebuild_prepare_graph(value, *value->d.state.current);
},
[]() -> Private::Stage_Run {
if constexpr (Prepare_Data_Renderable<Object>) {
return [](Root* root) {
auto* value = static_cast<Object*>(root);
run_prepare_data(value);
};
}
else {
return nullptr;
}
}(),
[]() -> Private::Graph_Builder {
if constexpr (Prepare_Graph_Renderable<Object>) {
return [](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.build_prepare_graph(value, *value->d.state.current);
};
}
else {
return nullptr;
}
}()
},
{
[](Root* root, bool dirty) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_paint(value, *value->d.state.current, dirty);
},
[](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.should_rebuild_paint_graph(value, *value->d.state.current);
},
[]() -> Private::Stage_Run {
if constexpr (Paint_Data_Renderable<Object>) {
return [](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
private_data.paint(value);
};
}
else {
return nullptr;
}
}(),
[]() -> Private::Graph_Builder {
if constexpr (Paint_Graph_Renderable<Object>) {
return [](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.build_paint_graph(value, *value->d.state.current);
};
}
else {
return nullptr;
}
}()
},
{
[](Root* root) {
auto* value = static_cast<Object*>(root);
return static_cast<State*>(value->d.state.current);
},
[](Root* root) {
auto* value = static_cast<Object*>(root);
value->template notify_state<Renderable_State_Tag>();
}
}
};
data.dispatch = &dispatch;
object->template mark_dirty<Prepare_Data_Tag>();
}
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);
}
}
+7
View File
@@ -0,0 +1,7 @@
#include "scene.hpp"
namespace aethera {
Scene::Private::Private() : runtime(std::make_unique<Runtime>()) {}
Scene::Private::~Private() {
if (runtime->taskflow) detail::clear_stage_observers(runtime->taskflow.get());
}
}
+44
View File
@@ -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<Scene> 创建可使用实例;编辑 Dependency_Graph 后调用 advance() 提交结构变化,再调用 process(...) 执行当前场景。
*/
struct Scene : Def<Scene, Root, State_Type<Scene_State_Tag>, Dependency_Graph_Type<Prepare_Data_Tag, Renderable>, Dependency_Graph_Type<Paint_Tag, Renderable>> {
/* Scene 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */
struct Prop : Prev_Prop {};
/* Scene 每次 process(...) 后发布的总图结构与执行统计。 */
struct State : Prev_State<Scene_State_Tag> {
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> runtime; /* Scene 唯一运行时构建产物的所有权。 */
Private();
~Private();
/* Def CRTP hook:所有缓冲推进后重建必要的总 Taskflow,并更新 Scene_State_Tag 状态层。 */
template <Attached Object>
void after_advance(Object* object,
Prop* pending_prop,
State_Access<State> pending_states,
const Prop* current_prop,
State_Access<State> current_states);
/* Impl CRTP 入口:同步执行当前总 Taskflow;派生 Private 只有在替换完整 Scene 处理语义时才应覆盖。 */
template <Attached Object, typename Callback>
void process(Object* object, Callback&& callback) requires std::invocable<Callback, const Result&>;
/* 派生 Scene 的 Private 还可覆盖 Def::Private 的四个 State 生命周期 hook,并通过 State_Access::get<Tag>() 访问状态层。 */
};
};
}
#include "scene.ipp"
+211
View File
@@ -0,0 +1,211 @@
#pragma once
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace aethera {
struct Scene::Private::Runtime {
std::unique_ptr<tf::Taskflow> taskflow;
};
template <Attached Object, typename Callback>
void Scene::Private::process(Object* object, Callback&& callback) requires std::invocable<Callback, const Result&> {
auto& state = static_cast<State&>(*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<Scene_State_Tag>();
Result result;
std::invoke(std::forward<Callback>(callback), std::as_const(result));
}
template <Attached Object>
void Scene::Private::after_advance(Object* object,
Prop* pending_prop,
State_Access<State> pending_states,
const Prop* current_prop,
State_Access<State> current_states) {
auto* resource = detail::task_memory_resource();
auto& scene_state = current_states.get<Scene_State_Tag>();
scene_state.taskflow_rebuilt = false;
std::pmr::unordered_set<Root*> 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<Prepare_Data_Tag, Paint_Tag>(
[&](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<tf::Taskflow>();
auto& taskflow = *runtime->taskflow;
auto prepare_dependencies = object->template current_dependency_graph<Prepare_Data_Tag>();
auto paint_dependencies = object->template current_dependency_graph<Paint_Tag>();
std::pmr::unordered_set<Renderable*> 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<Root*, Stage_Tasks> 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<Prepare_Data_Tag>();
}
state.prepare_task_count = data->prepare_graph->num_tasks();
}
else {
state.prepare_task_count = 1;
}
bool dirty = root->template dirty<Prepare_Data_Tag>();
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<tf::Taskflow>();
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<Prepare_Data_Tag>();
root->template mark_dirty<Paint_Tag>();
}).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<Paint_Tag>();
}
state.paint_task_count = data->paint_graph->num_tasks();
}
else {
state.paint_task_count = 1;
}
bool dirty = root->template dirty<Paint_Tag>();
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<tf::Taskflow>();
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<Paint_Tag>();
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<Root*> visited{resource};
std::pmr::vector<const Dependency_Graph::Node*> 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<Prepare_Data_Tag, Paint_Tag>(
[](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;
}
}
@@ -1,10 +1,10 @@
#include "double_buffer/object.hpp"
#include "double_buffer/model.hpp"
#include <gtest/gtest.h>
namespace {
struct Node_State_Tag {};
struct Graph_State_Tag {};
struct Graph_Tag {};
struct Node_Object : double_buffer::Impl<Node_Object, double_buffer::Root, double_buffer::State_Type<Node_State_Tag>> {
struct Node_Object : double_buffer::Def<Node_Object, double_buffer::Root, double_buffer::State_Type<Node_State_Tag>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Node_State_Tag> {
int value{};
@@ -12,27 +12,24 @@ struct Node_Object : double_buffer::Impl<Node_Object, double_buffer::Root, doubl
};
struct Private : Prev_Private {};
};
using Node = double_buffer::Attach_Object<Node_Object>;
struct Graph_Object : double_buffer::Impl<Graph_Object, double_buffer::Root, double_buffer::State_Type<Graph_State_Tag>, double_buffer::Rely_Type<Graph_Tag, Node_Object>> {
using Node = double_buffer::Impl<Node_Object>;
struct Graph_Object : double_buffer::Def<Graph_Object, double_buffer::Root, double_buffer::State_Type<Graph_State_Tag>, double_buffer::Dependency_Graph_Type<Graph_Tag, Node_Object>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Graph_State_Tag> {
bool operator==(const State&) const = default;
};
struct Private : Prev_Private {};
};
using Graph = double_buffer::Attach_Object<Graph_Object>;
static_assert(!double_buffer::Attached<Node_Object>);
static_assert(double_buffer::Attached<Node>);
static_assert(!double_buffer::detail::Rely_Object<Node_Object>);
static_assert(double_buffer::detail::Rely_Object<Node>);
static_assert(!double_buffer::detail::State_Rely_Source<Node_Object, &Node_Object::State::value>);
static_assert(double_buffer::detail::State_Rely_Source<Node, &Node_Object::State::value>);
using Graph = double_buffer::Impl<Graph_Object>;
static_assert(!double_buffer::detail::Dependency_Object<Node_Object>);
static_assert(double_buffer::detail::Dependency_Object<Node>);
static_assert(!double_buffer::detail::State_Dependency_Source<Node_Object, &Node_Object::State::value>);
}
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<Graph_Tag>(
auto result = graph.edit_dependency_graph<Graph_Tag>(
[&](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<Graph_Tag>();
auto& buffer = graph.d.dependency_graph_storage.get<Graph_Tag>();
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<Graph_Tag>([&](auto& editor) { editor.add(&node); }).has_value());
graph.exchange();
auto& buffer = graph.d.rely_storage.get<Graph_Tag>();
ASSERT_TRUE(graph.edit_dependency_graph<Graph_Tag>([&](auto& editor) { editor.add(&node); }).has_value());
graph.advance();
auto& buffer = graph.d.dependency_graph_storage.get<Graph_Tag>();
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<Graph_Tag>(
ASSERT_TRUE(graph.edit_dependency_graph<Graph_Tag>(
[&](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_Tag>();
graph.advance();
auto view = graph.current_dependency_graph<Graph_Tag>();
std::vector<double_buffer::Root*> 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<Graph_Tag>());
}
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<Graph_Tag>(
auto result = graph.edit_dependency_graph<Graph_Tag>(
[&](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<Graph_Tag>().empty());
EXPECT_EQ(result.error(), double_buffer::Dependency_Graph_Error::cycle);
EXPECT_TRUE(graph.pending_dependency_graph<Graph_Tag>().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<Node>();
ASSERT_TRUE(graph.edit_rely<Graph_Tag>([&](auto& editor) { editor.add(node.get()); }).has_value());
graph.exchange();
ASSERT_EQ(graph.current_rely<Graph_Tag>().size(), 1u);
ASSERT_TRUE(graph.edit_dependency_graph<Graph_Tag>([&](auto& editor) { editor.add(node.get()); }).has_value());
graph.advance();
ASSERT_EQ(graph.current_dependency_graph<Graph_Tag>().size(), 1u);
node.reset();
EXPECT_TRUE(graph.current_rely<Graph_Tag>().empty());
EXPECT_TRUE(graph.rely<Graph_Tag>().empty());
EXPECT_TRUE(graph.current_dependency_graph<Graph_Tag>().empty());
EXPECT_TRUE(graph.pending_dependency_graph<Graph_Tag>().empty());
}
@@ -1,4 +1,4 @@
#include "double_buffer/core.hpp"
#include "double_buffer/mechanism.hpp"
#include <gtest/gtest.h>
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<Value> 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<Value, double_buffer::Buffer_Direction::inward> buffer;
TEST(commit_publish_double_buffer, commit_preserves_incremental_pending_state) {
double_buffer::Commit_Double_Buffer<Value> 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<Value, double_buffer::Buffer_Direction::outward> buffer;
TEST(commit_publish_double_buffer, publish_preserves_incremental_current_prop) {
double_buffer::Publish_Double_Buffer<Value> 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);
+32 -12
View File
@@ -1,8 +1,8 @@
#include "double_buffer/object.hpp"
#include "double_buffer/model.hpp"
#include <gtest/gtest.h>
namespace {
struct Object_State_Tag {};
struct Test_Object : double_buffer::Impl<Test_Object, double_buffer::Root, double_buffer::State_Type<Object_State_Tag>> {
struct Test_Object : double_buffer::Def<Test_Object, double_buffer::Root, double_buffer::State_Type<Object_State_Tag>> {
struct Prop : Prev_Prop {
int first{};
int second{};
@@ -14,9 +14,9 @@ struct Test_Object : double_buffer::Impl<Test_Object, double_buffer::Root, doubl
};
struct Private : Prev_Private {};
};
using Object = double_buffer::Attach_Object<Test_Object>;
using Object = double_buffer::Impl<Test_Object>;
struct Derived_State_Tag {};
struct Derived_Object : double_buffer::Impl<Derived_Object, Test_Object, double_buffer::State_Type<Derived_State_Tag>> {
struct Derived_Object : double_buffer::Def<Derived_Object, Test_Object, double_buffer::State_Type<Derived_State_Tag>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Derived_State_Tag> {
int derived{};
@@ -24,29 +24,29 @@ struct Derived_Object : double_buffer::Impl<Derived_Object, Test_Object, double_
};
struct Private : Prev_Private {};
};
using Derived = double_buffer::Attach_Object<Derived_Object>;
using Derived = double_buffer::Impl<Derived_Object>;
}
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<Object_State_Tag>();
EXPECT_EQ(calls, 1);
@@ -86,6 +86,26 @@ TEST(state_tag, inherited_tags_remain_independently_addressable) {
static_assert(double_buffer::detail::State_Tag_In<double_buffer::Root_State_Tag, Derived::States>);
static_assert(double_buffer::detail::State_Tag_In<Object_State_Tag, Derived::States>);
static_assert(double_buffer::detail::State_Tag_In<Derived_State_Tag, Derived::States>);
static_assert(std::same_as<
decltype(std::declval<double_buffer::State_Access<Derived::State>>().template get<Object_State_Tag>()),
Test_Object::State&
>);
static_assert(std::same_as<
decltype(std::declval<double_buffer::State_Access<const Derived::State>>().template get<Derived_State_Tag>()),
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<Object_State_Tag>().first = 31;
states.get<Derived_State_Tag>().derived = 47;
double_buffer::State_Access<Test_Object::State> base_states = states;
EXPECT_EQ(base_states.get<Object_State_Tag>().first, 31);
const auto& const_state = state;
double_buffer::State_Access current_states{const_state};
EXPECT_EQ(current_states.get<Object_State_Tag>().first, 31);
EXPECT_EQ(current_states.get<Derived_State_Tag>().derived, 47);
}
TEST(state_tag, state_chain_keeps_default_equality_usable) {
Test_Object::State first;
Test_Object::State second;
+37 -10
View File
@@ -1,9 +1,9 @@
#include "render.hpp"
#include "scene.hpp"
#include <gtest/gtest.h>
namespace {
struct Direct_State_Tag {};
struct Graph_State_Tag {};
struct Direct_Renderable : double_buffer::Impl<Direct_Renderable, aethera::Renderable, double_buffer::State_Type<Direct_State_Tag>> {
struct Direct_Renderable : double_buffer::Def<Direct_Renderable, aethera::Renderable, double_buffer::State_Type<Direct_State_Tag>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Direct_State_Tag> {
bool operator==(const State&) const = default;
@@ -19,7 +19,7 @@ struct Direct_Renderable : double_buffer::Impl<Direct_Renderable, aethera::Rende
}
};
};
struct Graph_Renderable : double_buffer::Impl<Graph_Renderable, aethera::Renderable, double_buffer::State_Type<Graph_State_Tag>> {
struct Graph_Renderable : double_buffer::Def<Graph_Renderable, aethera::Renderable, double_buffer::State_Type<Graph_State_Tag>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Graph_State_Tag> {
bool operator==(const State&) const = default;
@@ -43,11 +43,11 @@ struct Graph_Renderable : double_buffer::Impl<Graph_Renderable, aethera::Rendera
}
};
};
using Direct = double_buffer::Attach_Object<Direct_Renderable>;
using Graph = double_buffer::Attach_Object<Graph_Renderable>;
using Scene = double_buffer::Attach_Object<aethera::Scene>;
using Direct = double_buffer::Impl<Direct_Renderable>;
using Graph = double_buffer::Impl<Graph_Renderable>;
using Scene = double_buffer::Impl<aethera::Scene>;
struct Dependency_State_Tag {};
struct Dependency_Renderable : double_buffer::Impl<Dependency_Renderable, aethera::Renderable, double_buffer::State_Type<Dependency_State_Tag>> {
struct Dependency_Renderable : double_buffer::Def<Dependency_Renderable, aethera::Renderable, double_buffer::State_Type<Dependency_State_Tag>> {
struct Prop : Prev_Prop {};
struct State : Prev_State<Dependency_State_Tag> {
int revision{};
@@ -66,10 +66,10 @@ struct Dependency_Renderable : double_buffer::Impl<Dependency_Renderable, aether
}
};
};
using Dependency = double_buffer::Attach_Object<Dependency_Renderable>;
using Dependency = double_buffer::Impl<Dependency_Renderable>;
template <typename Renderable>
void add_renderable(Scene& scene, Renderable* renderable) {
ASSERT_TRUE((scene.edit_rely<aethera::Prepare_Data_Tag, aethera::Paint_Tag>(
ASSERT_TRUE((scene.edit_dependency_graph<aethera::Prepare_Data_Tag, aethera::Paint_Tag>(
[&](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<aethera::Task_Runtime_State_Tag>();
}
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<aethera::Scene_State_Tag>([&](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<aethera::Prepare_Data_Tag, aethera::Paint_Tag>(
ASSERT_TRUE((scene.edit_dependency_graph<aethera::Prepare_Data_Tag, aethera::Paint_Tag>(
[&](auto& prepare, auto& paint) {
prepare.add(&source);
prepare.add(&target);