引入子图模式
This commit is contained in:
@@ -122,14 +122,24 @@ public:
|
||||
std::swap(pending, current);
|
||||
}
|
||||
};
|
||||
namespace detail {
|
||||
struct State_Root {};
|
||||
}
|
||||
template <typename Tag, Prop_State Prev = detail::State_Root>
|
||||
struct State_Type : Prev {
|
||||
using Tag_Type = Tag;
|
||||
using Prev_State = Prev;
|
||||
};
|
||||
template <typename Tag, Prop_State T = Tag>
|
||||
struct Tagged_Buffer {
|
||||
using Tag_Type = Tag;
|
||||
using Value_Type = T;
|
||||
};
|
||||
template <typename Tag>
|
||||
struct Root;
|
||||
template <typename Tag, typename Object>
|
||||
struct Rely_Type {
|
||||
using Tag_Type = Tag;
|
||||
using Object_Type = Object;
|
||||
private:
|
||||
std::atomic_flag clean = ATOMIC_FLAG_INIT;
|
||||
protected:
|
||||
@@ -163,17 +173,42 @@ template <typename Tag, Prop_State T>
|
||||
struct Is_Tagged_Buffer<Tagged_Buffer<Tag, T>> : std::true_type {};
|
||||
template <typename T>
|
||||
struct Is_Rely_Type : std::false_type {};
|
||||
template <typename Tag>
|
||||
struct Is_Rely_Type<Rely_Type<Tag>> : std::true_type {};
|
||||
template <typename Tag, typename Object>
|
||||
struct Is_Rely_Type<Rely_Type<Tag, Object>> : std::true_type {};
|
||||
template <typename T>
|
||||
struct Is_State_Type : std::false_type {};
|
||||
template <typename Tag, Prop_State Prev>
|
||||
struct Is_State_Type<State_Type<Tag, Prev>> : std::true_type {};
|
||||
template <typename T>
|
||||
concept Buffer_Type = Is_Tagged_Buffer<T>::value;
|
||||
template <typename T>
|
||||
concept Rely_Mechanism = Is_Rely_Type<T>::value;
|
||||
template <typename T>
|
||||
concept State_Mechanism = Is_State_Type<T>::value;
|
||||
template <typename T>
|
||||
concept Mechanism_Type = requires {
|
||||
requires Buffer_Type<T> || Rely_Mechanism<T>;
|
||||
requires Buffer_Type<T> || Rely_Mechanism<T> || State_Mechanism<T>;
|
||||
};
|
||||
template <typename T>
|
||||
concept Tagged_State = Prop_State<T> && requires {
|
||||
typename T::Tag_Type;
|
||||
typename T::Prev_State;
|
||||
requires std::derived_from<T, State_Type<typename T::Tag_Type, typename T::Prev_State>>;
|
||||
};
|
||||
template <typename State>
|
||||
struct State_Layers {
|
||||
using Type = std::tuple<>;
|
||||
};
|
||||
template <Tagged_State State>
|
||||
struct State_Layers<State> {
|
||||
using Type = decltype(std::tuple_cat(
|
||||
std::declval<std::tuple<State>>(),
|
||||
std::declval<typename State_Layers<typename State::Prev_State>::Type>()
|
||||
));
|
||||
};
|
||||
template <typename State>
|
||||
using State_Layers_T = typename State_Layers<State>::Type;
|
||||
template <typename T>
|
||||
struct Member_Pointer_Traits;
|
||||
template <typename Member, typename Owner>
|
||||
struct Member_Pointer_Traits<Member Owner::*> {
|
||||
@@ -197,6 +232,8 @@ template <Mechanism_Type T>
|
||||
using Mechanism_Buffer_Tuple = std::conditional_t<Buffer_Type<T>, std::tuple<T>, std::tuple<>>;
|
||||
template <Mechanism_Type T>
|
||||
using Mechanism_Rely_Tuple = std::conditional_t<Rely_Mechanism<T>, std::tuple<T>, std::tuple<>>;
|
||||
template <Mechanism_Type T>
|
||||
using Mechanism_State_Tuple = std::conditional_t<State_Mechanism<T>, std::tuple<T>, std::tuple<>>;
|
||||
template <typename Tag, typename Tuple>
|
||||
struct Has_Tag : std::false_type {};
|
||||
template <typename Tag, typename... Types>
|
||||
@@ -220,7 +257,7 @@ template <typename Tuple>
|
||||
struct Is_Rely_List : std::false_type {};
|
||||
template <typename... Relies>
|
||||
struct Is_Rely_List<std::tuple<Relies...>> : Tagged_List_Check<
|
||||
(Rely_Mechanism<Relies> && ...),
|
||||
(Rely_Mechanism<Relies> && ...) && (std::derived_from<typename Relies::Object_Type, Root> && ...),
|
||||
Relies...
|
||||
> {};
|
||||
template <typename Tuple>
|
||||
@@ -237,6 +274,122 @@ concept Rely_Tag_In = requires {
|
||||
requires Rely_List<Tuple>;
|
||||
requires Has_Tag<Tag, Tuple>::value;
|
||||
};
|
||||
template <typename Tag, Rely_List Tuple>
|
||||
struct Rely_Type_By_Tag;
|
||||
template <typename Tag, Rely_Mechanism... Relies>
|
||||
struct Rely_Type_By_Tag<Tag, std::tuple<Relies...>> {
|
||||
private:
|
||||
template <typename First, typename... Rest>
|
||||
struct Find {
|
||||
using Type = std::conditional_t<
|
||||
std::same_as<Tag, typename First::Tag_Type>,
|
||||
First,
|
||||
typename Find<Rest...>::Type
|
||||
>;
|
||||
};
|
||||
template <typename Last>
|
||||
struct Find<Last> {
|
||||
using Type = Last;
|
||||
};
|
||||
public:
|
||||
using Type = typename Find<Relies...>::Type;
|
||||
};
|
||||
template <typename Tag, Rely_List Tuple> requires Rely_Tag_In<Tag, Tuple>
|
||||
using Rely_Declaration = typename Rely_Type_By_Tag<Tag, Tuple>::Type;
|
||||
template <typename Tag, Rely_List Tuple> requires Rely_Tag_In<Tag, Tuple>
|
||||
using Rely_Bound_Object = typename Rely_Declaration<Tag, Tuple>::Object_Type;
|
||||
template <typename Tuple>
|
||||
struct Is_State_List : std::false_type {};
|
||||
template <typename... States>
|
||||
struct Is_State_List<std::tuple<States...>> : Tagged_List_Check<
|
||||
(Tagged_State<States> && ...),
|
||||
States...
|
||||
> {};
|
||||
template <typename Tuple>
|
||||
concept State_List = Is_State_List<Tuple>::value;
|
||||
template <typename State>
|
||||
concept State_Chain = Tagged_State<State> && State_List<State_Layers_T<State>>;
|
||||
template <typename Tag, typename Tuple>
|
||||
concept State_Tag_In = requires {
|
||||
requires State_List<Tuple>;
|
||||
requires Has_Tag<Tag, Tuple>::value;
|
||||
};
|
||||
template <typename State, typename Tuple>
|
||||
concept State_Chain_Matches = State_Chain<State> && State_List<Tuple> && [] {
|
||||
using Layers = State_Layers_T<State>;
|
||||
if constexpr (std::tuple_size_v<Layers> != std::tuple_size_v<Tuple>) return false;
|
||||
else {
|
||||
return []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
return (std::same_as<
|
||||
typename std::tuple_element_t<I, Layers>::Tag_Type,
|
||||
typename std::tuple_element_t<std::tuple_size_v<Tuple> - I - 1, Tuple>::Tag_Type
|
||||
> && ...);
|
||||
}(std::make_index_sequence<std::tuple_size_v<Tuple>>{});
|
||||
}
|
||||
}();
|
||||
template <State_Mechanism State, Prop_State Prev>
|
||||
struct Rebind_State;
|
||||
template <typename Tag, Prop_State Marker_Prev, Prop_State Prev>
|
||||
struct Rebind_State<State_Type<Tag, Marker_Prev>, Prev> {
|
||||
using Type = State_Type<Tag, Prev>;
|
||||
};
|
||||
template <State_Mechanism State, Prop_State Prev>
|
||||
using Rebind_State_T = typename Rebind_State<State, Prev>::Type;
|
||||
template <typename Tag, typename... States>
|
||||
consteval std::size_t state_tag_index() {
|
||||
std::size_t result{};
|
||||
std::size_t current{};
|
||||
((std::same_as<Tag, typename States::Tag_Type> ? result = current : result, ++current), ...);
|
||||
return result;
|
||||
}
|
||||
template <typename Tag, typename State>
|
||||
struct State_By_Tag;
|
||||
template <typename Tag, Tagged_State State>
|
||||
struct State_By_Tag<Tag, State> {
|
||||
private:
|
||||
using Layers = State_Layers_T<State>;
|
||||
template <std::size_t... I>
|
||||
static consteval std::size_t index(std::index_sequence<I...>) {
|
||||
std::size_t result{};
|
||||
((void)(std::same_as<Tag, typename std::tuple_element_t<I, Layers>::Tag_Type> ? result = I : result), ...);
|
||||
return result;
|
||||
}
|
||||
public:
|
||||
using Type = std::tuple_element_t<index(std::make_index_sequence<std::tuple_size_v<Layers>>{}), Layers>;
|
||||
};
|
||||
template <typename Tag, Tagged_State State>
|
||||
using State_Value = typename State_By_Tag<Tag, State>::Type;
|
||||
template <typename Callback, typename Tag, typename State, typename States>
|
||||
concept State_Callback_For = State_Tag_In<Tag, States> && State_Chain_Matches<State, States> && std::invocable<Callback, const State_Value<Tag, State>&>;
|
||||
template <typename Tuple>
|
||||
struct State_Callback_Storage_Impl;
|
||||
template <typename... States>
|
||||
struct State_Callback_Storage_Impl<std::tuple<States...>> {
|
||||
private:
|
||||
std::tuple<std::function<void(const States&)>...> callbacks;
|
||||
public:
|
||||
template <typename Tag, typename Callback> requires
|
||||
(std::same_as<Tag, typename States::Tag_Type> || ...)
|
||||
void set(Callback&& callback) {
|
||||
constexpr auto index = state_tag_index<Tag, States...>();
|
||||
using State = std::tuple_element_t<index, std::tuple<States...>>;
|
||||
std::get<index>(callbacks) = std::function<void(const State&)>{std::forward<Callback>(callback)};
|
||||
}
|
||||
template <typename Tag> requires (std::same_as<Tag, typename States::Tag_Type> || ...)
|
||||
void clear() {
|
||||
constexpr auto index = state_tag_index<Tag, States...>();
|
||||
std::get<index>(callbacks) = {};
|
||||
}
|
||||
template <typename Tag, Tagged_State State> requires (std::same_as<Tag, typename States::Tag_Type> || ...)
|
||||
void notify(const State& state) {
|
||||
constexpr auto index = state_tag_index<Tag, States...>();
|
||||
using Layer = std::tuple_element_t<index, std::tuple<States...>>;
|
||||
auto& callback = std::get<index>(callbacks);
|
||||
if (callback) callback(static_cast<const Layer&>(state));
|
||||
}
|
||||
};
|
||||
template <Tagged_State State>
|
||||
using State_Callback_Storage = State_Callback_Storage_Impl<State_Layers_T<State>>;
|
||||
template <typename Tag, Mechanism_Type... Types>
|
||||
consteval std::size_t tag_index() {
|
||||
std::size_t result{};
|
||||
@@ -307,7 +460,6 @@ constexpr const void* rely_id() {
|
||||
return &rely_token<Key>;
|
||||
}
|
||||
}
|
||||
struct Root;
|
||||
struct Rely;
|
||||
template <typename T>
|
||||
concept Root_Derived = std::derived_from<T, Root>;
|
||||
@@ -315,8 +467,10 @@ template <typename T>
|
||||
concept Object_Core = requires {
|
||||
requires Root_Derived<T>;
|
||||
typename T::Private;
|
||||
typename T::States;
|
||||
requires Prop_State<typename T::Prop>;
|
||||
requires Prop_State<typename T::State>;
|
||||
requires detail::State_Chain_Matches<typename T::State, typename T::States>;
|
||||
requires detail::Buffer_List<typename T::Buffers>;
|
||||
requires detail::Rely_List<typename T::Relies>;
|
||||
};
|
||||
@@ -331,11 +485,13 @@ enum class Rely_Error {
|
||||
missing_dependency,
|
||||
cycle
|
||||
};
|
||||
struct Root_State_Tag {};
|
||||
struct Root {
|
||||
using Buffers = std::tuple<>;
|
||||
using Relies = std::tuple<>;
|
||||
using States = std::tuple<State_Type<Root_State_Tag>>;
|
||||
struct Prop {};
|
||||
struct State {
|
||||
struct State : State_Type<Root_State_Tag> {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
private:
|
||||
@@ -416,6 +572,11 @@ concept Rely_Object = requires {
|
||||
requires Prop_State<typename T::State>;
|
||||
requires Buffer_List<typename T::Buffers>;
|
||||
};
|
||||
template <typename T>
|
||||
concept Bound_Rely_Object = Rely_Object<T> && requires(T* object) {
|
||||
typename T::Private;
|
||||
object->d;
|
||||
};
|
||||
template <typename Source, auto Member>
|
||||
concept State_Rely_Source = requires {
|
||||
requires Rely_Object<Source>;
|
||||
|
||||
@@ -7,13 +7,18 @@ concept Object = requires {
|
||||
typename T::This_Object;
|
||||
typename T::Prev_Object;
|
||||
typename T::Prev_Prop;
|
||||
typename T::Prev_State;
|
||||
typename T::State_Tag;
|
||||
typename T::template Prev_State<typename T::State_Tag>;
|
||||
typename T::template Prev_Builder<T>;
|
||||
typename T::Prev_Private;
|
||||
typename T::States;
|
||||
requires std::same_as<typename T::This_Object, T>;
|
||||
requires Object_Root<typename T::Prev_Object>;
|
||||
requires std::derived_from<typename T::Prop, typename T::Prev_Prop>;
|
||||
requires std::derived_from<typename T::State, typename T::Prev_State>;
|
||||
requires detail::State_Tag_In<typename T::State_Tag, typename T::States>;
|
||||
requires detail::State_Chain<typename T::template Prev_State<typename T::State_Tag>>;
|
||||
requires std::derived_from<typename T::State, typename T::template Prev_State<typename T::State_Tag>>;
|
||||
requires detail::State_Chain_Matches<typename T::State, typename T::States>;
|
||||
requires std::derived_from<typename T::template Builder<T>, typename T::template Prev_Builder<T>>;
|
||||
requires std::derived_from<typename T::Private, typename T::Prev_Private>;
|
||||
};
|
||||
@@ -28,12 +33,28 @@ using Impl_Relies = decltype(std::tuple_cat(
|
||||
std::declval<typename Base::Relies>(),
|
||||
std::declval<Mechanism_Rely_Tuple<Local_Mechanisms>>()...
|
||||
));
|
||||
template <Mechanism_Type... Local_Mechanisms>
|
||||
using Local_States = decltype(std::tuple_cat(
|
||||
std::declval<Mechanism_State_Tuple<Local_Mechanisms>>()...
|
||||
));
|
||||
template <Object_Root Base, Mechanism_Type... Local_Mechanisms>
|
||||
using Impl_States = decltype(std::tuple_cat(
|
||||
std::declval<typename Base::States>(),
|
||||
std::declval<Local_States<Local_Mechanisms...>>()
|
||||
));
|
||||
template <Object_Root Base, Mechanism_Type... Local_Mechanisms>
|
||||
using Impl_State_Base = Rebind_State_T<
|
||||
std::tuple_element_t<0, Local_States<Local_Mechanisms...>>,
|
||||
typename Base::State
|
||||
>;
|
||||
template <typename Base, typename... Local_Mechanisms>
|
||||
concept Impl_Mechanisms = requires {
|
||||
requires Object_Root<Base>;
|
||||
requires (Mechanism_Type<Local_Mechanisms> && ...);
|
||||
requires std::tuple_size_v<Local_States<Local_Mechanisms...>> == 1;
|
||||
requires Buffer_List<Impl_Buffers<Base, Local_Mechanisms...>>;
|
||||
requires Rely_List<Impl_Relies<Base, Local_Mechanisms...>>;
|
||||
requires State_List<Impl_States<Base, Local_Mechanisms...>>;
|
||||
};
|
||||
}
|
||||
template <typename T>
|
||||
@@ -51,10 +72,14 @@ struct Impl : Base {
|
||||
using Prev_Prop = typename Base::Prop;
|
||||
template <typename Object>
|
||||
using Prev_Builder = typename Base::template Builder<Object>;
|
||||
using Prev_State = typename Base::State;
|
||||
using State_Declaration = std::tuple_element_t<0, detail::Local_States<Local_Mechanisms...>>;
|
||||
using State_Tag = typename State_Declaration::Tag_Type;
|
||||
template <typename Tag> requires std::same_as<Tag, State_Tag>
|
||||
using Prev_State = detail::Rebind_State_T<State_Declaration, typename Base::State>;
|
||||
using Base_Private = typename Base::Private;
|
||||
using Buffers = detail::Impl_Buffers<Base, Local_Mechanisms...>;
|
||||
using Relies = detail::Impl_Relies<Base, Local_Mechanisms...>;
|
||||
using States = detail::Impl_States<Base, Local_Mechanisms...>;
|
||||
struct Private : Base_Private {
|
||||
template <typename Owner, typename Member, Prop_State State>
|
||||
void before_state_set(Self*, Member Owner::*, State*) {}
|
||||
@@ -81,11 +106,13 @@ struct Attach_Object : Obj {
|
||||
using State = typename Obj::State;
|
||||
using Buffers = typename Obj::Buffers;
|
||||
using Relies = typename Obj::Relies;
|
||||
using States = typename Obj::States;
|
||||
using Builder = typename Obj::template Builder<Attach_Object>;
|
||||
using Attached_Object = Obj;
|
||||
struct Private : Obj::Private, Double_Buffer<Prop> {
|
||||
using Allocator = std::pmr::polymorphic_allocator<std::byte>;
|
||||
Double_Buffer<State> state;
|
||||
detail::State_Callback_Storage<State> state_callbacks;
|
||||
detail::Buffer_Storage<Buffers> buffer_storage;
|
||||
detail::Rely_Storage<Relies> rely_storage;
|
||||
explicit Private(std::pmr::memory_resource* resource) : Double_Buffer<Prop>(std::allocator_arg, Allocator{resource}),
|
||||
@@ -99,7 +126,7 @@ struct Attach_Object : Obj {
|
||||
}
|
||||
private:
|
||||
friend struct Root;
|
||||
Lock lock;
|
||||
mutable Lock lock;
|
||||
template <bool Reverse, Object_Core Layer, typename Callback>
|
||||
void walk_private(Callback& callback) {
|
||||
if constexpr (requires { typename Layer::Prev_Object; }) {
|
||||
@@ -171,20 +198,22 @@ public:
|
||||
*buffer.current = *buffer.pending;
|
||||
}
|
||||
template <detail::Rely_Tag_In<Relies> Tag>
|
||||
[[nodiscard]] const Rely& rely() const {
|
||||
return *d.rely_storage.template get<Tag>().pending;
|
||||
[[nodiscard]] auto rely() const {
|
||||
using Object = detail::Rely_Bound_Object<Tag, Relies>;
|
||||
return d.rely_storage.template get<Tag>().pending->template typed_view<Object>();
|
||||
}
|
||||
template <detail::Rely_Tag_In<Relies> Tag>
|
||||
[[nodiscard]] const Rely& current_rely() const {
|
||||
return *d.rely_storage.template get<Tag>().current;
|
||||
[[nodiscard]] auto current_rely() const {
|
||||
using Object = detail::Rely_Bound_Object<Tag, Relies>;
|
||||
return d.rely_storage.template get<Tag>().current->template typed_view<Object>();
|
||||
}
|
||||
template <detail::Rely_Tag_In<Relies>... Tags, detail::Rely_Access_Callback_For<Tags...> Callback> requires
|
||||
detail::Unique_Types<Tags...>::value
|
||||
template <detail::Rely_Tag_In<Relies>... Tags, typename Callback> requires
|
||||
detail::Unique_Types<Tags...>::value && detail::Rely_Access_Callback_For<Callback, Relies, Tags...>
|
||||
void access_rely(Callback&& callback) {
|
||||
d.rely_storage.template access<Tags...>(std::forward<Callback>(callback));
|
||||
}
|
||||
template <detail::Rely_Tag_In<Relies>... Tags, detail::Rely_Edit_Callback_For<Tags...> Callback> requires
|
||||
detail::Unique_Types<Tags...>::value
|
||||
template <detail::Rely_Tag_In<Relies>... Tags, typename Callback> requires
|
||||
detail::Unique_Types<Tags...>::value && detail::Rely_Edit_Callback_For<Callback, Relies, Tags...>
|
||||
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));
|
||||
@@ -192,7 +221,8 @@ public:
|
||||
std::expected<void, Rely_Error> validate_rely() const {
|
||||
return d.rely_storage.validate_pending();
|
||||
}
|
||||
template <detail::Rely_Tag_In<Relies> Tag, detail::Rely_View_Node_Callback Callback>
|
||||
template <detail::Rely_Tag_In<Relies> Tag, typename Callback> requires
|
||||
detail::Rely_View_Node_Callback<Callback, detail::Rely_Bound_Object<Tag, Relies>>
|
||||
std::expected<void, Rely_Error> for_each_rely_topological(Callback&& callback) const {
|
||||
return current_rely<Tag>().for_each_topological_view(std::forward<Callback>(callback));
|
||||
}
|
||||
@@ -206,6 +236,26 @@ public:
|
||||
d.pending->*member = std::forward<Value>(value);
|
||||
return *this;
|
||||
}
|
||||
template <typename Tag, detail::State_Callback_For<Tag, State, States> Callback>
|
||||
void set_state_callback(Callback&& callback) {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
d.state_callbacks.template set<Tag>(std::forward<Callback>(callback));
|
||||
}
|
||||
template <detail::State_Tag_In<States> Tag>
|
||||
void clear_state_callback() {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
d.state_callbacks.template clear<Tag>();
|
||||
}
|
||||
template <typename Tag, detail::State_Callback_For<Tag, State, States> Callback>
|
||||
void access_state(Callback&& callback) const {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
using Layer = detail::State_Value<Tag, State>;
|
||||
std::invoke(std::forward<Callback>(callback), static_cast<const Layer&>(*d.state.current));
|
||||
}
|
||||
template <detail::State_Tag_In<States> Tag>
|
||||
void notify_state() {
|
||||
d.state_callbacks.template notify<Tag>(*d.state.current);
|
||||
}
|
||||
template <auto Member, detail::State_Member_Settable<Member, State> Value>
|
||||
void update_state(Value&& value) {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
|
||||
@@ -74,6 +74,49 @@ public:
|
||||
rely->for_each_edge(std::forward<Callback>(callback));
|
||||
}
|
||||
};
|
||||
template <Root_Derived Object>
|
||||
class Typed_View : public View {
|
||||
public:
|
||||
using Object_Type = Object;
|
||||
using Private_Type = typename Object::Private;
|
||||
explicit Typed_View(const Rely& value) : View(value) {}
|
||||
[[nodiscard]] bool bound(const Node& node) const noexcept {
|
||||
return node.data != nullptr;
|
||||
}
|
||||
[[nodiscard]] Object* object(const Node& node) const {
|
||||
if (!bound(node)) return nullptr;
|
||||
return static_cast<Object*>(node.object);
|
||||
}
|
||||
[[nodiscard]] Private_Type* private_data(const Node& node) const noexcept {
|
||||
return static_cast<Private_Type*>(node.data);
|
||||
}
|
||||
[[nodiscard]] Private_Type* private_data(Root* root) const noexcept {
|
||||
auto* node = this->find(root);
|
||||
return node ? private_data(*node) : nullptr;
|
||||
}
|
||||
template <std::invocable<Object*, Private_Type&> Callback>
|
||||
void for_each_bound(Callback&& callback) const {
|
||||
this->rely->for_each(
|
||||
[&](const Node& node) {
|
||||
auto* data = private_data(node);
|
||||
if (data) std::invoke(callback, static_cast<Object*>(node.object), *data);
|
||||
}
|
||||
);
|
||||
}
|
||||
template <std::invocable<const Typed_View&, const Node&> Callback>
|
||||
std::expected<void, Error> for_each_topological_view(Callback&& callback) const {
|
||||
return this->rely->for_each_topological(
|
||||
[&](const Node& node) {
|
||||
std::invoke(callback, *this, node);
|
||||
}
|
||||
);
|
||||
}
|
||||
template <std::invocable<const Node&> Callback>
|
||||
void for_each(Callback&& callback) const {
|
||||
this->rely->for_each(std::forward<Callback>(callback));
|
||||
}
|
||||
};
|
||||
template <Root_Derived Bound_Object>
|
||||
class Editor : public View {
|
||||
private:
|
||||
Rely* edit_rely;
|
||||
@@ -81,6 +124,7 @@ public:
|
||||
const void* target_dirty_key;
|
||||
bool bind_node_data;
|
||||
public:
|
||||
using Object_Type = Bound_Object;
|
||||
Editor(Rely& value, const void* tag, const void* dirty_key) : Editor(value, tag, dirty_key, true) {}
|
||||
Editor(Rely& value, const void* tag, const void* dirty_key, bool value_bind_node_data) : View(value),
|
||||
edit_rely(&value),
|
||||
@@ -90,21 +134,19 @@ public:
|
||||
void clear() {
|
||||
edit_rely->reset();
|
||||
}
|
||||
template <Root_Derived Object>
|
||||
template <detail::Bound_Rely_Object Object> requires std::derived_from<Object, Bound_Object>
|
||||
Node* add(Object* object) {
|
||||
return edit_rely->add_node(object, bind_node_data);
|
||||
return edit_rely->template add_node<Bound_Object>(object, bind_node_data);
|
||||
}
|
||||
template <Root_Derived Object>
|
||||
template <detail::Bound_Rely_Object Object> requires std::derived_from<Object, Bound_Object>
|
||||
void add(std::initializer_list<Object*> objects) {
|
||||
for (auto* object : objects) edit_rely->add_node(object, bind_node_data);
|
||||
}
|
||||
void add(std::initializer_list<Root*> objects) {
|
||||
for (auto* object : objects) edit_rely->add_node(object, bind_node_data);
|
||||
for (auto* object : objects) edit_rely->template add_node<Bound_Object>(object, bind_node_data);
|
||||
}
|
||||
bool remove(Root* object) {
|
||||
return edit_rely->remove_node(object);
|
||||
}
|
||||
void clear_dependencies(Root* target) {
|
||||
template <Root_Derived Target> requires std::derived_from<Target, Bound_Object>
|
||||
void clear_dependencies(Target* target) {
|
||||
edit_rely->clear_dependencies_impl(target);
|
||||
}
|
||||
void remove_dependents(Root* source) {
|
||||
@@ -113,9 +155,10 @@ public:
|
||||
void disconnect(Root* object) {
|
||||
edit_rely->disconnect_impl(object);
|
||||
}
|
||||
template <auto Member, detail::Rely_Object Target, detail::State_Rely_Source<Member> Source>
|
||||
template <auto Member, detail::Bound_Rely_Object Target, detail::State_Rely_Source<Member> Source> requires
|
||||
std::derived_from<Target, Bound_Object>
|
||||
Node* add_dependency(Target* target, Source* source) {
|
||||
return edit_rely->template add_dependency_runtime<Target, Source>(
|
||||
return edit_rely->template add_dependency_runtime<Bound_Object, Target, Source>(
|
||||
target,
|
||||
source,
|
||||
detail::rely_id<detail::State_Rely_Key<Member>>(),
|
||||
@@ -124,9 +167,10 @@ public:
|
||||
bind_node_data
|
||||
);
|
||||
}
|
||||
template <typename Buffer_Tag, detail::Rely_Object Target, detail::Buffer_Rely_Source<Buffer_Tag> Source>
|
||||
template <typename Buffer_Tag, detail::Bound_Rely_Object Target, detail::Buffer_Rely_Source<Buffer_Tag> Source> requires
|
||||
std::derived_from<Target, Bound_Object>
|
||||
Node* add_dependency(Target* target, Source* source) {
|
||||
return edit_rely->template add_dependency_runtime<Target, Source>(
|
||||
return edit_rely->template add_dependency_runtime<Bound_Object, Target, Source>(
|
||||
target,
|
||||
source,
|
||||
detail::rely_id<detail::Buffer_Rely_Key<Buffer_Tag>>(),
|
||||
@@ -135,9 +179,10 @@ public:
|
||||
bind_node_data
|
||||
);
|
||||
}
|
||||
template <typename Source_Tag, detail::Rely_Object Target, detail::Rely_Object Source>
|
||||
template <typename Source_Tag, detail::Bound_Rely_Object Target, detail::Rely_Object Source> requires
|
||||
std::derived_from<Target, Bound_Object>
|
||||
Node* add_dirty_dependency(Target* target, Source* source) {
|
||||
return edit_rely->template add_dependency_runtime<Target, Source>(
|
||||
return edit_rely->template add_dependency_runtime<Bound_Object, Target, Source>(
|
||||
target,
|
||||
source,
|
||||
detail::rely_id<detail::Dirty_Rely_Key<Source_Tag>>(),
|
||||
@@ -146,15 +191,18 @@ public:
|
||||
bind_node_data
|
||||
);
|
||||
}
|
||||
template <auto Member, detail::Rely_Object Target, detail::State_Rely_Source<Member> Source>
|
||||
template <auto Member, detail::Bound_Rely_Object Target, detail::State_Rely_Source<Member> Source> requires
|
||||
std::derived_from<Target, Bound_Object>
|
||||
bool remove_dependency(Target* target, Source* source) {
|
||||
return edit_rely->remove_edge(target, source, detail::rely_id<detail::State_Rely_Key<Member>>(), target_tag);
|
||||
}
|
||||
template <typename Buffer_Tag, detail::Rely_Object Target, detail::Buffer_Rely_Source<Buffer_Tag> Source>
|
||||
template <typename Buffer_Tag, detail::Bound_Rely_Object Target, detail::Buffer_Rely_Source<Buffer_Tag> Source> requires
|
||||
std::derived_from<Target, Bound_Object>
|
||||
bool remove_dependency(Target* target, Source* source) {
|
||||
return edit_rely->remove_edge(target, source, detail::rely_id<detail::Buffer_Rely_Key<Buffer_Tag>>(), target_tag);
|
||||
}
|
||||
bool remove_dependency(Root* target, Root* source) {
|
||||
template <Root_Derived Target> requires std::derived_from<Target, Bound_Object>
|
||||
bool remove_dependency(Target* target, Root* source) {
|
||||
return edit_rely->remove_dependency_impl(target, source);
|
||||
}
|
||||
};
|
||||
@@ -239,12 +287,13 @@ private:
|
||||
++structure_revision;
|
||||
rebuild();
|
||||
}
|
||||
template <Root_Derived Object>
|
||||
template <Root_Derived Bound_Object, Root_Derived Object>
|
||||
static Node::Bind node_bind() {
|
||||
if constexpr (requires(Object* object, Node* node) { object->bind_rely_node(node, object); }) {
|
||||
if constexpr (std::derived_from<Object, Bound_Object> && detail::Bound_Rely_Object<Object>) {
|
||||
return [](Node* node, Root* root) {
|
||||
auto* object = static_cast<Object*>(root);
|
||||
object->bind_rely_node(node, object);
|
||||
if constexpr (requires { object->bind_rely_object(object); }) object->bind_rely_object(object);
|
||||
node->data = static_cast<typename Bound_Object::Private*>(&object->d);
|
||||
};
|
||||
}
|
||||
else {
|
||||
@@ -256,9 +305,9 @@ private:
|
||||
if (node.bind) node.bind(&node, node.object);
|
||||
}
|
||||
}
|
||||
template <Root_Derived Object>
|
||||
template <Root_Derived Bound_Object, Root_Derived Object>
|
||||
Node* add_node(Object* object, bool bind_node) {
|
||||
auto bind = node_bind<Object>();
|
||||
auto bind = node_bind<Bound_Object, Object>();
|
||||
auto current = nodes.find(object);
|
||||
if (current != nodes.end()) {
|
||||
current->second->bind = bind;
|
||||
@@ -294,7 +343,7 @@ private:
|
||||
dirty_edges.swap(other.dirty_edges);
|
||||
std::swap(structure_revision, other.structure_revision);
|
||||
}
|
||||
template <typename Target, typename Source>
|
||||
template <Root_Derived Bound_Object, typename Target, typename Source>
|
||||
Node* add_dependency_runtime(
|
||||
Target* target,
|
||||
Source* source,
|
||||
@@ -302,8 +351,8 @@ private:
|
||||
const void* target_tag,
|
||||
const void* target_dirty_key,
|
||||
bool bind_node) {
|
||||
auto* target_node = add_node(target, bind_node);
|
||||
auto* source_node = add_node(source, bind_node);
|
||||
auto* target_node = add_node<Bound_Object>(target, bind_node);
|
||||
auto* source_node = add_node<Bound_Object>(source, bind_node);
|
||||
add_edge(target_node, source_node, source_key, target_tag, target_dirty_key);
|
||||
return source_node;
|
||||
}
|
||||
@@ -554,6 +603,10 @@ public:
|
||||
}
|
||||
);
|
||||
}
|
||||
template <Root_Derived Object>
|
||||
[[nodiscard]] Typed_View<Object> typed_view() const {
|
||||
return Typed_View<Object>{*this};
|
||||
}
|
||||
void emit(Root* source, const void* key) const {
|
||||
auto [first, last] = dirty_edges.equal_range(Dirty_Source{source, key});
|
||||
for (; first != last; ++first) {
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
#include "rely.hpp"
|
||||
namespace aethera {
|
||||
namespace detail {
|
||||
template <typename Callback, typename... Tags>
|
||||
concept Rely_Edit_Callback_For = std::invocable<
|
||||
template <typename Callback, typename Tuple, typename... Tags>
|
||||
concept Rely_Edit_Callback_For = Rely_List<Tuple> && std::invocable<
|
||||
Callback,
|
||||
std::conditional_t<true, Rely::Editor&, Tags>...
|
||||
Rely::Editor<Rely_Bound_Object<Tags, Tuple>>&...
|
||||
>;
|
||||
template <typename Callback>
|
||||
concept Rely_View_Node_Callback = std::invocable<Callback, const Rely::View&, const Rely::Node&>;
|
||||
template <typename Callback, typename Object>
|
||||
concept Rely_View_Node_Callback = Root_Derived<Object> && std::invocable<Callback, const Rely::Typed_View<Object>&, const Rely::Node&>;
|
||||
template <typename Callback>
|
||||
concept Rely_Current_Callback = std::invocable<Callback, const Rely&>;
|
||||
template <typename Callback, typename... Tags>
|
||||
concept Rely_Access_Callback_For = std::invocable<Callback, Rely_Type<Tags>&...>;
|
||||
template <typename Callback, typename Tuple, typename... Tags>
|
||||
concept Rely_Access_Callback_For = Rely_List<Tuple> && std::invocable<Callback, Rely_Declaration<Tags, Tuple>&...>;
|
||||
}
|
||||
inline bool Root::mark_dirty_id(const void* tag, const void* dirty_key) {
|
||||
if (std::find(dirty_tags.begin(), dirty_tags.end(), tag) != dirty_tags.end()) return false;
|
||||
@@ -75,19 +75,21 @@ public:
|
||||
const auto& get() const {
|
||||
return std::get<index<Tag>()>(relies);
|
||||
}
|
||||
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, Rely_Access_Callback_For<Tags...> Callback> requires Unique_Types<Tags...>::value
|
||||
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, typename Callback> requires
|
||||
Unique_Types<Tags...>::value && Rely_Access_Callback_For<Callback, std::tuple<Relies...>, Tags...>
|
||||
void access(Callback&& callback) {
|
||||
std::invoke(
|
||||
std::forward<Callback>(callback),
|
||||
static_cast<Rely_Type<Tags>&>(get<Tags>())...
|
||||
static_cast<Rely_Declaration<Tags, std::tuple<Relies...>>&>(get<Tags>())...
|
||||
);
|
||||
}
|
||||
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, Rely_Edit_Callback_For<Tags...> Callback> requires Unique_Types<Tags...>::value
|
||||
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, typename Callback> requires
|
||||
Unique_Types<Tags...>::value && Rely_Edit_Callback_For<Callback, std::tuple<Relies...>, Tags...>
|
||||
std::expected<void, Rely_Error> edit(Callback&& callback) {
|
||||
std::tuple<std::conditional_t<true, Rely, Tags>...> next(*get<Tags>().pending...);
|
||||
auto editors = [&]<std::size_t... I>(std::index_sequence<I...>) {
|
||||
return std::tuple{
|
||||
Rely::Editor(
|
||||
Rely::Editor<Rely_Bound_Object<Tags, std::tuple<Relies...>>>(
|
||||
std::get<I>(next),
|
||||
rely_id<Tags>(),
|
||||
rely_id<Dirty_Rely_Key<Tags>>(),
|
||||
@@ -195,10 +197,11 @@ public:
|
||||
const Rely& get() const {
|
||||
return std::get<index<Tag>()>(relies);
|
||||
}
|
||||
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, Rely_Edit_Callback_For<Tags...> Callback> requires Unique_Types<Tags...>::value
|
||||
template <Rely_Tag_In<std::tuple<Relies...>>... Tags, typename Callback> requires
|
||||
Unique_Types<Tags...>::value && Rely_Edit_Callback_For<Callback, std::tuple<Relies...>, Tags...>
|
||||
void edit(Callback&& callback) {
|
||||
auto editors = std::tuple{
|
||||
Rely::Editor(
|
||||
Rely::Editor<Rely_Bound_Object<Tags, std::tuple<Relies...>>>(
|
||||
get<Tags>(),
|
||||
rely_id<Tags>(),
|
||||
rely_id<Dirty_Rely_Key<Tags>>(),
|
||||
@@ -248,13 +251,15 @@ struct Root::Builder {
|
||||
object->template set_initial_buffer<Tag>(std::forward<Value>(value));
|
||||
return *this;
|
||||
}
|
||||
template <detail::Rely_Tag_In<typename Object::Relies>... Rely_Tags, detail::Rely_Edit_Callback_For<Rely_Tags...> Callback> requires
|
||||
detail::Unique_Types<Rely_Tags...>::value
|
||||
template <detail::Rely_Tag_In<typename Object::Relies>... Rely_Tags, typename Callback> requires
|
||||
detail::Unique_Types<Rely_Tags...>::value &&
|
||||
detail::Rely_Edit_Callback_For<Callback, typename Object::Relies, Rely_Tags...>
|
||||
Builder& edit_rely(Callback&& callback) {
|
||||
rely_storage.template edit<Rely_Tags...>(std::forward<Callback>(callback));
|
||||
return *this;
|
||||
}
|
||||
template <detail::Rely_Tag_In<typename Object::Relies> Rely_Tag, Root_Derived Node_Object>
|
||||
template <detail::Rely_Tag_In<typename Object::Relies> Rely_Tag, detail::Bound_Rely_Object Node_Object> requires
|
||||
(std::derived_from<Node_Object, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>)
|
||||
Builder& add_dependency_node(Node_Object* node) {
|
||||
return edit_rely<Rely_Tag>(
|
||||
[&](auto& editor) {
|
||||
@@ -274,7 +279,8 @@ struct Root::Builder {
|
||||
detail::Rely_Tag_In<typename Object::Relies> Rely_Tag,
|
||||
auto Member,
|
||||
detail::Rely_Object Target,
|
||||
detail::State_Rely_Source<Member> Source>
|
||||
detail::State_Rely_Source<Member> Source> requires
|
||||
(detail::Bound_Rely_Object<Target> && std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>)
|
||||
Builder& add_dependency(Target* target, Source* source) {
|
||||
return edit_rely<Rely_Tag>(
|
||||
[&](auto& editor) {
|
||||
@@ -286,7 +292,8 @@ struct Root::Builder {
|
||||
detail::Rely_Tag_In<typename Object::Relies> Rely_Tag,
|
||||
typename Buffer_Tag,
|
||||
detail::Rely_Object Target,
|
||||
detail::Buffer_Rely_Source<Buffer_Tag> Source>
|
||||
detail::Buffer_Rely_Source<Buffer_Tag> Source> requires
|
||||
(detail::Bound_Rely_Object<Target> && std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>)
|
||||
Builder& add_dependency(Target* target, Source* source) {
|
||||
return edit_rely<Rely_Tag>(
|
||||
[&](auto& editor) {
|
||||
@@ -298,7 +305,8 @@ struct Root::Builder {
|
||||
detail::Rely_Tag_In<typename Object::Relies> Rely_Tag,
|
||||
typename Source_Tag,
|
||||
Root_Derived Target,
|
||||
Root_Derived Source>
|
||||
Root_Derived Source> requires
|
||||
(detail::Bound_Rely_Object<Target> && std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>)
|
||||
Builder& add_dirty_dependency(Target* target, Source* source) {
|
||||
return edit_rely<Rely_Tag>(
|
||||
[&](auto& editor) {
|
||||
@@ -310,7 +318,8 @@ struct Root::Builder {
|
||||
detail::Rely_Tag_In<typename Object::Relies> Rely_Tag,
|
||||
auto Member,
|
||||
detail::Rely_Object Target,
|
||||
detail::State_Rely_Source<Member> Source>
|
||||
detail::State_Rely_Source<Member> Source> requires
|
||||
(detail::Bound_Rely_Object<Target> && std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>)
|
||||
Builder& remove_dependency(Target* target, Source* source) {
|
||||
return edit_rely<Rely_Tag>(
|
||||
[&](auto& editor) {
|
||||
@@ -322,7 +331,8 @@ struct Root::Builder {
|
||||
detail::Rely_Tag_In<typename Object::Relies> Rely_Tag,
|
||||
typename Buffer_Tag,
|
||||
detail::Rely_Object Target,
|
||||
detail::Buffer_Rely_Source<Buffer_Tag> Source>
|
||||
detail::Buffer_Rely_Source<Buffer_Tag> Source> requires
|
||||
(detail::Bound_Rely_Object<Target> && std::derived_from<Target, detail::Rely_Bound_Object<Rely_Tag, typename Object::Relies>>)
|
||||
Builder& remove_dependency(Target* target, Source* source) {
|
||||
return edit_rely<Rely_Tag>(
|
||||
[&](auto& editor) {
|
||||
|
||||
+288
-73
@@ -1,71 +1,284 @@
|
||||
#include "render_2D.hpp"
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <taskflow/observer/interface.hpp>
|
||||
namespace aethera::_2D {
|
||||
namespace {
|
||||
class Stage_Registry : Pinned {
|
||||
private:
|
||||
struct Binding {
|
||||
const tf::Taskflow* owner;
|
||||
Root* object;
|
||||
Renderable::Private* data;
|
||||
detail::Renderable_Stage stage;
|
||||
detail::Stage_Observer_Point point;
|
||||
};
|
||||
std::unordered_map<std::size_t, Binding> bindings;
|
||||
std::mutex mutex;
|
||||
public:
|
||||
void bind(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, detail::Renderable_Stage stage, detail::Stage_Observer_Point point) {
|
||||
std::lock_guard guard(mutex);
|
||||
bindings.insert_or_assign(task_hash, Binding{owner, object, data, stage, point});
|
||||
}
|
||||
void clear(const tf::Taskflow* owner) {
|
||||
std::lock_guard guard(mutex);
|
||||
std::erase_if(
|
||||
bindings,
|
||||
[owner](const auto& value) {
|
||||
return value.second.owner == owner;
|
||||
}
|
||||
);
|
||||
}
|
||||
void on_entry(std::size_t task_hash, std::uint64_t now) {
|
||||
std::lock_guard guard(mutex);
|
||||
auto current = bindings.find(task_hash);
|
||||
if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::End) return;
|
||||
auto& binding = current->second;
|
||||
auto& state = *binding.data->dispatch->state_access(binding.object);
|
||||
if (binding.stage == detail::Renderable_Stage::Prepare) {
|
||||
if (state.prepare_executed) state.prepare_execution_time_ns = now - state.prepare_execution_time_ns;
|
||||
}
|
||||
else if (state.paint_executed) {
|
||||
state.paint_execution_time_ns = now - state.paint_execution_time_ns;
|
||||
}
|
||||
}
|
||||
void on_exit(std::size_t task_hash, std::uint64_t now) {
|
||||
std::lock_guard guard(mutex);
|
||||
auto current = bindings.find(task_hash);
|
||||
if (current == bindings.end() || current->second.point != detail::Stage_Observer_Point::Begin) return;
|
||||
auto& binding = current->second;
|
||||
auto& state = *binding.data->dispatch->state_access(binding.object);
|
||||
if (binding.stage == detail::Renderable_Stage::Prepare) {
|
||||
state.prepare_execution_time_ns = state.prepare_executed ? now : 0;
|
||||
}
|
||||
else {
|
||||
state.paint_execution_time_ns = state.paint_executed ? now : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
class Task_Observer : public tf::ObserverInterface {
|
||||
private:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
Stage_Registry* stage_registry;
|
||||
struct Task_Statistics {
|
||||
std::atomic_size_t count{};
|
||||
std::atomic_uint64_t total_time_ns{};
|
||||
std::atomic_uint64_t min_time_ns{std::numeric_limits<std::uint64_t>::max()};
|
||||
std::atomic_uint64_t max_time_ns{};
|
||||
};
|
||||
struct Worker_Statistics {
|
||||
std::atomic_size_t task_count{};
|
||||
std::atomic_size_t peak_queue_size{};
|
||||
std::atomic_size_t max_queue_capacity{};
|
||||
std::atomic_uint64_t task_time_ns{};
|
||||
std::atomic_uint64_t busy_time_ns{};
|
||||
std::atomic_uint64_t min_task_time_ns{std::numeric_limits<std::uint64_t>::max()};
|
||||
std::atomic_uint64_t max_task_time_ns{};
|
||||
};
|
||||
std::vector<std::vector<Clock::time_point>> starts;
|
||||
std::vector<Clock::time_point> worker_busy_starts;
|
||||
std::unique_ptr<Worker_Statistics[]> worker_statistics;
|
||||
std::size_t worker_statistics_count{};
|
||||
std::array<Task_Statistics, tf::TASK_TYPES.size()> task_types;
|
||||
std::atomic_size_t active_tasks{};
|
||||
std::atomic_size_t peak_active_tasks{};
|
||||
std::atomic_size_t active_workers{};
|
||||
std::atomic_size_t peak_active_workers{};
|
||||
std::atomic_size_t completed_tasks{};
|
||||
std::atomic_size_t named_tasks{};
|
||||
std::atomic_size_t peak_queue_size{};
|
||||
std::atomic_size_t max_queue_capacity{};
|
||||
std::atomic_size_t max_predecessors{};
|
||||
std::atomic_size_t max_successors{};
|
||||
std::atomic_size_t max_strong_dependencies{};
|
||||
std::atomic_size_t max_weak_dependencies{};
|
||||
std::atomic_uint64_t total_execution_time_ns{};
|
||||
static void update_peak(std::atomic_size_t& value, std::size_t next) noexcept {
|
||||
std::atomic_uint64_t worker_busy_time_ns{};
|
||||
std::atomic_uint64_t first_task_time_ns{};
|
||||
std::atomic_uint64_t last_task_time_ns{};
|
||||
mutable std::mutex longest_mutex;
|
||||
std::uint64_t longest_task_time_ns{};
|
||||
std::size_t longest_task_hash{};
|
||||
std::string longest_task_name;
|
||||
tf::TaskType longest_task_type{tf::TaskType::UNDEFINED};
|
||||
static std::uint64_t clock_ns(Clock::time_point value) noexcept {
|
||||
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(value.time_since_epoch()).count());
|
||||
}
|
||||
template <typename T>
|
||||
static void update_max(std::atomic<T>& value, T next) noexcept {
|
||||
auto current = value.load(std::memory_order_relaxed);
|
||||
while (current < next && !value.compare_exchange_weak(current, next, std::memory_order_relaxed)) {}
|
||||
}
|
||||
static void update_min(std::atomic_uint64_t& value, std::uint64_t next) noexcept {
|
||||
auto current = value.load(std::memory_order_relaxed);
|
||||
while (next < current && !value.compare_exchange_weak(current, next, std::memory_order_relaxed)) {}
|
||||
}
|
||||
static void update_first(std::atomic_uint64_t& value, std::uint64_t next) noexcept {
|
||||
auto current = value.load(std::memory_order_relaxed);
|
||||
while ((current == 0 || next < current) && !value.compare_exchange_weak(current, next, std::memory_order_relaxed)) {}
|
||||
}
|
||||
static std::size_t task_type_index(tf::TaskType type) noexcept {
|
||||
return static_cast<std::size_t>(type);
|
||||
}
|
||||
public:
|
||||
explicit Task_Observer(Stage_Registry* registry) : stage_registry(registry) {}
|
||||
void set_up(std::size_t workers) override {
|
||||
starts.resize(workers);
|
||||
worker_busy_starts.resize(workers);
|
||||
worker_statistics = std::make_unique<Worker_Statistics[]>(workers);
|
||||
worker_statistics_count = workers;
|
||||
for (auto& worker : starts) worker.reserve(8);
|
||||
}
|
||||
void on_entry(tf::WorkerView worker, tf::TaskView) override {
|
||||
starts[worker.id()].push_back(Clock::now());
|
||||
auto active = active_tasks.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
update_peak(peak_active_tasks, active);
|
||||
update_peak(peak_queue_size, worker.queue_size());
|
||||
}
|
||||
void on_exit(tf::WorkerView worker, tf::TaskView) override {
|
||||
void on_entry(tf::WorkerView worker, tf::TaskView task) override {
|
||||
auto now = Clock::now();
|
||||
stage_registry->on_entry(task.hash_value(), clock_ns(now));
|
||||
auto& worker_starts = starts[worker.id()];
|
||||
if (worker_starts.empty()) {
|
||||
worker_busy_starts[worker.id()] = now;
|
||||
auto active = active_workers.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
update_max(peak_active_workers, active);
|
||||
}
|
||||
worker_starts.push_back(now);
|
||||
auto active = active_tasks.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
update_max(peak_active_tasks, active);
|
||||
update_max(peak_queue_size, worker.queue_size());
|
||||
update_max(max_queue_capacity, worker.queue_capacity());
|
||||
auto& worker_state = worker_statistics[worker.id()];
|
||||
update_max(worker_state.peak_queue_size, worker.queue_size());
|
||||
update_max(worker_state.max_queue_capacity, worker.queue_capacity());
|
||||
update_max(max_predecessors, task.num_predecessors());
|
||||
update_max(max_successors, task.num_successors());
|
||||
update_max(max_strong_dependencies, task.num_strong_dependencies());
|
||||
update_max(max_weak_dependencies, task.num_weak_dependencies());
|
||||
if (!task.name().empty()) named_tasks.fetch_add(1, std::memory_order_relaxed);
|
||||
update_first(first_task_time_ns, clock_ns(now));
|
||||
}
|
||||
void on_exit(tf::WorkerView worker, tf::TaskView task) override {
|
||||
auto now = Clock::now();
|
||||
stage_registry->on_exit(task.hash_value(), clock_ns(now));
|
||||
auto& worker_starts = starts[worker.id()];
|
||||
auto start = worker_starts.back();
|
||||
worker_starts.pop_back();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start).count();
|
||||
total_execution_time_ns.fetch_add(static_cast<std::uint64_t>(elapsed), std::memory_order_relaxed);
|
||||
auto elapsed = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(now - start).count());
|
||||
total_execution_time_ns.fetch_add(elapsed, std::memory_order_relaxed);
|
||||
completed_tasks.fetch_add(1, std::memory_order_relaxed);
|
||||
auto& worker_state = worker_statistics[worker.id()];
|
||||
worker_state.task_count.fetch_add(1, std::memory_order_relaxed);
|
||||
worker_state.task_time_ns.fetch_add(elapsed, std::memory_order_relaxed);
|
||||
update_min(worker_state.min_task_time_ns, elapsed);
|
||||
update_max(worker_state.max_task_time_ns, elapsed);
|
||||
auto type_index = task_type_index(task.type());
|
||||
if (type_index < task_types.size()) {
|
||||
auto& type = task_types[type_index];
|
||||
type.count.fetch_add(1, std::memory_order_relaxed);
|
||||
type.total_time_ns.fetch_add(elapsed, std::memory_order_relaxed);
|
||||
update_min(type.min_time_ns, elapsed);
|
||||
update_max(type.max_time_ns, elapsed);
|
||||
}
|
||||
{
|
||||
std::lock_guard guard(longest_mutex);
|
||||
if (longest_task_time_ns < elapsed) {
|
||||
longest_task_time_ns = elapsed;
|
||||
longest_task_hash = task.hash_value();
|
||||
longest_task_name = task.name();
|
||||
longest_task_type = task.type();
|
||||
}
|
||||
}
|
||||
active_tasks.fetch_sub(1, std::memory_order_relaxed);
|
||||
if (worker_starts.empty()) {
|
||||
auto busy = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(now - worker_busy_starts[worker.id()]).count());
|
||||
worker_busy_time_ns.fetch_add(busy, std::memory_order_relaxed);
|
||||
worker_state.busy_time_ns.fetch_add(busy, std::memory_order_relaxed);
|
||||
active_workers.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
update_max(last_task_time_ns, clock_ns(now));
|
||||
}
|
||||
std::size_t active_count() const noexcept {
|
||||
return active_tasks.load(std::memory_order_relaxed);
|
||||
}
|
||||
std::size_t peak_active_count() const noexcept {
|
||||
return peak_active_tasks.load(std::memory_order_relaxed);
|
||||
}
|
||||
std::size_t completed_count() const noexcept {
|
||||
return completed_tasks.load(std::memory_order_relaxed);
|
||||
}
|
||||
std::size_t peak_queue() const noexcept {
|
||||
return peak_queue_size.load(std::memory_order_relaxed);
|
||||
}
|
||||
std::uint64_t total_execution_time() const noexcept {
|
||||
return total_execution_time_ns.load(std::memory_order_relaxed);
|
||||
void write_state(Task_Runtime_State& state, std::size_t workers, std::size_t active_topologies) const {
|
||||
state.worker_count = workers;
|
||||
state.active_topology_count = active_topologies;
|
||||
state.active_task_count = active_tasks.load(std::memory_order_relaxed);
|
||||
state.peak_active_task_count = peak_active_tasks.load(std::memory_order_relaxed);
|
||||
state.active_worker_count = active_workers.load(std::memory_order_relaxed);
|
||||
state.peak_active_worker_count = peak_active_workers.load(std::memory_order_relaxed);
|
||||
state.observed_task_count = completed_tasks.load(std::memory_order_relaxed);
|
||||
state.named_task_count = named_tasks.load(std::memory_order_relaxed);
|
||||
state.peak_observed_worker_queue_size = peak_queue_size.load(std::memory_order_relaxed);
|
||||
state.max_observed_worker_queue_capacity = max_queue_capacity.load(std::memory_order_relaxed);
|
||||
state.max_predecessors = max_predecessors.load(std::memory_order_relaxed);
|
||||
state.max_successors = max_successors.load(std::memory_order_relaxed);
|
||||
state.max_strong_dependencies = max_strong_dependencies.load(std::memory_order_relaxed);
|
||||
state.max_weak_dependencies = max_weak_dependencies.load(std::memory_order_relaxed);
|
||||
state.total_task_time_ns = total_execution_time_ns.load(std::memory_order_relaxed);
|
||||
state.worker_busy_time_ns = worker_busy_time_ns.load(std::memory_order_relaxed);
|
||||
auto first = first_task_time_ns.load(std::memory_order_relaxed);
|
||||
auto last = last_task_time_ns.load(std::memory_order_relaxed);
|
||||
state.observed_wall_time_ns = first && last >= first ? last - first : 0;
|
||||
state.worker_utilization = workers && state.observed_wall_time_ns ?
|
||||
static_cast<double>(state.worker_busy_time_ns) * 100.0 / static_cast<double>(state.observed_wall_time_ns) / static_cast<double>(workers) : 0.0;
|
||||
for (std::size_t i = 0; i < task_types.size(); ++i) {
|
||||
const auto& source = task_types[i];
|
||||
auto& target = state.task_types[i];
|
||||
target.count = source.count.load(std::memory_order_relaxed);
|
||||
target.total_time_ns = source.total_time_ns.load(std::memory_order_relaxed);
|
||||
auto min = source.min_time_ns.load(std::memory_order_relaxed);
|
||||
target.min_time_ns = target.count ? min : 0;
|
||||
target.max_time_ns = source.max_time_ns.load(std::memory_order_relaxed);
|
||||
}
|
||||
state.workers.resize(worker_statistics_count);
|
||||
for (std::size_t i = 0; i < worker_statistics_count; ++i) {
|
||||
const auto& source = worker_statistics[i];
|
||||
auto& target = state.workers[i];
|
||||
target.id = i;
|
||||
target.task_count = source.task_count.load(std::memory_order_relaxed);
|
||||
target.peak_observed_queue_size = source.peak_queue_size.load(std::memory_order_relaxed);
|
||||
target.max_observed_queue_capacity = source.max_queue_capacity.load(std::memory_order_relaxed);
|
||||
target.task_time_ns = source.task_time_ns.load(std::memory_order_relaxed);
|
||||
target.busy_time_ns = source.busy_time_ns.load(std::memory_order_relaxed);
|
||||
target.idle_time_ns = state.observed_wall_time_ns > target.busy_time_ns ? state.observed_wall_time_ns - target.busy_time_ns : 0;
|
||||
auto min = source.min_task_time_ns.load(std::memory_order_relaxed);
|
||||
target.min_task_time_ns = target.task_count ? min : 0;
|
||||
target.max_task_time_ns = source.max_task_time_ns.load(std::memory_order_relaxed);
|
||||
target.utilization = state.observed_wall_time_ns ? static_cast<double>(target.busy_time_ns) * 100.0 / static_cast<double>(state.observed_wall_time_ns) : 0.0;
|
||||
}
|
||||
std::lock_guard guard(longest_mutex);
|
||||
state.longest_task_time_ns = longest_task_time_ns;
|
||||
state.longest_task_hash = longest_task_hash;
|
||||
state.longest_task_name = longest_task_name;
|
||||
state.longest_task_type = longest_task_type;
|
||||
}
|
||||
};
|
||||
class Task_Resource : Pinned {
|
||||
private:
|
||||
std::unique_ptr<tf::Executor> executor;
|
||||
std::shared_ptr<Task_Observer> observer;
|
||||
Stage_Registry stage_registry;
|
||||
std::pmr::memory_resource* memory;
|
||||
std::atomic_size_t active_taskflows{};
|
||||
std::atomic_size_t peak_active_taskflows{};
|
||||
std::atomic_size_t completed_taskflows{};
|
||||
std::atomic_size_t failed_taskflows{};
|
||||
Task_Runtime_State state;
|
||||
::aethera::detail::State_Callback_Storage<Task_Runtime_State> state_callbacks;
|
||||
std::recursive_mutex state_mutex;
|
||||
void create_executor(std::size_t workers, std::shared_ptr<tf::WorkerInterface> worker_interface) {
|
||||
executor = std::make_unique<tf::Executor>(workers, std::move(worker_interface));
|
||||
observer = executor->make_observer<Task_Observer>();
|
||||
observer = executor->make_observer<Task_Observer>(&stage_registry);
|
||||
state = {};
|
||||
}
|
||||
void ensure_executor() {
|
||||
if (!executor) {
|
||||
executor = std::make_unique<tf::Executor>();
|
||||
observer = executor->make_observer<Task_Observer>();
|
||||
}
|
||||
std::lock_guard guard(state_mutex);
|
||||
if (executor) return;
|
||||
executor = std::make_unique<tf::Executor>();
|
||||
observer = executor->make_observer<Task_Observer>(&stage_registry);
|
||||
}
|
||||
void publish_state() {
|
||||
std::lock_guard guard(state_mutex);
|
||||
observer->write_state(state, executor->num_workers(), executor->num_topologies());
|
||||
state.active_taskflow_count = active_taskflows.load(std::memory_order_relaxed);
|
||||
state.peak_active_taskflow_count = peak_active_taskflows.load(std::memory_order_relaxed);
|
||||
state.completed_taskflow_count = completed_taskflows.load(std::memory_order_relaxed);
|
||||
state.failed_taskflow_count = failed_taskflows.load(std::memory_order_relaxed);
|
||||
state_callbacks.template notify<Task_Runtime_State_Tag>(state);
|
||||
}
|
||||
public:
|
||||
Task_Resource() : memory(std::pmr::get_default_resource()) {}
|
||||
@@ -74,39 +287,50 @@ public:
|
||||
return value;
|
||||
}
|
||||
void initialize(std::size_t workers, std::shared_ptr<tf::WorkerInterface> worker_interface, Pmr pmr) {
|
||||
create_executor(workers, std::move(worker_interface));
|
||||
std::lock_guard guard(state_mutex);
|
||||
memory = pmr.resource();
|
||||
create_executor(workers, std::move(worker_interface));
|
||||
active_taskflows.store(0, std::memory_order_relaxed);
|
||||
peak_active_taskflows.store(0, std::memory_order_relaxed);
|
||||
completed_taskflows.store(0, std::memory_order_relaxed);
|
||||
failed_taskflows.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
std::uint64_t run(tf::Taskflow& taskflow) {
|
||||
ensure_executor();
|
||||
auto active = active_taskflows.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
auto peak = peak_active_taskflows.load(std::memory_order_relaxed);
|
||||
while (peak < active && !peak_active_taskflows.compare_exchange_weak(peak, active, std::memory_order_relaxed)) {}
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
executor->run(taskflow).get();
|
||||
auto elapsed = std::chrono::steady_clock::now() - start;
|
||||
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count());
|
||||
try {
|
||||
executor->run(taskflow).get();
|
||||
} catch (...) {
|
||||
active_taskflows.fetch_sub(1, std::memory_order_relaxed);
|
||||
failed_taskflows.fetch_add(1, std::memory_order_relaxed);
|
||||
publish_state();
|
||||
throw;
|
||||
}
|
||||
auto elapsed = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count());
|
||||
active_taskflows.fetch_sub(1, std::memory_order_relaxed);
|
||||
completed_taskflows.fetch_add(1, std::memory_order_relaxed);
|
||||
publish_state();
|
||||
return elapsed;
|
||||
}
|
||||
void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, detail::Renderable_Stage stage, detail::Stage_Observer_Point point) {
|
||||
stage_registry.bind(owner, task_hash, object, data, stage, point);
|
||||
}
|
||||
void clear_stage_observers(const tf::Taskflow* owner) {
|
||||
stage_registry.clear(owner);
|
||||
}
|
||||
std::pmr::memory_resource* memory_resource() const noexcept {
|
||||
return memory;
|
||||
}
|
||||
std::size_t worker_count() const noexcept {
|
||||
return executor ? executor->num_workers() : 0;
|
||||
void set_state_callback(std::function<void(const Task_Runtime_State&)> callback) {
|
||||
std::lock_guard guard(state_mutex);
|
||||
state_callbacks.template set<Task_Runtime_State_Tag>(std::move(callback));
|
||||
}
|
||||
std::size_t active_topology_count() const noexcept {
|
||||
return executor ? executor->num_topologies() : 0;
|
||||
}
|
||||
std::size_t active_count() const noexcept {
|
||||
return observer ? observer->active_count() : 0;
|
||||
}
|
||||
std::size_t peak_active_count() const noexcept {
|
||||
return observer ? observer->peak_active_count() : 0;
|
||||
}
|
||||
std::size_t completed_count() const noexcept {
|
||||
return observer ? observer->completed_count() : 0;
|
||||
}
|
||||
std::size_t peak_queue_size() const noexcept {
|
||||
return observer ? observer->peak_queue() : 0;
|
||||
}
|
||||
std::uint64_t total_execution_time_ns() const noexcept {
|
||||
return observer ? observer->total_execution_time() : 0;
|
||||
void clear_state_callback() {
|
||||
std::lock_guard guard(state_mutex);
|
||||
state_callbacks.template clear<Task_Runtime_State_Tag>();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -114,32 +338,23 @@ void initialize_runtime(std::size_t workers, std::shared_ptr<tf::WorkerInterface
|
||||
Task_Resource::instance().initialize(workers, std::move(worker_interface), pmr);
|
||||
}
|
||||
namespace detail {
|
||||
void set_runtime_state_callback(std::function<void(const Task_Runtime_State&)> callback) {
|
||||
Task_Resource::instance().set_state_callback(std::move(callback));
|
||||
}
|
||||
void clear_runtime_state_callback() {
|
||||
Task_Resource::instance().clear_state_callback();
|
||||
}
|
||||
std::uint64_t run_taskflow(tf::Taskflow& taskflow) {
|
||||
return Task_Resource::instance().run(taskflow);
|
||||
}
|
||||
void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point) {
|
||||
Task_Resource::instance().bind_stage_observer(owner, task_hash, object, data, stage, point);
|
||||
}
|
||||
void clear_stage_observers(const tf::Taskflow* owner) {
|
||||
Task_Resource::instance().clear_stage_observers(owner);
|
||||
}
|
||||
std::pmr::memory_resource* task_memory_resource() noexcept {
|
||||
return Task_Resource::instance().memory_resource();
|
||||
}
|
||||
std::size_t task_worker_count() noexcept {
|
||||
return Task_Resource::instance().worker_count();
|
||||
}
|
||||
std::size_t task_active_topology_count() noexcept {
|
||||
return Task_Resource::instance().active_topology_count();
|
||||
}
|
||||
std::size_t task_active_count() noexcept {
|
||||
return Task_Resource::instance().active_count();
|
||||
}
|
||||
std::size_t task_peak_active_count() noexcept {
|
||||
return Task_Resource::instance().peak_active_count();
|
||||
}
|
||||
std::size_t task_completed_count() noexcept {
|
||||
return Task_Resource::instance().completed_count();
|
||||
}
|
||||
std::size_t task_peak_queue_size() noexcept {
|
||||
return Task_Resource::instance().peak_queue_size();
|
||||
}
|
||||
std::uint64_t task_total_execution_time_ns() noexcept {
|
||||
return Task_Resource::instance().total_execution_time_ns();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+276
-187
@@ -1,74 +1,87 @@
|
||||
#pragma once
|
||||
#include "base.hpp"
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <taskflow/taskflow.hpp>
|
||||
namespace aethera::_2D {
|
||||
struct Prepare_Data_Tag {};
|
||||
struct Paint_Tag {};
|
||||
struct Color_Cache {};
|
||||
struct Task_Runtime_State_Tag {};
|
||||
struct Scene_State_Tag {};
|
||||
struct Renderable_State_Tag {};
|
||||
struct Task_Type_State {
|
||||
std::size_t count{};
|
||||
std::uint64_t total_time_ns{};
|
||||
std::uint64_t min_time_ns{};
|
||||
std::uint64_t max_time_ns{};
|
||||
bool operator==(const Task_Type_State&) const = default;
|
||||
};
|
||||
struct Task_Worker_State {
|
||||
std::size_t id{};
|
||||
std::size_t task_count{};
|
||||
std::size_t peak_observed_queue_size{};
|
||||
std::size_t max_observed_queue_capacity{};
|
||||
std::uint64_t task_time_ns{};
|
||||
std::uint64_t busy_time_ns{};
|
||||
std::uint64_t idle_time_ns{};
|
||||
std::uint64_t min_task_time_ns{};
|
||||
std::uint64_t max_task_time_ns{};
|
||||
double utilization{};
|
||||
bool operator==(const Task_Worker_State&) const = default;
|
||||
};
|
||||
struct Task_Runtime_State : State_Type<Task_Runtime_State_Tag> {
|
||||
std::size_t worker_count{};
|
||||
std::size_t active_topology_count{};
|
||||
std::size_t active_taskflow_count{};
|
||||
std::size_t peak_active_taskflow_count{};
|
||||
std::size_t completed_taskflow_count{};
|
||||
std::size_t failed_taskflow_count{};
|
||||
std::size_t active_task_count{};
|
||||
std::size_t peak_active_task_count{};
|
||||
std::size_t active_worker_count{};
|
||||
std::size_t peak_active_worker_count{};
|
||||
std::size_t observed_task_count{};
|
||||
std::size_t named_task_count{};
|
||||
std::size_t peak_observed_worker_queue_size{};
|
||||
std::size_t max_observed_worker_queue_capacity{};
|
||||
std::size_t max_predecessors{};
|
||||
std::size_t max_successors{};
|
||||
std::size_t max_strong_dependencies{};
|
||||
std::size_t max_weak_dependencies{};
|
||||
std::size_t longest_task_hash{};
|
||||
std::string longest_task_name;
|
||||
tf::TaskType longest_task_type{tf::TaskType::UNDEFINED};
|
||||
std::uint64_t longest_task_time_ns{};
|
||||
std::uint64_t total_task_time_ns{};
|
||||
std::uint64_t worker_busy_time_ns{};
|
||||
std::uint64_t observed_wall_time_ns{};
|
||||
double worker_utilization{};
|
||||
std::array<Task_Type_State, tf::TASK_TYPES.size()> task_types{};
|
||||
std::vector<Task_Worker_State> workers;
|
||||
bool operator==(const Task_Runtime_State&) const = default;
|
||||
};
|
||||
void initialize_runtime(std::size_t workers = std::thread::hardware_concurrency(),
|
||||
std::shared_ptr<tf::WorkerInterface> worker_interface = nullptr,
|
||||
Pmr pmr = {});
|
||||
namespace detail {
|
||||
void set_runtime_state_callback(std::function<void(const Task_Runtime_State&)> callback);
|
||||
void clear_runtime_state_callback();
|
||||
std::uint64_t run_taskflow(tf::Taskflow& taskflow);
|
||||
std::pmr::memory_resource* task_memory_resource() noexcept;
|
||||
std::size_t task_worker_count() noexcept;
|
||||
std::size_t task_active_topology_count() noexcept;
|
||||
std::size_t task_active_count() noexcept;
|
||||
std::size_t task_peak_active_count() noexcept;
|
||||
std::size_t task_completed_count() noexcept;
|
||||
std::size_t task_peak_queue_size() noexcept;
|
||||
std::uint64_t task_total_execution_time_ns() noexcept;
|
||||
}
|
||||
struct Renderable;
|
||||
struct Scene : Impl<Scene, Root, Rely_Type<Prepare_Data_Tag>, Rely_Type<Paint_Tag>> {
|
||||
template <typename Tag, typename Callback> requires
|
||||
std::same_as<Tag, Task_Runtime_State_Tag> && std::invocable<Callback, const Task_Runtime_State&>
|
||||
void set_runtime_state_callback(Callback&& callback) {
|
||||
detail::set_runtime_state_callback(std::function<void(const Task_Runtime_State&)>{std::forward<Callback>(callback)});
|
||||
}
|
||||
template <typename Tag> requires std::same_as<Tag, Task_Runtime_State_Tag>
|
||||
void clear_runtime_state_callback() {
|
||||
detail::clear_runtime_state_callback();
|
||||
}
|
||||
struct Renderable : Impl<Renderable, Root, State_Type<Renderable_State_Tag>, Tagged_Buffer<Color_Cache>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {
|
||||
bool taskflow_rebuilt{};
|
||||
std::size_t renderable_count{};
|
||||
std::size_t taskflow_task_count{};
|
||||
std::uint64_t taskflow_execution_time_ns{};
|
||||
std::size_t executor_workers{};
|
||||
std::size_t executor_active_topologies{};
|
||||
std::size_t executor_active_tasks{};
|
||||
std::size_t executor_peak_active_tasks{};
|
||||
std::size_t executor_completed_tasks{};
|
||||
std::size_t executor_peak_queue_size{};
|
||||
std::uint64_t executor_total_task_time_ns{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
struct Result {};
|
||||
friend struct Renderable;
|
||||
private:
|
||||
tf::Taskflow taskflow;
|
||||
std::size_t renderable_count{};
|
||||
bool taskflow_built{};
|
||||
bool taskflow_rebuilt{};
|
||||
public:
|
||||
void after_exchange(Attached auto* object, Prop*, State*, const Prop*, const State*);
|
||||
template <std::invocable<const Result&> Callback>
|
||||
void process(Attached auto* object, Callback&& callback) {
|
||||
auto& state = static_cast<State&>(*object->d.state.current);
|
||||
state.taskflow_rebuilt = taskflow_rebuilt;
|
||||
state.renderable_count = renderable_count;
|
||||
state.taskflow_task_count = taskflow.num_tasks();
|
||||
state.taskflow_execution_time_ns = 0;
|
||||
if (taskflow_built && !taskflow.empty()) state.taskflow_execution_time_ns = detail::run_taskflow(taskflow);
|
||||
state.executor_workers = detail::task_worker_count();
|
||||
state.executor_active_topologies = detail::task_active_topology_count();
|
||||
state.executor_active_tasks = detail::task_active_count();
|
||||
state.executor_peak_active_tasks = detail::task_peak_active_count();
|
||||
state.executor_completed_tasks = detail::task_completed_count();
|
||||
state.executor_peak_queue_size = detail::task_peak_queue_size();
|
||||
state.executor_total_task_time_ns = detail::task_total_execution_time_ns();
|
||||
Result result;
|
||||
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 {
|
||||
struct State : Prev_State<Renderable_State_Tag> {
|
||||
bool prepare_dirty{};
|
||||
bool paint_dirty{};
|
||||
bool prepare_executed{};
|
||||
@@ -77,19 +90,17 @@ struct Renderable : Impl<Renderable, Root, Tagged_Buffer<Color_Cache>> {
|
||||
bool paint_graph_rebuilt{};
|
||||
std::size_t prepare_task_count{};
|
||||
std::size_t paint_task_count{};
|
||||
std::uint64_t prepare_execution_time_ns{};
|
||||
std::uint64_t paint_execution_time_ns{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
protected:
|
||||
static void run_prepare_data(Attached auto* object) {
|
||||
using Object = std::remove_pointer_t<decltype(object)>;
|
||||
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
||||
if constexpr (requires { private_data.prepare_data(object); }) {
|
||||
private_data.prepare_data(object);
|
||||
}
|
||||
if constexpr (requires { private_data.prepare_data(object); }) private_data.prepare_data(object);
|
||||
auto callback = [&](auto& value) {
|
||||
if constexpr (requires { value.after_prepare_data(object); }) {
|
||||
value.after_prepare_data(object);
|
||||
}
|
||||
if constexpr (requires { value.after_prepare_data(object); }) value.after_prepare_data(object);
|
||||
};
|
||||
walk_private<true, typename Object::Attached_Object>(object, callback);
|
||||
}
|
||||
@@ -99,20 +110,22 @@ public:
|
||||
using Rebuild_Predicate = bool (*)(Root*);
|
||||
using Graph_Builder = tf::Taskflow (*)(Root*, std::pmr::memory_resource*);
|
||||
using State_Access = State* (*)(Root*);
|
||||
Run_Predicate prepare_predicate{};
|
||||
Run_Predicate paint_predicate{};
|
||||
Rebuild_Predicate prepare_rebuild_predicate{};
|
||||
Rebuild_Predicate paint_rebuild_predicate{};
|
||||
Graph_Builder prepare_builder{};
|
||||
Graph_Builder paint_builder{};
|
||||
State_Access state_access{};
|
||||
using State_Notify = void (*)(Root*);
|
||||
struct Dispatch {
|
||||
Run_Predicate prepare_predicate;
|
||||
Run_Predicate paint_predicate;
|
||||
Rebuild_Predicate prepare_rebuild_predicate;
|
||||
Rebuild_Predicate paint_rebuild_predicate;
|
||||
Graph_Builder prepare_builder;
|
||||
Graph_Builder paint_builder;
|
||||
State_Access state_access;
|
||||
State_Notify state_notify;
|
||||
};
|
||||
const Dispatch* dispatch{};
|
||||
tf::Taskflow prepare_graph;
|
||||
tf::Taskflow paint_graph;
|
||||
tf::Taskflow prepare_stage;
|
||||
tf::Taskflow paint_stage;
|
||||
bool prepare_graph_built{};
|
||||
bool paint_graph_built{};
|
||||
bool stages_built{};
|
||||
bool should_rebuild_prepare_graph(Attached auto*, const State&) {
|
||||
return false;
|
||||
}
|
||||
@@ -129,7 +142,7 @@ public:
|
||||
tf::Taskflow taskflow;
|
||||
taskflow.emplace([object] {
|
||||
run_prepare_data(object);
|
||||
});
|
||||
}).name("renderable.prepare.data");
|
||||
return taskflow;
|
||||
}
|
||||
tf::Taskflow build_paint_graph(Attached auto* object, const State&, std::pmr::memory_resource*) {
|
||||
@@ -138,109 +151,118 @@ public:
|
||||
taskflow.emplace([object] {
|
||||
auto& private_data = static_cast<typename Object::Private&>(object->d);
|
||||
private_data.paint(object);
|
||||
});
|
||||
}).name("renderable.paint.data");
|
||||
return taskflow;
|
||||
}
|
||||
void prepare_data(Attached auto*) {}
|
||||
void after_prepare_data(Attached auto*) {}
|
||||
void paint(Attached auto*) {}
|
||||
private:
|
||||
void build_stages(Root* root) {
|
||||
prepare_stage.clear();
|
||||
paint_stage.clear();
|
||||
auto prepare_if = prepare_stage.emplace([this, root] {
|
||||
auto& state = *state_access(root);
|
||||
state.prepare_graph_rebuilt = false;
|
||||
bool rebuild = prepare_rebuild_predicate(root);
|
||||
if (!prepare_graph_built || rebuild) {
|
||||
prepare_graph = prepare_builder(root, detail::task_memory_resource());
|
||||
prepare_graph_built = true;
|
||||
state.prepare_graph_rebuilt = true;
|
||||
root->template mark_dirty<Prepare_Data_Tag>();
|
||||
}
|
||||
bool dirty = root->template dirty<Prepare_Data_Tag>();
|
||||
state.prepare_dirty = dirty;
|
||||
state.prepare_task_count = prepare_graph.num_tasks();
|
||||
state.prepare_executed = prepare_predicate(root, dirty);
|
||||
return state.prepare_executed ? 0 : -1;
|
||||
});
|
||||
auto prepare_run = prepare_stage.composed_of(prepare_graph);
|
||||
auto prepare_done = prepare_stage.emplace([root] {
|
||||
root->template take_dirty<Prepare_Data_Tag>();
|
||||
root->template mark_dirty<Paint_Tag>();
|
||||
});
|
||||
prepare_if.precede(prepare_run);
|
||||
prepare_run.precede(prepare_done);
|
||||
auto paint_if = paint_stage.emplace([this, root] {
|
||||
auto& state = *state_access(root);
|
||||
state.paint_graph_rebuilt = false;
|
||||
bool rebuild = paint_rebuild_predicate(root);
|
||||
if (!paint_graph_built || rebuild) {
|
||||
paint_graph = paint_builder(root, detail::task_memory_resource());
|
||||
paint_graph_built = true;
|
||||
state.paint_graph_rebuilt = true;
|
||||
root->template mark_dirty<Paint_Tag>();
|
||||
}
|
||||
bool dirty = root->template dirty<Paint_Tag>();
|
||||
state.paint_dirty = dirty;
|
||||
state.paint_task_count = paint_graph.num_tasks();
|
||||
state.paint_executed = paint_predicate(root, dirty);
|
||||
return state.paint_executed ? 0 : -1;
|
||||
});
|
||||
auto paint_run = paint_stage.composed_of(paint_graph);
|
||||
auto paint_done = paint_stage.emplace([root] {
|
||||
root->template take_dirty<Paint_Tag>();
|
||||
});
|
||||
paint_if.precede(paint_run);
|
||||
paint_run.precede(paint_done);
|
||||
stages_built = true;
|
||||
}
|
||||
friend struct Renderable;
|
||||
};
|
||||
private:
|
||||
void bind_rely_node(Rely::Node* node, Attached auto* object) {
|
||||
void bind_rely_object(Attached auto* object) {
|
||||
using Object = std::remove_pointer_t<decltype(object)>;
|
||||
auto& data = static_cast<Private&>(object->d);
|
||||
data.prepare_predicate = [](Root* root, bool dirty) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_prepare(value, *value->d.state.current, dirty);
|
||||
static const Private::Dispatch dispatch{
|
||||
[](Root* root, bool dirty) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_prepare(value, *value->d.state.current, dirty);
|
||||
},
|
||||
[](Root* root, bool dirty) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_paint(value, *value->d.state.current, dirty);
|
||||
},
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_rebuild_prepare_graph(value, *value->d.state.current);
|
||||
},
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_rebuild_paint_graph(value, *value->d.state.current);
|
||||
},
|
||||
[](Root* root, std::pmr::memory_resource* resource) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.build_prepare_graph(value, *value->d.state.current, resource);
|
||||
},
|
||||
[](Root* root, std::pmr::memory_resource* resource) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.build_paint_graph(value, *value->d.state.current, resource);
|
||||
},
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
return static_cast<State*>(value->d.state.current);
|
||||
},
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
value->template notify_state<Renderable_State_Tag>();
|
||||
}
|
||||
};
|
||||
data.paint_predicate = [](Root* root, bool dirty) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_paint(value, *value->d.state.current, dirty);
|
||||
};
|
||||
data.prepare_rebuild_predicate = [](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_rebuild_prepare_graph(value, *value->d.state.current);
|
||||
};
|
||||
data.paint_rebuild_predicate = [](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.should_rebuild_paint_graph(value, *value->d.state.current);
|
||||
};
|
||||
data.prepare_builder = [](Root* root, std::pmr::memory_resource* resource) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.build_prepare_graph(value, *value->d.state.current, resource);
|
||||
};
|
||||
data.paint_builder = [](Root* root, std::pmr::memory_resource* resource) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(value->d);
|
||||
return private_data.build_paint_graph(value, *value->d.state.current, resource);
|
||||
};
|
||||
data.state_access = [](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
return static_cast<State*>(value->d.state.current);
|
||||
};
|
||||
if (!data.stages_built) data.build_stages(object);
|
||||
data.dispatch = &dispatch;
|
||||
object->template mark_dirty<Prepare_Data_Tag>();
|
||||
node->data = &data;
|
||||
}
|
||||
friend struct ::aethera::Rely;
|
||||
};
|
||||
namespace detail {
|
||||
enum class Renderable_Stage {
|
||||
Prepare,
|
||||
Paint
|
||||
};
|
||||
enum class Stage_Observer_Point {
|
||||
Begin,
|
||||
End
|
||||
};
|
||||
void bind_stage_observer(const tf::Taskflow* owner, std::size_t task_hash, Root* object, Renderable::Private* data, Renderable_Stage stage, Stage_Observer_Point point);
|
||||
void clear_stage_observers(const tf::Taskflow* owner);
|
||||
}
|
||||
struct Scene : Impl<Scene, Root, State_Type<Scene_State_Tag>, Rely_Type<Prepare_Data_Tag, Renderable>, Rely_Type<Paint_Tag, Renderable>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Scene_State_Tag> {
|
||||
bool taskflow_rebuilt{};
|
||||
std::size_t renderable_count{};
|
||||
std::size_t taskflow_task_count{};
|
||||
std::size_t taskflow_dependency_count{};
|
||||
std::size_t taskflow_max_predecessors{};
|
||||
std::size_t taskflow_max_successors{};
|
||||
std::uint64_t taskflow_execution_time_ns{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
struct Result {};
|
||||
private:
|
||||
tf::Taskflow taskflow;
|
||||
std::size_t renderable_count{};
|
||||
std::size_t taskflow_dependency_count{};
|
||||
std::size_t taskflow_max_predecessors{};
|
||||
std::size_t taskflow_max_successors{};
|
||||
bool taskflow_built{};
|
||||
bool taskflow_rebuilt{};
|
||||
public:
|
||||
~Private() {
|
||||
detail::clear_stage_observers(&taskflow);
|
||||
}
|
||||
void after_exchange(Attached auto* object, Prop*, State*, const Prop*, const State*);
|
||||
template <std::invocable<const Result&> Callback>
|
||||
void process(Attached auto* object, Callback&& callback) {
|
||||
auto& state = static_cast<State&>(*object->d.state.current);
|
||||
state.taskflow_rebuilt = taskflow_rebuilt;
|
||||
state.renderable_count = renderable_count;
|
||||
state.taskflow_task_count = taskflow.num_tasks();
|
||||
state.taskflow_dependency_count = taskflow_dependency_count;
|
||||
state.taskflow_max_predecessors = taskflow_max_predecessors;
|
||||
state.taskflow_max_successors = taskflow_max_successors;
|
||||
state.taskflow_execution_time_ns = 0;
|
||||
if (taskflow_built && !taskflow.empty()) state.taskflow_execution_time_ns = detail::run_taskflow(taskflow);
|
||||
object->template notify_state<Scene_State_Tag>();
|
||||
Result result;
|
||||
std::invoke(std::forward<Callback>(callback), std::as_const(result));
|
||||
}
|
||||
};
|
||||
};
|
||||
void Scene::Private::after_exchange(Attached auto* object, Prop*, State*, const Prop*, const State*) {
|
||||
auto* resource = detail::task_memory_resource();
|
||||
taskflow_rebuilt = false;
|
||||
@@ -262,40 +284,97 @@ void Scene::Private::after_exchange(Attached auto* object, Prop*, State*, const
|
||||
}
|
||||
);
|
||||
if (!taskflow_dirty) return;
|
||||
const auto& prepare_dependencies = object->template current_rely<Prepare_Data_Tag>();
|
||||
const auto& paint_dependencies = object->template current_rely<Paint_Tag>();
|
||||
std::pmr::unordered_set<Root*> renderables{resource};
|
||||
prepare_dependencies.for_each(
|
||||
[&](const Rely::Node& node) {
|
||||
if (node.data) renderables.insert(node.object);
|
||||
auto prepare_dependencies = object->template current_rely<Prepare_Data_Tag>();
|
||||
auto paint_dependencies = object->template current_rely<Paint_Tag>();
|
||||
std::pmr::unordered_set<Renderable*> renderables{resource};
|
||||
prepare_dependencies.for_each_bound(
|
||||
[&](Renderable* renderable, Renderable::Private&) {
|
||||
renderables.insert(renderable);
|
||||
}
|
||||
);
|
||||
paint_dependencies.for_each(
|
||||
[&](const Rely::Node& node) {
|
||||
if (node.data) renderables.insert(node.object);
|
||||
paint_dependencies.for_each_bound(
|
||||
[&](Renderable* renderable, Renderable::Private&) {
|
||||
renderables.insert(renderable);
|
||||
}
|
||||
);
|
||||
renderable_count = renderables.size();
|
||||
detail::clear_stage_observers(&taskflow);
|
||||
taskflow.clear();
|
||||
struct Stage_Tasks {
|
||||
tf::Task prepare;
|
||||
tf::Task paint;
|
||||
tf::Task prepare_entry;
|
||||
tf::Task prepare_exit;
|
||||
tf::Task paint_entry;
|
||||
tf::Task paint_exit;
|
||||
};
|
||||
std::pmr::unordered_map<Root*, Stage_Tasks> stage_tasks{resource};
|
||||
for (auto* root : renderables) {
|
||||
const Rely::Node* rely_node = prepare_dependencies.find(root);
|
||||
if (!rely_node || !rely_node->data) rely_node = paint_dependencies.find(root);
|
||||
if (!rely_node || !rely_node->data) continue;
|
||||
auto* data = static_cast<Renderable::Private*>(rely_node->data);
|
||||
auto prepare = taskflow.composed_of(data->prepare_stage);
|
||||
auto paint = taskflow.composed_of(data->paint_stage);
|
||||
prepare.precede(paint);
|
||||
stage_tasks.emplace(root, Stage_Tasks{prepare, paint});
|
||||
for (auto* renderable : renderables) {
|
||||
Root* root = renderable;
|
||||
auto* data = prepare_dependencies.private_data(root);
|
||||
if (!data) data = paint_dependencies.private_data(root);
|
||||
if (!data) continue;
|
||||
auto* dispatch = data->dispatch;
|
||||
auto prepare_if = taskflow.emplace([data, dispatch, root] {
|
||||
auto& state = *dispatch->state_access(root);
|
||||
state.prepare_graph_rebuilt = false;
|
||||
state.prepare_execution_time_ns = 0;
|
||||
bool rebuild = dispatch->prepare_rebuild_predicate(root);
|
||||
if (!data->prepare_graph_built || rebuild) {
|
||||
data->prepare_graph = dispatch->prepare_builder(root, detail::task_memory_resource());
|
||||
data->prepare_graph_built = true;
|
||||
state.prepare_graph_rebuilt = true;
|
||||
root->template mark_dirty<Prepare_Data_Tag>();
|
||||
}
|
||||
bool dirty = root->template dirty<Prepare_Data_Tag>();
|
||||
state.prepare_dirty = dirty;
|
||||
state.prepare_task_count = data->prepare_graph.num_tasks();
|
||||
state.prepare_executed = dispatch->prepare_predicate(root, dirty);
|
||||
return state.prepare_executed ? 0 : 1;
|
||||
}).name("renderable.prepare.condition");
|
||||
auto prepare_run = taskflow.composed_of(data->prepare_graph).name("renderable.prepare.graph");
|
||||
auto prepare_done = taskflow.emplace([dispatch, root] {
|
||||
auto& state = *dispatch->state_access(root);
|
||||
if (!state.prepare_executed) return;
|
||||
root->template take_dirty<Prepare_Data_Tag>();
|
||||
root->template mark_dirty<Paint_Tag>();
|
||||
}).name("renderable.prepare.complete");
|
||||
prepare_if.precede(prepare_run, prepare_done);
|
||||
prepare_run.precede(prepare_done);
|
||||
auto paint_if = taskflow.emplace([data, dispatch, root] {
|
||||
auto& state = *dispatch->state_access(root);
|
||||
state.paint_graph_rebuilt = false;
|
||||
state.paint_execution_time_ns = 0;
|
||||
bool rebuild = dispatch->paint_rebuild_predicate(root);
|
||||
if (!data->paint_graph_built || rebuild) {
|
||||
data->paint_graph = dispatch->paint_builder(root, detail::task_memory_resource());
|
||||
data->paint_graph_built = true;
|
||||
state.paint_graph_rebuilt = true;
|
||||
root->template mark_dirty<Paint_Tag>();
|
||||
}
|
||||
bool dirty = root->template dirty<Paint_Tag>();
|
||||
state.paint_dirty = dirty;
|
||||
state.paint_task_count = data->paint_graph.num_tasks();
|
||||
state.paint_executed = dispatch->paint_predicate(root, dirty);
|
||||
return state.paint_executed ? 0 : 1;
|
||||
}).name("renderable.paint.condition");
|
||||
auto paint_run = taskflow.composed_of(data->paint_graph).name("renderable.paint.graph");
|
||||
auto paint_done = taskflow.emplace([dispatch, root] {
|
||||
auto& state = *dispatch->state_access(root);
|
||||
if (state.paint_executed) root->template take_dirty<Paint_Tag>();
|
||||
dispatch->state_notify(root);
|
||||
}).name("renderable.paint.complete");
|
||||
paint_if.precede(paint_run, paint_done);
|
||||
paint_run.precede(paint_done);
|
||||
prepare_done.precede(paint_if);
|
||||
detail::bind_stage_observer(&taskflow, prepare_if.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::Begin);
|
||||
detail::bind_stage_observer(&taskflow, prepare_done.hash_value(), root, data, detail::Renderable_Stage::Prepare, detail::Stage_Observer_Point::End);
|
||||
detail::bind_stage_observer(&taskflow, paint_if.hash_value(), root, data, detail::Renderable_Stage::Paint, detail::Stage_Observer_Point::Begin);
|
||||
detail::bind_stage_observer(&taskflow, paint_done.hash_value(), root, data, detail::Renderable_Stage::Paint, detail::Stage_Observer_Point::End);
|
||||
stage_tasks.emplace(root, Stage_Tasks{prepare_if, prepare_done, paint_if, paint_done});
|
||||
}
|
||||
auto connect_dependencies = [&](const Rely& rely, bool prepare) {
|
||||
auto connect_dependencies = [&](const auto& rely, bool prepare) {
|
||||
rely.for_each(
|
||||
[&](const Rely::Node& rely_node) {
|
||||
if (!rely_node.data) return;
|
||||
if (!rely.private_data(rely_node)) return;
|
||||
auto target = stage_tasks.find(rely_node.object);
|
||||
if (target == stage_tasks.end()) return;
|
||||
std::pmr::unordered_set<Root*> visited{resource};
|
||||
@@ -305,11 +384,11 @@ void Scene::Private::after_exchange(Attached auto* object, Prop*, State*, const
|
||||
const auto* dependency = pending.back();
|
||||
pending.pop_back();
|
||||
if (!visited.insert(dependency->object).second) continue;
|
||||
if (dependency->data) {
|
||||
if (rely.private_data(*dependency)) {
|
||||
auto source = stage_tasks.find(dependency->object);
|
||||
if (source != stage_tasks.end()) {
|
||||
auto source_task = prepare ? source->second.prepare : source->second.paint;
|
||||
auto target_task = prepare ? target->second.prepare : target->second.paint;
|
||||
auto source_task = prepare ? source->second.prepare_exit : source->second.paint_exit;
|
||||
auto target_task = prepare ? target->second.prepare_entry : target->second.paint_entry;
|
||||
source_task.precede(target_task);
|
||||
}
|
||||
continue;
|
||||
@@ -321,6 +400,16 @@ void Scene::Private::after_exchange(Attached auto* object, Prop*, State*, const
|
||||
};
|
||||
connect_dependencies(prepare_dependencies, true);
|
||||
connect_dependencies(paint_dependencies, false);
|
||||
taskflow_dependency_count = 0;
|
||||
taskflow_max_predecessors = 0;
|
||||
taskflow_max_successors = 0;
|
||||
taskflow.for_each_task(
|
||||
[&](tf::Task task) {
|
||||
taskflow_dependency_count += task.num_successors();
|
||||
taskflow_max_predecessors = std::max(taskflow_max_predecessors, task.num_predecessors());
|
||||
taskflow_max_successors = std::max(taskflow_max_successors, task.num_successors());
|
||||
}
|
||||
);
|
||||
object->template access_rely<Prepare_Data_Tag, Paint_Tag>(
|
||||
[](auto& prepare_state, auto& paint_state) {
|
||||
if (prepare_state.dirty()) prepare_state.take_dirty();
|
||||
|
||||
Reference in New Issue
Block a user