升级
This commit is contained in:
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user