This commit is contained in:
2026-08-19 22:47:56 +08:00
parent c783fd88c2
commit 921a9fad2a
6 changed files with 374 additions and 275 deletions
+16
View File
@@ -1,5 +1,6 @@
#pragma once
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <concepts>
@@ -129,6 +130,19 @@ struct Tagged_Buffer {
template <typename Tag>
struct Rely_Type {
using Tag_Type = Tag;
private:
std::atomic_flag clean = ATOMIC_FLAG_INIT;
protected:
void mark_dirty() noexcept {
clean.clear(std::memory_order_release);
}
public:
[[nodiscard]] bool dirty() const noexcept {
return !clean.test(std::memory_order_acquire);
}
bool take_dirty() noexcept {
return !clean.test_and_set(std::memory_order_acq_rel);
}
};
namespace detail {
template <typename Value, typename Prop, typename Owner, typename Member>
@@ -308,6 +322,8 @@ concept Object_Core = requires {
};
namespace detail {
template <Rely_List Tuple>
struct Rely_Storage;
template <Rely_List Tuple>
struct Rely_Build_Storage;
}
enum class Rely_Error {
@@ -1,55 +0,0 @@
#pragma once
#include "core.hpp"
#include <oneapi/tbb/flow_graph.h>
namespace aethera {
struct Execution_Graph : Pinned {
using Message = oneapi::tbb::flow::continue_msg;
struct Node : Pinned {
using Execute = oneapi::tbb::flow::continue_node<Message>;
Execute execute;
bool has_dependency{};
template <std::invocable Callback>
Node(oneapi::tbb::flow::graph& graph, Callback&& callback) : execute(
graph,
[task = std::forward<Callback>(callback)](const Message&) mutable {
std::invoke(task);
return Message{};
}
) {}
};
struct Builder {
Execution_Graph* owner;
template <std::invocable Callback>
Node* add(Callback&& callback) {
owner->nodes.emplace_back(owner->graph, std::forward<Callback>(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<Message> start;
std::pmr::list<Node> 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();
}
};
}
+9
View File
@@ -181,6 +181,7 @@ public:
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) {
std::lock_guard<Lock> guard(lock);
return d.rely_storage.template edit<Tags...>(std::forward<Callback>(callback));
}
std::expected<void, Rely_Error> check_rely() const {
@@ -208,6 +209,14 @@ public:
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.render_scene(object, std::forward<Callback>(callback));
}
void render(Callback&& callback) {
std::lock_guard<Lock> guard(lock);
exchange_unlocked();
d.render_scene(this, std::forward<Callback>(callback));
}
void exchange() {
std::lock_guard<Lock> guard(lock);
exchange_unlocked();
+2 -4
View File
@@ -9,7 +9,6 @@ struct Rely {
std::pmr::vector<Node*> rely;
Bind bind;
void* data{};
mutable void* runtime{};
Node(Root* value, std::pmr::memory_resource* resource) : Node(value, resource, nullptr) {}
Node(Root* value, std::pmr::memory_resource* resource, Bind value_bind) : object(value), rely(resource), bind(value_bind) {}
};
@@ -160,6 +159,8 @@ public:
}
};
private:
template <detail::Rely_List Tuple>
friend struct detail::Rely_Storage;
template <detail::Rely_List Tuple>
friend struct detail::Rely_Build_Storage;
std::pmr::memory_resource* pmr_resource;
@@ -450,9 +451,6 @@ public:
std::pmr::memory_resource* memory_resource() const noexcept {
return pmr_resource;
}
std::uint64_t revision() const noexcept {
return structure_revision;
}
Node* find(Root* object) {
auto current = nodes.find(object);
if (current == nodes.end()) return nullptr;
+18 -2
View File
@@ -29,11 +29,14 @@ struct Rely_Storage<std::tuple<Relies...>> {
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
private:
template <Rely_Mechanism Mechanism>
struct Rely_Buffer : Double_Buffer<Rely> {
struct Rely_Buffer : Double_Buffer<Rely>, Mechanism {
using Base = Double_Buffer<Rely>;
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;
template <Rely_Tag_In<std::tuple<Relies...>> Tag>
@@ -104,8 +107,20 @@ public:
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>().cache->structure_revision)...
};
}(std::index_sequence_for<Tags...>{});
[&]<std::size_t... I>(std::index_sequence<I...>) {
(get<Tags>().cache->swap(std::get<I>(next)), ...);
(
[&] {
auto& buffer = get<Tags>();
buffer.cache->swap(std::get<I>(next));
if (std::get<I>(changed)) buffer.mark_structure_dirty();
}(),
...
);
}(std::index_sequence_for<Tags...>{});
return {};
}
@@ -158,6 +173,7 @@ private:
buffer.cache->bind_nodes();
*buffer.render = *buffer.cache;
buffer.render->bind();
buffer.mark_structure_dirty();
}
public:
Rely_Build_Storage() : Rely_Build_Storage(std::pmr::get_default_resource()) {}
+329 -214
View File
@@ -1,34 +1,123 @@
#pragma once
#include "base.hpp"
#include "base/execution_graph.hpp"
#include <oneapi/tbb/flow_graph.h>
namespace aethera::_2D {
struct Prepare_Data_Tag {};
struct Paint_Tag {};
struct Prepare_Graph_Tag {};
struct Paint_Graph_Tag {};
struct Color_Cache {};
struct Secene;
struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>> {
public:
struct Private;
struct Node_Data {
Renderable* renderable{};
};
struct Graph_Update {
bool prepare{};
bool paint{};
[[nodiscard]] explicit operator bool() const {
return prepare || paint;
struct Renderable;
struct Secene : Impl<Secene, Root, Rely_Type<Prepare_Data_Tag>, Rely_Type<Paint_Tag>> {
struct Prop : Prev_Prop {};
struct State : Prev_State {};
struct Private : Prev_Private {
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{};
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) {
Result result;
if (graph && tasks && !tasks->empty()) {
(void)start->try_put(Message{});
graph->wait_for_all();
}
std::invoke(std::forward<Callback>(callback), std::as_const(result));
}
};
};
struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>> {
struct Prop : Prev_Prop {};
struct State : Prev_State {
bool operator==(const State&) const = default;
};
private:
Node_Data node_data;
std::unique_ptr<Execution_Graph> prepare_graph;
std::unique_ptr<Execution_Graph> paint_graph;
friend struct Secene;
protected:
template <Attached Object>
static void execute_prepare_data(Object* object) {
@@ -45,19 +134,34 @@ protected:
}
public:
struct Private : Prev_Private {
using Need_Call = bool (*)(Root*, bool);
using Make_Call = void (*)(Root*, Secene::Private&);
Need_Call need_prepare_call{};
Need_Call need_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 typename Object::State&) {
bool need_update_prepare_graph(Object*, const State&) {
return false;
}
template <Attached Object>
bool need_update_paint_graph(Object*, const typename Object::State&) {
bool need_update_paint_graph(Object*, const State&) {
return false;
}
template <Attached Object>
void make_prepare_graph(
Object* object,
const typename Object::State&,
Execution_Graph::Builder& graph) {
bool need_prepare(Object*, const State&, bool dirty) {
return dirty;
}
template <Attached Object>
bool need_paint(Object*, 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);
@@ -65,10 +169,7 @@ public:
);
}
template <Attached Object>
void make_paint_graph(
Object* object,
const typename Object::State&,
Execution_Graph::Builder& graph) {
void make_paint_graph(Object* object, const State&, auto& graph) {
graph.add(
[object] {
auto& private_data = static_cast<typename Object::Private&>(object->d);
@@ -79,204 +180,218 @@ public:
template <Attached Object>
void prepare_data(Object*) {}
template <Attached Object>
void after_prepare_data(Object* object) {
static_cast<Renderable*>(object)->mark_paint_dirty();
}
void after_prepare_data(Object*) {}
template <Attached Object>
void after_exchange(
Object* object,
typename Object::Prop*,
typename Object::State*,
const typename Object::Prop*,
const typename Object::State* state) {
auto* renderable = static_cast<Renderable*>(object);
void after_exchange(Object* object, Prop*, State*, const Prop*, const State*) {
auto& private_data = static_cast<typename Object::Private&>(object->d);
if (
!renderable->prepare_graph ||
private_data.template need_update_prepare_graph<Object>(object, *state)) {
auto graph = std::make_unique<Execution_Graph>(object->memory_resource());
auto builder = graph->builder();
private_data.template make_prepare_graph<Object>(object, *state, builder);
graph->finish();
renderable->prepare_graph = std::move(graph);
renderable->mark_dirty<Prepare_Graph_Tag>();
renderable->mark_prepare_dirty();
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 (
!renderable->paint_graph ||
private_data.template need_update_paint_graph<Object>(object, *state)) {
auto graph = std::make_unique<Execution_Graph>(object->memory_resource());
auto builder = graph->builder();
private_data.template make_paint_graph<Object>(object, *state, builder);
graph->finish();
renderable->paint_graph = std::move(graph);
renderable->mark_dirty<Paint_Graph_Tag>();
renderable->mark_paint_dirty();
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*) {}
};
public:
private:
template <Attached Object>
void bind_rely_node(Rely::Node* node) {
if (!node_data.renderable) mark_prepare_dirty();
node_data.renderable = this;
node->data = &node_data;
}
void mark_prepare_dirty() {
mark_dirty<Prepare_Data_Tag>();
}
[[nodiscard]] bool need_prepare_data() const {
return dirty<Prepare_Data_Tag>();
}
void mark_paint_dirty() {
mark_dirty<Paint_Tag>();
}
[[nodiscard]] Graph_Update graph_update() const {
return Graph_Update{dirty<Prepare_Graph_Tag>(), dirty<Paint_Graph_Tag>()};
}
Graph_Update take_graph_update() {
return Graph_Update{take_dirty<Prepare_Graph_Tag>(), take_dirty<Paint_Graph_Tag>()};
}
};
namespace detail {
template <typename Tag>
concept Stage_Tag = requires {
requires std::same_as<Tag, Prepare_Data_Tag> || std::same_as<Tag, Paint_Tag>;
};
}
struct Secene : Impl<Secene, Root, Rely_Type<Prepare_Data_Tag>, Rely_Type<Paint_Tag>> {
struct Prop : Prev_Prop {};
struct State : Prev_State {};
struct Private : Prev_Private {
template <detail::Stage_Tag Tag>
struct Stage : Pinned {
Execution_Graph graph;
std::uint64_t revision;
template <Attached Object>
Stage(const Object* object, std::pmr::memory_resource* resource) : graph(resource),
revision(object->template render_rely<Tag>().revision()) {
auto builder = graph.builder();
(void)object->template for_each_rely_dag<Tag>(
[&](const Rely::View& view, const Rely::Node& rely_node) {
auto* data = static_cast<Renderable::Node_Data*>(rely_node.data);
Execution_Graph::Node* node;
if constexpr (std::same_as<Tag, Prepare_Data_Tag>) {
node = builder.add(
[root = rely_node.object, renderable = data->renderable] {
if (root->take_dirty<Prepare_Data_Tag>() && renderable->prepare_graph) {
renderable->prepare_graph->run();
}
}
);
}
else {
node = builder.add(
[renderable = data->renderable] {
if (renderable->take_dirty<Paint_Tag>() && renderable->paint_graph) {
renderable->paint_graph->run();
}
}
);
}
rely_node.runtime = node;
for (const auto* dependency : view.get_rely(rely_node.object)) {
builder.add_rely(node, static_cast<Execution_Graph::Node*>(dependency->runtime));
}
}
);
graph.finish();
}
void wait() {
graph.wait();
}
void run() {
graph.run();
}
auto* object = static_cast<Object*>(this);
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);
};
template <Attached Object>
static void update_stages(const Object* object) {
const auto& prepare_rely = object->template render_rely<Prepare_Data_Tag>();
const auto& paint_rely = object->template render_rely<Paint_Tag>();
if (
object->prepare_stage &&
object->paint_stage &&
object->prepare_stage->revision == prepare_rely.revision() &&
object->paint_stage->revision == paint_rely.revision())
return;
if (object->prepare_stage) object->prepare_stage->wait();
if (object->paint_stage) object->paint_stage->wait();
auto prepare = std::make_unique<Stage<Prepare_Data_Tag>>(object, object->memory_resource());
auto paint = std::make_unique<Stage<Paint_Tag>>(object, object->memory_resource());
object->prepare_stage = std::move(prepare);
object->paint_stage = std::move(paint);
}
template <Attached Object>
static void execute_prepare(const Object* object) {
if (!object->prepare_stage || !object->paint_stage) update_stages(object);
object->prepare_stage->run();
}
template <Attached Object>
static void execute_paint(const Object* object) {
if (!object->prepare_stage || !object->paint_stage) update_stages(object);
object->prepare_stage->run();
object->paint_stage->run();
}
template <Attached Object>
void after_exchange(
Object* object,
typename Object::Prop*,
typename Object::State*,
const typename Object::Prop*,
const typename Object::State*) {
std::pmr::unordered_set<Root*> exchanged{object->memory_resource()};
object->for_each_render_rely(
[&](const Rely& rely) {
rely.for_each(
[&](const Rely::Node& node) {
if (exchanged.insert(node.object).second) node.object->exchange_object();
}
);
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);
};
data.make_prepare_call = [](Root* root, Secene::Private& graph) {
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);
};
data.make_paint_call = [](Root* root, Secene::Private& graph) {
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);
};
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();
std::pmr::unordered_set<Root*> exchanged{resource};
exchanged.insert(object);
object->for_each_render_rely(
[&](const Rely& rely) {
rely.for_each(
[&](const Rely::Node& node) {
if (exchanged.insert(node.object).second) node.object->exchange_object();
}
);
update_stages(object);
}
};
private:
using Call = void (*)(const Secene*);
Call prepare_execute_call{};
Call paint_execute_call{};
mutable std::unique_ptr<typename Private::template Stage<Prepare_Data_Tag>> prepare_stage;
mutable std::unique_ptr<typename Private::template Stage<Paint_Tag>> paint_stage;
protected:
template <Attached Object>
void bind_object_crtp() {
Prev_Object::template bind_object_crtp<Object>();
prepare_execute_call = [](const Secene* scene) {
Private::execute_prepare(static_cast<const Object*>(scene));
};
paint_execute_call = [](const Secene* scene) {
Private::execute_paint(static_cast<const Object*>(scene));
};
}
public:
void execute_prepare() const {
prepare_execute_call(this);
}
void execute_paint() const {
paint_execute_call(this);
}
template <typename Object>
struct Builder : Prev_Builder<Object> {
using Base = Prev_Builder<Object>;
using Base::Base;
std::expected<std::unique_ptr<Object>, Rely_Error> build() {
auto result = Base::build();
if (!result) return std::unexpected(result.error());
Private::update_stages(result->get());
return result;
);
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();
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;
}
);
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 (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);
struct Endpoints {
Task* prepare_entry;
Task* prepare_exit;
Task* paint_entry;
Task* paint_exit;
};
};
std::pmr::unordered_map<Root*, Endpoints> endpoints{resource};
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);
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});
}
building_enabled = nullptr;
auto connect_rely = [&](const Rely& rely, bool prepare) {
rely.for_each(
[&](const Rely::Node& rely_node) {
if (!rely_node.data) return;
auto target = endpoints.find(rely_node.object);
if (target == endpoints.end()) return;
std::pmr::unordered_set<Root*> visited{resource};
std::pmr::vector<const Rely::Node*> pending{resource};
for (const auto* dependency : rely_node.rely) pending.push_back(dependency);
while (!pending.empty()) {
const auto* dependency = pending.back();
pending.pop_back();
if (!visited.insert(dependency->object).second) continue;
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);
}
continue;
}
for (const auto* next : dependency->rely) pending.push_back(next);
}
}
);
};
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;
}
}
}