diff --git a/AGENTS.md b/AGENTS.md index 41ed756..329b42c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,7 @@ 函数定义 函数调用 等参数不允许换行 注意所有facade用到的函数名都定义在这里 D: \ae\proj\Aethera\kernel\kernel\include\global.hpp 这样重复的名称不用重复写 光写facade即可 +枚举定义要换行 加注释 facade_builder 定义也要换行 `` 除非特别说明不允许写任何同步等待代码。 除非特别说明不允许写任何同步等待代码。 @@ -26,7 +27,9 @@ 除非特别说明不允许写任何 快照, 任何线程问题先完了都要给我说明 我来审核。 `` -跨模块多态对象统一使用 proxy 表达,proxy 的实际所有权由构造时承载的指针类型决定:裸指针为非拥有借用,make_proxy/unique_ptr 为独占所有权,make_proxy_shared/shared_ptr 为共享所有权,proxy_view 为非拥有观察,weak_proxy 为弱所有权。接口和字段注释必须写明实际所有权与生命周期,不再强制使用 not_null 或 owner 标注普通指针。 +跨模块多态对象统一使用 proxy 表达,proxy 的实际所有权由构造时承载的指针类型决定:裸指针为非拥有借用,make_proxy/unique_ptr +为独占所有权,make_proxy_shared/shared_ptr 为共享所有权,proxy_view 为非拥有观察,weak_proxy +为弱所有权。接口和字段注释必须写明实际所有权与生命周期,不再强制使用 not_null 或 owner 标注普通指针。 我使用 里的这个环境 D:\ae\proj\Aethera\CMakePresets.json "toolchain/vs2022.json" ====================[ 构建 | Aethera_Kernel_check | vs2022_debug ]================ diff --git a/kernel/kernel/module/frame_policy/export/export.h b/kernel/kernel/module/frame_policy/export/export.h index 15cb846..d4d234b 100644 --- a/kernel/kernel/module/frame_policy/export/export.h +++ b/kernel/kernel/module/frame_policy/export/export.h @@ -1,12 +1,24 @@ #pragma once #include - +#include 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 +enum struct Frame_Policy_Configuration_Result : std::uint8_t { + invalid_user_frame_rate, + interval_out_of_range, + invalid_statistics_window +}; +enum struct Throttled_Latest_Only_Start_State_Result : std::uint8_t { + already_running, + start_in_progress, + stop_in_progress, + dependency_unavailable, + frame_unavailable +}; +using Throttled_Latest_Only_Start_Result = std::variant; +enum struct Throttled_Latest_Only_Stop_Result : std::uint8_t { + already_stopped, + already_stopping, + start_in_progress, + completion_missing +}; } // namespace aethera diff --git a/kernel/kernel/module/frame_policy/rely_facade.h b/kernel/kernel/module/frame_policy/rely_facade.h index 2083f11..844d9ae 100644 --- a/kernel/kernel/module/frame_policy/rely_facade.h +++ b/kernel/kernel/module/frame_policy/rely_facade.h @@ -1,19 +1,17 @@ #pragma once #include "export/export.h" #include "global.hpp" +#include "scene/export/export.h" #include "time_thread/export/export.h" -#include #include #include namespace aethera::frame_policy { using Timer_Id = aethera::Timer_Id; using Timer_Callback = aethera::Timer_Callback; -struct Frame : facade_builder::build {}; -using Frame_Completion = std::function&)>; -using Render_Completion = std::function&, std::exception_ptr)>; -struct Scene : facade_builder::add_convention<_create_frame, proxy()>::add_convention<_render, Render_Scene_Result(proxy&, Render_Completion)>::support_relocation::build {}; -struct Sink : facade_builder::add_convention<_send, void(proxy&, Frame_Completion)>::support_relocation::build {}; +using Frame_Completion = std::function&)>; +struct Scene : facade_builder::add_convention<_create_frame, proxy()>::add_convention<_render, scene::Render_Result(proxy&, scene::Render_Completion)>::support_relocation::build {}; +struct Sink : facade_builder::add_convention<_send, void(proxy&, Frame_Completion)>::support_relocation::build {}; using Timer_Service = time_thread::Timer_Service_Frame_Facade; proxy make_timer_service(); } // namespace aethera::frame_policy diff --git a/kernel/kernel/module/frame_policy/src/Frame_Policy.hpp b/kernel/kernel/module/frame_policy/src/Frame_Policy.hpp index 9c59a4e..964f837 100644 --- a/kernel/kernel/module/frame_policy/src/Frame_Policy.hpp +++ b/kernel/kernel/module/frame_policy/src/Frame_Policy.hpp @@ -1,5 +1,6 @@ #pragma once #include "../export/export.h" +#include "scene/export/export.h" #include "concurrent/base/Concurrent_Struct.hpp" #include "model/Model.hpp" #include "statistics/Sliding_Statistics.hpp" @@ -35,7 +36,7 @@ struct Frame_Policy_State { Statistics_Summary send_time_ns; /* 发送耗时滑动统计,单位纳秒。 */ Statistics_Summary end_to_end_time_ns; /* 渲染开始至发送完成耗时统计,单位纳秒。 */ std::optional configuration_error; /* 动态配置无效时记录原因;有效时为空。 */ - std::optional render_submission_error; /* Scene 拒绝最近一次渲染提交时的策略级原因;下一次提交成功后清空。 */ + std::optional render_submission_error; /* Scene 拒绝最近一次渲染提交时的原始原因;下一次提交成功后清空。 */ }; } // namespace aethera #include "Frame_Policy.ipp" diff --git a/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.cpp b/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.cpp index 12be7fd..2c1891c 100644 --- a/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.cpp +++ b/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.cpp @@ -1,5 +1,6 @@ #include "Throttled_Latest_Only.hpp" #include +#include #include #include namespace aethera { @@ -7,16 +8,6 @@ namespace { auto positive_duration(std::chrono::steady_clock::duration duration) noexcept -> std::chrono::nanoseconds { return std::max(std::chrono::nanoseconds{1}, std::chrono::duration_cast(duration)); } -auto start_result(Frame_Policy::Configuration_Result result) noexcept -> Throttled_Latest_only::Start_Result { - using Configuration_Result = Frame_Policy::Configuration_Result; - using Start_Result = Throttled_Latest_only::Start_Result; - switch (result) { - case Configuration_Result::invalid_user_frame_rate: return Start_Result::invalid_user_frame_rate; - case Configuration_Result::interval_out_of_range: return Start_Result::interval_out_of_range; - case Configuration_Result::invalid_statistics_window: return Start_Result::invalid_statistics_window; - } - std::terminate(); -} } // namespace Throttled_Latest_only::Throttled_Latest_only(proxy timer_service, proxy scene, proxy sink) : Def(std::move(timer_service), std::move(scene), std::move(sink)) {} Throttled_Latest_only::~Throttled_Latest_only() noexcept { @@ -53,23 +44,23 @@ auto Throttled_Latest_only::Private::prepare_start() -> std::expected, Frame_Count> created_frames; + std::array, Frame_Count> created_frames; try { for (auto& created_frame : created_frames) { created_frame = scene->create_frame(); if (!created_frame) { rollback_start(); - return std::unexpected(Start_Result::frame_unavailable); + return std::unexpected(Start_Result{Start_State_Result::frame_unavailable}); } } std::lock_guard lock(mutex); @@ -84,7 +75,7 @@ auto Throttled_Latest_only::Private::prepare_start() -> std::expected std::expected, Frame_Count> retired_frames; + std::array, Frame_Count> retired_frames; { std::lock_guard lock(mutex); if (phase != Phase::starting || timer_id) { @@ -220,7 +211,7 @@ void Throttled_Latest_only::Private::frame_due() { return; } try { - const auto result = scene->render(frames[selected_index].frame, [this, selected_index](proxy& completed_frame, std::exception_ptr failure) { + const auto result = scene->render(frames[selected_index].frame, [this, selected_index](proxy& completed_frame, std::exception_ptr failure) { if (failure) { abandon_render(selected_index); std::rethrow_exception(failure); @@ -228,29 +219,30 @@ void Throttled_Latest_only::Private::frame_due() { rendered(selected_index, completed_frame); }); record_render_submission(result); - if (result != frame_policy::Render_Scene_Result::submitted) abandon_render(selected_index); + if (!result) abandon_render(selected_index); } catch (...) { abandon_render(selected_index); throw; } } -void Throttled_Latest_only::Private::record_render_submission(frame_policy::Render_Scene_Result result) { +void Throttled_Latest_only::Private::record_render_submission(scene::Render_Result result) { std::lock_guard lock(mutex); auto& state = *get().internal.use(); - switch (result) { - case frame_policy::Render_Scene_Result::submitted: state.render_submission_error.reset(); - break; - case frame_policy::Render_Scene_Result::runtime_not_initialized: state.render_submission_error = Frame_Policy_Render_Submission_Result::runtime_not_initialized; - break; - case frame_policy::Render_Scene_Result::task_graph_invalid: state.render_submission_error = Frame_Policy_Render_Submission_Result::task_graph_invalid; - break; - case frame_policy::Render_Scene_Result::frame_missing: - case frame_policy::Render_Scene_Result::completion_missing: std::terminate(); + if (result) state.render_submission_error.reset(); + else { + const bool impossible = std::visit([](auto error) { + using Error = decltype(error); + if constexpr (std::same_as) return true; + if constexpr (std::same_as) return error == Run_Taskflow_Result::completion_missing || error == Run_Taskflow_Result::observation_unavailable; + return false; + }, result.error()); + if (impossible) std::terminate(); + state.render_submission_error = result.error(); } publish_state(); } -void Throttled_Latest_only::Private::rendered(std::size_t slot_index, proxy& completed_frame) { +void Throttled_Latest_only::Private::rendered(std::size_t slot_index, proxy& completed_frame) { bool dispatch = false; { std::lock_guard lock(mutex); @@ -298,7 +290,7 @@ void Throttled_Latest_only::Private::rendered(std::size_t slot_index, proxysend(frames[slot_index].frame, [this, slot_index](proxy& completed_frame) { + sink->send(frames[slot_index].frame, [this, slot_index](proxy& completed_frame) { sent(slot_index, completed_frame); }); } @@ -307,7 +299,7 @@ void Throttled_Latest_only::Private::dispatch_send(std::size_t slot_index) { throw; } } -void Throttled_Latest_only::Private::sent(std::size_t slot_index, proxy& completed_frame) { +void Throttled_Latest_only::Private::sent(std::size_t slot_index, proxy& completed_frame) { std::size_t next_send_index = Frame_Count; { std::lock_guard lock(mutex); @@ -361,7 +353,7 @@ void Throttled_Latest_only::Private::abandon_send(std::size_t slot_index) { finish_stop_if_ready(); } void Throttled_Latest_only::Private::finish_stop_if_ready() { - std::array, Frame_Count> retired_frames; + std::array, Frame_Count> retired_frames; Stop_Completion completion; { std::lock_guard lock(mutex); diff --git a/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.hpp b/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.hpp index dc187f5..77b3805 100644 --- a/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.hpp +++ b/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.hpp @@ -11,6 +11,7 @@ namespace aethera { */ struct Throttled_Latest_only : Def { using Start_Result = Throttled_Latest_Only_Start_Result; + using Start_State_Result = Throttled_Latest_Only_Start_State_Result; using Stop_Result = Throttled_Latest_Only_Stop_Result; using Stop_Completion = std::function; struct Private; diff --git a/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.ipp b/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.ipp index 31aed00..daba79b 100644 --- a/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.ipp +++ b/kernel/kernel/module/frame_policy/src/Throttled_Latest_Only.ipp @@ -18,7 +18,7 @@ struct Throttled_Latest_only::Private : Prev_Private { sending }; struct Frame_Slot { - proxy frame; /* 帧池在本槽唯一拥有并循环复用的物理帧。 */ + proxy frame; /* 帧池在本槽唯一拥有并循环复用的物理帧。 */ Frame_Phase phase{Frame_Phase::idle}; /* 本槽当前用途的唯一权威状态。 */ std::chrono::steady_clock::time_point render_start; /* 当前帧开始渲染的单调时刻。 */ std::chrono::steady_clock::time_point send_start; /* 当前帧开始发送的单调时刻。 */ @@ -35,10 +35,10 @@ private: void cancel_failed(); void timer_cancelled(); void frame_due(); - void record_render_submission(frame_policy::Render_Scene_Result result); - void rendered(std::size_t slot_index, proxy& completed_frame); + void record_render_submission(scene::Render_Result result); + void rendered(std::size_t slot_index, proxy& completed_frame); void dispatch_send(std::size_t slot_index); - void sent(std::size_t slot_index, proxy& completed_frame); + void sent(std::size_t slot_index, proxy& completed_frame); void abandon_render(std::size_t slot_index); void abandon_send(std::size_t slot_index); void finish_stop_if_ready(); diff --git a/kernel/kernel/module/frame_policy/test/Throttled_Latest_Only_Tests.cpp b/kernel/kernel/module/frame_policy/test/Throttled_Latest_Only_Tests.cpp index f1ef64f..7fabf7b 100644 --- a/kernel/kernel/module/frame_policy/test/Throttled_Latest_Only_Tests.cpp +++ b/kernel/kernel/module/frame_policy/test/Throttled_Latest_Only_Tests.cpp @@ -18,12 +18,12 @@ namespace { using namespace std::chrono_literals; struct Pending_Frame { - std::reference_wrapper> frame; /* 被 Scene/Sink 借用的策略帧。 */ + std::reference_wrapper> frame; /* 被 Scene/Sink 借用的策略帧。 */ aethera::frame_policy::Frame_Completion completion; /* 归还同一帧的完成回调所有权。 */ }; struct Pending_Render_Frame { - std::reference_wrapper> frame; /* Borrowed policy frame; the policy outlives the pending render. */ - aethera::frame_policy::Render_Completion completion; /* Owns the render completion until this frame is returned. */ + std::reference_wrapper> frame; /* Borrowed policy frame; the policy outlives the pending render. */ + aethera::scene::Render_Completion completion; /* Owns the render completion until this frame is returned. */ }; struct Frame_Policy_Test_Control { @@ -55,7 +55,7 @@ struct Frame_Policy_Test_Control { std::deque rendering_frames; /* Scene 尚未归还的帧及其回调。 */ std::deque sending_frames; /* Sink 尚未归还的帧及其回调。 */ - std::vector*> + std::vector*> rendered_frame_addresses; /* 可空、非拥有;记录每次渲染采用的槽位地址。 */ aethera::frame_policy::Timer_Callback timer_callback; /* 周期服务持有的到期回调。 */ aethera::frame_policy::Timer_Callback cancel_completion; /* 测试显式完成异步取消的回调。 */ @@ -66,7 +66,7 @@ struct Frame_Policy_Test_Control { std::size_t frames_alive{}; /* 尚未析构的物理帧数量。 */ std::size_t render_calls{}; /* Scene 已接受的渲染借用次数。 */ std::size_t send_calls{}; /* Sink 已接受的发送借用次数。 */ - aethera::frame_policy::Render_Scene_Result render_result{aethera::frame_policy::Render_Scene_Result::submitted}; /* Scene 对下一次及后续渲染提交返回的结果。 */ + aethera::scene::Render_Result render_result{}; /* Scene 对下一次及后续渲染提交返回的原始结果。 */ std::exception_ptr render_failure{}; /* Failure delivered by the next render completion. */ bool complete_render_synchronously{}; /* Scene 是否在 render 内立即归还帧。 */ bool complete_send_synchronously{}; /* Sink 是否在 send 内立即归还帧。 */ @@ -122,21 +122,21 @@ struct Test_Scene : aethera::Def { struct Test_Scene::Private : Prev_Private { explicit Private(std::shared_ptr control) : control(std::move(control)) {} - aethera::proxy create_frame() { + aethera::proxy create_frame() { ++control->frames_created; - return pro::make_proxy(control); + return pro::make_proxy(control); } - aethera::frame_policy::Render_Scene_Result render(aethera::proxy& frame, aethera::frame_policy::Render_Completion completion) { + aethera::scene::Render_Result render(aethera::proxy& frame, aethera::scene::Render_Completion completion) { control->rendered_frame_addresses.push_back(std::addressof(frame)); ++control->render_calls; - if (control->render_result != aethera::frame_policy::Render_Scene_Result::submitted) return control->render_result; + if (!control->render_result) return control->render_result; if (control->complete_render_synchronously) { completion(frame, std::exchange(control->render_failure, {})); - return aethera::frame_policy::Render_Scene_Result::submitted; + return {}; } control->rendering_frames.push_back({frame, std::move(completion)}); - return aethera::frame_policy::Render_Scene_Result::submitted; + return {}; } std::shared_ptr control; /* Scene 测试行为的共享控制数据。 */ @@ -150,7 +150,7 @@ struct Test_Sink : aethera::Def { struct Test_Sink::Private : Prev_Private { explicit Private(std::shared_ptr control) : control(std::move(control)) {} - void send(aethera::proxy& frame, aethera::frame_policy::Frame_Completion completion) { + void send(aethera::proxy& frame, aethera::frame_policy::Frame_Completion completion) { ++control->send_calls; if (control->throw_from_send) { throw std::runtime_error("test sink failure"); @@ -183,10 +183,10 @@ TEST(Throttled_Latest_Only, Publishes_And_Clears_Scene_Submission_Failure) { auto control = std::make_shared(); auto policy = make_policy(control); ASSERT_TRUE(policy->start().has_value()); - control->render_result = aethera::frame_policy::Render_Scene_Result::runtime_not_initialized; + control->render_result = std::unexpected(aethera::scene::Render_Error{aethera::Run_Taskflow_Result::runtime_not_initialized}); control->fire_timer(); - policy->read([](const aethera::Frame_Policy_State& state) { ASSERT_TRUE(state.render_submission_error.has_value()); EXPECT_EQ(*state.render_submission_error, aethera::Frame_Policy_Render_Submission_Result::runtime_not_initialized); }); - control->render_result = aethera::frame_policy::Render_Scene_Result::submitted; + policy->read([](const aethera::Frame_Policy_State& state) { ASSERT_TRUE(state.render_submission_error.has_value()); EXPECT_EQ(std::get(*state.render_submission_error), aethera::Run_Taskflow_Result::runtime_not_initialized); }); + control->render_result = aethera::scene::Render_Result{}; control->fire_timer(); policy->read([](const aethera::Frame_Policy_State& state) { EXPECT_FALSE(state.render_submission_error.has_value()); }); control->complete_render(); @@ -432,7 +432,7 @@ TEST(Throttled_Latest_Only, Reports_Invalid_Configuration) { auto missing_dependencies = missing_builder.build(); const auto missing_start = missing_dependencies->start(); ASSERT_FALSE(missing_start); - EXPECT_EQ(missing_start.error(), aethera::Throttled_Latest_only::Start_Result::dependency_unavailable); + EXPECT_EQ(std::get(missing_start.error()), aethera::Throttled_Latest_only::Start_State_Result::dependency_unavailable); const auto missing_stop = missing_dependencies->stop({}); ASSERT_FALSE(missing_stop); EXPECT_EQ(missing_stop.error(), aethera::Throttled_Latest_only::Stop_Result::completion_missing); @@ -441,20 +441,20 @@ TEST(Throttled_Latest_Only, Reports_Invalid_Configuration) { auto policy = make_policy(control, 0.0); const auto invalid_rate = policy->start(); ASSERT_FALSE(invalid_rate); - EXPECT_EQ(invalid_rate.error(), aethera::Throttled_Latest_only::Start_Result::invalid_user_frame_rate); + EXPECT_EQ(std::get(invalid_rate.error()), aethera::Frame_Policy_Configuration_Result::invalid_user_frame_rate); auto tiny_rate = make_policy(control, std::numeric_limits::denorm_min()); const auto invalid_interval = tiny_rate->start(); ASSERT_FALSE(invalid_interval); - EXPECT_EQ(invalid_interval.error(), aethera::Throttled_Latest_only::Start_Result::interval_out_of_range); + EXPECT_EQ(std::get(invalid_interval.error()), aethera::Frame_Policy_Configuration_Result::interval_out_of_range); auto invalid_statistics = make_policy(control, 60.0, false, false, true, 0); const auto invalid_statistics_result = invalid_statistics->start(); ASSERT_FALSE(invalid_statistics_result); - EXPECT_EQ(invalid_statistics_result.error(), aethera::Throttled_Latest_only::Start_Result::invalid_statistics_window); + EXPECT_EQ(std::get(invalid_statistics_result.error()), aethera::Frame_Policy_Configuration_Result::invalid_statistics_window); auto invalid_adaptive_window = make_policy(control, std::nullopt, true, false, false, 0); const auto invalid_adaptive_result = invalid_adaptive_window->start(); ASSERT_FALSE(invalid_adaptive_result); - EXPECT_EQ(invalid_adaptive_result.error(), aethera::Throttled_Latest_only::Start_Result::invalid_statistics_window); + EXPECT_EQ(std::get(invalid_adaptive_result.error()), aethera::Frame_Policy_Configuration_Result::invalid_statistics_window); } TEST(Throttled_Latest_Only, Render_Failure_Releases_Slot_Before_It_Escapes) { diff --git a/kernel/kernel/module/main.cmake b/kernel/kernel/module/main.cmake index 64db0aa..c8ada79 100644 --- a/kernel/kernel/module/main.cmake +++ b/kernel/kernel/module/main.cmake @@ -16,11 +16,11 @@ 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) +aethera_kernel_add_module(Aethera_Kernel_Scene scene Aethera_Kernel_Task_Flow) add_library(Aethera_Kernel::Scene ALIAS Aethera_Kernel_Scene) + +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) diff --git a/kernel/kernel/module/scene/export/export.h b/kernel/kernel/module/scene/export/export.h index 584c814..d9b810b 100644 --- a/kernel/kernel/module/scene/export/export.h +++ b/kernel/kernel/module/scene/export/export.h @@ -1,6 +1,19 @@ #pragma once +#include "global.hpp" +#include "task_flow/export/export.h" #include - -namespace aethera { -enum struct Scene_Create_Taskflow_Result : std::uint8_t { component_unavailable, dependency_cycle, already_composed, dependency_unavailable }; -} // namespace aethera +#include +#include +#include +#include +#include +namespace aethera::scene { +struct Frame : facade_builder::build {}; +using Render_Completion = std::function&, std::exception_ptr)>; +enum struct Render_State_Result : std::uint8_t { + frame_missing, + completion_missing +}; +using Render_Error = std::variant; +using Render_Result = std::expected; +} // namespace aethera::scene diff --git a/kernel/kernel/module/scene/src/Scene.cpp b/kernel/kernel/module/scene/src/Scene.cpp index f95d0fc..655bb24 100644 --- a/kernel/kernel/module/scene/src/Scene.cpp +++ b/kernel/kernel/module/scene/src/Scene.cpp @@ -9,42 +9,36 @@ struct Scene_Frame {}; } proxy scene::make_task_graph(std::string name) { return task_flow::make_task_graph(std::move(name)); } Scene::Private::Private() = default; -proxy Scene::Private::create_frame() { - return pro::make_proxy(); +proxy Scene::Private::create_frame() { + return pro::make_proxy(); } proxy& Scene::Private::taskflow() { return graph; } -auto Scene::Private::create_taskflow() -> std::expected, Create_Taskflow_Result> { +auto Scene::Private::create_taskflow() -> std::expected, Create_Taskflow_Error> { auto result = scene::make_task_graph("scene.renderables"); std::unordered_map> nodes; auto& renderables = get().internal.use(); - std::optional error; + std::optional error; renderables.for_each_topological([&](Dag_Node_Id id, proxy& renderable) { if (error) return; auto component = renderable->taskflow(); auto composed = result->compose("renderable." + std::to_string(id.value), component); if (!composed) { - switch (composed.error()) { - case Compose_Task_Graph_Result::component_unavailable: error = Create_Taskflow_Result::component_unavailable; - break; - case Compose_Task_Graph_Result::dependency_cycle: error = Create_Taskflow_Result::dependency_cycle; - break; - case Compose_Task_Graph_Result::already_composed: error = Create_Taskflow_Result::already_composed; - break; - } + error = composed.error(); return; } nodes.emplace(id.value, std::move(*composed)); }); 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)) != Precede_Task_Graph_Result::preceded) error = Create_Taskflow_Result::dependency_unavailable; + const auto dependency = result->precede(nodes.at(source.value), nodes.at(target.value)); + if (dependency != Precede_Task_Graph_Result::preceded) error = dependency; }); if (error) return std::unexpected(*error); return result; } -auto Scene::Private::advance() -> std::expected { +auto Scene::Private::advance() -> std::expected { auto& renderables = get(); renderables.internal.advance(); renderables.internal.use().for_each_topological([](Dag_Node_Id, proxy& renderable) { @@ -55,20 +49,16 @@ auto Scene::Private::advance() -> std::expected { graph = std::move(*next_graph); return {}; } -frame_policy::Render_Scene_Result Scene::Private::render(proxy& frame, frame_policy::Render_Completion completion) { - if (!frame) return frame_policy::Render_Scene_Result::frame_missing; - if (!completion) return frame_policy::Render_Scene_Result::completion_missing; - if (!advance()) return frame_policy::Render_Scene_Result::task_graph_invalid; +scene::Render_Result Scene::Private::render(proxy& frame, scene::Render_Completion completion) { + if (!frame) return std::unexpected(scene::Render_Error{scene::Render_State_Result::frame_missing}); + if (!completion) return std::unexpected(scene::Render_Error{scene::Render_State_Result::completion_missing}); + const auto advanced = advance(); + if (!advanced) return std::unexpected(std::visit([](auto error) -> scene::Render_Error { return error; }, advanced.error())); const auto result = graph->run([frame = std::ref(frame), completion = std::move(completion)](std::exception_ptr failure) mutable { completion(frame.get(), std::move(failure)); }); - switch (result) { - 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(); + if (result != Run_Taskflow_Result::submitted) return std::unexpected(scene::Render_Error{result}); + return {}; } Scene::Scene() = default; Scene::~Scene() noexcept = default; diff --git a/kernel/kernel/module/scene/src/Scene.hpp b/kernel/kernel/module/scene/src/Scene.hpp index 9e91ea3..3c529e9 100644 --- a/kernel/kernel/module/scene/src/Scene.hpp +++ b/kernel/kernel/module/scene/src/Scene.hpp @@ -1,6 +1,5 @@ #pragma once #include "../export/export.h" -#include "frame_policy/rely_facade.h" #include "model/Model.hpp" #include "relation/Dag.hpp" #include diff --git a/kernel/kernel/module/scene/src/Scene.ipp b/kernel/kernel/module/scene/src/Scene.ipp index fccafa1..45b1b03 100644 --- a/kernel/kernel/module/scene/src/Scene.ipp +++ b/kernel/kernel/module/scene/src/Scene.ipp @@ -1,13 +1,14 @@ #pragma once +#include namespace aethera { struct Scene::Private : Prev_Private { - using Create_Taskflow_Result = Scene_Create_Taskflow_Result; + using Create_Taskflow_Error = std::variant; Private(); - proxy create_frame(); - frame_policy::Render_Scene_Result render(proxy& frame, frame_policy::Render_Completion completion); + proxy create_frame(); + scene::Render_Result render(proxy& frame, scene::Render_Completion completion); proxy& taskflow(); - auto create_taskflow() -> std::expected, Create_Taskflow_Result>; - auto advance() -> std::expected; + auto create_taskflow() -> std::expected, Create_Taskflow_Error>; + auto advance() -> std::expected; private: proxy graph{scene::make_task_graph("scene")}; /* 唯一拥有当前组合任务图的跨模块代理。 */ }; diff --git a/kernel/kernel/module/scene/test/Scene_Tests.cpp b/kernel/kernel/module/scene/test/Scene_Tests.cpp index 664f3fb..5a1fe24 100644 --- a/kernel/kernel/module/scene/test/Scene_Tests.cpp +++ b/kernel/kernel/module/scene/test/Scene_Tests.cpp @@ -19,8 +19,6 @@ struct Test_Dag_Renderable : aethera::Def, aethera::frame_policy::Scene>); - TEST(Scene, Advances_Renderables_And_Composes_Task_Graph_In_Dag_Order) { auto advance_order = std::make_shared>(); aethera::Dag::Builder builder; @@ -81,11 +79,17 @@ 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&, std::exception_ptr) { completed = true; }), aethera::frame_policy::Render_Scene_Result::runtime_not_initialized); + const auto unavailable_runtime = scene.d->render(frame, [&](aethera::proxy&, std::exception_ptr) { completed = true; }); + ASSERT_FALSE(unavailable_runtime); + EXPECT_EQ(std::get(unavailable_runtime.error()), aethera::Run_Taskflow_Result::runtime_not_initialized); EXPECT_FALSE(completed); - aethera::proxy empty_frame; - EXPECT_EQ(scene.d->render(empty_frame, [&](aethera::proxy&, 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); + aethera::proxy empty_frame; + const auto missing_frame = scene.d->render(empty_frame, [&](aethera::proxy&, std::exception_ptr) {}); + ASSERT_FALSE(missing_frame); + EXPECT_EQ(std::get(missing_frame.error()), aethera::scene::Render_State_Result::frame_missing); + const auto missing_completion = scene.d->render(frame, {}); + ASSERT_FALSE(missing_completion); + EXPECT_EQ(std::get(missing_completion.error()), aethera::scene::Render_State_Result::completion_missing); } TEST(Scene, Rejects_The_Same_Renderable_Task_Graph_Twice) { @@ -98,6 +102,8 @@ TEST(Scene, Rejects_The_Same_Renderable_Task_Graph_Twice) { aethera::Scene scene; scene.write(std::move(*graph)); auto frame = scene.d->create_frame(); - EXPECT_EQ(scene.d->render(frame, [](aethera::proxy&, std::exception_ptr) {}), aethera::frame_policy::Render_Scene_Result::task_graph_invalid); + const auto rendered = scene.d->render(frame, [](aethera::proxy&, std::exception_ptr) {}); + ASSERT_FALSE(rendered); + EXPECT_EQ(std::get(rendered.error()), aethera::Compose_Task_Graph_Result::already_composed); } } // namespace diff --git a/kernel/kernel/module/task_flow/export/export.h b/kernel/kernel/module/task_flow/export/export.h index 8ddda4b..90f54a1 100644 --- a/kernel/kernel/module/task_flow/export/export.h +++ b/kernel/kernel/module/task_flow/export/export.h @@ -6,25 +6,70 @@ #include #include #include - 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 }; +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; using Taskflow_Exception_Boundary = std::function; namespace task_flow { struct Task_Node : facade_builder::add_skill::build {}; struct Task_Graph_Component : facade_builder::add_skill::build {}; -struct Task_Graph_Common_Facade : facade_builder::add_convention<_component, proxy()>::add_convention<_empty, bool() const noexcept>::support_relocation::build {}; -struct Task_Graph_Build_Facade : facade_builder::add_facade_with_substitution::add_convention<_add, std::expected, Add_Task_Graph_Result>(std::string, std::function)>::add_convention<_clear, void()>::build {}; +struct Task_Graph_Common_Facade : facade_builder +::add_convention<_component, proxy()> +::add_convention<_empty, bool() const noexcept> +::support_relocation +::build {}; +struct Task_Graph_Build_Facade : facade_builder::add_facade_with_substitution +::add_convention<_add, std::expected, Add_Task_Graph_Result>(std::string, std::function)> +::add_convention<_clear, void()>::build {}; struct Task_Graph_Compose_Facade : facade_builder::add_facade_with_substitution::add_convention<_compose, std::expected, Compose_Task_Graph_Result>(std::string, proxy&)>::add_convention<_precede, Precede_Task_Graph_Result(proxy&, proxy&)>::add_convention<_run, Run_Taskflow_Result(Taskflow_Completion)>::build {}; struct Task_Graph_Facade : facade_builder::add_facade_with_substitution::add_facade_with_substitution::build {}; proxy make_task_graph(std::string name);