更新
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#include "global.hpp"
|
||||
#include "../scene/rely_facade.h"
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <proxy/proxy.h>
|
||||
#include <string>
|
||||
namespace aethera::renderable {
|
||||
enum struct Add_Task_Graph_Result : std::uint8_t { work_missing };
|
||||
struct Task_Node : facade_builder::add_skill<pro::skills::rtti>::build {};
|
||||
struct Task_Graph : facade_builder::add_convention<_add, std::expected<proxy<Task_Node>, Add_Task_Graph_Result>(std::string, std::function<void()>)>::add_convention<_clear, void()>::add_convention<_empty, bool() const noexcept>::add_convention<_taskflow, proxy<scene::Task_Graph>()>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
proxy<Task_Graph> make_task_graph(std::string name);
|
||||
} // namespace aethera::renderable
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include "global.hpp"
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <proxy/proxy.h>
|
||||
#include <string>
|
||||
namespace aethera::scene {
|
||||
enum struct Compose_Task_Graph_Result : std::uint8_t { component_unavailable, dependency_cycle, already_composed };
|
||||
enum struct Precede_Task_Graph_Result : std::uint8_t { preceded, node_unavailable, dependency_cycle };
|
||||
enum struct Run_Task_Graph_Result : std::uint8_t { submitted, runtime_not_initialized, completion_missing };
|
||||
using Taskflow_Completion = std::function<void(std::exception_ptr)>;
|
||||
struct Task_Node : facade_builder::add_skill<pro::skills::rtti>::build {};
|
||||
struct Task_Graph_Component : facade_builder::add_skill<pro::skills::rtti>::build {};
|
||||
struct Task_Graph : facade_builder::add_convention<_component, proxy<Task_Graph_Component>()>::add_convention<_compose, std::expected<proxy<Task_Node>, Compose_Task_Graph_Result>(std::string, proxy<Task_Graph_Component>&)>::add_convention<_precede, Precede_Task_Graph_Result(proxy<Task_Node>&, proxy<Task_Node>&)>::add_convention<_empty, bool() const noexcept>::add_convention<_run, Run_Task_Graph_Result(Taskflow_Completion)>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
proxy<Task_Graph> make_task_graph(std::string name);
|
||||
struct Renderable : facade_builder::add_convention<_advance, void()>::add_convention<_taskflow, proxy<Task_Graph>()>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
} // namespace aethera::scene
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
namespace aethera {
|
||||
enum struct Add_Task_Graph_Result : std::uint8_t { work_missing };
|
||||
enum struct Add_Condition_Task_Graph_Result : std::uint8_t { condition_missing };
|
||||
enum struct Compose_Task_Graph_Result : std::uint8_t { component_unavailable, dependency_cycle, already_composed };
|
||||
enum struct Precede_Task_Graph_Result : std::uint8_t { preceded, node_unavailable, node_stale, different_graph };
|
||||
enum struct Describe_Task_Graph_Result : std::uint8_t { described, key_missing };
|
||||
enum struct Describe_Task_Graph_Node_Result : std::uint8_t { described, node_unavailable, node_stale, key_missing };
|
||||
} // namespace aethera
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
namespace aethera {
|
||||
enum struct Run_Taskflow_Result : std::uint8_t {
|
||||
submitted,
|
||||
runtime_not_initialized,
|
||||
completion_missing,
|
||||
observation_unavailable
|
||||
};
|
||||
enum struct Taskflow_Unknown_Failure_Policy : std::uint8_t {
|
||||
fast_fail,
|
||||
exception
|
||||
};
|
||||
using Taskflow_Completion = std::function<void(std::exception_ptr)>;
|
||||
/* Runtime owns one immutable copy for its process lifetime; borrowed captures must outlive the runtime. */
|
||||
using Taskflow_Exception_Boundary = std::function<void(std::exception_ptr)>;
|
||||
} // namespace aethera
|
||||
@@ -1,90 +0,0 @@
|
||||
#include "Task_Graph.hpp"
|
||||
#include "Task_Runtime.hpp"
|
||||
#include "function/renderable/rely_facade.h"
|
||||
#include "function/scene/rely_facade.h"
|
||||
#include "model/Model.hpp"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace aethera {
|
||||
namespace detail {
|
||||
struct Scene_Task_Node { explicit Scene_Task_Node(Task_Node value) : value(std::move(value)) {} Task_Node value; };
|
||||
struct Scene_Task_Component { explicit Scene_Task_Component(std::shared_ptr<Task_Graph> value) : value(std::move(value)) {} std::shared_ptr<Task_Graph> value; };
|
||||
struct Renderable_Task_Node { explicit Renderable_Task_Node(Task_Node value) : value(std::move(value)) {} Task_Node value; };
|
||||
|
||||
struct Scene_Task_Graph_Model : Def<Scene_Task_Graph_Model, Root> {
|
||||
struct Private;
|
||||
explicit Scene_Task_Graph_Model(std::shared_ptr<Task_Graph> graph) : Def(std::move(graph)) {}
|
||||
};
|
||||
|
||||
struct Renderable_Task_Graph_Model : Def<Renderable_Task_Graph_Model, Root> {
|
||||
struct Private;
|
||||
explicit Renderable_Task_Graph_Model(std::shared_ptr<Task_Graph> graph) : Def(std::move(graph)) {}
|
||||
};
|
||||
|
||||
struct Scene_Task_Graph_Model::Private : Prev_Private {
|
||||
explicit Private(std::shared_ptr<Task_Graph> graph) : graph(std::move(graph)) {}
|
||||
proxy<scene::Task_Graph_Component> component() { return pro::make_proxy<scene::Task_Graph_Component, Scene_Task_Component>(graph); }
|
||||
auto compose(std::string name, proxy<scene::Task_Graph_Component>& child) -> std::expected<proxy<scene::Task_Node>, scene::Compose_Task_Graph_Result>;
|
||||
scene::Precede_Task_Graph_Result precede(proxy<scene::Task_Node>& before, proxy<scene::Task_Node>& after);
|
||||
bool empty() const noexcept { return graph->empty(); }
|
||||
scene::Run_Task_Graph_Result run(scene::Taskflow_Completion completion);
|
||||
std::shared_ptr<Task_Graph> graph; /* 共享同一权威任务图,仅用于为不同消费模块生成视图。 */
|
||||
};
|
||||
|
||||
struct Renderable_Task_Graph_Model::Private : Prev_Private {
|
||||
explicit Private(std::shared_ptr<Task_Graph> graph) : graph(std::move(graph)) {}
|
||||
auto add(std::string name, std::function<void()> work) -> std::expected<proxy<renderable::Task_Node>, renderable::Add_Task_Graph_Result>;
|
||||
void clear() { graph->clear(); }
|
||||
bool empty() const noexcept { return graph->empty(); }
|
||||
proxy<scene::Task_Graph> taskflow() { return make_model_proxy<scene::Task_Graph, Scene_Task_Graph_Model>(graph); }
|
||||
std::shared_ptr<Task_Graph> graph; /* 共享同一权威任务图,仅用于为不同消费模块生成视图。 */
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
auto detail::Scene_Task_Graph_Model::Private::compose(std::string name, proxy<scene::Task_Graph_Component>& child) -> std::expected<proxy<scene::Task_Node>, scene::Compose_Task_Graph_Result> {
|
||||
auto* component = child ? proxy_cast<Scene_Task_Component>(&*child) : nullptr;
|
||||
if (component == nullptr) return std::unexpected(scene::Compose_Task_Graph_Result::component_unavailable);
|
||||
auto result = graph->compose(std::move(name), *component->value);
|
||||
if (!result) {
|
||||
switch (result.error()) {
|
||||
case aethera::Compose_Task_Graph_Result::component_unavailable: return std::unexpected(scene::Compose_Task_Graph_Result::component_unavailable);
|
||||
case aethera::Compose_Task_Graph_Result::dependency_cycle: return std::unexpected(scene::Compose_Task_Graph_Result::dependency_cycle);
|
||||
case aethera::Compose_Task_Graph_Result::already_composed: return std::unexpected(scene::Compose_Task_Graph_Result::already_composed);
|
||||
}
|
||||
}
|
||||
return pro::make_proxy<scene::Task_Node, Scene_Task_Node>(std::move(*result));
|
||||
}
|
||||
|
||||
scene::Precede_Task_Graph_Result detail::Scene_Task_Graph_Model::Private::precede(proxy<scene::Task_Node>& before, proxy<scene::Task_Node>& after) {
|
||||
auto* before_node = before ? proxy_cast<Scene_Task_Node>(&*before) : nullptr;
|
||||
auto* after_node = after ? proxy_cast<Scene_Task_Node>(&*after) : nullptr;
|
||||
if (before_node == nullptr || after_node == nullptr) return scene::Precede_Task_Graph_Result::node_unavailable;
|
||||
switch (before_node->value.precede(after_node->value)) {
|
||||
case aethera::Precede_Task_Graph_Result::preceded: return scene::Precede_Task_Graph_Result::preceded;
|
||||
case aethera::Precede_Task_Graph_Result::node_unavailable: return scene::Precede_Task_Graph_Result::node_unavailable;
|
||||
case aethera::Precede_Task_Graph_Result::node_stale:
|
||||
case aethera::Precede_Task_Graph_Result::different_graph: return scene::Precede_Task_Graph_Result::node_unavailable;
|
||||
}
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
scene::Run_Task_Graph_Result detail::Scene_Task_Graph_Model::Private::run(scene::Taskflow_Completion completion) {
|
||||
switch (run_taskflow(*graph, std::move(completion))) {
|
||||
case Run_Taskflow_Result::submitted: return scene::Run_Task_Graph_Result::submitted;
|
||||
case Run_Taskflow_Result::runtime_not_initialized: return scene::Run_Task_Graph_Result::runtime_not_initialized;
|
||||
case Run_Taskflow_Result::completion_missing: return scene::Run_Task_Graph_Result::completion_missing;
|
||||
case Run_Taskflow_Result::observation_unavailable: std::terminate();
|
||||
}
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
auto detail::Renderable_Task_Graph_Model::Private::add(std::string name, std::function<void()> work) -> std::expected<proxy<renderable::Task_Node>, renderable::Add_Task_Graph_Result> {
|
||||
auto result = graph->add(std::move(name), std::move(work));
|
||||
if (!result) return std::unexpected(renderable::Add_Task_Graph_Result::work_missing);
|
||||
return pro::make_proxy<renderable::Task_Node, Renderable_Task_Node>(std::move(*result));
|
||||
}
|
||||
|
||||
proxy<scene::Task_Graph> scene::make_task_graph(std::string name) { return make_model_proxy<scene::Task_Graph, detail::Scene_Task_Graph_Model>(std::make_shared<aethera::Task_Graph>(std::move(name))); }
|
||||
proxy<renderable::Task_Graph> renderable::make_task_graph(std::string name) { return make_model_proxy<renderable::Task_Graph, detail::Renderable_Task_Graph_Model>(std::make_shared<aethera::Task_Graph>(std::move(name))); }
|
||||
} // namespace aethera
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace aethera {
|
||||
enum struct Frame_Policy_Configuration_Result : std::uint8_t { invalid_user_frame_rate, interval_out_of_range, invalid_statistics_window };
|
||||
enum struct Frame_Policy_Render_Submission_Result : std::uint8_t { runtime_not_initialized, task_graph_invalid };
|
||||
enum struct Throttled_Latest_Only_Start_Result : std::uint8_t { already_running, start_in_progress, stop_in_progress, dependency_unavailable, frame_unavailable, invalid_user_frame_rate, interval_out_of_range, invalid_statistics_window };
|
||||
enum struct Throttled_Latest_Only_Stop_Result : std::uint8_t { already_stopped, already_stopping, start_in_progress, completion_missing };
|
||||
namespace frame_policy {
|
||||
enum struct Render_Scene_Result : std::uint8_t { submitted, runtime_not_initialized, task_graph_invalid, frame_missing, completion_missing };
|
||||
} // namespace frame_policy
|
||||
} // namespace aethera
|
||||
+3
@@ -1,10 +1,13 @@
|
||||
#include "Frame_Policy.hpp"
|
||||
#include "rely_facade.h"
|
||||
#include "time_thread/src/Timer_Service_Model.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace aethera {
|
||||
proxy<frame_policy::Timer_Service> frame_policy::make_timer_service() { return time_thread::make_timer_service_proxy<frame_policy::Timer_Service>(make_steady_timer_time_source()); }
|
||||
namespace {
|
||||
auto capacity_fps(const Sliding_Statistics& statistics) noexcept -> std::optional<double> {
|
||||
const double average = statistics.summary().average;
|
||||
+1
-9
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "../export/export.h"
|
||||
#include "concurrent/base/Concurrent_Struct.hpp"
|
||||
#include "model/Model.hpp"
|
||||
#include "statistics/Sliding_Statistics.hpp"
|
||||
@@ -8,15 +9,6 @@
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
namespace aethera {
|
||||
enum struct Frame_Policy_Configuration_Result : std::uint8_t {
|
||||
invalid_user_frame_rate,
|
||||
interval_out_of_range,
|
||||
invalid_statistics_window
|
||||
};
|
||||
enum struct Frame_Policy_Render_Submission_Result : std::uint8_t {
|
||||
runtime_not_initialized,
|
||||
task_graph_invalid
|
||||
};
|
||||
struct Frame_Policy_Prop {
|
||||
std::optional<double> user_frames_per_second; /* 有值时作为用户帧率上限;无值时不参与限速。 */
|
||||
bool render_rate_limit_enabled{}; /* true 时用渲染平均耗时推导理论生成上限。 */
|
||||
+3
-17
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "function/frame_policy/Frame_Policy.hpp"
|
||||
#include "Frame_Policy.hpp"
|
||||
#include "rely_facade.h"
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
@@ -10,22 +10,8 @@ namespace aethera {
|
||||
* 三槽均忙时丢弃本次 tick;stop completion 前排空 Scene/Sink 借用并销毁全部帧。
|
||||
*/
|
||||
struct Throttled_Latest_only : Def<Throttled_Latest_only, Frame_Policy> {
|
||||
enum struct Start_Result : std::uint8_t {
|
||||
already_running,
|
||||
start_in_progress,
|
||||
stop_in_progress,
|
||||
dependency_unavailable,
|
||||
frame_unavailable,
|
||||
invalid_user_frame_rate,
|
||||
interval_out_of_range,
|
||||
invalid_statistics_window
|
||||
};
|
||||
enum struct Stop_Result : std::uint8_t {
|
||||
already_stopped,
|
||||
already_stopping,
|
||||
start_in_progress,
|
||||
completion_missing
|
||||
};
|
||||
using Start_Result = Throttled_Latest_Only_Start_Result;
|
||||
using Stop_Result = Throttled_Latest_Only_Stop_Result;
|
||||
using Stop_Completion = std::function<void()>;
|
||||
struct Private;
|
||||
Throttled_Latest_only(proxy<frame_policy::Timer_Service> timer_service, proxy<frame_policy::Scene> scene, proxy<frame_policy::Sink> sink);
|
||||
+1
-1
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "../export/export.h"
|
||||
#include "global.hpp"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
@@ -12,7 +13,6 @@ using Timer_Callback = std::function<void()>;
|
||||
struct Frame : facade_builder::build {};
|
||||
using Frame_Completion = std::function<void(proxy<Frame>&)>;
|
||||
using Render_Completion = std::function<void(proxy<Frame>&, std::exception_ptr)>;
|
||||
enum struct Render_Scene_Result : std::uint8_t { submitted, runtime_not_initialized, task_graph_invalid, frame_missing, completion_missing };
|
||||
struct Scene : facade_builder::add_convention<_create_frame, proxy<Frame>()>::add_convention<_render, Render_Scene_Result(proxy<Frame>&, Render_Completion)>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
struct Sink : facade_builder::add_convention<_send, void(proxy<Frame>&, Frame_Completion)>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
struct Timer_Service : facade_builder::add_convention<_schedule_every, Timer_Id(std::chrono::nanoseconds, Timer_Callback)>::add_convention<_cancel, void(Timer_Id, Timer_Callback)>::add_convention<_reschedule, void(Timer_Id, std::chrono::nanoseconds)>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "function/frame_policy/Throttled_Latest_Only.hpp"
|
||||
#include "frame_policy/src/Throttled_Latest_Only.hpp"
|
||||
#include "statistics/Sliding_Statistics.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -0,0 +1,26 @@
|
||||
function(aethera_kernel_add_module target module_name)
|
||||
set(module_dir "${Aethera_Kernel_module_dir}/${module_name}")
|
||||
append_glob_source(module_sources "${module_dir}/src" "${module_dir}/export")
|
||||
add_library(${target} STATIC ${module_sources})
|
||||
target_include_directories(${target} PUBLIC "$<BUILD_INTERFACE:${Aethera_Kernel_module_dir}>")
|
||||
target_link_libraries(${target} PUBLIC Aethera_Kernel_Core ${ARGN})
|
||||
append_glob_source(Aethera_Kernel_module_test_sources "${module_dir}/test")
|
||||
set(Aethera_Kernel_module_test_sources "${Aethera_Kernel_module_test_sources}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
aethera_kernel_add_module(Aethera_Kernel_Task_Flow task_flow Taskflow::Taskflow)
|
||||
add_library(Aethera_Kernel::Task_Flow ALIAS Aethera_Kernel_Task_Flow)
|
||||
target_link_libraries(Aethera_Kernel_Task_Flow PRIVATE Threads::Threads)
|
||||
|
||||
aethera_kernel_add_module(Aethera_Kernel_Time_Thread time_thread)
|
||||
add_library(Aethera_Kernel::Time_Thread ALIAS Aethera_Kernel_Time_Thread)
|
||||
target_link_libraries(Aethera_Kernel_Time_Thread PRIVATE Threads::Threads)
|
||||
|
||||
aethera_kernel_add_module(Aethera_Kernel_Frame_Policy frame_policy Aethera_Kernel_Time_Thread)
|
||||
add_library(Aethera_Kernel::Frame_Policy ALIAS Aethera_Kernel_Frame_Policy)
|
||||
|
||||
aethera_kernel_add_module(Aethera_Kernel_Renderable renderable Aethera_Kernel_Task_Flow)
|
||||
add_library(Aethera_Kernel::Renderable ALIAS Aethera_Kernel_Renderable)
|
||||
|
||||
aethera_kernel_add_module(Aethera_Kernel_Scene scene Aethera_Kernel_Task_Flow Aethera_Kernel_Renderable Aethera_Kernel_Frame_Policy)
|
||||
add_library(Aethera_Kernel::Scene ALIAS Aethera_Kernel_Scene)
|
||||
@@ -0,0 +1 @@
|
||||
#pragma once
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "global.hpp"
|
||||
#include "task_flow/export/export.h"
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <proxy/proxy.h>
|
||||
#include <string>
|
||||
|
||||
namespace aethera::renderable {
|
||||
struct Task_Graph : facade_builder::add_convention<_add, std::expected<proxy<task_flow::Task_Node>, aethera::Add_Task_Graph_Result>(std::string, std::function<void()>)>::add_convention<_component, proxy<task_flow::Task_Graph_Component>()>::add_convention<_clear, void()>::add_convention<_empty, bool() const noexcept>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
proxy<Task_Graph> make_task_graph(std::string name);
|
||||
} // namespace aethera::renderable
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "renderable.hpp"
|
||||
#include "task_flow/src/Task_Graph_Model.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace aethera {
|
||||
proxy<renderable::Task_Graph> renderable::make_task_graph(std::string name) { return task_flow::make_task_graph<renderable::Task_Graph>(std::move(name)); }
|
||||
} // namespace aethera
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
namespace aethera {
|
||||
template <typename Self> struct Renderable<Self>::Private : Prev_Private {
|
||||
void advance();
|
||||
proxy<scene::Task_Graph> taskflow();
|
||||
proxy<task_flow::Task_Graph_Component> taskflow();
|
||||
proxy<renderable::Task_Graph>& internal_taskflow() noexcept { return graph; }
|
||||
private:
|
||||
proxy<renderable::Task_Graph> graph{renderable::make_task_graph("renderable")}; /* 唯一拥有本 Renderable 的任务图代理。 */
|
||||
@@ -11,5 +11,5 @@ template <typename Self> void Renderable<Self>::Private::advance() {
|
||||
auto& private_data = static_cast<typename Self::Private&>(*this);
|
||||
if constexpr (requires { private_data.advance_renderable(); }) private_data.advance_renderable();
|
||||
}
|
||||
template <typename Self> proxy<scene::Task_Graph> Renderable<Self>::Private::taskflow() { return graph->taskflow(); }
|
||||
template <typename Self> proxy<task_flow::Task_Graph_Component> Renderable<Self>::Private::taskflow() { return graph->component(); }
|
||||
} // namespace aethera
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "renderable/src/renderable.hpp"
|
||||
#include "scene/src/rely_facade.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace {
|
||||
struct Test_Renderable : aethera::Def<Test_Renderable, aethera::Renderable<Test_Renderable>> {
|
||||
struct Private : Prev_Private {
|
||||
void advance_renderable() { ++advance_count; auto& graph = internal_taskflow(); graph->clear(); (void)graph->add("test.prepare", [] {}); }
|
||||
int advance_count{};
|
||||
};
|
||||
};
|
||||
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<Test_Renderable*>, aethera::scene::Renderable>);
|
||||
|
||||
TEST(Renderable, Facade_Uses_Private_Task_Graph_Capabilities) {
|
||||
auto renderable = aethera::make_model_proxy<aethera::scene::Renderable, Test_Renderable>();
|
||||
renderable->advance();
|
||||
EXPECT_TRUE(renderable->taskflow());
|
||||
}
|
||||
} // namespace
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace aethera {
|
||||
enum struct Scene_Create_Taskflow_Result : std::uint8_t { component_unavailable, dependency_cycle, already_composed, dependency_unavailable };
|
||||
} // namespace aethera
|
||||
+12
-10
@@ -1,4 +1,5 @@
|
||||
#include "Scene.hpp"
|
||||
#include "task_flow/src/Task_Graph_Model.hpp"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@@ -6,6 +7,7 @@ namespace aethera {
|
||||
namespace detail {
|
||||
struct Scene_Frame {};
|
||||
}
|
||||
proxy<scene::Task_Graph> scene::make_task_graph(std::string name) { return task_flow::make_task_graph<scene::Task_Graph>(std::move(name)); }
|
||||
Scene::Private::Private() = default;
|
||||
proxy<frame_policy::Frame> Scene::Private::create_frame() {
|
||||
return pro::make_proxy<frame_policy::Frame, detail::Scene_Frame>();
|
||||
@@ -15,21 +17,20 @@ proxy<scene::Task_Graph>& Scene::Private::taskflow() {
|
||||
}
|
||||
auto Scene::Private::create_taskflow() -> std::expected<proxy<scene::Task_Graph>, Create_Taskflow_Result> {
|
||||
auto result = scene::make_task_graph("scene.renderables");
|
||||
std::unordered_map<std::uint64_t, proxy<scene::Task_Node>> nodes;
|
||||
std::unordered_map<std::uint64_t, proxy<task_flow::Task_Node>> nodes;
|
||||
auto& renderables = get<Scene_Renderable_Dag>().internal.use();
|
||||
std::optional<Create_Taskflow_Result> error;
|
||||
renderables.for_each_topological([&](Dag_Node_Id id, proxy<scene::Renderable>& renderable) {
|
||||
if (error) return;
|
||||
auto taskflow = renderable->taskflow();
|
||||
auto component = taskflow->component();
|
||||
auto component = renderable->taskflow();
|
||||
auto composed = result->compose("renderable." + std::to_string(id.value), component);
|
||||
if (!composed) {
|
||||
switch (composed.error()) {
|
||||
case scene::Compose_Task_Graph_Result::component_unavailable: error = Create_Taskflow_Result::component_unavailable;
|
||||
case Compose_Task_Graph_Result::component_unavailable: error = Create_Taskflow_Result::component_unavailable;
|
||||
break;
|
||||
case scene::Compose_Task_Graph_Result::dependency_cycle: error = Create_Taskflow_Result::dependency_cycle;
|
||||
case Compose_Task_Graph_Result::dependency_cycle: error = Create_Taskflow_Result::dependency_cycle;
|
||||
break;
|
||||
case scene::Compose_Task_Graph_Result::already_composed: error = Create_Taskflow_Result::already_composed;
|
||||
case Compose_Task_Graph_Result::already_composed: error = Create_Taskflow_Result::already_composed;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
@@ -38,7 +39,7 @@ auto Scene::Private::create_taskflow() -> std::expected<proxy<scene::Task_Graph>
|
||||
});
|
||||
if (error) return std::unexpected(*error);
|
||||
renderables.for_each_dependency([&](Dag_Node_Id target, Dag_Node_Id source) {
|
||||
if (result->precede(nodes.at(source.value), nodes.at(target.value)) != scene::Precede_Task_Graph_Result::preceded) error = Create_Taskflow_Result::dependency_unavailable;
|
||||
if (result->precede(nodes.at(source.value), nodes.at(target.value)) != Precede_Task_Graph_Result::preceded) error = Create_Taskflow_Result::dependency_unavailable;
|
||||
});
|
||||
if (error) return std::unexpected(*error);
|
||||
return result;
|
||||
@@ -62,9 +63,10 @@ frame_policy::Render_Scene_Result Scene::Private::render(proxy<frame_policy::Fra
|
||||
completion(frame.get(), std::move(failure));
|
||||
});
|
||||
switch (result) {
|
||||
case scene::Run_Task_Graph_Result::submitted: return frame_policy::Render_Scene_Result::submitted;
|
||||
case scene::Run_Task_Graph_Result::runtime_not_initialized: return frame_policy::Render_Scene_Result::runtime_not_initialized;
|
||||
case scene::Run_Task_Graph_Result::completion_missing: return frame_policy::Render_Scene_Result::completion_missing;
|
||||
case Run_Taskflow_Result::submitted: return frame_policy::Render_Scene_Result::submitted;
|
||||
case Run_Taskflow_Result::runtime_not_initialized: return frame_policy::Render_Scene_Result::runtime_not_initialized;
|
||||
case Run_Taskflow_Result::completion_missing: return frame_policy::Render_Scene_Result::completion_missing;
|
||||
case Run_Taskflow_Result::observation_unavailable: std::terminate();
|
||||
}
|
||||
std::terminate();
|
||||
}
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "../frame_policy/rely_facade.h"
|
||||
#include "../export/export.h"
|
||||
#include "frame_policy/src/rely_facade.h"
|
||||
#include "model/Model.hpp"
|
||||
#include "relation/Dag.hpp"
|
||||
#include <functional>
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
namespace aethera {
|
||||
struct Scene::Private : Prev_Private {
|
||||
enum struct Create_Taskflow_Result : std::uint8_t { component_unavailable, dependency_cycle, already_composed, dependency_unavailable };
|
||||
using Create_Taskflow_Result = Scene_Create_Taskflow_Result;
|
||||
Private();
|
||||
proxy<frame_policy::Frame> create_frame();
|
||||
frame_policy::Render_Scene_Result render(proxy<frame_policy::Frame>& frame, frame_policy::Render_Completion completion);
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "global.hpp"
|
||||
#include "task_flow/export/export.h"
|
||||
#include <expected>
|
||||
#include <proxy/proxy.h>
|
||||
#include <string>
|
||||
|
||||
namespace aethera::scene {
|
||||
struct Task_Graph : facade_builder::add_convention<_component, proxy<task_flow::Task_Graph_Component>()>::add_convention<_compose, std::expected<proxy<task_flow::Task_Node>, aethera::Compose_Task_Graph_Result>(std::string, proxy<task_flow::Task_Graph_Component>&)>::add_convention<_precede, aethera::Precede_Task_Graph_Result(proxy<task_flow::Task_Node>&, proxy<task_flow::Task_Node>&)>::add_convention<_empty, bool() const noexcept>::add_convention<_run, aethera::Run_Taskflow_Result(aethera::Taskflow_Completion)>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
proxy<Task_Graph> make_task_graph(std::string name);
|
||||
struct Renderable : facade_builder::add_convention<_advance, void()>::add_convention<_taskflow, proxy<task_flow::Task_Graph_Component>()>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
} // namespace aethera::scene
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "renderable/src/renderable.hpp"
|
||||
#include "scene/src/Scene.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
struct Test_Renderable : aethera::Def<Test_Renderable, aethera::Renderable<Test_Renderable>> {
|
||||
struct Private : Prev_Private { void advance_renderable() { auto& graph = internal_taskflow(); graph->clear(); (void)graph->add("test.prepare", [] {}); } };
|
||||
};
|
||||
struct Test_Dag_Renderable : aethera::Def<Test_Dag_Renderable, aethera::Renderable<Test_Dag_Renderable>> {
|
||||
Test_Dag_Renderable(std::shared_ptr<std::vector<int>> advance_order, int id) : Def(std::move(advance_order), id) {}
|
||||
struct Private : Prev_Private {
|
||||
Private(std::shared_ptr<std::vector<int>> advance_order, int id) : advance_order(std::move(advance_order)), id(id) {}
|
||||
void advance_renderable() { advance_order->push_back(id); auto& graph = internal_taskflow(); graph->clear(); (void)graph->add("prepare", [] {}); }
|
||||
std::shared_ptr<std::vector<int>> advance_order;
|
||||
int id{};
|
||||
};
|
||||
};
|
||||
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<aethera::Scene*>, aethera::frame_policy::Scene>);
|
||||
|
||||
TEST(Scene, Advances_Renderables_And_Composes_Task_Graph_In_Dag_Order) {
|
||||
auto advance_order = std::make_shared<std::vector<int>>();
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder builder;
|
||||
builder.add({2}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 2));
|
||||
builder.add({1}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 1));
|
||||
builder.add({3}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 3));
|
||||
builder.add_dependency({2}, {1});
|
||||
builder.add_dependency({3}, {1});
|
||||
auto graph = builder.build();
|
||||
ASSERT_TRUE(graph.has_value());
|
||||
aethera::Scene scene;
|
||||
EXPECT_TRUE(scene.d->create_frame());
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>(std::move(*graph));
|
||||
EXPECT_TRUE(scene.d->advance().has_value());
|
||||
EXPECT_EQ(*advance_order, (std::vector<int>{1, 2, 3}));
|
||||
EXPECT_FALSE(scene.d->taskflow()->empty());
|
||||
advance_order->clear();
|
||||
std::optional<aethera::Edit_Dag_Result> edit_error;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>([](aethera::Dag<aethera::scene::Renderable>::Editor& editor) { editor.remove({99}); }, [&](std::expected<void, aethera::Edit_Dag_Result> result) { if (!result) edit_error = result.error(); });
|
||||
EXPECT_TRUE(scene.d->advance().has_value());
|
||||
ASSERT_TRUE(edit_error.has_value());
|
||||
EXPECT_EQ(*edit_error, aethera::Edit_Dag_Result::node_not_found);
|
||||
EXPECT_EQ(*advance_order, (std::vector<int>{1, 2, 3}));
|
||||
}
|
||||
|
||||
TEST(Scene, Dag_Rejects_Unallocated_Id_And_Missing_Object) {
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder invalid_id_builder;
|
||||
invalid_id_builder.add({0}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Renderable>());
|
||||
const auto invalid_id = invalid_id_builder.build();
|
||||
ASSERT_FALSE(invalid_id.has_value());
|
||||
EXPECT_EQ(invalid_id.error(), aethera::Build_Dag_Result::invalid_node_id);
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder missing_object_builder;
|
||||
missing_object_builder.add({1}, {});
|
||||
const auto missing_object = missing_object_builder.build();
|
||||
ASSERT_FALSE(missing_object.has_value());
|
||||
EXPECT_EQ(missing_object.error(), aethera::Build_Dag_Result::node_object_missing);
|
||||
}
|
||||
|
||||
TEST(Scene, Owned_Dag_Rolls_Back_The_Whole_Invalid_Edit) {
|
||||
auto advance_order = std::make_shared<std::vector<int>>();
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder builder;
|
||||
builder.add({1}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 1));
|
||||
auto graph = builder.build();
|
||||
ASSERT_TRUE(graph.has_value());
|
||||
aethera::Scene scene;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>(std::move(*graph));
|
||||
ASSERT_TRUE(scene.d->advance().has_value());
|
||||
advance_order->clear();
|
||||
std::optional<aethera::Edit_Dag_Result> edit_error;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>([advance_order](aethera::Dag<aethera::scene::Renderable>::Editor& editor) { editor.add({2}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 2)); editor.add_dependency({2}, {99}); }, [&](std::expected<void, aethera::Edit_Dag_Result> result) { if (!result) edit_error = result.error(); });
|
||||
ASSERT_TRUE(scene.d->advance().has_value());
|
||||
ASSERT_TRUE(edit_error.has_value());
|
||||
EXPECT_EQ(*edit_error, aethera::Edit_Dag_Result::node_not_found);
|
||||
EXPECT_EQ(*advance_order, (std::vector<int>{1}));
|
||||
}
|
||||
|
||||
TEST(Scene, Render_Returns_Known_Submission_Result) {
|
||||
aethera::Scene scene;
|
||||
auto frame = scene.d->create_frame();
|
||||
bool completed{};
|
||||
EXPECT_EQ(scene.d->render(frame, [&](aethera::proxy<aethera::frame_policy::Frame>&, std::exception_ptr) { completed = true; }), aethera::frame_policy::Render_Scene_Result::runtime_not_initialized);
|
||||
EXPECT_FALSE(completed);
|
||||
aethera::proxy<aethera::frame_policy::Frame> empty_frame;
|
||||
EXPECT_EQ(scene.d->render(empty_frame, [&](aethera::proxy<aethera::frame_policy::Frame>&, std::exception_ptr) {}), aethera::frame_policy::Render_Scene_Result::frame_missing);
|
||||
EXPECT_EQ(scene.d->render(frame, {}), aethera::frame_policy::Render_Scene_Result::completion_missing);
|
||||
}
|
||||
|
||||
TEST(Scene, Rejects_The_Same_Renderable_Task_Graph_Twice) {
|
||||
Test_Renderable renderable;
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder builder;
|
||||
builder.add({1}, aethera::model_proxy_view<aethera::scene::Renderable>(renderable));
|
||||
builder.add({2}, aethera::model_proxy_view<aethera::scene::Renderable>(renderable));
|
||||
auto graph = builder.build();
|
||||
ASSERT_TRUE(graph.has_value());
|
||||
aethera::Scene scene;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>(std::move(*graph));
|
||||
auto frame = scene.d->create_frame();
|
||||
EXPECT_EQ(scene.d->render(frame, [](aethera::proxy<aethera::frame_policy::Frame>&, std::exception_ptr) {}), aethera::frame_policy::Render_Scene_Result::task_graph_invalid);
|
||||
}
|
||||
} // namespace
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "global.hpp"
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <proxy/proxy.h>
|
||||
|
||||
namespace aethera {
|
||||
enum struct Add_Task_Graph_Result : std::uint8_t { work_missing };
|
||||
enum struct Add_Condition_Task_Graph_Result : std::uint8_t { condition_missing };
|
||||
enum struct Compose_Task_Graph_Result : std::uint8_t { component_unavailable, dependency_cycle, already_composed };
|
||||
enum struct Precede_Task_Graph_Result : std::uint8_t { preceded, node_unavailable, node_stale, different_graph };
|
||||
enum struct Describe_Task_Graph_Result : std::uint8_t { described, key_missing };
|
||||
enum struct Describe_Task_Graph_Node_Result : std::uint8_t { described, node_unavailable, node_stale, key_missing };
|
||||
enum struct Run_Taskflow_Result : std::uint8_t { submitted, runtime_not_initialized, completion_missing, observation_unavailable };
|
||||
enum struct Taskflow_Unknown_Failure_Policy : std::uint8_t { fast_fail, exception };
|
||||
enum struct Initialize_Task_Runtime_Result : std::uint8_t { initialized, invalid_worker_count, exception_boundary_missing, exception_boundary_unexpected, already_initialized, initialization_in_progress };
|
||||
enum struct Take_Taskflow_Observation_Result : std::uint8_t { not_recorded, recording, already_taken };
|
||||
using Taskflow_Completion = std::function<void(std::exception_ptr)>;
|
||||
using Taskflow_Exception_Boundary = std::function<void(std::exception_ptr)>;
|
||||
namespace task_flow {
|
||||
struct Task_Node : facade_builder::add_skill<pro::skills::rtti>::build {};
|
||||
struct Task_Graph_Component : facade_builder::add_skill<pro::skills::rtti>::build {};
|
||||
} // namespace task_flow
|
||||
} // namespace aethera
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "Task_Graph_Types.hpp"
|
||||
#include "../export/export.h"
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "Task_Graph.hpp"
|
||||
#include "../export/export.h"
|
||||
#include "model/Model.hpp"
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace aethera::task_flow {
|
||||
struct Task_Graph_Model : Def<Task_Graph_Model, Root> {
|
||||
struct Private;
|
||||
explicit Task_Graph_Model(std::shared_ptr<aethera::Task_Graph> graph) : Def(std::move(graph)) {}
|
||||
};
|
||||
|
||||
struct Task_Graph_Model::Private : Prev_Private {
|
||||
explicit Private(std::shared_ptr<aethera::Task_Graph> graph) : graph(std::move(graph)) {}
|
||||
auto add(std::string name, std::function<void()> work) -> std::expected<proxy<Task_Node>, Add_Task_Graph_Result>;
|
||||
proxy<Task_Graph_Component> component();
|
||||
auto compose(std::string name, proxy<Task_Graph_Component>& child) -> std::expected<proxy<Task_Node>, Compose_Task_Graph_Result>;
|
||||
Precede_Task_Graph_Result precede(proxy<Task_Node>& before, proxy<Task_Node>& after);
|
||||
void clear();
|
||||
bool empty() const noexcept;
|
||||
Run_Taskflow_Result run(Taskflow_Completion completion);
|
||||
private:
|
||||
std::shared_ptr<aethera::Task_Graph> graph; /* 所有消费 facade 共享的唯一权威任务图。 */
|
||||
};
|
||||
|
||||
template <typename Facade> proxy<Facade> make_task_graph(std::string name) { return make_model_proxy<Facade, Task_Graph_Model>(std::make_shared<aethera::Task_Graph>(std::move(name))); }
|
||||
} // namespace aethera::task_flow
|
||||
+1
-9
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include "Task_Graph.hpp"
|
||||
#include "Task_Runtime_Types.hpp"
|
||||
#include "../export/export.h"
|
||||
#include "Taskflow_Observation.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -8,14 +8,6 @@
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
namespace aethera {
|
||||
enum struct Initialize_Task_Runtime_Result : std::uint8_t {
|
||||
initialized,
|
||||
invalid_worker_count,
|
||||
exception_boundary_missing,
|
||||
exception_boundary_unexpected,
|
||||
already_initialized,
|
||||
initialization_in_progress
|
||||
};
|
||||
Initialize_Task_Runtime_Result initialize_task_runtime(std::size_t workers = std::thread::hardware_concurrency(), Taskflow_Unknown_Failure_Policy failure_policy = Taskflow_Unknown_Failure_Policy::fast_fail, Taskflow_Exception_Boundary exception_boundary = {});
|
||||
/* 完成回调接收业务任务传播出的 Unknown Failure;本次执行不记录观察数据。 */
|
||||
Run_Taskflow_Result run_taskflow(
|
||||
+1
-5
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "../export/export.h"
|
||||
#include "Taskflow_Trace.hpp"
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
@@ -8,11 +9,6 @@ namespace aethera {
|
||||
namespace detail {
|
||||
struct Taskflow_Observation_Execution;
|
||||
}
|
||||
enum struct Take_Taskflow_Observation_Result : std::uint8_t {
|
||||
not_recorded,
|
||||
recording,
|
||||
already_taken
|
||||
};
|
||||
/*
|
||||
* 前台诊断显式请求的单次 Taskflow 执行记录,例如连续请求 N 帧时为每帧分别创建一个对象。
|
||||
* 它只给帧附加原始任务执行信息,不属于业务状态、持续事件流或可复用快照;未传入执行接口时不记录。
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include "../Task_Graph.hpp"
|
||||
#include "../Task_Runtime_Types.hpp"
|
||||
#include "../../export/export.h"
|
||||
#include "../Taskflow_Observation.hpp"
|
||||
#include "../Taskflow_Trace.hpp"
|
||||
#include <chrono>
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "Task_Graph_Model.hpp"
|
||||
#include "Task_Runtime.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace aethera::task_flow {
|
||||
namespace detail {
|
||||
struct Proxied_Task_Node { explicit Proxied_Task_Node(aethera::Task_Node value) : value(std::move(value)) {} aethera::Task_Node value; };
|
||||
struct Proxied_Task_Graph_Component { explicit Proxied_Task_Graph_Component(std::shared_ptr<aethera::Task_Graph> value) : value(std::move(value)) {} std::shared_ptr<aethera::Task_Graph> value; };
|
||||
} // namespace detail
|
||||
|
||||
auto Task_Graph_Model::Private::add(std::string name, std::function<void()> work) -> std::expected<proxy<Task_Node>, Add_Task_Graph_Result> {
|
||||
auto result = graph->add(std::move(name), std::move(work));
|
||||
if (!result) return std::unexpected(result.error());
|
||||
return pro::make_proxy<Task_Node, detail::Proxied_Task_Node>(std::move(*result));
|
||||
}
|
||||
|
||||
proxy<Task_Graph_Component> Task_Graph_Model::Private::component() { return pro::make_proxy<Task_Graph_Component, detail::Proxied_Task_Graph_Component>(graph); }
|
||||
|
||||
auto Task_Graph_Model::Private::compose(std::string name, proxy<Task_Graph_Component>& child) -> std::expected<proxy<Task_Node>, Compose_Task_Graph_Result> {
|
||||
auto* component = child ? proxy_cast<detail::Proxied_Task_Graph_Component>(&*child) : nullptr;
|
||||
if (component == nullptr) return std::unexpected(Compose_Task_Graph_Result::component_unavailable);
|
||||
auto result = graph->compose(std::move(name), *component->value);
|
||||
if (!result) return std::unexpected(result.error());
|
||||
return pro::make_proxy<Task_Node, detail::Proxied_Task_Node>(std::move(*result));
|
||||
}
|
||||
|
||||
Precede_Task_Graph_Result Task_Graph_Model::Private::precede(proxy<Task_Node>& before, proxy<Task_Node>& after) {
|
||||
auto* before_node = before ? proxy_cast<detail::Proxied_Task_Node>(&*before) : nullptr;
|
||||
auto* after_node = after ? proxy_cast<detail::Proxied_Task_Node>(&*after) : nullptr;
|
||||
if (before_node == nullptr || after_node == nullptr) return Precede_Task_Graph_Result::node_unavailable;
|
||||
return before_node->value.precede(after_node->value);
|
||||
}
|
||||
|
||||
void Task_Graph_Model::Private::clear() { graph->clear(); }
|
||||
bool Task_Graph_Model::Private::empty() const noexcept { return graph->empty(); }
|
||||
Run_Taskflow_Result Task_Graph_Model::Private::run(Taskflow_Completion completion) { return run_taskflow(*graph, std::move(completion)); }
|
||||
} // namespace aethera::task_flow
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
#include "task_flow/Task_Runtime.hpp"
|
||||
#include "task_flow/detail/Taskflow_Execution.ipp"
|
||||
#include "task_flow/src/Task_Runtime.hpp"
|
||||
#include "../src/detail/Taskflow_Execution.ipp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
namespace aethera {
|
||||
using Timer_Id = std::uint64_t;
|
||||
using Timer_Callback = std::function<void()>;
|
||||
using Timer_Time_Point = std::chrono::steady_clock::time_point;
|
||||
enum struct Make_Timer_Service_Result : std::uint8_t { time_source_unavailable };
|
||||
} // namespace aethera
|
||||
+12
-19
@@ -1,8 +1,7 @@
|
||||
#include "Timer_Service.hpp"
|
||||
#include "Timer_Service_Model.hpp"
|
||||
|
||||
#include "detail/Timer_Scheduler.hpp"
|
||||
#include "function/frame_policy/rely_facade.h"
|
||||
#include "model/Model.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
@@ -22,7 +21,10 @@ struct Steady_Timer_Time_Source : Def<Steady_Timer_Time_Source, Root> {
|
||||
struct Steady_Timer_Time_Source::Private : Prev_Private {
|
||||
Timer_Time_Point now() const noexcept { return std::chrono::steady_clock::now(); }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace time_thread {
|
||||
namespace detail = aethera::detail;
|
||||
struct Thread_Timer_Service_State : Immovable {
|
||||
struct Impl {
|
||||
static constexpr std::chrono::seconds Maximum_Wait{1};
|
||||
@@ -189,19 +191,13 @@ struct Thread_Timer_Service_State : Immovable {
|
||||
|
||||
std::shared_ptr<Impl> impl; /* proxy 与计时线程共享的运行状态所有权。 */
|
||||
};
|
||||
struct Thread_Timer_Service : Def<Thread_Timer_Service, Root> {
|
||||
struct Private;
|
||||
explicit Thread_Timer_Service(proxy<Timer_Time_Source> time_source) : Def(std::move(time_source)) {}
|
||||
};
|
||||
struct Thread_Timer_Service::Private : Prev_Private {
|
||||
explicit Private(proxy<Timer_Time_Source> time_source) : state(std::move(time_source)) {}
|
||||
Timer_Id schedule_after(std::chrono::nanoseconds delay, Timer_Callback callback) { return state.schedule_after(delay, std::move(callback)); }
|
||||
Timer_Id schedule_every(std::chrono::nanoseconds interval, Timer_Callback callback) { return state.schedule_every(interval, std::move(callback)); }
|
||||
void reschedule(Timer_Id id, std::chrono::nanoseconds delay) { state.reschedule(id, delay); }
|
||||
void cancel(Timer_Id id, Timer_Callback completion) { state.cancel(id, std::move(completion)); }
|
||||
Thread_Timer_Service_State state;
|
||||
};
|
||||
}
|
||||
} // namespace time_thread
|
||||
|
||||
Timer_Id time_thread::Timer_Service_Model::Private::schedule_after(std::chrono::nanoseconds delay, Timer_Callback callback) { return state->schedule_after(delay, std::move(callback)); }
|
||||
Timer_Id time_thread::Timer_Service_Model::Private::schedule_every(std::chrono::nanoseconds interval, Timer_Callback callback) { return state->schedule_every(interval, std::move(callback)); }
|
||||
void time_thread::Timer_Service_Model::Private::reschedule(Timer_Id id, std::chrono::nanoseconds delay) { state->reschedule(id, delay); }
|
||||
void time_thread::Timer_Service_Model::Private::cancel(Timer_Id id, Timer_Callback completion) { state->cancel(id, std::move(completion)); }
|
||||
std::shared_ptr<time_thread::Thread_Timer_Service_State> time_thread::make_timer_service_state(proxy<Timer_Time_Source> time_source) { return std::make_shared<Thread_Timer_Service_State>(std::move(time_source)); }
|
||||
|
||||
proxy<Timer_Time_Source> make_steady_timer_time_source() { return make_model_proxy<Timer_Time_Source, Steady_Timer_Time_Source>(); }
|
||||
|
||||
@@ -211,9 +207,6 @@ make_timer_service(proxy<Timer_Time_Source> time_source) {
|
||||
return std::unexpected(
|
||||
Make_Timer_Service_Result::time_source_unavailable);
|
||||
}
|
||||
return make_model_proxy_shared<Timer_Service, Thread_Timer_Service>(std::move(time_source));
|
||||
}
|
||||
proxy<frame_policy::Timer_Service> frame_policy::make_timer_service() {
|
||||
return make_model_proxy_shared<frame_policy::Timer_Service, Thread_Timer_Service>(make_steady_timer_time_source());
|
||||
return time_thread::make_timer_service_proxy<Timer_Service>(std::move(time_source));
|
||||
}
|
||||
}
|
||||
+1
-8
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../export/export.h"
|
||||
#include "Timer_Time_Source.hpp"
|
||||
|
||||
#include <chrono>
|
||||
@@ -9,10 +10,6 @@
|
||||
#include <proxy/proxy.h>
|
||||
|
||||
namespace aethera {
|
||||
using Timer_Id = std::uint64_t;
|
||||
using Timer_Callback = std::function<void()>;
|
||||
|
||||
|
||||
struct Timer_Service : facade_builder
|
||||
::add_convention<
|
||||
_schedule_every,
|
||||
@@ -29,10 +26,6 @@ struct Timer_Service : facade_builder
|
||||
::support_relocation<pro::constraint_level::nothrow>
|
||||
::build {};
|
||||
|
||||
enum struct Make_Timer_Service_Result : std::uint8_t {
|
||||
time_source_unavailable
|
||||
};
|
||||
|
||||
[[nodiscard]] std::expected<proxy<Timer_Service>, Make_Timer_Service_Result>
|
||||
make_timer_service(proxy<Timer_Time_Source> time_source);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "Timer_Service.hpp"
|
||||
#include "model/Model.hpp"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace aethera::time_thread {
|
||||
struct Thread_Timer_Service_State;
|
||||
|
||||
struct Timer_Service_Model : Def<Timer_Service_Model, Root> {
|
||||
struct Private;
|
||||
explicit Timer_Service_Model(std::shared_ptr<Thread_Timer_Service_State> state) : Def(std::move(state)) {}
|
||||
};
|
||||
|
||||
struct Timer_Service_Model::Private : Prev_Private {
|
||||
explicit Private(std::shared_ptr<Thread_Timer_Service_State> state) : state(std::move(state)) {}
|
||||
Timer_Id schedule_after(std::chrono::nanoseconds delay, Timer_Callback callback);
|
||||
Timer_Id schedule_every(std::chrono::nanoseconds interval, Timer_Callback callback);
|
||||
void reschedule(Timer_Id id, std::chrono::nanoseconds delay);
|
||||
void cancel(Timer_Id id, Timer_Callback completion);
|
||||
private:
|
||||
std::shared_ptr<Thread_Timer_Service_State> state; /* facade 与计时线程共享同一运行状态所有权。 */
|
||||
};
|
||||
|
||||
std::shared_ptr<Thread_Timer_Service_State> make_timer_service_state(proxy<Timer_Time_Source> time_source);
|
||||
template <typename Facade> proxy<Facade> make_timer_service_proxy(proxy<Timer_Time_Source> time_source) { return make_model_proxy_shared<Facade, Timer_Service_Model>(make_timer_service_state(std::move(time_source))); }
|
||||
} // namespace aethera::time_thread
|
||||
+1
-4
@@ -1,15 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../export/export.h"
|
||||
#include "global.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <proxy/proxy.h>
|
||||
|
||||
namespace aethera {
|
||||
using Timer_Time_Point = std::chrono::steady_clock::time_point;
|
||||
|
||||
|
||||
|
||||
struct Timer_Time_Source : facade_builder::add_convention<_now, Timer_Time_Point() const noexcept>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
|
||||
[[nodiscard]] proxy<Timer_Time_Source> make_steady_timer_time_source();
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "time_thread/Timer_Service.hpp"
|
||||
#include "time_thread/Timer_Time_Source.hpp"
|
||||
#include "../Timer_Service.hpp"
|
||||
#include "../Timer_Time_Source.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
#include "time_thread/Timer_Service.hpp"
|
||||
#include "time_thread/Timer_Time_Source.hpp"
|
||||
#include "time_thread/detail/Timer_Scheduler.hpp"
|
||||
#include "time_thread/src/Timer_Service.hpp"
|
||||
#include "time_thread/src/Timer_Time_Source.hpp"
|
||||
#include "../src/detail/Timer_Scheduler.hpp"
|
||||
#include "model/Model.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -1,22 +1,14 @@
|
||||
#include "concurrent/base/Concurrent_List.hpp"
|
||||
#include "../../include/function/renderable/renderable.hpp"
|
||||
#include "../../include/function/scene/Scene.hpp"
|
||||
#include "model/Model.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
PRO_DEF_MEM_DISPATCH(Test_Model_Private_Read, private_value);
|
||||
struct Test_Model_Private_Facade : aethera::facade_builder::add_convention<Test_Model_Private_Read, int() const>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
PRO_DEF_MEM_DISPATCH(Other_Test_Model_Private_Read, other_private_value);
|
||||
struct Other_Test_Model_Private_Facade : aethera::facade_builder::add_convention<Other_Test_Model_Private_Read, int() const>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
struct Test_Model_Prop {
|
||||
int base_number{};
|
||||
int number{};
|
||||
int other{};
|
||||
};
|
||||
struct Test_Model_Prop { int base_number{}; int number{}; int other{}; };
|
||||
struct Test_Model_Numbers : std::vector<int> {};
|
||||
struct Test_Model_Base : aethera::Model_Layer<aethera::Root, aethera::Storage_Registration<Test_Model_Prop, aethera::Import_Struct_With_Dirty>, aethera::Storage_Registration<Test_Model_Numbers, aethera::Import_List>> {
|
||||
using Prop = Test_Model_Prop;
|
||||
@@ -25,30 +17,11 @@ struct Test_Model_Base : aethera::Model_Layer<aethera::Root, aethera::Storage_Re
|
||||
struct Builder : Prev_Builder {};
|
||||
};
|
||||
struct Test_Model : aethera::Def<Test_Model, Test_Model_Base> {
|
||||
struct Private : Prev_Private {
|
||||
int private_value() const { return 41; }
|
||||
int other_private_value() const { return 43; }
|
||||
};
|
||||
struct Private : Prev_Private { int private_value() const { return 41; } int other_private_value() const { return 43; } };
|
||||
};
|
||||
struct Test_Renderable : aethera::Def<Test_Renderable, aethera::Renderable<Test_Renderable>> {
|
||||
struct Private : Prev_Private {
|
||||
void advance_renderable() { ++advance_count; auto& graph = internal_taskflow(); graph->clear(); (void)graph->add("test.prepare", [] {}); }
|
||||
int advance_count{};
|
||||
};
|
||||
};
|
||||
struct Test_Dag_Renderable : aethera::Def<Test_Dag_Renderable, aethera::Renderable<Test_Dag_Renderable>> {
|
||||
Test_Dag_Renderable(std::shared_ptr<std::vector<int>> advance_order, int id) : Def(std::move(advance_order), id) {}
|
||||
struct Private : Prev_Private {
|
||||
Private(std::shared_ptr<std::vector<int>> advance_order, int id) : advance_order(std::move(advance_order)), id(id) {}
|
||||
void advance_renderable() { advance_order->push_back(id); auto& graph = internal_taskflow(); graph->clear(); (void)graph->add("prepare", [] {}); }
|
||||
std::shared_ptr<std::vector<int>> advance_order;
|
||||
int id{};
|
||||
};
|
||||
};
|
||||
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<Test_Renderable*>, aethera::scene::Renderable>);
|
||||
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<aethera::Scene*>, aethera::frame_policy::Scene>);
|
||||
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<Test_Model*>, Test_Model_Private_Facade>);
|
||||
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<Test_Model*>, Other_Test_Model_Private_Facade>);
|
||||
|
||||
TEST(Model, Registered_Value_Types_Build_Independent_Storages) {
|
||||
Test_Model::Builder builder;
|
||||
int struct_initializer_calls{};
|
||||
@@ -68,6 +41,7 @@ TEST(Model, Registered_Value_Types_Build_Independent_Storages) {
|
||||
numbers.internal.advance();
|
||||
EXPECT_EQ(*numbers.internal.use(), (std::vector<int>{13, 17}));
|
||||
}
|
||||
|
||||
TEST(Model, Registered_Struct_Write_Publishes_To_The_Same_Tagged_Storage) {
|
||||
auto model = Test_Model::Builder{}.build();
|
||||
model->write<aethera::Import_Struct_With_Dirty, Test_Model_Prop>(&Test_Model_Prop::number, 13);
|
||||
@@ -75,91 +49,11 @@ TEST(Model, Registered_Struct_Write_Publishes_To_The_Same_Tagged_Storage) {
|
||||
properties.internal.advance();
|
||||
EXPECT_EQ(properties.internal.use()->number, 13);
|
||||
}
|
||||
|
||||
TEST(Model, Consumer_Facade_Selects_One_Model_Private_View) {
|
||||
auto model = aethera::make_model_proxy<Test_Model_Private_Facade, Test_Model>();
|
||||
EXPECT_EQ(model->private_value(), 41);
|
||||
auto other_model = aethera::make_model_proxy<Other_Test_Model_Private_Facade, Test_Model>();
|
||||
EXPECT_EQ(other_model->other_private_value(), 43);
|
||||
}
|
||||
TEST(Model, Renderable_Facade_Uses_Private_Task_Graph_Capabilities) {
|
||||
auto renderable = aethera::make_model_proxy<aethera::scene::Renderable, Test_Renderable>();
|
||||
renderable->advance();
|
||||
auto graph = renderable->taskflow();
|
||||
EXPECT_TRUE(graph);
|
||||
}
|
||||
TEST(Model, Scene_Advances_Renderables_And_Composes_Task_Graph_In_Dag_Order) {
|
||||
auto advance_order = std::make_shared<std::vector<int>>();
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder builder;
|
||||
builder.add({2}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 2));
|
||||
builder.add({1}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 1));
|
||||
builder.add({3}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 3));
|
||||
builder.add_dependency({2}, {1});
|
||||
builder.add_dependency({3}, {1});
|
||||
auto graph = builder.build();
|
||||
ASSERT_TRUE(graph.has_value());
|
||||
aethera::Scene scene;
|
||||
EXPECT_TRUE(scene.d->create_frame());
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>(std::move(*graph));
|
||||
EXPECT_TRUE(scene.d->advance().has_value());
|
||||
EXPECT_EQ(*advance_order, (std::vector<int>{1, 2, 3}));
|
||||
EXPECT_FALSE(scene.d->taskflow()->empty());
|
||||
advance_order->clear();
|
||||
std::optional<aethera::Edit_Dag_Result> edit_error;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>([](aethera::Dag<aethera::scene::Renderable>::Editor& editor) { editor.remove({99}); }, [&](std::expected<void, aethera::Edit_Dag_Result> result) { if (!result) edit_error = result.error(); });
|
||||
EXPECT_TRUE(scene.d->advance().has_value());
|
||||
ASSERT_TRUE(edit_error.has_value());
|
||||
EXPECT_EQ(*edit_error, aethera::Edit_Dag_Result::node_not_found);
|
||||
EXPECT_EQ(*advance_order, (std::vector<int>{1, 2, 3}));
|
||||
}
|
||||
TEST(Model, Dag_Rejects_Unallocated_Id_And_Missing_Object) {
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder invalid_id_builder;
|
||||
invalid_id_builder.add({0}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Renderable>());
|
||||
const auto invalid_id = invalid_id_builder.build();
|
||||
ASSERT_FALSE(invalid_id.has_value());
|
||||
EXPECT_EQ(invalid_id.error(), aethera::Build_Dag_Result::invalid_node_id);
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder missing_object_builder;
|
||||
missing_object_builder.add({1}, {});
|
||||
const auto missing_object = missing_object_builder.build();
|
||||
ASSERT_FALSE(missing_object.has_value());
|
||||
EXPECT_EQ(missing_object.error(), aethera::Build_Dag_Result::node_object_missing);
|
||||
}
|
||||
TEST(Model, Owned_Dag_Rolls_Back_The_Whole_Invalid_Edit) {
|
||||
auto advance_order = std::make_shared<std::vector<int>>();
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder builder;
|
||||
builder.add({1}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 1));
|
||||
auto graph = builder.build();
|
||||
ASSERT_TRUE(graph.has_value());
|
||||
aethera::Scene scene;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>(std::move(*graph));
|
||||
ASSERT_TRUE(scene.d->advance().has_value());
|
||||
advance_order->clear();
|
||||
std::optional<aethera::Edit_Dag_Result> edit_error;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>([advance_order](aethera::Dag<aethera::scene::Renderable>::Editor& editor) { editor.add({2}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Dag_Renderable>(advance_order, 2)); editor.add_dependency({2}, {99}); }, [&](std::expected<void, aethera::Edit_Dag_Result> result) { if (!result) edit_error = result.error(); });
|
||||
ASSERT_TRUE(scene.d->advance().has_value());
|
||||
ASSERT_TRUE(edit_error.has_value());
|
||||
EXPECT_EQ(*edit_error, aethera::Edit_Dag_Result::node_not_found);
|
||||
EXPECT_EQ(*advance_order, (std::vector<int>{1}));
|
||||
}
|
||||
TEST(Model, Scene_Render_Returns_Known_Submission_Result) {
|
||||
aethera::Scene scene;
|
||||
auto frame = scene.d->create_frame();
|
||||
bool completed{};
|
||||
EXPECT_EQ(scene.d->render(frame, [&](aethera::proxy<aethera::frame_policy::Frame>&, std::exception_ptr) { completed = true; }), aethera::frame_policy::Render_Scene_Result::runtime_not_initialized);
|
||||
EXPECT_FALSE(completed);
|
||||
aethera::proxy<aethera::frame_policy::Frame> empty_frame;
|
||||
EXPECT_EQ(scene.d->render(empty_frame, [&](aethera::proxy<aethera::frame_policy::Frame>&, std::exception_ptr) {}), aethera::frame_policy::Render_Scene_Result::frame_missing);
|
||||
EXPECT_EQ(scene.d->render(frame, {}), aethera::frame_policy::Render_Scene_Result::completion_missing);
|
||||
}
|
||||
TEST(Model, Scene_Rejects_The_Same_Renderable_Task_Graph_Twice) {
|
||||
Test_Renderable renderable;
|
||||
aethera::Dag<aethera::scene::Renderable>::Builder builder;
|
||||
builder.add({1}, aethera::model_proxy_view<aethera::scene::Renderable>(renderable));
|
||||
builder.add({2}, aethera::model_proxy_view<aethera::scene::Renderable>(renderable));
|
||||
auto graph = builder.build();
|
||||
ASSERT_TRUE(graph.has_value());
|
||||
aethera::Scene scene;
|
||||
scene.write<aethera::Owned_Dag, aethera::Scene_Renderable_Dag>(std::move(*graph));
|
||||
auto frame = scene.d->create_frame();
|
||||
EXPECT_EQ(scene.d->render(frame, [](aethera::proxy<aethera::frame_policy::Frame>&, std::exception_ptr) {}), aethera::frame_policy::Render_Scene_Result::task_graph_invalid);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
+17
-12
@@ -25,27 +25,35 @@ if (Aethera_BUILD_TESTS)
|
||||
find_package(benchmark CONFIG REQUIRED)
|
||||
endif ()
|
||||
set(Aethera_Kernel_source_dir "${CMAKE_CURRENT_LIST_DIR}/kernel/include")
|
||||
set(Aethera_Kernel_module_dir "${CMAKE_CURRENT_LIST_DIR}/kernel/module")
|
||||
set(Aethera_concurrentqueue_dir "${CMAKE_CURRENT_LIST_DIR}/third_party/concurrentqueue-1.0.5")
|
||||
add_subdirectory("${Aethera_concurrentqueue_dir}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/aethera_concurrentqueue" EXCLUDE_FROM_ALL)
|
||||
append_glob_source(Aethera_Kernel_sources "${Aethera_Kernel_source_dir}")
|
||||
add_library(Aethera_Kernel STATIC ${Aethera_Kernel_sources})
|
||||
target_include_directories(Aethera_Kernel PUBLIC
|
||||
append_glob_source(Aethera_Kernel_Core_sources "${Aethera_Kernel_source_dir}")
|
||||
add_library(Aethera_Kernel_Core STATIC ${Aethera_Kernel_Core_sources})
|
||||
target_include_directories(Aethera_Kernel_Core PUBLIC
|
||||
"$<BUILD_INTERFACE:${Aethera_Kernel_source_dir}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/third_party>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/third_party/ratas-master/src>"
|
||||
)
|
||||
target_compile_features(Aethera_Kernel PUBLIC cxx_std_20)
|
||||
target_link_libraries(Aethera_Kernel PUBLIC
|
||||
Taskflow::Taskflow
|
||||
target_compile_features(Aethera_Kernel_Core PUBLIC cxx_std_20)
|
||||
target_link_libraries(Aethera_Kernel_Core PUBLIC
|
||||
magic_enum::magic_enum
|
||||
msft_proxy4::proxy
|
||||
"$<BUILD_INTERFACE:concurrentqueue>"
|
||||
)
|
||||
target_link_libraries(Aethera_Kernel PRIVATE Threads::Threads)
|
||||
if (MSVC)
|
||||
target_compile_options(Aethera_Kernel PUBLIC /utf-8)
|
||||
target_compile_options(Aethera_Kernel_Core PUBLIC /utf-8)
|
||||
endif ()
|
||||
include("${Aethera_Kernel_module_dir}/main.cmake")
|
||||
add_library(Aethera_Kernel INTERFACE)
|
||||
target_link_libraries(Aethera_Kernel INTERFACE
|
||||
Aethera_Kernel_Core
|
||||
Aethera_Kernel_Task_Flow
|
||||
Aethera_Kernel_Time_Thread
|
||||
Aethera_Kernel_Renderable
|
||||
Aethera_Kernel_Scene
|
||||
Aethera_Kernel_Frame_Policy)
|
||||
if (Aethera_BUILD_TESTS)
|
||||
set(Aethera_Kernel_test_dir "${CMAKE_CURRENT_LIST_DIR}/kernel/test")
|
||||
set(Aethera_Kernel_cdb
|
||||
@@ -57,10 +65,7 @@ if (Aethera_BUILD_TESTS)
|
||||
|
||||
append_glob_source(Aethera_Kernel_module_test_sources
|
||||
"${Aethera_Kernel_test_dir}/concurrent"
|
||||
"${Aethera_Kernel_test_dir}/model"
|
||||
"${Aethera_Kernel_test_dir}/task_flow"
|
||||
"${Aethera_Kernel_test_dir}/time_thread"
|
||||
"${Aethera_Kernel_test_dir}/function/frame_policy")
|
||||
"${Aethera_Kernel_test_dir}/model")
|
||||
foreach (Aethera_Kernel_module_test_source IN LISTS Aethera_Kernel_module_test_sources)
|
||||
if (NOT Aethera_Kernel_module_test_source MATCHES "\\.(c|cc|cpp|cxx)$")
|
||||
continue()
|
||||
|
||||
Reference in New Issue
Block a user