升级
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
@@ -129,6 +130,19 @@ struct Tagged_Buffer {
|
|||||||
template <typename Tag>
|
template <typename Tag>
|
||||||
struct Rely_Type {
|
struct Rely_Type {
|
||||||
using Tag_Type = Tag;
|
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 {
|
namespace detail {
|
||||||
template <typename Value, typename Prop, typename Owner, typename Member>
|
template <typename Value, typename Prop, typename Owner, typename Member>
|
||||||
@@ -308,6 +322,8 @@ concept Object_Core = requires {
|
|||||||
};
|
};
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <Rely_List Tuple>
|
template <Rely_List Tuple>
|
||||||
|
struct Rely_Storage;
|
||||||
|
template <Rely_List Tuple>
|
||||||
struct Rely_Build_Storage;
|
struct Rely_Build_Storage;
|
||||||
}
|
}
|
||||||
enum class Rely_Error {
|
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
|
template <detail::Rely_Tag_In<Relies>... Tags, detail::Rely_Edit_Callback_For<Tags...> Callback> requires
|
||||||
detail::Unique_Types<Tags...>::value
|
detail::Unique_Types<Tags...>::value
|
||||||
std::expected<void, Rely_Error> edit_rely(Callback&& callback) {
|
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));
|
return d.rely_storage.template edit<Tags...>(std::forward<Callback>(callback));
|
||||||
}
|
}
|
||||||
std::expected<void, Rely_Error> check_rely() const {
|
std::expected<void, Rely_Error> check_rely() const {
|
||||||
@@ -208,6 +209,14 @@ public:
|
|||||||
after_state_set(Member);
|
after_state_set(Member);
|
||||||
this->emit_rely_source(detail::rely_id<detail::State_Rely_Key<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() {
|
void exchange() {
|
||||||
std::lock_guard<Lock> guard(lock);
|
std::lock_guard<Lock> guard(lock);
|
||||||
exchange_unlocked();
|
exchange_unlocked();
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ struct Rely {
|
|||||||
std::pmr::vector<Node*> rely;
|
std::pmr::vector<Node*> rely;
|
||||||
Bind bind;
|
Bind bind;
|
||||||
void* data{};
|
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) : Node(value, resource, nullptr) {}
|
||||||
Node(Root* value, std::pmr::memory_resource* resource, Bind value_bind) : object(value), rely(resource), bind(value_bind) {}
|
Node(Root* value, std::pmr::memory_resource* resource, Bind value_bind) : object(value), rely(resource), bind(value_bind) {}
|
||||||
};
|
};
|
||||||
@@ -160,6 +159,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
|
template <detail::Rely_List Tuple>
|
||||||
|
friend struct detail::Rely_Storage;
|
||||||
template <detail::Rely_List Tuple>
|
template <detail::Rely_List Tuple>
|
||||||
friend struct detail::Rely_Build_Storage;
|
friend struct detail::Rely_Build_Storage;
|
||||||
std::pmr::memory_resource* pmr_resource;
|
std::pmr::memory_resource* pmr_resource;
|
||||||
@@ -450,9 +451,6 @@ public:
|
|||||||
std::pmr::memory_resource* memory_resource() const noexcept {
|
std::pmr::memory_resource* memory_resource() const noexcept {
|
||||||
return pmr_resource;
|
return pmr_resource;
|
||||||
}
|
}
|
||||||
std::uint64_t revision() const noexcept {
|
|
||||||
return structure_revision;
|
|
||||||
}
|
|
||||||
Node* find(Root* object) {
|
Node* find(Root* object) {
|
||||||
auto current = nodes.find(object);
|
auto current = nodes.find(object);
|
||||||
if (current == nodes.end()) return nullptr;
|
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>;
|
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
|
||||||
private:
|
private:
|
||||||
template <Rely_Mechanism Mechanism>
|
template <Rely_Mechanism Mechanism>
|
||||||
struct Rely_Buffer : Double_Buffer<Rely> {
|
struct Rely_Buffer : Double_Buffer<Rely>, Mechanism {
|
||||||
using Base = Double_Buffer<Rely>;
|
using Base = Double_Buffer<Rely>;
|
||||||
using allocator_type = typename Base::allocator_type;
|
using allocator_type = typename Base::allocator_type;
|
||||||
Rely_Buffer() = default;
|
Rely_Buffer() = default;
|
||||||
Rely_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {}
|
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;
|
std::tuple<Rely_Buffer<Relies>...> relies;
|
||||||
template <Rely_Tag_In<std::tuple<Relies...>> Tag>
|
template <Rely_Tag_In<std::tuple<Relies...>> Tag>
|
||||||
@@ -104,8 +107,20 @@ public:
|
|||||||
next
|
next
|
||||||
);
|
);
|
||||||
if (!result) return result;
|
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...>) {
|
[&]<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...>{});
|
}(std::index_sequence_for<Tags...>{});
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -158,6 +173,7 @@ private:
|
|||||||
buffer.cache->bind_nodes();
|
buffer.cache->bind_nodes();
|
||||||
*buffer.render = *buffer.cache;
|
*buffer.render = *buffer.cache;
|
||||||
buffer.render->bind();
|
buffer.render->bind();
|
||||||
|
buffer.mark_structure_dirty();
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
Rely_Build_Storage() : Rely_Build_Storage(std::pmr::get_default_resource()) {}
|
Rely_Build_Storage() : Rely_Build_Storage(std::pmr::get_default_resource()) {}
|
||||||
|
|||||||
+329
-214
@@ -1,34 +1,123 @@
|
|||||||
|
#pragma once
|
||||||
#include "base.hpp"
|
#include "base.hpp"
|
||||||
#include "base/execution_graph.hpp"
|
#include <oneapi/tbb/flow_graph.h>
|
||||||
namespace aethera::_2D {
|
namespace aethera::_2D {
|
||||||
struct Prepare_Data_Tag {};
|
struct Prepare_Data_Tag {};
|
||||||
struct Paint_Tag {};
|
struct Paint_Tag {};
|
||||||
struct Prepare_Graph_Tag {};
|
|
||||||
struct Paint_Graph_Tag {};
|
|
||||||
struct Color_Cache {};
|
struct Color_Cache {};
|
||||||
struct Secene;
|
struct Renderable;
|
||||||
struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>> {
|
struct Secene : Impl<Secene, Root, Rely_Type<Prepare_Data_Tag>, Rely_Type<Paint_Tag>> {
|
||||||
public:
|
struct Prop : Prev_Prop {};
|
||||||
struct Private;
|
struct State : Prev_State {};
|
||||||
struct Node_Data {
|
struct Private : Prev_Private {
|
||||||
Renderable* renderable{};
|
struct Result {};
|
||||||
};
|
friend struct Renderable;
|
||||||
struct Graph_Update {
|
private:
|
||||||
bool prepare{};
|
using Message = oneapi::tbb::flow::continue_msg;
|
||||||
bool paint{};
|
struct Task : Pinned {
|
||||||
[[nodiscard]] explicit operator bool() const {
|
using Gate = oneapi::tbb::flow::continue_node<Message>;
|
||||||
return prepare || paint;
|
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 Prop : Prev_Prop {};
|
||||||
struct State : Prev_State {
|
struct State : Prev_State {
|
||||||
bool operator==(const State&) const = default;
|
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:
|
protected:
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
static void execute_prepare_data(Object* object) {
|
static void execute_prepare_data(Object* object) {
|
||||||
@@ -45,19 +134,34 @@ protected:
|
|||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
struct Private : Prev_Private {
|
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>
|
template <Attached Object>
|
||||||
bool need_update_prepare_graph(Object*, const typename Object::State&) {
|
bool need_update_prepare_graph(Object*, const State&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
bool need_update_paint_graph(Object*, const typename Object::State&) {
|
bool need_update_paint_graph(Object*, const State&) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
void make_prepare_graph(
|
bool need_prepare(Object*, const State&, bool dirty) {
|
||||||
Object* object,
|
return dirty;
|
||||||
const typename Object::State&,
|
}
|
||||||
Execution_Graph::Builder& graph) {
|
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(
|
graph.add(
|
||||||
[object] {
|
[object] {
|
||||||
execute_prepare_data(object);
|
execute_prepare_data(object);
|
||||||
@@ -65,10 +169,7 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
void make_paint_graph(
|
void make_paint_graph(Object* object, const State&, auto& graph) {
|
||||||
Object* object,
|
|
||||||
const typename Object::State&,
|
|
||||||
Execution_Graph::Builder& graph) {
|
|
||||||
graph.add(
|
graph.add(
|
||||||
[object] {
|
[object] {
|
||||||
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
||||||
@@ -79,204 +180,218 @@ public:
|
|||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
void prepare_data(Object*) {}
|
void prepare_data(Object*) {}
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
void after_prepare_data(Object* object) {
|
void after_prepare_data(Object*) {}
|
||||||
static_cast<Renderable*>(object)->mark_paint_dirty();
|
|
||||||
}
|
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
void after_exchange(
|
void after_exchange(Object* object, Prop*, State*, const Prop*, const State*) {
|
||||||
Object* object,
|
|
||||||
typename Object::Prop*,
|
|
||||||
typename Object::State*,
|
|
||||||
const typename Object::Prop*,
|
|
||||||
const typename Object::State* state) {
|
|
||||||
auto* renderable = static_cast<Renderable*>(object);
|
|
||||||
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
||||||
if (
|
const auto& state = *object->d.state.render;
|
||||||
!renderable->prepare_graph ||
|
if (private_data.template need_update_prepare_graph<Object>(object, state)) {
|
||||||
private_data.template need_update_prepare_graph<Object>(object, *state)) {
|
prepare_graph_dirty = true;
|
||||||
auto graph = std::make_unique<Execution_Graph>(object->memory_resource());
|
object->template mark_dirty<Prepare_Data_Tag>();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
if (
|
if (private_data.template need_update_paint_graph<Object>(object, state)) {
|
||||||
!renderable->paint_graph ||
|
paint_graph_dirty = true;
|
||||||
private_data.template need_update_paint_graph<Object>(object, *state)) {
|
object->template mark_dirty<Paint_Tag>();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
void paint(Object*) {}
|
void paint(Object*) {}
|
||||||
};
|
};
|
||||||
public:
|
private:
|
||||||
template <Attached Object>
|
template <Attached Object>
|
||||||
void bind_rely_node(Rely::Node* node) {
|
void bind_rely_node(Rely::Node* node) {
|
||||||
if (!node_data.renderable) mark_prepare_dirty();
|
auto* object = static_cast<Object*>(this);
|
||||||
node_data.renderable = this;
|
auto& data = static_cast<Private&>(object->d);
|
||||||
node->data = &node_data;
|
data.need_prepare_call = [](Root* root, bool dirty) {
|
||||||
}
|
auto* value = static_cast<Object*>(root);
|
||||||
void mark_prepare_dirty() {
|
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||||
mark_dirty<Prepare_Data_Tag>();
|
return private_data.template need_prepare<Object>(value, *value->d.state.render, dirty);
|
||||||
}
|
|
||||||
[[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();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
template <Attached Object>
|
data.need_paint_call = [](Root* root, bool dirty) {
|
||||||
static void update_stages(const Object* object) {
|
auto* value = static_cast<Object*>(root);
|
||||||
const auto& prepare_rely = object->template render_rely<Prepare_Data_Tag>();
|
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||||
const auto& paint_rely = object->template render_rely<Paint_Tag>();
|
return private_data.template need_paint<Object>(value, *value->d.state.render, dirty);
|
||||||
if (
|
};
|
||||||
object->prepare_stage &&
|
data.make_prepare_call = [](Root* root, Secene::Private& graph) {
|
||||||
object->paint_stage &&
|
auto* value = static_cast<Object*>(root);
|
||||||
object->prepare_stage->revision == prepare_rely.revision() &&
|
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||||
object->paint_stage->revision == paint_rely.revision())
|
private_data.template make_prepare_graph<Object>(value, *value->d.state.render, graph);
|
||||||
return;
|
};
|
||||||
if (object->prepare_stage) object->prepare_stage->wait();
|
data.make_paint_call = [](Root* root, Secene::Private& graph) {
|
||||||
if (object->paint_stage) object->paint_stage->wait();
|
auto* value = static_cast<Object*>(root);
|
||||||
auto prepare = std::make_unique<Stage<Prepare_Data_Tag>>(object, object->memory_resource());
|
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||||
auto paint = std::make_unique<Stage<Paint_Tag>>(object, object->memory_resource());
|
private_data.template make_paint_graph<Object>(value, *value->d.state.render, graph);
|
||||||
object->prepare_stage = std::move(prepare);
|
};
|
||||||
object->paint_stage = std::move(paint);
|
object->template mark_dirty<Prepare_Data_Tag>();
|
||||||
}
|
node->data = &data;
|
||||||
template <Attached Object>
|
}
|
||||||
static void execute_prepare(const Object* object) {
|
friend struct ::aethera::Rely;
|
||||||
if (!object->prepare_stage || !object->paint_stage) update_stages(object);
|
};
|
||||||
object->prepare_stage->run();
|
template <Attached Object>
|
||||||
}
|
void Secene::Private::after_exchange(Object* object, Prop*, State*, const Prop*, const State*) {
|
||||||
template <Attached Object>
|
auto* resource = object->memory_resource();
|
||||||
static void execute_paint(const Object* object) {
|
std::pmr::unordered_set<Root*> exchanged{resource};
|
||||||
if (!object->prepare_stage || !object->paint_stage) update_stages(object);
|
exchanged.insert(object);
|
||||||
object->prepare_stage->run();
|
object->for_each_render_rely(
|
||||||
object->paint_stage->run();
|
[&](const Rely& rely) {
|
||||||
}
|
rely.for_each(
|
||||||
template <Attached Object>
|
[&](const Rely::Node& node) {
|
||||||
void after_exchange(
|
if (exchanged.insert(node.object).second) node.object->exchange_object();
|
||||||
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();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
update_stages(object);
|
|
||||||
}
|
}
|
||||||
};
|
);
|
||||||
private:
|
auto& prepare_state = object->d.rely_storage.template get<Prepare_Data_Tag>();
|
||||||
using Call = void (*)(const Secene*);
|
auto& paint_state = object->d.rely_storage.template get<Paint_Tag>();
|
||||||
Call prepare_execute_call{};
|
bool prepare_graph_dirty = prepare_state.dirty();
|
||||||
Call paint_execute_call{};
|
bool paint_graph_dirty = paint_state.dirty();
|
||||||
mutable std::unique_ptr<typename Private::template Stage<Prepare_Data_Tag>> prepare_stage;
|
const auto& prepare_rely = object->template render_rely<Prepare_Data_Tag>();
|
||||||
mutable std::unique_ptr<typename Private::template Stage<Paint_Tag>> paint_stage;
|
const auto& paint_rely = object->template render_rely<Paint_Tag>();
|
||||||
protected:
|
std::pmr::unordered_set<Root*> renderables{resource};
|
||||||
template <Attached Object>
|
prepare_rely.for_each(
|
||||||
void bind_object_crtp() {
|
[&](const Rely::Node& node) {
|
||||||
Prev_Object::template bind_object_crtp<Object>();
|
if (!node.data) return;
|
||||||
prepare_execute_call = [](const Secene* scene) {
|
renderables.insert(node.object);
|
||||||
Private::execute_prepare(static_cast<const Object*>(scene));
|
auto* data = static_cast<Renderable::Private*>(node.data);
|
||||||
};
|
prepare_graph_dirty = prepare_graph_dirty || data->prepare_graph_dirty;
|
||||||
paint_execute_call = [](const Secene* scene) {
|
paint_graph_dirty = paint_graph_dirty || data->paint_graph_dirty;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
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