更换为 taskflow了

This commit is contained in:
2026-08-20 09:05:26 +08:00
parent 921a9fad2a
commit 90a0b89a6c
7 changed files with 231 additions and 297 deletions
+5
View File
@@ -178,6 +178,11 @@ public:
[[nodiscard]] const Rely& render_rely() const {
return *d.rely_storage.template get<Tag>().render;
}
template <detail::Rely_Tag_In<Relies>... Tags, detail::Rely_Render_State_Callback_For<Tags...> Callback> requires
detail::Unique_Types<Tags...>::value
void render_rely(Callback&& callback) {
d.rely_storage.template render<Tags...>(std::forward<Callback>(callback));
}
template <detail::Rely_Tag_In<Relies>... Tags, detail::Rely_Edit_Callback_For<Tags...> Callback> requires
detail::Unique_Types<Tags...>::value
std::expected<void, Rely_Error> edit_rely(Callback&& callback) {
+3 -2
View File
@@ -241,9 +241,10 @@ private:
}
template <Root_Derived Object>
static Node::Bind node_bind() {
if constexpr (requires(Object* object, Node* node) { object->template bind_rely_node<Object>(node); }) {
if constexpr (requires(Object* object, Node* node) { object->bind_rely_node(node, object); }) {
return [](Node* node, Root* root) {
static_cast<Object*>(root)->template bind_rely_node<Object>(node);
auto* object = static_cast<Object*>(root);
object->bind_rely_node(node, object);
};
}
else {
+9
View File
@@ -11,6 +11,8 @@ template <typename Callback>
concept Rely_View_Node_Callback = std::invocable<Callback, const Rely::View&, const Rely::Node&>;
template <typename Callback>
concept Rely_Render_Callback = std::invocable<Callback, const Rely&>;
template <typename Callback, typename... Tags>
concept Rely_Render_State_Callback_For = std::invocable<Callback, Rely_Type<Tags>&...>;
}
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;
@@ -73,6 +75,13 @@ public:
const auto& get() const {
return std::get<index<Tag>()>(relies);
}
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, Rely_Render_State_Callback_For<Tags...> Callback> requires Unique_Types<Tags...>::value
void render(Callback&& callback) {
std::invoke(
std::forward<Callback>(callback),
static_cast<Rely_Type<Tags>&>(get<Tags>())...
);
}
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, Rely_Edit_Callback_For<Tags...> Callback> requires Unique_Types<Tags...>::value
std::expected<void, Rely_Error> edit(Callback&& callback) {
std::tuple<std::conditional_t<true, Rely, Tags>...> next(*get<Tags>().cache...);
+38
View File
@@ -0,0 +1,38 @@
#include "render_2D.hpp"
namespace aethera::_2D {
namespace {
class Task_Resource : Pinned {
private:
std::unique_ptr<tf::Executor> executor_value;
std::pmr::memory_resource* memory_value;
public:
Task_Resource() : memory_value(std::pmr::get_default_resource()) {}
static Task_Resource& instance() {
static Task_Resource value;
return value;
}
void initialize(std::size_t workers, std::shared_ptr<tf::WorkerInterface> worker_interface, Pmr pmr) {
executor_value = std::make_unique<tf::Executor>(workers, std::move(worker_interface));
memory_value = pmr.resource();
}
tf::Executor& executor() {
if (!executor_value) executor_value = std::make_unique<tf::Executor>();
return *executor_value;
}
std::pmr::memory_resource* memory_resource() const noexcept {
return memory_value;
}
};
}
void initialize(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 {
tf::Executor& task_executor() {
return Task_Resource::instance().executor();
}
std::pmr::memory_resource* task_memory_resource() noexcept {
return Task_Resource::instance().memory_resource();
}
}
}
+137 -265
View File
@@ -1,10 +1,17 @@
#pragma once
#include "base.hpp"
#include <oneapi/tbb/flow_graph.h>
#include <taskflow/taskflow.hpp>
namespace aethera::_2D {
struct Prepare_Data_Tag {};
struct Paint_Tag {};
struct Color_Cache {};
void initialize(std::size_t workers = std::thread::hardware_concurrency(),
std::shared_ptr<tf::WorkerInterface> worker_interface = nullptr,
Pmr pmr = {});
namespace detail {
tf::Executor& task_executor();
std::pmr::memory_resource* task_memory_resource() noexcept;
}
struct Renderable;
struct Secene : Impl<Secene, Root, Rely_Type<Prepare_Data_Tag>, Rely_Type<Paint_Tag>> {
struct Prop : Prev_Prop {};
@@ -13,102 +20,14 @@ struct Secene : Impl<Secene, Root, Rely_Type<Prepare_Data_Tag>, Rely_Type<Paint_
struct Result {};
friend struct Renderable;
private:
using Message = oneapi::tbb::flow::continue_msg;
struct Task : Pinned {
using Gate = oneapi::tbb::flow::continue_node<Message>;
using Execute = oneapi::tbb::flow::async_node<Message, Message>;
using Gateway = typename Execute::gateway_type;
struct Async_Tag {};
struct Completion {
Gateway* gateway;
void operator()() const {
(void)gateway->try_put(Message{});
gateway->release_wait();
}
};
Gate gate;
Execute execute;
template <std::invocable Callback>
Task(oneapi::tbb::flow::graph& graph, bool* enabled, Callback&& callback) : gate(
graph,
[](const Message&) {
return Message{};
}
),
execute(
graph,
oneapi::tbb::flow::unlimited,
[state = enabled, task = std::forward<Callback>(callback)](const Message&, Gateway& gateway) mutable {
if (!state || *state) std::invoke(task);
(void)gateway.try_put(Message{});
}
) {
oneapi::tbb::flow::make_edge(gate, execute);
}
template <std::invocable<Completion> Callback>
Task(
oneapi::tbb::flow::graph& graph,
bool* enabled,
Async_Tag,
Callback&& callback) : gate(
graph,
[](const Message&) {
return Message{};
}
),
execute(
graph,
oneapi::tbb::flow::unlimited,
[state = enabled, task = std::forward<Callback>(callback)](const Message&, Gateway& gateway) mutable {
if (state && !*state) {
(void)gateway.try_put(Message{});
return;
}
gateway.reserve_wait();
try {
std::invoke(task, Completion{&gateway});
}
catch (...) {
gateway.release_wait();
throw;
}
}
) {
oneapi::tbb::flow::make_edge(gate, execute);
}
};
std::unique_ptr<oneapi::tbb::flow::graph> graph;
std::unique_ptr<oneapi::tbb::flow::broadcast_node<Message>> start;
std::unique_ptr<std::pmr::list<Task>> tasks;
bool* building_enabled{};
tf::Taskflow graph;
bool graph_ready{};
public:
template <std::invocable Callback>
Task* add(Callback&& callback) {
tasks->emplace_back(*graph, building_enabled, std::forward<Callback>(callback));
return &tasks->back();
}
template <std::invocable<typename Task::Completion> Callback>
Task* add_async(Callback&& callback) {
tasks->emplace_back(
*graph,
building_enabled,
typename Task::Async_Tag{},
std::forward<Callback>(callback)
);
return &tasks->back();
}
void add_rely(Task* target, Task* source) {
oneapi::tbb::flow::make_edge(source->execute, target->gate);
}
template <Attached Object>
void after_exchange(Object* object, Prop*, State*, const Prop*, const State*);
template <Attached Object, std::invocable<const Result&> Callback>
void render_scene(Object*, Callback&& callback) {
void after_exchange(Attached auto* object, Prop*, State*, const Prop*, const State*);
template <std::invocable<const Result&> Callback>
void render_scene(Root*, Callback&& callback) {
Result result;
if (graph && tasks && !tasks->empty()) {
(void)start->try_put(Message{});
graph->wait_for_all();
}
if (graph_ready && !graph.empty()) detail::task_executor().run(graph).get();
std::invoke(std::forward<Callback>(callback), std::as_const(result));
}
};
@@ -119,15 +38,15 @@ struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>> {
bool operator==(const State&) const = default;
};
protected:
template <Attached Object>
static void execute_prepare_data(Object* object) {
static void execute_prepare_data(Attached auto* object) {
using Object = std::remove_pointer_t<decltype(object)>;
auto& private_data = static_cast<typename Object::Private&>(object->d);
if constexpr (requires { private_data.template prepare_data<Object>(object); }) {
private_data.template prepare_data<Object>(object);
if constexpr (requires { private_data.prepare_data(object); }) {
private_data.prepare_data(object);
}
auto callback = [&](auto& value) {
if constexpr (requires { value.template after_prepare_data<Object>(object); }) {
value.template after_prepare_data<Object>(object);
if constexpr (requires { value.after_prepare_data(object); }) {
value.after_prepare_data(object);
}
};
walk_private<true, typename Object::Attached_Object>(object, callback);
@@ -135,103 +54,136 @@ protected:
public:
struct Private : Prev_Private {
using Need_Call = bool (*)(Root*, bool);
using Make_Call = void (*)(Root*, Secene::Private&);
using Update_Call = bool (*)(Root*);
using Make_Call = tf::Taskflow (*)(Root*, std::pmr::memory_resource*);
Need_Call need_prepare_call{};
Need_Call need_paint_call{};
Update_Call update_prepare_call{};
Update_Call update_paint_call{};
Make_Call make_prepare_call{};
Make_Call make_paint_call{};
bool prepare_graph_dirty{true};
bool paint_graph_dirty{true};
bool prepare_enabled{};
bool paint_enabled{};
template <Attached Object>
bool need_update_prepare_graph(Object*, const State&) {
tf::Taskflow prepare_graph;
tf::Taskflow paint_graph;
tf::Taskflow prepare_flow;
tf::Taskflow paint_flow;
bool prepare_graph_ready{};
bool paint_graph_ready{};
bool flow_ready{};
bool need_update_prepare_graph(Attached auto*, const State&) {
return false;
}
template <Attached Object>
bool need_update_paint_graph(Object*, const State&) {
bool need_update_paint_graph(Attached auto*, const State&) {
return false;
}
template <Attached Object>
bool need_prepare(Object*, const State&, bool dirty) {
bool need_prepare(Attached auto*, const State&, bool dirty) {
return dirty;
}
template <Attached Object>
bool need_paint(Object*, const State&, bool dirty) {
bool need_paint(Attached auto*, const State&, bool dirty) {
return dirty;
}
template <Attached Object>
void make_prepare_graph(Object* object, const State&, auto& graph) {
graph.add(
[object] {
execute_prepare_data(object);
tf::Taskflow make_prepare_graph(Attached auto* object, const State&, std::pmr::memory_resource*) {
tf::Taskflow graph;
graph.emplace([object] {
execute_prepare_data(object);
});
return graph;
}
tf::Taskflow make_paint_graph(Attached auto* object, const State&, std::pmr::memory_resource*) {
using Object = std::remove_pointer_t<decltype(object)>;
tf::Taskflow graph;
graph.emplace([object] {
auto& private_data = static_cast<typename Object::Private&>(object->d);
private_data.paint(object);
});
return graph;
}
void prepare_data(Attached auto*) {}
void after_prepare_data(Attached auto*) {}
void paint(Attached auto*) {}
private:
void make_flow(Root* root) {
prepare_flow.clear();
paint_flow.clear();
auto prepare_if = prepare_flow.emplace([this, root] {
bool update = update_prepare_call(root);
if (!prepare_graph_ready || update) {
prepare_graph = make_prepare_call(root, detail::task_memory_resource());
prepare_graph_ready = true;
root->template mark_dirty<Prepare_Data_Tag>();
}
);
}
template <Attached Object>
void make_paint_graph(Object* object, const State&, auto& graph) {
graph.add(
[object] {
auto& private_data = static_cast<typename Object::Private&>(object->d);
private_data.template paint<Object>(object);
return need_prepare_call(root, root->template dirty<Prepare_Data_Tag>()) ? 0 : -1;
});
auto prepare_run = prepare_flow.composed_of(prepare_graph);
auto prepare_done = prepare_flow.emplace([root] {
root->template take_dirty<Prepare_Data_Tag>();
root->template mark_dirty<Paint_Tag>();
});
prepare_if.precede(prepare_run);
prepare_run.precede(prepare_done);
auto paint_if = paint_flow.emplace([this, root] {
bool update = update_paint_call(root);
if (!paint_graph_ready || update) {
paint_graph = make_paint_call(root, detail::task_memory_resource());
paint_graph_ready = true;
root->template mark_dirty<Paint_Tag>();
}
);
return need_paint_call(root, root->template dirty<Paint_Tag>()) ? 0 : -1;
});
auto paint_run = paint_flow.composed_of(paint_graph);
auto paint_done = paint_flow.emplace([root] {
root->template take_dirty<Paint_Tag>();
});
paint_if.precede(paint_run);
paint_run.precede(paint_done);
flow_ready = true;
}
template <Attached Object>
void prepare_data(Object*) {}
template <Attached Object>
void after_prepare_data(Object*) {}
template <Attached Object>
void after_exchange(Object* object, Prop*, State*, const Prop*, const State*) {
auto& private_data = static_cast<typename Object::Private&>(object->d);
const auto& state = *object->d.state.render;
if (private_data.template need_update_prepare_graph<Object>(object, state)) {
prepare_graph_dirty = true;
object->template mark_dirty<Prepare_Data_Tag>();
}
if (private_data.template need_update_paint_graph<Object>(object, state)) {
paint_graph_dirty = true;
object->template mark_dirty<Paint_Tag>();
}
}
template <Attached Object>
void paint(Object*) {}
friend struct Renderable;
};
private:
template <Attached Object>
void bind_rely_node(Rely::Node* node) {
auto* object = static_cast<Object*>(this);
void bind_rely_node(Rely::Node* node, Attached auto* object) {
using Object = std::remove_pointer_t<decltype(object)>;
auto& data = static_cast<Private&>(object->d);
data.need_prepare_call = [](Root* root, bool dirty) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.template need_prepare<Object>(value, *value->d.state.render, dirty);
return private_data.need_prepare(value, *value->d.state.render, dirty);
};
data.need_paint_call = [](Root* root, bool dirty) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.template need_paint<Object>(value, *value->d.state.render, dirty);
return private_data.need_paint(value, *value->d.state.render, dirty);
};
data.make_prepare_call = [](Root* root, Secene::Private& graph) {
data.update_prepare_call = [](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
private_data.template make_prepare_graph<Object>(value, *value->d.state.render, graph);
return private_data.need_update_prepare_graph(value, *value->d.state.render);
};
data.make_paint_call = [](Root* root, Secene::Private& graph) {
data.update_paint_call = [](Root* root) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
private_data.template make_paint_graph<Object>(value, *value->d.state.render, graph);
return private_data.need_update_paint_graph(value, *value->d.state.render);
};
data.make_prepare_call = [](Root* root, std::pmr::memory_resource* resource) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.make_prepare_graph(value, *value->d.state.render, resource);
};
data.make_paint_call = [](Root* root, std::pmr::memory_resource* resource) {
auto* value = static_cast<Object*>(root);
auto& private_data = static_cast<typename Object::Private&>(value->d);
return private_data.make_paint_graph(value, *value->d.state.render, resource);
};
if (!data.flow_ready) data.make_flow(object);
object->template mark_dirty<Prepare_Data_Tag>();
node->data = &data;
}
friend struct ::aethera::Rely;
};
template <Attached Object>
void Secene::Private::after_exchange(Object* object, Prop*, State*, const Prop*, const State*) {
auto* resource = object->memory_resource();
void Secene::Private::after_exchange(Attached auto* object, Prop*, State*, const Prop*, const State*) {
auto* resource = detail::task_memory_resource();
std::pmr::unordered_set<Root*> exchanged{resource};
exchanged.insert(object);
// 交换双缓冲
object->for_each_render_rely(
[&](const Rely& rely) {
rely.for_each(
@@ -241,44 +193,30 @@ void Secene::Private::after_exchange(Object* object, Prop*, State*, const Prop*,
);
}
);
auto& prepare_state = object->d.rely_storage.template get<Prepare_Data_Tag>();
auto& paint_state = object->d.rely_storage.template get<Paint_Tag>();
bool prepare_graph_dirty = prepare_state.dirty();
bool paint_graph_dirty = paint_state.dirty();
bool scene_graph_dirty = !graph_ready;
object->template render_rely<Prepare_Data_Tag, Paint_Tag>(
[&](auto& prepare_state, auto& paint_state) {
scene_graph_dirty = scene_graph_dirty || prepare_state.dirty() || paint_state.dirty();
}
);
if (!scene_graph_dirty) return;
const auto& prepare_rely = object->template render_rely<Prepare_Data_Tag>();
const auto& paint_rely = object->template render_rely<Paint_Tag>();
std::pmr::unordered_set<Root*> renderables{resource};
prepare_rely.for_each(
[&](const Rely::Node& node) {
if (!node.data) return;
renderables.insert(node.object);
auto* data = static_cast<Renderable::Private*>(node.data);
prepare_graph_dirty = prepare_graph_dirty || data->prepare_graph_dirty;
paint_graph_dirty = paint_graph_dirty || data->paint_graph_dirty;
if (node.data) renderables.insert(node.object);
}
);
paint_rely.for_each(
[&](const Rely::Node& node) {
if (!node.data) return;
renderables.insert(node.object);
auto* data = static_cast<Renderable::Private*>(node.data);
prepare_graph_dirty = prepare_graph_dirty || data->prepare_graph_dirty;
paint_graph_dirty = paint_graph_dirty || data->paint_graph_dirty;
if (node.data) renderables.insert(node.object);
}
);
if (graph && !prepare_graph_dirty && !paint_graph_dirty) return;
if (graph) graph->wait_for_all();
tasks.reset();
start.reset();
graph.reset();
graph = std::make_unique<oneapi::tbb::flow::graph>();
start = std::make_unique<oneapi::tbb::flow::broadcast_node<Message>>(*graph);
tasks = std::make_unique<std::pmr::list<Task>>(resource);
graph.clear();
struct Endpoints {
Task* prepare_entry;
Task* prepare_exit;
Task* paint_entry;
Task* paint_exit;
tf::Task prepare;
tf::Task paint;
};
std::pmr::unordered_map<Root*, Endpoints> endpoints{resource};
for (auto* root : renderables) {
@@ -286,74 +224,11 @@ void Secene::Private::after_exchange(Object* object, Prop*, State*, const Prop*,
if (!rely_node || !rely_node->data) rely_node = paint_rely.find(root);
if (!rely_node || !rely_node->data) continue;
auto* data = static_cast<Renderable::Private*>(rely_node->data);
tasks->emplace_back(
*graph,
nullptr,
[root, data] {
data->prepare_enabled = data->need_prepare_call(root, root->template dirty<Prepare_Data_Tag>());
}
);
auto* prepare_entry = &tasks->back();
tasks->emplace_back(
*graph,
nullptr,
[root, data] {
if (!data->prepare_enabled) return;
root->template take_dirty<Prepare_Data_Tag>();
root->template mark_dirty<Paint_Tag>();
}
);
auto* prepare_exit = &tasks->back();
tasks->emplace_back(
*graph,
nullptr,
[root, data] {
data->paint_enabled = data->need_paint_call(root, root->template dirty<Paint_Tag>());
}
);
auto* paint_entry = &tasks->back();
tasks->emplace_back(
*graph,
nullptr,
[root, data] {
if (data->paint_enabled) root->template take_dirty<Paint_Tag>();
}
);
auto* paint_exit = &tasks->back();
oneapi::tbb::flow::make_edge(*start, prepare_entry->gate);
oneapi::tbb::flow::make_edge(*start, paint_entry->gate);
oneapi::tbb::flow::make_edge(prepare_exit->execute, paint_entry->gate);
auto prepare_begin = tasks->size();
building_enabled = &data->prepare_enabled;
data->make_prepare_call(root, *this);
if (tasks->size() == prepare_begin) {
oneapi::tbb::flow::make_edge(prepare_entry->execute, prepare_exit->gate);
}
else {
std::size_t index = 0;
for (auto& task : *tasks) {
if (index++ < prepare_begin) continue;
oneapi::tbb::flow::make_edge(prepare_entry->execute, task.gate);
oneapi::tbb::flow::make_edge(task.execute, prepare_exit->gate);
}
}
auto paint_begin = tasks->size();
building_enabled = &data->paint_enabled;
data->make_paint_call(root, *this);
if (tasks->size() == paint_begin) {
oneapi::tbb::flow::make_edge(paint_entry->execute, paint_exit->gate);
}
else {
std::size_t index = 0;
for (auto& task : *tasks) {
if (index++ < paint_begin) continue;
oneapi::tbb::flow::make_edge(paint_entry->execute, task.gate);
oneapi::tbb::flow::make_edge(task.execute, paint_exit->gate);
}
}
endpoints.emplace(root, Endpoints{prepare_entry, prepare_exit, paint_entry, paint_exit});
auto prepare = graph.composed_of(data->prepare_flow);
auto paint = graph.composed_of(data->paint_flow);
prepare.precede(paint);
endpoints.emplace(root, Endpoints{prepare, paint});
}
building_enabled = nullptr;
auto connect_rely = [&](const Rely& rely, bool prepare) {
rely.for_each(
[&](const Rely::Node& rely_node) {
@@ -370,9 +245,9 @@ void Secene::Private::after_exchange(Object* object, Prop*, State*, const Prop*,
if (dependency->data) {
auto source = endpoints.find(dependency->object);
if (source != endpoints.end()) {
auto* source_exit = prepare ? source->second.prepare_exit : source->second.paint_exit;
auto* target_entry = prepare ? target->second.prepare_entry : target->second.paint_entry;
oneapi::tbb::flow::make_edge(source_exit->execute, target_entry->gate);
auto source_task = prepare ? source->second.prepare : source->second.paint;
auto target_task = prepare ? target->second.prepare : target->second.paint;
source_task.precede(target_task);
}
continue;
}
@@ -383,15 +258,12 @@ void Secene::Private::after_exchange(Object* object, Prop*, State*, const Prop*,
};
connect_rely(prepare_rely, true);
connect_rely(paint_rely, false);
if (prepare_state.dirty()) prepare_state.take_dirty();
if (paint_state.dirty()) paint_state.take_dirty();
for (auto* root : renderables) {
const Rely::Node* rely_node = prepare_rely.find(root);
if (!rely_node || !rely_node->data) rely_node = paint_rely.find(root);
if (!rely_node || !rely_node->data) continue;
auto* data = static_cast<Renderable::Private*>(rely_node->data);
data->prepare_graph_dirty = false;
data->paint_graph_dirty = false;
}
object->template render_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();
}
);
graph_ready = true;
}
}