diff --git a/kernel/src/kernel/base/execution_graph.hpp b/kernel/src/kernel/base/execution_graph.hpp new file mode 100644 index 0000000..9355937 --- /dev/null +++ b/kernel/src/kernel/base/execution_graph.hpp @@ -0,0 +1,55 @@ +#pragma once +#include "core.hpp" +#include +namespace aethera { +struct Execution_Graph : Pinned { + using Message = oneapi::tbb::flow::continue_msg; + struct Node : Pinned { + using Execute = oneapi::tbb::flow::continue_node; + Execute execute; + bool has_dependency{}; + template + Node(oneapi::tbb::flow::graph& graph, Callback&& callback) : execute( + graph, + [task = std::forward(callback)](const Message&) mutable { + std::invoke(task); + return Message{}; + } + ) {} + }; + struct Builder { + Execution_Graph* owner; + template + Node* add(Callback&& callback) { + owner->nodes.emplace_back(owner->graph, std::forward(callback)); + return &owner->nodes.back(); + } + void add_rely(Node* target, Node* source) { + oneapi::tbb::flow::make_edge(source->execute, target->execute); + target->has_dependency = true; + } + }; +private: + oneapi::tbb::flow::graph graph; + oneapi::tbb::flow::broadcast_node start; + std::pmr::list nodes; +public: + explicit Execution_Graph(std::pmr::memory_resource* resource) : start(graph), nodes(resource) {} + Builder builder() { + return Builder{this}; + } + void finish() { + for (auto& node : nodes) { + if (!node.has_dependency) oneapi::tbb::flow::make_edge(start, node.execute); + } + } + void wait() { + graph.wait_for_all(); + } + void run() { + if (nodes.empty()) return; + (void)start.try_put(Message{}); + wait(); + } +}; +} diff --git a/kernel/src/kernel/render_2D.hpp b/kernel/src/kernel/render_2D.hpp index 7c8b583..8c41550 100644 --- a/kernel/src/kernel/render_2D.hpp +++ b/kernel/src/kernel/render_2D.hpp @@ -1,12 +1,10 @@ -#pragma once #include "base.hpp" -#include +#include "base/execution_graph.hpp" namespace aethera::_2D { struct Prepare_Data_Tag {}; struct Paint_Tag {}; -namespace detail { -using Flow_Message = oneapi::tbb::flow::continue_msg; -} +struct Prepare_Graph_Tag {}; +struct Paint_Graph_Tag {}; struct Color_Cache {}; struct Secene; struct Renderable : Impl> { @@ -15,12 +13,22 @@ public: struct Node_Data { Renderable* renderable{}; }; + struct Graph_Update { + bool prepare{}; + bool paint{}; + [[nodiscard]] explicit operator bool() const { + return prepare || paint; + } + }; struct Prop : Prev_Prop {}; struct State : Prev_State { bool operator==(const State&) const = default; }; private: Node_Data node_data; + std::unique_ptr prepare_graph; + std::unique_ptr paint_graph; + friend struct Secene; protected: template static void execute_prepare_data(Object* object) { @@ -37,38 +45,82 @@ protected: } public: struct Private : Prev_Private { + template + bool need_update_prepare_graph(Object*, const typename Object::State&) { + return false; + } + template + bool need_update_paint_graph(Object*, const typename Object::State&) { + return false; + } + template + void make_prepare_graph( + Object* object, + const typename Object::State&, + Execution_Graph::Builder& graph) { + graph.add( + [object] { + execute_prepare_data(object); + } + ); + } + template + void make_paint_graph( + Object* object, + const typename Object::State&, + Execution_Graph::Builder& graph) { + graph.add( + [object] { + auto& private_data = static_cast(object->d); + private_data.template paint(object); + } + ); + } template void prepare_data(Object*) {} template void after_prepare_data(Object* object) { - static_cast(object)->paint_dirty = true; + static_cast(object)->mark_paint_dirty(); + } + template + void after_exchange( + Object* object, + typename Object::Prop*, + typename Object::State*, + const typename Object::Prop*, + const typename Object::State* state) { + auto* renderable = static_cast(object); + auto& private_data = static_cast(object->d); + if ( + !renderable->prepare_graph || + private_data.template need_update_prepare_graph(object, *state)) { + auto graph = std::make_unique(object->memory_resource()); + auto builder = graph->builder(); + private_data.template make_prepare_graph(object, *state, builder); + graph->finish(); + renderable->prepare_graph = std::move(graph); + renderable->mark_dirty(); + renderable->mark_prepare_dirty(); + } + if ( + !renderable->paint_graph || + private_data.template need_update_paint_graph(object, *state)) { + auto graph = std::make_unique(object->memory_resource()); + auto builder = graph->builder(); + private_data.template make_paint_graph(object, *state, builder); + graph->finish(); + renderable->paint_graph = std::move(graph); + renderable->mark_dirty(); + renderable->mark_paint_dirty(); + } } template void paint(Object*) {} }; -private: - using Call = void (*)(Renderable*); - Call prepare_call{}; - Call paint_call{}; - bool paint_dirty{true}; - friend struct Secene; -protected: - template - void bind_object_crtp() { - Prev_Object::template bind_object_crtp(); - prepare_call = [](Renderable* value) { - execute_prepare_data(static_cast(value)); - }; - paint_call = [](Renderable* value) { - auto* current = static_cast(value); - auto& private_data = static_cast(current->d); - private_data.template paint(current); - }; - } public: template void bind_rely_node(Rely::Node* node) { - if (!node_data.renderable) mark_dirty(); + if (!node_data.renderable) mark_prepare_dirty(); node_data.renderable = this; node->data = &node_data; } @@ -79,15 +131,13 @@ public: return dirty(); } void mark_paint_dirty() { - paint_dirty = true; + mark_dirty(); } - bool take_paint_dirty() { - if (!paint_dirty) return false; - paint_dirty = false; - return true; + [[nodiscard]] Graph_Update graph_update() const { + return Graph_Update{dirty(), dirty()}; } - void paint() { - paint_call(this); + Graph_Update take_graph_update() { + return Graph_Update{take_dirty(), take_dirty()}; } }; namespace detail { @@ -100,54 +150,49 @@ struct Secene : Impl, Rely_Type struct Stage : Pinned { - struct Node : Pinned { - using Execute = oneapi::tbb::flow::continue_node; - static auto body(Root* object, Renderable::Node_Data* data) { - if constexpr (std::same_as) { - return [object, renderable = data->renderable](const Message&) { - if (object->take_dirty()) renderable->prepare_call(renderable); - return Message{}; - }; - } - else { - return [renderable = data->renderable](const Message&) { - if (renderable->take_paint_dirty()) renderable->paint_call(renderable); - return Message{}; - }; - } - } - Execute execute; - Node(oneapi::tbb::flow::graph& graph, Root* object, Renderable::Node_Data* data) : execute(graph, body(object, data)) {} - }; - oneapi::tbb::flow::graph graph; - oneapi::tbb::flow::broadcast_node start; - std::pmr::list nodes; + Execution_Graph graph; std::uint64_t revision; template - Stage(const Object* object, std::pmr::memory_resource* resource) : start(graph), - nodes(resource), + Stage(const Object* object, std::pmr::memory_resource* resource) : graph(resource), revision(object->template render_rely().revision()) { + auto builder = graph.builder(); (void)object->template for_each_rely_dag( [&](const Rely::View& view, const Rely::Node& rely_node) { auto* data = static_cast(rely_node.data); - nodes.emplace_back(graph, rely_node.object, data); - auto* node = &nodes.back(); + Execution_Graph::Node* node; + if constexpr (std::same_as) { + node = builder.add( + [root = rely_node.object, renderable = data->renderable] { + if (root->take_dirty() && renderable->prepare_graph) { + renderable->prepare_graph->run(); + } + } + ); + } + else { + node = builder.add( + [renderable = data->renderable] { + if (renderable->take_dirty() && renderable->paint_graph) { + renderable->paint_graph->run(); + } + } + ); + } rely_node.runtime = node; - auto dependencies = view.get_rely(rely_node.object); - if (dependencies.empty()) oneapi::tbb::flow::make_edge(start, node->execute); - for (const auto* dependency : dependencies) { - auto* source = static_cast(dependency->runtime); - oneapi::tbb::flow::make_edge(source->execute, node->execute); + for (const auto* dependency : view.get_rely(rely_node.object)) { + builder.add_rely(node, static_cast(dependency->runtime)); } } ); + graph.finish(); + } + void wait() { + graph.wait(); } void run() { - (void)start.try_put(Message{}); - graph.wait_for_all(); + graph.run(); } }; template @@ -160,8 +205,8 @@ struct Secene : Impl, Rely_Typeprepare_stage->revision == prepare_rely.revision() && object->paint_stage->revision == paint_rely.revision()) return; - if (object->prepare_stage) object->prepare_stage->graph.wait_for_all(); - if (object->paint_stage) object->paint_stage->graph.wait_for_all(); + if (object->prepare_stage) object->prepare_stage->wait(); + if (object->paint_stage) object->paint_stage->wait(); auto prepare = std::make_unique>(object, object->memory_resource()); auto paint = std::make_unique>(object, object->memory_resource()); object->prepare_stage = std::move(prepare);