diff --git a/.gitignore b/.gitignore index 459b6a7..6af6a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ /output/ /.playwright-cli/ /package/ +/kernel/src_old/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a4196f..8fc85f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) include("${CMAKE_CURRENT_LIST_DIR}/third_party/build_infra/start.cmake") include("${CMAKE_CURRENT_LIST_DIR}/cmake/package.cmake") +set(CMAKE_FOLDER "Aethera_Kernel") include("${CMAKE_CURRENT_LIST_DIR}/kernel/main.cmake") +set(CMAKE_FOLDER "render_2D") include("${CMAKE_CURRENT_LIST_DIR}/render_2D/main.cmake") +set(CMAKE_FOLDER "render_3D") include("${CMAKE_CURRENT_LIST_DIR}/render_3D/main.cmake") +set(CMAKE_FOLDER "mcp") include("${CMAKE_CURRENT_LIST_DIR}/mcp/main.cmake") +set(CMAKE_FOLDER "web_server") include("${CMAKE_CURRENT_LIST_DIR}/web_server/main.cmake") include("${CMAKE_CURRENT_LIST_DIR}/third_party/build_infra/end.cmake") diff --git a/kernel/kernel/include/global.hpp b/kernel/kernel/include/global.hpp new file mode 100644 index 0000000..cbc79e0 --- /dev/null +++ b/kernel/kernel/include/global.hpp @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +namespace aethera { +struct Non_Copyable { +protected: + Non_Copyable() = default; + ~Non_Copyable() = default; + Non_Copyable(const Non_Copyable&) = delete; + Non_Copyable& operator=(const Non_Copyable&) = delete; + Non_Copyable(Non_Copyable&&) = default; + Non_Copyable& operator=(Non_Copyable&&) = default; +}; +struct Immovable { +protected: + Immovable() = default; + ~Immovable() = default; + Immovable(const Immovable&) = delete; + Immovable& operator=(const Immovable&) = delete; + Immovable(Immovable&&) = delete; + Immovable& operator=(Immovable&&) = delete; +}; +template +concept Basic_Lockable = requires(T& lock) { + { lock.lock() } -> std::same_as; + { lock.unlock() } -> std::same_as; +}; +template +concept Lockable = Basic_Lockable && requires(T& lock) { + { lock.try_lock() } -> std::convertible_to; +}; +template +concept Exchange_Value = +std::default_initializable && +std::copy_constructible && +std::assignable_from; +template +concept Member_Value = std::is_object_v; +template +concept Copyable_Member = +Member_Value && +std::copy_constructible>; +template +concept Assignable_To = std::assignable_from; +template +concept Invocable_With = std::invocable; +template +concept Value_Reader = +Invocable_With && +(!std::is_member_object_pointer_v>); +template +concept Value_Writer = +Invocable_With && +(!std::is_member_object_pointer_v>); +template +concept Clearable = requires(T& value) { + { value.clear() } -> std::same_as; +}; +template +concept List_Value = +std::default_initializable && +std::ranges::range && +Clearable; +struct Spin_Lock : Immovable { + Spin_Lock() noexcept = default; + void lock() noexcept { + while (flag_.test_and_set(std::memory_order_acquire)) { + while (flag_.test(std::memory_order_relaxed)) { + std::this_thread::yield(); + } + } + } + [[nodiscard]] bool try_lock() noexcept { + return !flag_.test_and_set(std::memory_order_acquire); + } + void unlock() noexcept { + flag_.clear(std::memory_order_release); + } + std::atomic_flag flag_ = ATOMIC_FLAG_INIT; +}; +static_assert(Lockable); +} diff --git a/kernel/kernel/include/model/Concurrent_Model.hpp b/kernel/kernel/include/model/Concurrent_Model.hpp new file mode 100644 index 0000000..2c53ab4 --- /dev/null +++ b/kernel/kernel/include/model/Concurrent_Model.hpp @@ -0,0 +1,44 @@ +#pragma once +#include "Concurrent_Storage.hpp" +namespace aethera { +template +struct Root { + struct Prop {}; + struct Private {}; + struct Builder {}; +}; +template +struct Def : Base { + struct Private : Self::Private { + Import_Struct prop; + }; + Private* d{}; + struct Builder { + using Prop = Self::Prop; + Self* ret; + Builder() { + ret = new Self; + ret->d = new Private; + } + Self* build() { + return ret; + } + template T> + Builder& set_prop(Member Prop::* member, T&& value) { + Prop& ir = ret->d->prop.internal.internal_read; + Prop& ew = ret->d->prop.internal.external_write; + ir.*member = value; + ew.*member = std::forward(value); + return *this; + } + }; + template + Self& set_prop(Args&&... args) { + this->d->prop.write(std::forward(args)...); + return static_cast(*this); + } + using Prev_Prop = Base::Prop; + using Prev_Private = Base::Private; + using Prev_Builder = Base::Builder; +}; +} diff --git a/kernel/kernel/include/model/Concurrent_Storage.hpp b/kernel/kernel/include/model/Concurrent_Storage.hpp new file mode 100644 index 0000000..38044b0 --- /dev/null +++ b/kernel/kernel/include/model/Concurrent_Storage.hpp @@ -0,0 +1,187 @@ +#pragma once +#include "../global.hpp" +namespace aethera { +template +struct Export_Struct { + template Fn> + void read(Fn&& fn) const { + std::lock_guard lock(internal.mtx); + std::invoke(std::forward(fn), std::as_const(internal.external_read)); + } + template + [[nodiscard]] auto read(Member Val::* member) const -> std::remove_cv_t { + std::lock_guard lock(internal.mtx); + return internal.external_read.*member; + } + struct Internal { + void advance() { + std::lock_guard lock(mtx); + external_read = internal_read; + } + [[nodiscard]] Val* use() { + return &internal_read; + } + [[nodiscard]] const Val* use() const { + return &internal_read; + } + Val internal_read{}; + Val external_read{}; + mutable Spin_Lock mtx; + }; + Internal internal{}; +}; +template +struct Import_Struct { + template Fn> + void write(Fn&& fn) { + std::lock_guard lock(internal.mtx); + std::invoke(std::forward(fn), internal.external_write); + } + template T> + void write(Member Val::* member, T&& value) { + std::lock_guard lock(internal.mtx); + internal.external_write.*member = std::forward(value); + } + template Fn> + void read(Fn&& fn) const { + std::lock_guard lock(internal.mtx); + std::invoke(std::forward(fn), std::as_const(internal.external_write)); + } + template + [[nodiscard]] auto read(Member Val::* member) const -> std::remove_cv_t { + std::lock_guard lock(internal.mtx); + return internal.external_write.*member; + } + struct Internal { + void exchange() { + std::lock_guard lock(mtx); + internal_read = external_write; + } + [[nodiscard]] Val* use() { + return &internal_read; + } + [[nodiscard]] const Val* use() const { + return &internal_read; + } + Val internal_read{}; + Val external_write{}; + mutable Spin_Lock mtx; + }; + Internal internal{}; +}; +template +struct Import_List { + template Fn> + void write(Fn&& fn) { + std::lock_guard lock(internal.mtx); + std::invoke(std::forward(fn), *internal.external); + } + struct Internal { + [[nodiscard]] Val* use() { + return internal; + } + [[nodiscard]] const Val* use() const { + return internal; + } + void advance() { + std::lock_guard lock(mtx); + std::swap(internal, external); + external->clear(); + } + Val buf[2]{}; + Val* internal = &buf[0]; + Val* external = &buf[1]; + Spin_Lock mtx; + }; + Internal internal{}; +}; +template +struct Export_List { + template Fn> + void read(Fn&& fn) const { + std::lock_guard lock(internal.mtx); + std::invoke(std::forward(fn), std::as_const(*internal.external)); + } + struct Internal { + [[nodiscard]] Val* use() { + return internal; + } + [[nodiscard]] const Val* use() const { + return internal; + } + void advance() { + std::lock_guard lock(mtx); + std::swap(internal, external); + internal->clear(); + } + Val buf[2]{}; + Val* internal = &buf[0]; + Val* external = &buf[1]; + mutable Spin_Lock mtx; + }; + Internal internal{}; +}; +template +struct Readable_Import_Batch { + template Fn> + void write(Fn&& fn) { + std::lock_guard lock(internal.mtx); + std::invoke(std::forward(fn), *internal.external_write); + } + template Fn> + void read(Fn&& fn) const { + std::lock_guard lock(internal.mtx); + std::invoke(std::forward(fn), std::as_const(*internal.external_read)); + } + struct Internal { + [[nodiscard]] Val* use() { + return internal_read; + } + [[nodiscard]] const Val* use() const { + return internal_read; + } + void advance() { + std::lock_guard lock(mtx); + Val* reuse = external_read; + external_read = internal_read; + internal_read = external_write; + external_write = reuse; + external_write->clear(); + } + Val buf[3]{}; + Val* external_write = &buf[0]; + Val* internal_read = &buf[1]; + Val* external_read = &buf[2]; + mutable Spin_Lock mtx; + }; + Internal internal{}; +}; +template +struct Tagged_Storage : T { + using tag_type = Tag; + using storage_type = T; +}; +template +struct Tagged_Storage_Set : Storages... { + template + [[nodiscard]] auto& get() noexcept { + return get_impl(*this); + } + template + [[nodiscard]] const auto& get() const noexcept { + return get_impl(*this); + } + template + static T& get_impl(Tagged_Storage& value) noexcept { + return value; + } + template + static const T& get_impl(const Tagged_Storage& value) noexcept { + return value; + } +}; +template +using Storage = Tagged_Storage; +template +using Storage_Set = Tagged_Storage_Set; +} diff --git a/kernel/src/kernel/Task_Graph.cpp b/kernel/kernel/include/task_flow/Task_Graph.cpp similarity index 71% rename from kernel/src/kernel/Task_Graph.cpp rename to kernel/kernel/include/task_flow/Task_Graph.cpp index 7619291..feb6d90 100644 --- a/kernel/src/kernel/Task_Graph.cpp +++ b/kernel/kernel/include/task_flow/Task_Graph.cpp @@ -30,80 +30,12 @@ struct Task_Graph::Private { ? base : base + "#" + std::to_string(occurrence); } - void collect_nodes(std::string_view parent, - const std::vector>& inherited, - std::vector& result) const { - auto graph_attributes = inherited; - for (const auto& attribute : attributes) { - const auto found = std::ranges::find( - graph_attributes, attribute.first, - &std::pair::first); - if (found == graph_attributes.end()) graph_attributes.push_back(attribute); - else found->second = attribute.second; - } - for (const auto& source : nodes) { - Taskflow_Graph_Trace::Node node{}; - node.native_id = static_cast(source.task.hash_value()); - auto local_id = std::string_view(source.node_id); - const auto& graph_name = taskflow.name(); - if (!graph_name.empty() && local_id.starts_with(graph_name) && - local_id.size() > graph_name.size() && - local_id[graph_name.size()] == '/') - local_id.remove_prefix(graph_name.size() + 1); - node.node_id = parent.empty() - ? source.node_id - : std::string(parent) + "/" + std::string(local_id); - node.parent_node_id = std::string(parent); - node.name = source.name; - node.type = std::string(tf::to_string(source.task.type())); - node.attributes = graph_attributes; - for (const auto& attribute : source.attributes) { - const auto found = std::ranges::find( - node.attributes, attribute.first, - &std::pair::first); - if (found == node.attributes.end()) node.attributes.push_back(attribute); - else found->second = attribute.second; - } - source.task.for_each_predecessor([&](tf::Task value) { - node.predecessors.push_back( - static_cast(value.hash_value())); - }); - source.task.for_each_successor([&](tf::Task value) { - node.successors.push_back( - static_cast(value.hash_value())); - }); - const auto module_id = node.node_id; - result.push_back(std::move(node)); - if (source.child) source.child->collect_nodes(module_id, node.attributes, result); - } - } - void bind_frame(Render_Frame* frame) noexcept { - for (auto& node : nodes) { - node.task.data(frame); - if (node.child) node.child->bind_frame(frame); - } - } }; struct Task_Node::Private { std::shared_ptr graph{}; /* 节点所属 DAG 及其静态生命周期。 */ std::size_t index{}; /* DAG 内节点槽位;只在实现单元解释。 */ std::uint64_t generation{}; /* 防止 clear 后的旧句柄指向复用槽位。 */ }; -namespace detail { -void* Task_Graph_Access::native_storage(Task_Graph& graph) noexcept { - return std::addressof(graph.d->taskflow); -} -std::vector Task_Graph_Access::nodes( - const Task_Graph& graph) { - std::vector result; - graph.d->collect_nodes({}, {}, result); - return result; -} -void Task_Graph_Access::bind_frame(Task_Graph& graph, - Render_Frame* frame) noexcept { - graph.d->bind_frame(frame); -} -} Task_Node::Task_Node() = default; Task_Node::~Task_Node() = default; Task_Node::Task_Node(const Task_Node&) = default; diff --git a/kernel/src/kernel/Task_Graph.hpp b/kernel/kernel/include/task_flow/Task_Graph.hpp similarity index 96% rename from kernel/src/kernel/Task_Graph.hpp rename to kernel/kernel/include/task_flow/Task_Graph.hpp index 324ee82..3980527 100644 --- a/kernel/src/kernel/Task_Graph.hpp +++ b/kernel/kernel/include/task_flow/Task_Graph.hpp @@ -4,9 +4,6 @@ #include #include namespace aethera { -namespace detail { -struct Task_Graph_Access; -} struct Task_Graph; /* Task_Graph 中单个业务节点的可复制句柄,仅用于静态构图。 */ struct Task_Node { @@ -57,8 +54,7 @@ public: static void corun_until(std::function predicate); private: friend struct Task_Node::Private; - friend struct detail::Task_Graph_Access; struct Private; std::shared_ptr d; /* 唯一业务 DAG 定义的不透明所有权。 */ }; -} + diff --git a/kernel/src/kernel/Task_Graph_Internal.hpp b/kernel/kernel/include/task_flow/Task_Graph_Internal.hpp similarity index 57% rename from kernel/src/kernel/Task_Graph_Internal.hpp rename to kernel/kernel/include/task_flow/Task_Graph_Internal.hpp index 674ebfe..0577ee6 100644 --- a/kernel/src/kernel/Task_Graph_Internal.hpp +++ b/kernel/kernel/include/task_flow/Task_Graph_Internal.hpp @@ -1,23 +1,11 @@ #pragma once #include "Task_Graph.hpp" -#include "frame.hpp" -#include "ownership.hpp" #include #include -#include #include #include namespace aethera::detail { -/* 仅供 Kernel 实现单元把不透明业务图交给原生 Executor 和帧观测器。 */ -struct Task_Graph_Access { - [[nodiscard]] static void* native_storage(Task_Graph& graph) noexcept; - [[nodiscard]] static std::vector nodes( - const Task_Graph& graph); - static void bind_frame( - Task_Graph& graph, - Render_Frame* frame) noexcept; -}; /* Task_Graph.cpp 到全局 Kernel Executor 的内部桥;不得在模块公共头文件中声明。 */ void corun_taskflow_until(std::function predicate); /* 构图期登记稳定业务名;只供 watchdog 故障路径按原生节点 ID 解析。 */ diff --git a/kernel/kernel/test/main.cpp b/kernel/kernel/test/main.cpp new file mode 100644 index 0000000..1a5a39f --- /dev/null +++ b/kernel/kernel/test/main.cpp @@ -0,0 +1,204 @@ +#include + +#include +#include +#include +#include +#include + +// ============================================================ +// 1. 定义动态分派 +// ============================================================ + +PRO_DEF_MEM_DISPATCH(MemName, Name); +PRO_DEF_MEM_DISPATCH(MemRender, Render); +PRO_DEF_MEM_DISPATCH(MemResize, Resize); + + +// ============================================================ +// 2. 基础 RenderBackend facade +// ============================================================ + +struct RenderBackend + : pro::facade_builder + ::add_convention< + MemName, + std::string_view() const noexcept + > + ::add_convention< + MemRender, + void(std::uint64_t frame) + > + ::build +{}; + + +// ============================================================ +// 3. 更强的 facade +// +// Proxy 4.1: +// add_facade_with_substitution +// +// 不只是继承 RenderBackend 的能力, +// 还允许: +// +// proxy +// ↓ +// proxy +// +// ============================================================ + +struct ResizableRenderBackend + : pro::facade_builder + ::add_facade_with_substitution + ::add_convention< + MemResize, + void(std::uint32_t width, std::uint32_t height) + > + + // Proxy 4.x skill API + ::add_skill + ::add_skill + + ::build +{}; + + +// ============================================================ +// 4. 具体实现 +// +// 注意: +// 没有继承 RenderBackend +// 没有 virtual +// 完全不知道 proxy/facade 的存在 +// ============================================================ + +class VulkanBackend { +public: + explicit VulkanBackend(std::string gpu) + : gpu_(std::move(gpu)) {} + + std::string_view Name() const noexcept { + return gpu_; + } + + void Render(std::uint64_t frame) { + std::cout + << "[Vulkan] render frame " + << frame + << '\n'; + } + + void Resize( + std::uint32_t width, + std::uint32_t height + ) { + std::cout + << "[Vulkan] resize " + << width + << " x " + << height + << '\n'; + } + +private: + std::string gpu_; +}; + + +// ============================================================ +// 5. 一个只接受基础 RenderBackend 的函数 +// ============================================================ + +void SubmitFrame(pro::proxy backend) +{ + std::cout + << "backend: " + << backend->Name() + << '\n'; + + backend->Render(100); +} + + +// ============================================================ +// main +// ============================================================ + +int main() +{ + // -------------------------------------------------------- + // owning proxy + // + // VulkanBackend 本身不需要继承任何接口。 + // -------------------------------------------------------- + + pro::proxy backend = + pro::make_proxy< + ResizableRenderBackend, + VulkanBackend + >("RTX 5090"); + + backend->Render(1); + backend->Resize(3840, 2160); + + std::cout + << "name = " + << backend->Name() + << '\n'; + + + // -------------------------------------------------------- + // non-owning view + // + // as_view skill 允许: + // + // proxy& -> proxy_view + // + // 不转移所有权。 + // -------------------------------------------------------- + + pro::proxy_view view = backend; + + view->Render(2); + view->Resize(1920, 1080); + + + // -------------------------------------------------------- + // RTTI + // + // facade 开启 skills::rtti 后,可以检查真正的对象类型。 + // -------------------------------------------------------- + + std::cout + << "target type = " + << proxy_typeid(*backend).name() + << '\n'; + + auto& vk = + proxy_cast(*backend); + + vk.Render(3); + + + // -------------------------------------------------------- + // ★ Proxy 4.1 新特性 + // + // ResizableRenderBackend + // ↓ + // RenderBackend + // + // 这是 add_facade_with_substitution 带来的。 + // -------------------------------------------------------- + + pro::proxy basic = + std::move(backend); + + basic->Render(4); + + // 或者直接: + // + // SubmitFrame(std::move(backend)); + + SubmitFrame(std::move(basic)); +} \ No newline at end of file diff --git a/kernel/main.cmake b/kernel/main.cmake index a44635e..8a0b1f8 100644 --- a/kernel/main.cmake +++ b/kernel/main.cmake @@ -9,7 +9,6 @@ if (Aethera_BUILD_TESTS) ) endif () rcl_add_dependency_action_targets(Aethera_Kernel_env ${Aethera_Kernel_dependencies}) -set_target_properties(Aethera_Kernel_env PROPERTIES FOLDER Aethera_Kernel) library_get_missing_with_rely(Aethera_Kernel_dependencies_installed ${Aethera_Kernel_dependencies}) if (Aethera_Kernel_dependencies_installed) rcl_log_append(${CMAKE_CURRENT_LIST_LINE} "[FATAL_ERROR] Aethera_Kernel dependencies are not installed. Build Aethera_Kernel_env first") @@ -25,13 +24,12 @@ if (Aethera_BUILD_TESTS) find_package(GTest CONFIG REQUIRED) find_package(benchmark CONFIG REQUIRED) endif () -set(Aethera_Kernel_source_dir "${CMAKE_CURRENT_LIST_DIR}/src/kernel") +set(Aethera_Kernel_source_dir "${CMAKE_CURRENT_LIST_DIR}/kernel/include") 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}) -set_target_properties(Aethera_Kernel PROPERTIES EXPORT_NAME Kernel) target_include_directories(Aethera_Kernel PUBLIC "$" "$" @@ -51,30 +49,13 @@ if (MSVC) target_compile_options(Aethera_Kernel PUBLIC /utf-8) endif () if (Aethera_BUILD_TESTS) - set(Aethera_Kernel_test_dir "${CMAKE_CURRENT_LIST_DIR}/src/test") + set(Aethera_Kernel_test_dir "${CMAKE_CURRENT_LIST_DIR}/kernel/test") append_glob_source(Aethera_Kernel_test_sources "${Aethera_Kernel_test_dir}") add_executable(Aethera_Kernel_exe ${Aethera_Kernel_test_sources}) target_link_libraries(Aethera_Kernel_exe PRIVATE Aethera_Kernel GTest::gtest GTest::gmock) - add_test(NAME Aethera_Kernel_exe COMMAND Aethera_Kernel_exe) - set_tests_properties(Aethera_Kernel_exe PROPERTIES - LABELS "Aethera_Kernel" - ENVIRONMENT "Aethera_ERROR_MODE=exception") - list(APPEND Aethera_Kernel_test_targets Aethera_Kernel_exe) - add_executable(Aethera_Frame_Policy_Benchmarks - "${CMAKE_CURRENT_LIST_DIR}/src/benchmark/Frame_Policy_Benchmarks.cpp") - target_link_libraries(Aethera_Frame_Policy_Benchmarks PRIVATE - Aethera_Kernel - benchmark::benchmark) - add_custom_target(Aethera_Frame_Policy_benchmark - COMMAND Aethera_Frame_Policy_Benchmarks - --benchmark_min_time=1s - --benchmark_repetitions=3 - --benchmark_report_aggregates_only=true - DEPENDS Aethera_Frame_Policy_Benchmarks - USES_TERMINAL) endif () if (Aethera_BUILD_TESTS) add_custom_target(Aethera_Kernel_check diff --git a/kernel/src/benchmark/Frame_Policy_Benchmarks.cpp b/kernel/src/benchmark/Frame_Policy_Benchmarks.cpp deleted file mode 100644 index 3e34fc8..0000000 --- a/kernel/src/benchmark/Frame_Policy_Benchmarks.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "Frame_Policy/Manual_Frame_Policy.hpp" -#include -#include - -namespace { - -struct Benchmark_Frame final : aethera::Render_Frame { - Benchmark_Frame() : Render_Frame({}) {} -}; - -struct Benchmark_Scene final : aethera::Render_Frame_Scene { - aethera::Render_Frame_Result render_frame( - aethera::not_null frame, - aethera::Render_Frame_Callbacks callbacks) override { - if (callbacks.submitted) callbacks.submitted(frame, false); - if (callbacks.completed) callbacks.completed(frame); - return aethera::Render_Frame_Result::submitted; - } -}; - -std::shared_ptr make_policy( - Benchmark_Scene& scene) { - return std::make_shared( - aethera::Frame_Policy::Dependencies{ - .scene = &scene, - .create_frame = [] { - return std::make_unique(); - }, - .prepare_frame = []( - aethera::not_null, - const aethera::Frame_Production&) {}, - .publish_frame = []( - aethera::not_null, - const aethera::Frame_Production&) { - const auto now = std::chrono::steady_clock::now(); - return aethera::Frame_Publication_Dispatch{ - .started_at = now, - .completed_at = now, - .succeeded = true}; - }, - .retain_owner = [] { return std::make_shared(1); }, - .report_failure = [](std::exception_ptr) {}, - .frame_capacity = 3}); -} - -void frame_policy_state_access(benchmark::State& state) { - Benchmark_Scene scene; - const auto policy = make_policy(scene); - for (auto _ : state) { - static_cast(_); - policy->access_state( - [](const aethera::Frame_Policy::State& current) { - benchmark::DoNotOptimize( - current.observation.completed_frame_count); - }); - } -} - -BENCHMARK(frame_policy_state_access)->UseRealTime(); - -} - -BENCHMARK_MAIN(); diff --git a/kernel/src/kernel/Frame_Policy/Fixed_Rate_Frame_Policy.cpp b/kernel/src/kernel/Frame_Policy/Fixed_Rate_Frame_Policy.cpp deleted file mode 100644 index 28f876c..0000000 --- a/kernel/src/kernel/Frame_Policy/Fixed_Rate_Frame_Policy.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "Fixed_Rate_Frame_Policy.hpp" -#include "Frame_Scheduler.hpp" -#include -#include -#include - -namespace aethera { - -struct Fixed_Rate_Frame_Policy::Private { - Frame_Scheduler::Timer timer{}; /* 本 Policy 的轻量周期节点。 */ - std::atomic frames_per_second{30.0}; /* 固定频率唯一配置来源。 */ -}; - -Fixed_Rate_Frame_Policy::Fixed_Rate_Frame_Policy( - Dependencies dependencies, double frames_per_second) - : Frame_Policy(std::move(dependencies)), fixed(std::make_unique()) { - set_frame_rate(frames_per_second); -} -Fixed_Rate_Frame_Policy::~Fixed_Rate_Frame_Policy() = default; -Frame_Policy_Type Fixed_Rate_Frame_Policy::type() const noexcept { - return Frame_Policy_Type::fixed_rate; -} -std::string_view Fixed_Rate_Frame_Policy::name() const noexcept { - return "fixed_rate"; -} -void Fixed_Rate_Frame_Policy::start() { - const auto weak = weak_from_this(); - fixed->timer = Frame_Scheduler::instance().make_timer( - [weak](Frame_Scheduler::Tick tick) { - const auto policy = weak.lock(); - if (!policy) return; - auto* fixed_policy = static_cast( - policy.get()); - if (fixed_policy->read_state().lifecycle != - Frame_Policy_Lifecycle::running) return; - fixed_policy->request_automatic_frame(Frame_Request{ - .issued_at = tick.issued_at, - .sequence = tick.sequence, - .time_milliseconds = tick.time_milliseconds, - .source = Frame_Request_Source::periodic}); - }); - fixed->timer.start_periodic( - fixed->frames_per_second.load(std::memory_order_acquire)); -} -void Fixed_Rate_Frame_Policy::stop(Stop_Callback completed) { - fixed->timer.cancel(); - begin_stop(std::move(completed)); -} -void Fixed_Rate_Frame_Policy::set_frame_rate(double frames_per_second) { - if (!std::isfinite(frames_per_second) || frames_per_second <= 0.0) - throw std::invalid_argument("fixed frame rate must be positive"); - fixed->frames_per_second.store( - frames_per_second, std::memory_order_release); - if (fixed->timer.valid() && - read_state().lifecycle == - Frame_Policy_Lifecycle::running) - fixed->timer.start_periodic(frames_per_second); -} -double Fixed_Rate_Frame_Policy::frame_rate() const noexcept { - return fixed->frames_per_second.load(std::memory_order_acquire); -} -void Fixed_Rate_Frame_Policy::production_capacity_available() {} - -} diff --git a/kernel/src/kernel/Frame_Policy/Fixed_Rate_Frame_Policy.hpp b/kernel/src/kernel/Frame_Policy/Fixed_Rate_Frame_Policy.hpp deleted file mode 100644 index 01889b3..0000000 --- a/kernel/src/kernel/Frame_Policy/Fixed_Rate_Frame_Policy.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "Frame_Policy.hpp" -#include - -namespace aethera { - -class Fixed_Rate_Frame_Policy final : public Frame_Policy { -public: - explicit Fixed_Rate_Frame_Policy(Dependencies dependencies, - double frames_per_second = 30.0); - ~Fixed_Rate_Frame_Policy() override; - [[nodiscard]] Frame_Policy_Type type() const noexcept override; - [[nodiscard]] std::string_view name() const noexcept override; - void start() override; - void stop(Stop_Callback completed) override; - void set_frame_rate(double frames_per_second); - [[nodiscard]] double frame_rate() const noexcept; -private: - struct Private; - std::unique_ptr fixed; /* 固定频率时钟的唯一所有权。 */ - void production_capacity_available() override; -}; - -} diff --git a/kernel/src/kernel/Frame_Policy/Frame_Policy.cpp b/kernel/src/kernel/Frame_Policy/Frame_Policy.cpp deleted file mode 100644 index bc48305..0000000 --- a/kernel/src/kernel/Frame_Policy/Frame_Policy.cpp +++ /dev/null @@ -1,502 +0,0 @@ -#include "Frame_Policy.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace aethera { -namespace { - -std::uint64_t monotonic_nanoseconds( - std::chrono::steady_clock::time_point value = - std::chrono::steady_clock::now()) noexcept { - return static_cast( - std::chrono::duration_cast( - value.time_since_epoch()).count()); -} - -std::uint64_t duration_nanoseconds( - std::chrono::steady_clock::duration value) noexcept { - return static_cast(std::max( - 0, std::chrono::duration_cast(value).count())); -} - -} - -std::uint64_t Frame_Policy::Private::submission_generation( - const Frame_Policy* object) const noexcept { - return object->stream_submission_generation< - detail::Frame_Policy_Request_Stream_Tag>() + - object->stream_submission_generation< - detail::Frame_Policy_Scene_Stream_Tag>() + - object->stream_submission_generation< - detail::Frame_Policy_Control_Stream_Tag>() + - object->stream_submission_generation< - detail::Frame_Policy_Feedback_Stream_Tag>(); - } - -void Frame_Policy::Private::schedule(not_null object) { - bool idle{}; - if (!consumer_active.compare_exchange_strong( - idle, true, std::memory_order_acq_rel, - std::memory_order_acquire)) - return; - const auto weak = object->weak_from_this(); - try { - schedule_task("frame.policy.consume", [weak] { - const auto policy = weak.lock(); - if (!policy) return; - auto& data = double_buffer::detail::Internal_Access::layer< - Frame_Policy>(policy.get()); - data.consume(not_null{policy.get()}); - }); - } - catch (...) { - consumer_active.store(false, std::memory_order_release); - runtime->report_failure(std::current_exception()); - } - } - -void Frame_Policy::Private::submit_scene(not_null object, - detail::Frame_Policy_Scene_Message message) { - object->submit_stream( - std::move(message)); - schedule(object); - } - -void Frame_Policy::Private::drain_requests(not_null object, - Frame_Policy::State& state) { - object->exchange_stream(); - object->access_rendering_stream< - detail::Frame_Policy_Request_Stream_Tag>( - [&](std::span requests) { - const auto& prop = double_buffer::detail::Internal_Access:: - current_prop(object.get()); - for (auto& request : requests) { - if (state.lifecycle != Frame_Policy_Lifecycle::running || - !prop.render_enabled) { - ++state.observation.policy_rejection_count; - continue; - } - runtime->coalesce(std::move(request)); - } - }); - } - -void Frame_Policy::Private::drain_controls(not_null object, - Frame_Policy::State& state) { - object->exchange_stream(); - object->access_rendering_stream< - detail::Frame_Policy_Control_Stream_Tag>( - [&](std::span controls) { - for (auto& control : controls) { - std::visit([&](auto& value) { - using Value = std::remove_cvref_t; - if constexpr (std::same_as< - Value, - detail::Frame_Policy_Reset_Statistics>) { - ++runtime->reset_generation; - if (runtime->active_count() == 0) { - state.observation = {}; - state.observation.generation = - runtime->reset_generation; - runtime->statistics.reset(); - } - } - else if constexpr (std::same_as< - Value, - detail::Frame_Policy_Stop>) { - if (state.lifecycle != - Frame_Policy_Lifecycle::running) - return; - runtime->stop_callback = - std::move(value.completed); - runtime->pending_request.reset(); - state.lifecycle = - Frame_Policy_Lifecycle::stopping; - } - }, control); - } - }); - object->exchange_stream(); - object->access_rendering_stream< - detail::Frame_Policy_Feedback_Stream_Tag>( - [](std::span) {}); - } - -void Frame_Policy::Private::record_submission( - Frame_Slot& slot, Frame_Policy::State& state) { - ++state.observation.request_count; - ++state.observation.accepted_request_count; - ++state.observation.submitted_frame_count; - switch (slot.production.request.source) { - case Frame_Request_Source::periodic: - ++state.observation.periodic_request_count; - break; - case Frame_Request_Source::maximum_rate: - ++state.observation.maximum_rate_request_count; - break; - case Frame_Request_Source::immediate: - case Frame_Request_Source::unspecified: - ++state.observation.immediate_request_count; - break; - } - } - -void Frame_Policy::Private::complete_frame( - not_null object, Frame_Slot& slot, - std::chrono::steady_clock::time_point completed_at, - Frame_Policy::State& state, - bool& capacity_available) { - slot.state = Frame_State::publishing; - Frame_Publication_Dispatch dispatch{}; - try { - dispatch = runtime->publish_frame( - not_null{slot.frame.get()}, slot.production); - } - catch (...) { - runtime->report_failure(std::current_exception()); - } - auto& observation = state.observation; - const auto observed_ns = monotonic_nanoseconds(completed_at); - if (observation.generation != runtime->reset_generation) { - observation = {}; - observation.generation = runtime->reset_generation; - runtime->statistics.reset(); - } - if (observation.observation_started_ns == 0) - observation.observation_started_ns = observed_ns; - observation.observed_until_ns = observed_ns; - ++observation.completed_frame_count; - const auto submitted_at = - slot.submitted_at.time_since_epoch().count() == 0 - ? completed_at : slot.submitted_at; - const auto queue_ns = duration_nanoseconds( - submitted_at - slot.production.request.issued_at); - const auto completion_ns = duration_nanoseconds( - completed_at - submitted_at); - observation.latest_tick_queue_ns = queue_ns; - observation.tick_queue_total_ns += queue_ns; - observation.maximum_tick_queue_ns = std::max( - observation.maximum_tick_queue_ns, queue_ns); - observation.latest_completion_latency_ns = completion_ns; - observation.completion_latency_total_ns += completion_ns; - observation.maximum_completion_latency_ns = std::max( - observation.maximum_completion_latency_ns, completion_ns); - if (observation.first_completion_ns == 0) - observation.first_completion_ns = observed_ns; - if (observation.last_completion_ns != 0) { - const auto interval = observed_ns - observation.last_completion_ns; - ++observation.completion_interval_count; - observation.latest_completion_interval_ns = interval; - observation.completion_interval_total_ns += interval; - const auto value = static_cast(interval); - observation.completion_interval_squared_total_ns2 += value * value; - } - observation.last_completion_ns = observed_ns; - observation.last_frame_sequence = slot.production.sequence; - observation.last_request_source = slot.production.request.source; - observation.frame_statistics = runtime->statistics.submit(*slot.frame); - observation.pixel_width = dispatch.pixel_width; - observation.pixel_height = dispatch.pixel_height; - if (slot.released_after_submission) - ++observation.overlapped_release_count; - else - ++observation.completion_gated_release_count; - if (dispatch.completed_at >= dispatch.started_at && - dispatch.started_at.time_since_epoch().count() != 0) { - const auto publication_ns = duration_nanoseconds( - dispatch.completed_at - dispatch.started_at); - observation.latest_publication_dispatch_latency_ns = publication_ns; - observation.publication_dispatch_latency_total_ns += publication_ns; - observation.maximum_publication_dispatch_latency_ns = std::max( - observation.maximum_publication_dispatch_latency_ns, - publication_ns); - } - if (dispatch.succeeded || dispatch.asynchronous_feedback_count != 0) - ++observation.published_frame_count; - else - ++observation.publication_failed_count; - - slot.owner_lifetime.reset(); - slot.state = Frame_State::available; - if (!slot.released_after_submission) { - slot.producer_release_requested = true; - if (slot.render_returned && runtime->production_active) { - runtime->production_active = false; - capacity_available = true; - } - } - } - -void Frame_Policy::Private::drain_scene(not_null object, - Frame_Policy::State& state, - bool& capacity_available) { - object->exchange_stream(); - object->access_rendering_stream( - [&](std::span messages) { - for (auto& message : messages) { - std::visit([&](auto& value) { - using Value = std::remove_cvref_t; - if constexpr (std::same_as< - Value, - detail::Frame_Policy_Scene_Submitted>) { - auto* slot = runtime->find_frame(value.frame); - if (!slot) - throw std::logic_error( - "Scene submitted an unowned policy frame"); - if (slot->state == Frame_State::available) return; - const bool first_submission = - slot->submitted_at.time_since_epoch().count() == 0; - slot->submitted_at = value.submitted_at; - slot->released_after_submission = - value.released_after_submission; - slot->state = Frame_State::scene_borrowed; - if (first_submission) record_submission(*slot, state); - if (!value.released_after_submission) return; - slot->producer_release_requested = true; - if (slot->render_returned && - runtime->production_active) { - runtime->production_active = false; - capacity_available = true; - } - } - else if constexpr (std::same_as< - Value, - detail::Frame_Policy_Scene_Completed>) { - auto* slot = runtime->find_frame(value.frame); - if (!slot) - throw std::logic_error( - "Scene completed an unowned policy frame"); - if (slot->state == Frame_State::available) return; - complete_frame(object, *slot, value.completed_at, - state, capacity_available); - } - else if constexpr (std::same_as< - Value, - detail::Frame_Policy_Render_Returned>) { - auto* slot = runtime->find_frame(value.frame); - if (!slot) - throw std::logic_error( - "Scene returned an unowned policy frame"); - slot->render_returned = true; - if (value.result == Render_Frame_Result::submitted) { - if (slot->state == Frame_State::preparing) { - slot->submitted_at = - std::chrono::steady_clock::now(); - slot->state = Frame_State::scene_borrowed; - record_submission(*slot, state); - } - if (slot->producer_release_requested && - runtime->production_active) { - runtime->production_active = false; - capacity_available = true; - } - return; - } - if (slot->state != Frame_State::available) { - slot->owner_lifetime.reset(); - slot->state = Frame_State::available; - ++state.observation.scene_rejection_count; - } - if (runtime->production_active) { - runtime->production_active = false; - capacity_available = true; - } - } - }, message); - } - }); - } - -void Frame_Policy::Private::produce(not_null object, - const Frame_Policy::Prop& prop, - Frame_Policy::State& state) { - if (runtime->production_active || !runtime->pending_request || - state.lifecycle != Frame_Policy_Lifecycle::running || - !prop.render_enabled) - return; - auto* slot = runtime->acquire_frame(); - if (!slot) { - ++state.observation.frame_slot_backpressure_count; - return; - } - auto request = std::move(*runtime->pending_request); - runtime->pending_request.reset(); - runtime->production_active = true; - slot->owner_lifetime = runtime->retain_owner(); - slot->production = Frame_Production{ - std::move(request), runtime->next_sequence++}; - slot->submitted_at = {}; - slot->render_returned = false; - slot->producer_release_requested = false; - slot->released_after_submission = false; - try { - runtime->prepare_frame( - not_null{slot->frame.get()}, slot->production); - const auto weak = object->weak_from_this(); - const auto result = runtime->scene->render_frame( - not_null{slot->frame.get()}, - Render_Frame_Callbacks{ - .submitted = [weak](not_null frame, - bool released) { - const auto policy = weak.lock(); - if (!policy) return; - auto& data = double_buffer::detail::Internal_Access:: - layer(policy.get()); - data.submit_scene( - not_null{policy.get()}, - detail::Frame_Policy_Scene_Submitted{ - frame, std::chrono::steady_clock::now(), - released}); - }, - .completed = [weak](not_null frame) { - const auto policy = weak.lock(); - if (!policy) return; - auto& data = double_buffer::detail::Internal_Access:: - layer(policy.get()); - data.submit_scene( - not_null{policy.get()}, - detail::Frame_Policy_Scene_Completed{ - frame, std::chrono::steady_clock::now()}); - }}); - submit_scene( - object, - detail::Frame_Policy_Render_Returned{ - not_null{slot->frame.get()}, result}); - } - catch (...) { - slot->owner_lifetime.reset(); - slot->state = Frame_State::available; - runtime->production_active = false; - runtime->report_failure(std::current_exception()); - } - } - -void Frame_Policy::Private::finish_stop(Frame_Policy::State& state) { - if (state.lifecycle != Frame_Policy_Lifecycle::stopping || - runtime->production_active || runtime->active_count() != 0) - return; - state.lifecycle = Frame_Policy_Lifecycle::stopped; - runtime->pending_request.reset(); - auto callback = std::move(runtime->stop_callback); - if (callback) callback(); - } - -void Frame_Policy::Private::consume(not_null object) noexcept { - auto observed_generation = submission_generation(object.get()); - try { - for (;;) { - object->advance(); - auto& state = double_buffer::detail::Internal_Access:: - pending_state(object.get()); - drain_controls(object, state); - drain_requests(object, state); - bool capacity_available{}; - drain_scene(object, state, capacity_available); - const auto& prop = double_buffer::detail::Internal_Access:: - current_prop(object.get()); - produce(object, prop, state); - state.observation.active_frame_count = runtime->active_count(); - finish_stop(state); - object->publish_state(); - if (capacity_available && - state.lifecycle == Frame_Policy_Lifecycle::running) - object->production_capacity_available(); - const auto current_generation = - submission_generation(object.get()); - const bool can_produce = runtime->pending_request && - !runtime->production_active && - runtime->active_count() < runtime->frames.size() && - state.lifecycle == Frame_Policy_Lifecycle::running && - prop.render_enabled; - if (current_generation == observed_generation && !can_produce) { - observed_generation = current_generation; - break; - } - observed_generation = current_generation; - } - } - catch (...) { - runtime->report_failure(std::current_exception()); - } - consumer_active.store(false, std::memory_order_release); - if (submission_generation(object.get()) != observed_generation) - schedule(object); - } -Frame_Policy::Frame_Policy(Dependencies dependencies) { - const auto frame_capacity = dependencies.frame_capacity; - auto attached = std::make_unique< - double_buffer::detail::Attached_Private>( - this, memory_resource()); - d = attached.release(); - auto& private_data = - double_buffer::detail::Internal_Access::layer(this); - private_data.bind_private_crtp(this); - bind_object_crtp(); - private_data.initialize(std::move(dependencies)); - auto& prop = double_buffer::detail::Internal_Access::pending_prop(this); - prop.frame_capacity = frame_capacity; - advance(); -} - -Frame_Policy::~Frame_Policy() = default; - -void Frame_Policy::request_frame(Frame_Request request) { - request.source = Frame_Request_Source::immediate; - request_automatic_frame(std::move(request)); -} - -void Frame_Policy::request_automatic_frame(Frame_Request request) { - if (!std::isfinite(request.time_milliseconds) || - request.time_milliseconds < 0.0) - throw std::invalid_argument( - "frame request time must be finite and non-negative"); - if (request.width == 0 || request.height == 0) - throw std::invalid_argument("frame request extent must be non-zero"); - if (request.issued_at.time_since_epoch().count() == 0) - request.issued_at = std::chrono::steady_clock::now(); - submit_stream( - std::move(request)); - double_buffer::detail::Internal_Access::layer(this). - schedule(not_null{this}); -} - -void Frame_Policy::begin_stop(Stop_Callback completed) { - if (!completed) - throw std::invalid_argument( - "Frame Policy stop requires a completion callback"); - submit_stream( - detail::Frame_Policy_Stop{std::move(completed)}); - double_buffer::detail::Internal_Access::layer(this). - schedule(not_null{this}); -} - -void Frame_Policy::submit_publication_feedback( - Frame_Publication_Feedback feedback) noexcept { - try { - submit_stream( - std::move(feedback)); - double_buffer::detail::Internal_Access::layer(this). - schedule(not_null{this}); - } - catch (...) { - double_buffer::detail::Internal_Access::layer(this). - runtime->report_failure(std::current_exception()); - } -} - -void Frame_Policy::reset_statistics() { - submit_stream( - detail::Frame_Policy_Reset_Statistics{}); - double_buffer::detail::Internal_Access::layer(this). - schedule(not_null{this}); -} - -} diff --git a/kernel/src/kernel/Frame_Policy/Frame_Policy.hpp b/kernel/src/kernel/Frame_Policy/Frame_Policy.hpp deleted file mode 100644 index a23463b..0000000 --- a/kernel/src/kernel/Frame_Policy/Frame_Policy.hpp +++ /dev/null @@ -1,196 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace aethera { - -enum struct Frame_Policy_Lifecycle : std::uint8_t { running, stopping, stopped }; -enum struct Frame_Policy_Type : std::uint8_t { manual, fixed_rate, maximum_rate }; - -struct Frame_Request { - std::chrono::steady_clock::time_point issued_at{}; - std::uint64_t sequence{}; - double time_milliseconds{}; - std::uint32_t width{320}; - std::uint32_t height{192}; - Frame_Request_Source source{Frame_Request_Source::immediate}; - bool operator==(const Frame_Request&) const = default; -}; - -struct Frame_Production { - Frame_Request request{}; - std::uint64_t sequence{}; -}; - -struct Frame_Publication_Feedback { - std::chrono::steady_clock::time_point completed_at{}; - std::uint64_t sequence{}; - bool succeeded{}; -}; - -struct Frame_Publication_Dispatch { - std::chrono::steady_clock::time_point started_at{}; - std::chrono::steady_clock::time_point completed_at{}; - std::size_t asynchronous_feedback_count{}; - bool succeeded{}; - std::uint32_t pixel_width{}; - std::uint32_t pixel_height{}; -}; - -struct Frame_Policy_State { - std::uint64_t generation{}; - std::uint64_t observation_started_ns{}; - std::uint64_t observed_until_ns{}; - std::uint64_t request_count{}; - std::uint64_t periodic_request_count{}; - std::uint64_t immediate_request_count{}; - std::uint64_t maximum_rate_request_count{}; - std::uint64_t accepted_request_count{}; - std::uint64_t policy_rejection_count{}; - std::uint64_t frame_slot_backpressure_count{}; - std::uint64_t scene_rejection_count{}; - std::uint64_t submitted_frame_count{}; - std::uint64_t completed_frame_count{}; - std::uint64_t active_frame_count{}; - std::uint64_t first_completion_ns{}; - std::uint64_t last_completion_ns{}; - std::uint64_t completion_interval_count{}; - std::uint64_t latest_completion_interval_ns{}; - std::uint64_t completion_interval_total_ns{}; - double completion_interval_squared_total_ns2{}; - std::uint64_t latest_tick_queue_ns{}; - std::uint64_t tick_queue_total_ns{}; - std::uint64_t maximum_tick_queue_ns{}; - std::uint64_t latest_completion_latency_ns{}; - std::uint64_t completion_latency_total_ns{}; - std::uint64_t maximum_completion_latency_ns{}; - std::uint64_t latest_publication_dispatch_latency_ns{}; - std::uint64_t publication_dispatch_latency_total_ns{}; - std::uint64_t maximum_publication_dispatch_latency_ns{}; - std::uint64_t published_frame_count{}; - std::uint64_t publication_failed_count{}; - std::uint64_t last_frame_sequence{}; - Frame_Request_Source last_request_source{}; - std::uint64_t overlapped_release_count{}; - std::uint64_t completion_gated_release_count{}; - Frame_Statistics_State frame_statistics{}; - std::uint32_t pixel_width{}; - std::uint32_t pixel_height{}; - bool operator==(const Frame_Policy_State&) const = default; -}; - -using Frame_Policy_Stop_Callback = std::move_only_function; - -namespace detail { -struct Frame_Policy_Request_Stream_Tag {}; -struct Frame_Policy_Scene_Stream_Tag {}; -struct Frame_Policy_Control_Stream_Tag {}; -struct Frame_Policy_Feedback_Stream_Tag {}; - -struct Frame_Policy_Scene_Submitted { - not_null frame; - std::chrono::steady_clock::time_point submitted_at{}; - bool released_after_submission{}; -}; -struct Frame_Policy_Scene_Completed { - not_null frame; - std::chrono::steady_clock::time_point completed_at{}; -}; -struct Frame_Policy_Render_Returned { - not_null frame; - Render_Frame_Result result{Render_Frame_Result::backend_unavailable}; -}; -using Frame_Policy_Scene_Message = std::variant< - std::monostate, - Frame_Policy_Scene_Submitted, - Frame_Policy_Scene_Completed, - Frame_Policy_Render_Returned>; - -struct Frame_Policy_Reset_Statistics {}; -struct Frame_Policy_Stop { Frame_Policy_Stop_Callback completed{}; }; -using Frame_Policy_Control = std::variant< - std::monostate, - Frame_Policy_Reset_Statistics, - Frame_Policy_Stop>; -} - -class Frame_Policy : - public double_buffer::Def< - Frame_Policy, - double_buffer::Root, - double_buffer::Mpmc_Triple_Buffer< - detail::Frame_Policy_Request_Stream_Tag, Frame_Request>, - double_buffer::Mpmc_Triple_Buffer< - detail::Frame_Policy_Scene_Stream_Tag, - detail::Frame_Policy_Scene_Message>, - double_buffer::Mpmc_Triple_Buffer< - detail::Frame_Policy_Control_Stream_Tag, - detail::Frame_Policy_Control>, - double_buffer::Mpmc_Triple_Buffer< - detail::Frame_Policy_Feedback_Stream_Tag, - Frame_Publication_Feedback>>, - public std::enable_shared_from_this { -public: - using Stop_Callback = Frame_Policy_Stop_Callback; - using Frame_Creator = std::move_only_function()>; - using Frame_Preparer = std::move_only_function, const Frame_Production&)>; - using Frame_Publisher = std::move_only_function, const Frame_Production&)>; - using Owner_Retainer = std::move_only_function()>; - using Failure_Handler = std::move_only_function; - - struct Prop : Prev_Prop { - std::size_t frame_capacity{3}; - bool render_enabled{true}; - bool pixel_delivery_enabled{true}; - bool operator==(const Prop&) const = default; - }; - struct State : Prev_State { - Frame_Policy_State observation{}; - Frame_Policy_Lifecycle lifecycle{Frame_Policy_Lifecycle::running}; - bool operator==(const State&) const = default; - }; - struct Private; - - struct Dependencies { - not_null scene; - Frame_Creator create_frame{}; - Frame_Preparer prepare_frame{}; - Frame_Publisher publish_frame{}; - Owner_Retainer retain_owner{}; - Failure_Handler report_failure{}; - std::size_t frame_capacity{3}; - }; - - virtual ~Frame_Policy(); - Frame_Policy(const Frame_Policy&) = delete; - Frame_Policy& operator=(const Frame_Policy&) = delete; - [[nodiscard]] virtual Frame_Policy_Type type() const noexcept = 0; - [[nodiscard]] virtual std::string_view name() const noexcept = 0; - virtual void start() = 0; - virtual void stop(Stop_Callback completed) = 0; - void request_frame(Frame_Request request); - void submit_publication_feedback(Frame_Publication_Feedback feedback) noexcept; - void reset_statistics(); - -protected: - explicit Frame_Policy(Dependencies dependencies); - void request_automatic_frame(Frame_Request request); - void begin_stop(Stop_Callback completed); - virtual void production_capacity_available() = 0; -}; - -} - -#include "Frame_Policy.ipp" diff --git a/kernel/src/kernel/Frame_Policy/Frame_Policy.ipp b/kernel/src/kernel/Frame_Policy/Frame_Policy.ipp deleted file mode 100644 index cf66954..0000000 --- a/kernel/src/kernel/Frame_Policy/Frame_Policy.ipp +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace aethera { - -struct Frame_Policy::Private : Prev_Private { - enum struct Frame_State : std::uint8_t { - available, - preparing, - scene_borrowed, - publishing - }; - struct Frame_Slot { - std::unique_ptr frame{}; - Frame_State state{Frame_State::available}; - Frame_Production production{}; - std::chrono::steady_clock::time_point submitted_at{}; - std::shared_ptr owner_lifetime{}; - bool render_returned{}; - bool producer_release_requested{}; - bool released_after_submission{}; - }; - struct Runtime { - not_null scene; - Frame_Creator create_frame{}; - Frame_Preparer prepare_frame{}; - Frame_Publisher publish_frame{}; - Owner_Retainer retain_owner{}; - Failure_Handler report_failure{}; - std::vector> frames{}; - std::optional pending_request{}; - Frame_Statistics_Accumulator statistics{600}; - Stop_Callback stop_callback{}; - std::uint64_t next_sequence{1}; - std::uint64_t reset_generation{}; - bool production_active{}; - - explicit Runtime(Dependencies dependencies) - : scene(dependencies.scene), - create_frame(std::move(dependencies.create_frame)), - prepare_frame(std::move(dependencies.prepare_frame)), - publish_frame(std::move(dependencies.publish_frame)), - retain_owner(std::move(dependencies.retain_owner)), - report_failure(std::move(dependencies.report_failure)) { - if (!create_frame || !prepare_frame || !publish_frame || - !retain_owner || !report_failure) - throw std::invalid_argument( - "Frame Policy requires complete runtime dependencies"); - if (dependencies.frame_capacity == 0) - throw std::invalid_argument( - "Frame Policy requires at least one frame slot"); - frames.reserve(dependencies.frame_capacity); - for (std::size_t index = 0; - index < dependencies.frame_capacity; ++index) { - auto slot = std::make_unique(); - slot->frame = create_frame(); - if (!slot->frame) - throw std::logic_error( - "Frame Policy creator returned an empty frame"); - frames.push_back(std::move(slot)); - } - } - - [[nodiscard]] Frame_Slot* find_frame( - not_null frame) noexcept { - const auto found = std::ranges::find_if( - frames, [frame](const auto& slot) { - return slot->frame.get() == frame.get(); - }); - return found == frames.end() ? nullptr : found->get(); - } - [[nodiscard]] std::size_t active_count() const noexcept { - return static_cast(std::ranges::count_if( - frames, [](const auto& slot) { - return slot->state != Frame_State::available; - })); - } - [[nodiscard]] Frame_Slot* acquire_frame() noexcept { - const auto found = std::ranges::find_if( - frames, [](const auto& slot) { - return slot->state == Frame_State::available; - }); - if (found == frames.end()) return nullptr; - (*found)->state = Frame_State::preparing; - return found->get(); - } - void coalesce(Frame_Request request) { - if (pending_request && - pending_request->source == Frame_Request_Source::immediate && - request.source != Frame_Request_Source::immediate) - return; - pending_request = std::move(request); - } - }; - - std::unique_ptr runtime{}; - std::atomic_bool consumer_active{}; - - void initialize(Dependencies dependencies) { - runtime = std::make_unique(std::move(dependencies)); - } - template - void after_prop_set(Frame_Policy* object, Member Owner::*, - double_buffer::Prop_Access) { - schedule(not_null{object}); - } - [[nodiscard]] std::uint64_t submission_generation( - const Frame_Policy* object) const noexcept; - void schedule(not_null object); - void submit_scene(not_null object, - detail::Frame_Policy_Scene_Message message); - void drain_requests(not_null object, - Frame_Policy::State& state); - void drain_controls(not_null object, - Frame_Policy::State& state); - void record_submission(Frame_Slot& slot, Frame_Policy::State& state); - void complete_frame(not_null object, Frame_Slot& slot, - std::chrono::steady_clock::time_point completed_at, - Frame_Policy::State& state, - bool& capacity_available); - void drain_scene(not_null object, - Frame_Policy::State& state, - bool& capacity_available); - void produce(not_null object, - const Frame_Policy::Prop& prop, - Frame_Policy::State& state); - void finish_stop(Frame_Policy::State& state); - void consume(not_null object) noexcept; -}; - -} diff --git a/kernel/src/kernel/Frame_Policy/Frame_Scheduler.cpp b/kernel/src/kernel/Frame_Policy/Frame_Scheduler.cpp deleted file mode 100644 index 3be87e7..0000000 --- a/kernel/src/kernel/Frame_Policy/Frame_Scheduler.cpp +++ /dev/null @@ -1,340 +0,0 @@ -#include "Frame_Scheduler.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace aethera { -namespace { -using Scheduler_Clock = Frame_Scheduler::Clock; -using Nanoseconds = std::chrono::nanoseconds; - -::Tick duration_to_ticks(Nanoseconds duration) noexcept { - if (duration <= Nanoseconds::zero()) return 1; - const auto quantum = std::chrono::duration_cast( - Frame_Scheduler::tick_duration).count(); - const auto value = duration.count(); - const auto ticks = (value + quantum - 1) / quantum; - return static_cast<::Tick>(std::max(1, ticks)); -} - -Nanoseconds fps_period(double fps) { - if (!std::isfinite(fps) || fps <= 0.0) - throw std::invalid_argument("frame scheduler fps must be positive"); - const auto count = static_cast( - std::llround(1'000'000'000.0 / fps)); - if (count <= 0) - throw std::invalid_argument("frame scheduler fps exceeds nanosecond resolution"); - return Nanoseconds{count}; -} -} - -struct Frame_Scheduler::Timer::State { - enum struct Mode : std::uint8_t { stopped, once, periodic }; - - enum struct Command_Type : std::uint8_t { - periodic, - once, - cancel, - destroy, - stop - }; - struct Command { - Command_Type type{}; - std::shared_ptr state{}; - Nanoseconds value{}; - }; - struct Command_Ingress { - moodycamel::BlockingConcurrentQueue commands{256}; - std::atomic_bool accepting{true}; - - [[nodiscard]] bool enqueue(Command command) { - if (command.type != Command_Type::destroy && - command.type != Command_Type::stop && - !accepting.load(std::memory_order_acquire)) - return false; - return commands.enqueue(std::move(command)); - } - }; - - not_null owner; - std::weak_ptr ingress; - std::uint64_t id{}; - Handler handler{}; - std::atomic_bool alive{true}; - Mode mode{Mode::stopped}; /* 仅 Scheduler thread 访问。 */ - Nanoseconds period{}; /* 仅 Scheduler thread 访问。 */ - Scheduler_Clock::time_point next_deadline{}; /* 真实 deadline,不按 tick 累积。 */ - TimerEvent> event; - - State(not_null value_owner, - const std::shared_ptr& value_ingress, - std::uint64_t value_id, - Handler value_handler); -}; - -struct Frame_Scheduler::Private { - using Command_Type = Timer::State::Command_Type; - using Command = Timer::State::Command; - using Command_Ingress = Timer::State::Command_Ingress; - - std::shared_ptr ingress{ - std::make_shared()}; - TimerWheel wheel{}; - Scheduler_Clock::time_point origin{Scheduler_Clock::now()}; - Scheduler_Clock::time_point last_advance{origin}; - std::atomic_uint64_t next_id{1}; - std::atomic_bool stopping{}; - std::jthread thread{}; - - Private() : thread([this](std::stop_token stop) { run(stop); }) {} - - ~Private() { - ingress->accepting.store(false, std::memory_order_release); - stopping.store(true, std::memory_order_release); - thread.request_stop(); - enqueue({Command_Type::stop}); - } - - void enqueue(Command command) { - if (!ingress->enqueue(std::move(command))) std::terminate(); - } - - std::shared_ptr make_timer(Handler handler) { - if (!handler) throw std::invalid_argument("frame scheduler timer handler is empty"); - const auto id = next_id.fetch_add(1, std::memory_order_relaxed); - auto state = std::make_shared( - this, ingress, id, std::move(handler)); - return state; - } - - - ::Tick delay_ticks(Scheduler_Clock::time_point deadline, - Scheduler_Clock::time_point now) const noexcept { - return duration_to_ticks(std::chrono::duration_cast(deadline - now)); - } - - void schedule_state(Timer::State& state, - Scheduler_Clock::time_point now) { - if (!state.alive.load(std::memory_order_acquire) || - state.mode == Timer::State::Mode::stopped) - return; - state.event.cancel(); - wheel.schedule(&state.event, delay_ticks(state.next_deadline, now)); - } - - void process_command(Command command, - Scheduler_Clock::time_point now) { - auto& state = command.state; - if (!state) return; - switch (command.type) { - case Command_Type::periodic: - if (!state->alive.load(std::memory_order_acquire)) break; - state->mode = Timer::State::Mode::periodic; - state->period = command.value; - /* 同频 Plot 不要永久锁在同一 deadline 上。稳定 timer id 给 - * 每个周期分配固定 phase;时间值仍来自同一全局 origin。 */ - { - constexpr std::uint64_t phase_slots = 8; - const auto phase_slot = (state->id - 1U) % phase_slots; - const auto phase = Nanoseconds{ - state->period.count() * - static_cast(phase_slot) / - static_cast(phase_slots)}; - state->next_deadline = now + state->period + phase; - } - schedule_state(*state, now); - break; - case Command_Type::once: - if (!state->alive.load(std::memory_order_acquire)) break; - state->mode = Timer::State::Mode::once; - state->period = {}; - state->next_deadline = now + std::max(command.value, Nanoseconds{1}); - schedule_state(*state, now); - break; - case Command_Type::cancel: - state->event.cancel(); - state->mode = Timer::State::Mode::stopped; - break; - case Command_Type::destroy: - state->event.cancel(); - state->mode = Timer::State::Mode::stopped; - break; - case Command_Type::stop: - break; - } - } - - void process_commands(Scheduler_Clock::time_point now) { - Command command; - while (ingress->commands.try_dequeue(command)) { - process_command(std::move(command), now); - now = Scheduler_Clock::now(); - } - } - - void fire(Timer::State& state) noexcept { - if (!state.alive.load(std::memory_order_acquire)) return; - const auto issued_at = Scheduler_Clock::now(); - const ::Tick sequence = wheel.now(); - try { - state.handler(Frame_Scheduler::Tick{ - issued_at, - static_cast(sequence), - std::chrono::duration( - issued_at - origin).count()}); - } - catch (...) { - /* Timer callback 是控制面入口,不能因为业务异常终止全局时钟。 */ - } - if (!state.alive.load(std::memory_order_acquire)) return; - if (state.mode == Timer::State::Mode::once) { - state.mode = Timer::State::Mode::stopped; - return; - } - if (state.mode != Timer::State::Mode::periodic || - state.period <= Nanoseconds::zero()) - return; - - /* callback 自身也可能消耗控制线程时间;重取 now,避免用触发前时间 - * 重排 deadline 导致已经过期的周期再次被排入 timer wheel。 */ - const auto reschedule_now = Scheduler_Clock::now(); - state.next_deadline += state.period; - if (state.next_deadline <= reschedule_now) { - const auto missed = - (reschedule_now - state.next_deadline) / state.period + 1; - state.next_deadline += state.period * missed; - } - schedule_state(state, reschedule_now); - } - - void advance_to(Scheduler_Clock::time_point now) { - const auto elapsed = std::chrono::duration_cast(now - last_advance); - const auto quantum = std::chrono::duration_cast( - Frame_Scheduler::tick_duration); - if (elapsed < quantum) return; - const auto ticks = static_cast<::Tick>(elapsed / quantum); - bool finished = wheel.advance(ticks, 256); - while (!finished) finished = wheel.advance(0, 256); - last_advance += quantum * static_cast(ticks); - } - - void run(std::stop_token stop) noexcept { - try { - while (!stop.stop_requested() && - !stopping.load(std::memory_order_acquire)) { - auto now = Scheduler_Clock::now(); - process_commands(now); - advance_to(now); - - if (stop.stop_requested() || - stopping.load(std::memory_order_acquire)) - break; - - constexpr ::Tick max_sleep_ticks = 10'000; /* 最多 1 s,便于可靠处理停止。 */ - const ::Tick next = wheel.ticks_to_next_event(max_sleep_ticks); - const auto deadline = last_advance + - Frame_Scheduler::tick_duration * static_cast(next); - Command command; - if (ingress->commands.wait_dequeue_timed( - command, std::max(deadline - Scheduler_Clock::now(), - Scheduler_Clock::duration::zero()))) - process_command(std::move(command), Scheduler_Clock::now()); - } - } - catch (...) { - stopping.store(true, std::memory_order_release); - } - } -}; - -Frame_Scheduler::Timer::State::State( - not_null value_owner, - const std::shared_ptr& value_ingress, - std::uint64_t value_id, - Handler value_handler) - : owner(value_owner), ingress(value_ingress), id(value_id), - handler(std::move(value_handler)), - event([this] { - owner->fire(*this); - }) {} - -Frame_Scheduler::Frame_Scheduler() : d(std::make_unique()) {} -Frame_Scheduler::~Frame_Scheduler() = default; - -Frame_Scheduler& Frame_Scheduler::instance() { - static Frame_Scheduler scheduler; - return scheduler; -} - -Frame_Scheduler::Timer Frame_Scheduler::make_timer(Handler handler) { - return Timer(d->make_timer(std::move(handler))); -} - -Frame_Scheduler::Timer::Timer(std::shared_ptr state) noexcept - : state_(std::move(state)) {} - -Frame_Scheduler::Timer::~Timer() noexcept { - if (!state_) return; - state_->alive.store(false, std::memory_order_release); - const auto ingress = state_->ingress.lock(); - if (ingress && !ingress->enqueue( - {Private::Command_Type::destroy, state_, {}})) - std::terminate(); -} - -Frame_Scheduler::Timer::Timer(Timer&& other) noexcept - : state_(std::exchange(other.state_, {})) {} - -Frame_Scheduler::Timer& Frame_Scheduler::Timer::operator=(Timer&& other) noexcept { - if (this == &other) return *this; - this->~Timer(); - new (this) Timer(std::move(other)); - return *this; -} - -void Frame_Scheduler::Timer::start_periodic(double fps) { - if (!state_) throw std::logic_error("frame scheduler timer is empty"); - if (fps <= 0.0) { - cancel(); - return; - } - state_->alive.store(true, std::memory_order_release); - const auto ingress = state_->ingress.lock(); - if (!ingress || !ingress->enqueue( - {Private::Command_Type::periodic, state_, fps_period(fps)})) { - state_->alive.store(false, std::memory_order_release); - throw std::runtime_error("frame scheduler is stopping"); - } -} - -void Frame_Scheduler::Timer::start_once(std::chrono::nanoseconds delay) { - if (!state_) throw std::logic_error("frame scheduler timer is empty"); - state_->alive.store(true, std::memory_order_release); - const auto ingress = state_->ingress.lock(); - if (!ingress || !ingress->enqueue( - {Private::Command_Type::once, state_, delay})) { - state_->alive.store(false, std::memory_order_release); - throw std::runtime_error("frame scheduler is stopping"); - } -} - -void Frame_Scheduler::Timer::cancel() noexcept { - if (!state_) return; - state_->alive.store(false, std::memory_order_release); - const auto ingress = state_->ingress.lock(); - if (ingress) static_cast(ingress->enqueue( - {Private::Command_Type::cancel, state_, {}})); -} - -bool Frame_Scheduler::Timer::valid() const noexcept { - return static_cast(state_); -} - -} diff --git a/kernel/src/kernel/Frame_Policy/Frame_Scheduler.hpp b/kernel/src/kernel/Frame_Policy/Frame_Scheduler.hpp deleted file mode 100644 index 5d09301..0000000 --- a/kernel/src/kernel/Frame_Policy/Frame_Scheduler.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace aethera { - -/* - * 全局轻量时间调度器。 - * - * - 整个进程只有一个控制线程。 - * - 底层使用 Ratas hierarchical timer wheel。 - * - callback 在控制线程执行,因此 callback 必须只做轻量控制工作, - * 真正 CPU 工作应继续投递到 Taskflow。 - * - Timer 是 RAII 句柄;每个 Scene/服务只持有轻量 Timer,不持有线程。 - */ -struct Frame_Scheduler final { - using Clock = std::chrono::steady_clock; - - struct Tick { - Clock::time_point issued_at{}; - std::uint64_t sequence{}; /* 全局时间轮 tick,所有 2D/3D Scene 共用同一时间轴。 */ - double time_milliseconds{}; /* 从全局 Scheduler origin 开始的单调毫秒。 */ - }; - - using Handler = std::function; - - struct Timer final { - public: - Timer() = default; - ~Timer() noexcept; - Timer(const Timer&) = delete; - Timer& operator=(const Timer&) = delete; - Timer(Timer&& other) noexcept; - Timer& operator=(Timer&& other) noexcept; - - /* 固定频率;fps <= 0 等价于 cancel。 */ - void start_periodic(double fps); - /* 单次延迟;delay <= 0 会在下一个 Scheduler 周期尽快触发。 */ - void start_once(std::chrono::nanoseconds delay); - void cancel() noexcept; - [[nodiscard]] bool valid() const noexcept; - - private: - struct State; - explicit Timer(std::shared_ptr state) noexcept; - std::shared_ptr state_{}; - friend struct Frame_Scheduler; - }; - - [[nodiscard]] static Frame_Scheduler& instance(); - [[nodiscard]] Timer make_timer(Handler handler); - - /* Ratas 量化粒度;策略层仍保存真实 deadline,避免周期累计漂移。 */ - static constexpr std::chrono::microseconds tick_duration{100}; - -private: - Frame_Scheduler(); - ~Frame_Scheduler(); - Frame_Scheduler(const Frame_Scheduler&) = delete; - Frame_Scheduler& operator=(const Frame_Scheduler&) = delete; - - struct Private; - std::unique_ptr d; -}; - -} diff --git a/kernel/src/kernel/Frame_Policy/Manual_Frame_Policy.cpp b/kernel/src/kernel/Frame_Policy/Manual_Frame_Policy.cpp deleted file mode 100644 index 678a830..0000000 --- a/kernel/src/kernel/Frame_Policy/Manual_Frame_Policy.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "Manual_Frame_Policy.hpp" - -namespace aethera { - -Manual_Frame_Policy::Manual_Frame_Policy(Dependencies dependencies) - : Frame_Policy(std::move(dependencies)) {} -Manual_Frame_Policy::~Manual_Frame_Policy() = default; -Frame_Policy_Type Manual_Frame_Policy::type() const noexcept { - return Frame_Policy_Type::manual; -} -std::string_view Manual_Frame_Policy::name() const noexcept { - return "manual"; -} -void Manual_Frame_Policy::start() {} -void Manual_Frame_Policy::stop(Stop_Callback completed) { - begin_stop(std::move(completed)); -} -void Manual_Frame_Policy::production_capacity_available() {} - -} diff --git a/kernel/src/kernel/Frame_Policy/Manual_Frame_Policy.hpp b/kernel/src/kernel/Frame_Policy/Manual_Frame_Policy.hpp deleted file mode 100644 index 71d6988..0000000 --- a/kernel/src/kernel/Frame_Policy/Manual_Frame_Policy.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "Frame_Policy.hpp" - -namespace aethera { - -class Manual_Frame_Policy final : public Frame_Policy { -public: - explicit Manual_Frame_Policy(Dependencies dependencies); - ~Manual_Frame_Policy() override; - [[nodiscard]] Frame_Policy_Type type() const noexcept override; - [[nodiscard]] std::string_view name() const noexcept override; - void start() override; - void stop(Stop_Callback completed) override; -private: - void production_capacity_available() override; -}; - -} diff --git a/kernel/src/kernel/Frame_Policy/Maximum_Rate_Frame_Policy.cpp b/kernel/src/kernel/Frame_Policy/Maximum_Rate_Frame_Policy.cpp deleted file mode 100644 index 02e0323..0000000 --- a/kernel/src/kernel/Frame_Policy/Maximum_Rate_Frame_Policy.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "Maximum_Rate_Frame_Policy.hpp" - -namespace aethera { - -struct Maximum_Rate_Frame_Policy::Private { - std::chrono::steady_clock::time_point clock_origin{ - std::chrono::steady_clock::now()}; /* 自动帧动画时间原点。 */ -}; - -Maximum_Rate_Frame_Policy::Maximum_Rate_Frame_Policy( - Dependencies dependencies) - : Frame_Policy(std::move(dependencies)), - maximum(std::make_unique()) {} -Maximum_Rate_Frame_Policy::~Maximum_Rate_Frame_Policy() = default; -Frame_Policy_Type Maximum_Rate_Frame_Policy::type() const noexcept { - return Frame_Policy_Type::maximum_rate; -} -std::string_view Maximum_Rate_Frame_Policy::name() const noexcept { - return "maximum_rate"; -} -void Maximum_Rate_Frame_Policy::start() { - production_capacity_available(); -} -void Maximum_Rate_Frame_Policy::stop(Stop_Callback completed) { - begin_stop(std::move(completed)); -} -void Maximum_Rate_Frame_Policy::production_capacity_available() { - if (read_state().lifecycle != - Frame_Policy_Lifecycle::running || - !read_prop().render_enabled) return; - const auto now = std::chrono::steady_clock::now(); - request_automatic_frame(Frame_Request{ - .issued_at = now, - .time_milliseconds = std::chrono::duration( - now - maximum->clock_origin).count(), - .source = Frame_Request_Source::maximum_rate}); -} - -} diff --git a/kernel/src/kernel/Frame_Policy/Maximum_Rate_Frame_Policy.hpp b/kernel/src/kernel/Frame_Policy/Maximum_Rate_Frame_Policy.hpp deleted file mode 100644 index 2489023..0000000 --- a/kernel/src/kernel/Frame_Policy/Maximum_Rate_Frame_Policy.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "Frame_Policy.hpp" -#include - -namespace aethera { - -class Maximum_Rate_Frame_Policy final : public Frame_Policy { -public: - explicit Maximum_Rate_Frame_Policy(Dependencies dependencies); - ~Maximum_Rate_Frame_Policy() override; - [[nodiscard]] Frame_Policy_Type type() const noexcept override; - [[nodiscard]] std::string_view name() const noexcept override; - void start() override; - void stop(Stop_Callback completed) override; -private: - struct Private; - std::unique_ptr maximum; /* 最大速率生产时间线的唯一所有权。 */ - void production_capacity_available() override; -}; - -} diff --git a/kernel/src/kernel/Taskflow_Frame_Access.hpp b/kernel/src/kernel/Taskflow_Frame_Access.hpp deleted file mode 100644 index 30d3964..0000000 --- a/kernel/src/kernel/Taskflow_Frame_Access.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "frame.hpp" -#include "Task_Graph.hpp" -#include "ownership.hpp" -#include -#include -#include - -namespace aethera::detail { -struct Taskflow_Graph_Token { - not_null frame; /* DAG 运行记录所属外部帧;帧完成前有效。 */ - std::size_t index{}; /* 帧内 graph 记录索引。 */ -}; -struct Taskflow_Graph_Timing { - std::chrono::steady_clock::time_point executor_finished{}; - std::chrono::steady_clock::time_point runtime_bookkeeping_finished{}; - std::chrono::steady_clock::time_point runtime_publish_finished{}; - std::chrono::steady_clock::time_point observer_unbind_finished{}; -}; -struct Taskflow_Frame_Access { - using Clock = std::chrono::steady_clock; - static void begin_capture(Render_Frame& frame, std::size_t workers); - static void finish_capture(Render_Frame& frame) noexcept; - [[nodiscard]] static std::size_t append_task( - Render_Frame& frame, std::size_t worker, std::uint64_t native_id, - std::size_t queue_size, std::size_t queue_capacity, - Clock::time_point entered, Clock::time_point started, - Clock::time_point finished, std::uint64_t cooperative_wait_ns, - std::span> - cooperative_waits); - static void finish_task_observer( - Render_Frame& frame, std::size_t worker, std::size_t task, - Clock::time_point completed) noexcept; - [[nodiscard]] static Taskflow_Graph_Token begin_graph( - Render_Frame& frame, Task_Graph& graph, std::string_view stage); - static void finish_graph(Taskflow_Graph_Token token, - const Taskflow_Graph_Timing& timing) noexcept; -}; -} diff --git a/kernel/src/kernel/double_buffer/Dependency_Graph.hpp b/kernel/src/kernel/double_buffer/Dependency_Graph.hpp deleted file mode 100644 index ea232aa..0000000 --- a/kernel/src/kernel/double_buffer/Dependency_Graph.hpp +++ /dev/null @@ -1,690 +0,0 @@ -#pragma once -#include "mechanism.hpp" -namespace double_buffer { -// Dependency_Graph 是有向无环依赖图。Node 保存对象和直接前驱;Edge 可选携带 dirty 来源键,整体对象边只参与拓扑排序。 -struct Dependency_Graph { - using Error = Dependency_Graph_Error; - struct Node { - using Bind = void (*)(Node*, Root*); - aethera::not_null object; - std::pmr::vector> dependencies; - Bind bind; - void* data{}; - Node(Root* value, std::pmr::memory_resource* resource) : Node(value, resource, nullptr) {} - Node(Root* value, std::pmr::memory_resource* resource, Bind value_bind) : object(value), dependencies(resource), bind(value_bind) {} - }; - struct Edge { - aethera::not_null target; - aethera::not_null source; - const void* source_key; - const void* target_tag; - const void* target_dirty_key; - bool operator==(const Edge&) const = default; - }; -private: - struct Dirty_Source { - aethera::not_null object; - const void* key; - bool operator==(const Dirty_Source&) const = default; - }; - struct Dirty_Source_Hash { - std::size_t operator()(const Dirty_Source& value) const { - auto object = std::hash{}(value.object.get()); - auto key = std::hash{}(value.key); - return object ^ (key + 0x9e3779b9 + (object << 6) + (object >> 2)); - } - }; - struct Dirty_Target { - aethera::not_null object; - const void* tag; - const void* dirty_key; - }; -public: - struct View { - protected: - aethera::not_null dependency_graph; - explicit View(const Dependency_Graph& value) : dependency_graph(&value) {} - friend struct Dependency_Graph; - public: - bool empty() const { - return dependency_graph->empty(); - } - std::size_t size() const { - return dependency_graph->size(); - } - const Node* find(Root* object) const { - return dependency_graph->find(object); - } - bool contains(Root* object) const { - return dependency_graph->contains(object); - } - bool depends_on(Root* target, Root* source) const { - return dependency_graph->depends_on(target, source); - } - std::vector dependents(Root* source) const { - return dependency_graph->dependents(source); - } - std::vector dependencies(Root* object) const { - return dependency_graph->dependencies(object); - } - std::vector find_edges(Root* target, Root* source) const { - return dependency_graph->find_edges(target, source); - } - template Callback> - void for_each_edge(Callback&& callback) const { - dependency_graph->for_each_edge(std::forward < Callback > (callback)); - } - }; - template - struct Typed_View : public View { - public: - using Object_Type = Object; - using Private_Type = typename Object::Private; - explicit Typed_View(const Dependency_Graph& value) : View(value) {} - [[nodiscard]] bool bound(const Node& node) const noexcept { - return node.data != nullptr; - } - [[nodiscard]] Object* object(const Node& node) const { - if (!bound(node)) return nullptr; - return static_cast(node.object.get()); - } - [[nodiscard]] Private_Type* private_data(const Node& node) const noexcept { - return static_cast(node.data); - } - [[nodiscard]] Private_Type* private_data(Root* root) const noexcept { - auto* node = this->find(root); - return node ? private_data(*node) : nullptr; - } - template Callback> - void for_each_bound(Callback&& callback) const { - this->dependency_graph->for_each( - [&](const Node& node) { - auto* data = private_data(node); - if (data) std::invoke( - callback, static_cast(node.object.get()), *data); - } - ); - } - template Callback> - std::expected for_each_topological_view(Callback&& callback) const { - return this->dependency_graph->for_each_topological( - [&](const Node& node) { - std::invoke(callback, *this, node); - } - ); - } - template Callback> - void for_each(Callback&& callback) const { - this->dependency_graph->for_each(std::forward < Callback > (callback)); - } - }; - // Editor 只操作待提交图;结构是否合法由编辑完成后的拓扑验证统一判断,不在每个底层操作重复检查。 - template - struct Editor : public View { - private: - aethera::not_null edit_dependency_graph; - const void* target_tag; - const void* target_dirty_key; - bool bind_node_data; - public: - using Object_Type = Bound_Object; - Editor(Dependency_Graph& value, const void* tag, const void* dirty_key) : Editor(value, tag, dirty_key, true) {} - Editor(Dependency_Graph& value, const void* tag, const void* dirty_key, bool value_bind_node_data) : View(value), - edit_dependency_graph(&value), - target_tag(tag), - target_dirty_key(dirty_key), - bind_node_data(value_bind_node_data) {} - void clear() { - edit_dependency_graph->reset(); - } - template Object> - Node* add(Object* object) { - return edit_dependency_graph->template add_node(object, bind_node_data); - } - template Object> - void add(std::initializer_list objects) { - for (auto* object : objects) edit_dependency_graph->template add_node(object, bind_node_data); - } - bool remove(Root* object) { - return edit_dependency_graph->remove_node(object); - } - template Target> - void clear_dependencies(Target* target) { - edit_dependency_graph->clear_dependencies_impl(target); - } - void remove_dependents(Root* source) { - edit_dependency_graph->remove_dependents_impl(source); - } - void disconnect(Root* object) { - edit_dependency_graph->disconnect_impl(object); - } - /* 添加整个对象依赖;只形成拓扑边,不注册 dirty 传播。 */ - template Target, detail::Dependency_Object Source> - Node* add_dependency(Target* target, Source* source) { - return edit_dependency_graph->template add_dependency_runtime(target, source, nullptr, nullptr, nullptr, bind_node_data); - } - /* 添加 Prop 字段级依赖;source 通过 set 修改该字段时目标阶段变脏。 */ - template Target, detail::Prop_Dependency_Source Source> - Node* add_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph->template add_dependency_runtime(target, source, detail::dependency_id>(), target_tag, target_dirty_key, bind_node_data); - } - /* 添加 Prop 声明层级依赖;Owner 声明的任一字段通过 set 修改时目标阶段变脏。 */ - template Target, detail::Prop_Layer_Dependency_Source Source> - Node* add_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph->template add_dependency_runtime(target, source, detail::dependency_id>(), target_tag, target_dirty_key, bind_node_data); - } - template Target, detail::State_Dependency_Source Source> - Node* add_dependency(Target* target, Source* source) { - return edit_dependency_graph->template add_dependency_runtime( - target, - source, - detail::dependency_id>(), - target_tag, - target_dirty_key, - bind_node_data - ); - } - /* 添加状态层级依赖;该 Tag 层任一字段通过 update_state 修改都会使目标阶段 dirty。 */ - template Target, detail::State_Layer_Dependency_Source Source> - Node* add_state_dependency(Target* target, Source* source) { - return edit_dependency_graph->template add_dependency_runtime( - target, - source, - detail::dependency_id>(), - target_tag, - target_dirty_key, - bind_node_data - ); - } - template Target, detail::Buffer_Dependency_Source Source> - Node* add_dependency(Target* target, Source* source) { - return edit_dependency_graph->template add_dependency_runtime( - target, - source, - detail::dependency_id>(), - target_tag, - target_dirty_key, - bind_node_data - ); - } - template Target, detail::Dependency_Object Source> - Node* add_dirty_dependency(Target* target, Source* source) { - return edit_dependency_graph->template add_dependency_runtime( - target, - source, - detail::dependency_id>(), - target_tag, - target_dirty_key, - bind_node_data - ); - } - template Target, detail::State_Dependency_Source Source> - bool remove_dependency(Target* target, Source* source) { - return edit_dependency_graph->remove_edge(target, source, detail::dependency_id>(), target_tag); - } - template Target, detail::Prop_Dependency_Source Source> - bool remove_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph->remove_edge(target, source, detail::dependency_id>(), target_tag); - } - template Target, detail::Prop_Layer_Dependency_Source Source> - bool remove_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph->remove_edge(target, source, detail::dependency_id>(), target_tag); - } - /* 移除指定 State Tag 的状态层级依赖,不影响同对象上的字段级依赖。 */ - template Target, detail::State_Layer_Dependency_Source Source> - bool remove_state_dependency(Target* target, Source* source) { - return edit_dependency_graph->remove_edge( - target, - source, - detail::dependency_id>(), - target_tag - ); - } - template Target, detail::Buffer_Dependency_Source Source> - bool remove_dependency(Target* target, Source* source) { - return edit_dependency_graph->remove_edge(target, source, detail::dependency_id>(), target_tag); - } - template Target> - bool remove_dependency(Target* target, Root* source) { - return edit_dependency_graph->remove_dependency_impl(target, source); - } - }; -private: - friend struct Root; - template - friend struct detail::Dependency_Graph_Storage; - template - friend struct detail::Dependency_Graph_Build_Storage; - aethera::not_null pmr_resource; - std::pmr::list list; - std::pmr::unordered_map> nodes; - std::pmr::vector edges; - std::pmr::unordered_multimap dirty_edges; - std::uint64_t structure_revision{}; - mutable bool bound{}; - std::optional> mirror{}; - Node* emplace_node(Root* object, Node::Bind bind) { - auto current = nodes.find(object); - if (current != nodes.end()) { - current->second->bind = bind; - return current->second.get(); - } - list.emplace_back(object, pmr_resource.get(), bind); - auto* result = &list.back(); - nodes.emplace(object, aethera::not_null{result}); - return result; - } - static bool contains_node_dependency(Node* node, Node* dependency) { - return std::find(node->dependencies.begin(), node->dependencies.end(), dependency) != node->dependencies.end(); - } - void add_node_dependency(Node* node, Node* dependency) { - if (!contains_node_dependency(node, dependency)) node->dependencies.push_back(dependency); - } - bool pair_has_edge(Root* target, Root* source) const { - return std::find_if( - edges.begin(), - edges.end(), - [&](const Edge& edge) { - return edge.target.get() == target && edge.source.get() == source; - } - ) != edges.end(); - } - void rebuild_node_dependencies() { - for (auto& node : list) node.dependencies.clear(); - for (const auto& edge : edges) { - auto target = nodes.find(edge.target.get()); - auto source = nodes.find(edge.source.get()); - if (target == nodes.end() || source == nodes.end()) continue; - add_node_dependency(target->second.get(), source->second.get()); - } - } - void rebuild_dirty_edges() { - dirty_edges.clear(); - for (const auto& edge : edges) { - if (!edge.source_key || !edge.target_tag || !edge.target_dirty_key) continue; - Dirty_Source source{edge.source, edge.source_key}; - auto [first, last] = dirty_edges.equal_range(source); - auto current = std::find_if( - first, - last, - [&](const auto& value) { - const auto& target = value.second; - return target.object == edge.target && - target.tag == edge.target_tag && - target.dirty_key == edge.target_dirty_key; - } - ); - if (current == last) dirty_edges.emplace(source, Dirty_Target{edge.target, edge.target_tag, edge.target_dirty_key}); - } - } - void rebuild() { - rebuild_node_dependencies(); - rebuild_dirty_edges(); - } - void add_edge( - Node* target, - Node* source, - const void* source_key, - const void* target_tag, - const void* target_dirty_key) { - Edge edge{target->object, source->object, source_key, target_tag, - target_dirty_key}; - if (std::find(edges.begin(), edges.end(), edge) != edges.end()) return; - edges.push_back(edge); - ++structure_revision; - rebuild(); - } - template - static Node::Bind node_bind() { - if constexpr (std::derived_from < Object, Bound_Object > && detail ::Bound_Dependency_Object < Object >) - { - return [](Node* node, Root* root) { - auto* object = static_cast(root); - if constexpr (requires { object->bind_dependency_graph_object(object); }) object->bind_dependency_graph_object(object); - node->data = static_cast(object->d); - }; - } - else - { - return nullptr; - } - } - void bind_nodes() { - for (auto& node : list) { - if (node.bind) node.bind(&node, node.object.get()); - } - } - template - Node* add_node(Object* object, bool bind_node) { - auto bind = node_bind(); - auto current = nodes.find(object); - if (current != nodes.end()) { - current->second->bind = bind; - if (bind_node && bind) bind(current->second.get(), object); - return current->second.get(); - } - auto* node = emplace_node(object, bind); - if (bind_node && bind) bind(node, object); - ++structure_revision; - return node; - } - void clear_data() { - dirty_edges.clear(); - edges.clear(); - nodes.clear(); - list.clear(); - } - void pair_with(Dependency_Graph& other) noexcept { - mirror = &other; - other.mirror = this; - } - void detach_destroyed(Root* object) { - remove_node(object); - if (mirror) (*mirror)->remove_node(object); - } - void reset() { - if (list.empty() && edges.empty()) return; - clear_data(); - ++structure_revision; - } - void copy_from(const Dependency_Graph& other) { - for (const auto& node : other.list) - emplace_node(node.object.get(), node.bind)->data = node.data; - edges = other.edges; - structure_revision = other.structure_revision; - rebuild(); - } - void swap_data(Dependency_Graph& other) noexcept { - list.swap(other.list); - nodes.swap(other.nodes); - edges.swap(other.edges); - dirty_edges.swap(other.dirty_edges); - std::swap(structure_revision, other.structure_revision); - } - template - Node* add_dependency_runtime( - Target* target, - Source* source, - const void* source_key, - const void* target_tag, - const void* target_dirty_key, - bool bind_node) { - auto* target_node = add_node(target, bind_node); - auto* source_node = add_node(source, bind_node); - add_edge(target_node, source_node, source_key, target_tag, target_dirty_key); - return source_node; - } - bool remove_edge(Root* target, Root* source, const void* source_key, const void* target_tag) { - auto old_size = edges.size(); - std::erase_if( - edges, - [&](const Edge& edge) { - return edge.target.get() == target && - edge.source.get() == source && - edge.source_key == source_key && - edge.target_tag == target_tag; - } - ); - if (old_size == edges.size()) return false; - ++structure_revision; - rebuild(); - return true; - } - bool remove_node(Root* object) { - auto current = nodes.find(object); - if (current == nodes.end()) return false; - Node* node = current->second.get(); - std::erase_if( - edges, - [&](const Edge& edge) { - return edge.target.get() == object || edge.source.get() == object; - } - ); - nodes.erase(current); - std::erase_if( - list, - [&](const Node& value) { - return &value == node; - } - ); - ++structure_revision; - rebuild(); - return true; - } - void clear_dependencies_impl(Root* target) { - auto old_size = edges.size(); - std::erase_if(edges, [&](const Edge& edge) { - return edge.target.get() == target; - }); - if (old_size == edges.size()) return; - ++structure_revision; - rebuild(); - } - void remove_dependents_impl(Root* source) { - auto old_size = edges.size(); - std::erase_if(edges, [&](const Edge& edge) { - return edge.source.get() == source; - }); - if (old_size == edges.size()) return; - ++structure_revision; - rebuild(); - } - void disconnect_impl(Root* object) { - auto old_size = edges.size(); - std::erase_if( - edges, - [&](const Edge& edge) { - return edge.target.get() == object || edge.source.get() == object; - } - ); - if (old_size == edges.size()) return; - ++structure_revision; - rebuild(); - } - bool remove_dependency_impl(Root* target, Root* source) { - auto old_size = edges.size(); - std::erase_if( - edges, - [&](const Edge& edge) { - return edge.target.get() == target && edge.source.get() == source; - } - ); - if (old_size == edges.size()) return false; - ++structure_revision; - rebuild(); - return true; - } - Dependency_Graph(const Dependency_Graph& other, std::pmr::memory_resource* resource) : Dependency_Graph(resource) { - copy_from(other); - } -public: - using allocator_type = std::pmr::polymorphic_allocator; - Dependency_Graph() : Dependency_Graph(std::pmr::get_default_resource()) {} - Dependency_Graph(std::allocator_arg_t, const allocator_type& allocator) : Dependency_Graph(allocator.resource()) {} - explicit Dependency_Graph(std::pmr::memory_resource* resource) : pmr_resource(resource), - list(resource), - nodes(resource), - edges(resource), - dirty_edges(resource) {} - Dependency_Graph(const Dependency_Graph& other) - : Dependency_Graph(other.pmr_resource.get()) { - copy_from(other); - } - Dependency_Graph& operator=(const Dependency_Graph& other) { - if (this == &other) return *this; - unbind(); - clear_data(); - copy_from(other); - return *this; - } - Dependency_Graph(Dependency_Graph&&) = delete; - Dependency_Graph& operator=(Dependency_Graph&&) = delete; - ~Dependency_Graph() { - unbind(); - } - void swap(Dependency_Graph& other) { - bool this_bound = bound; - bool other_bound = other.bound; - unbind(); - other.unbind(); - if (pmr_resource == other.pmr_resource) { - swap_data(other); - } - else { - Dependency_Graph this_value(*this, other.pmr_resource.get()); - Dependency_Graph other_value(other, pmr_resource.get()); - clear_data(); - other.clear_data(); - copy_from(other_value); - other.copy_from(this_value); - } - if (this_bound) bind(); - if (other_bound) other.bind(); - } - void bind() const { - if (bound) return; - for (const auto& node : list) - node.object->bind_dependency_graph(aethera::not_null{this}); - bound = true; - } - void unbind() const { - if (!bound) return; - for (const auto& node : list) - node.object->unbind_dependency_graph(aethera::not_null{this}); - bound = false; - } - bool empty() const { - return list.empty(); - } - std::size_t size() const { - return list.size(); - } - std::pmr::memory_resource* memory_resource() const noexcept { - return pmr_resource.get(); - } - Node* find(Root* object) { - auto current = nodes.find(object); - if (current == nodes.end()) return nullptr; - return current->second.get(); - } - const Node* find(Root* object) const { - auto current = nodes.find(object); - if (current == nodes.end()) return nullptr; - return current->second.get(); - } - bool contains(Root* object) const { - return nodes.contains(object); - } - bool depends_on(Root* target, Root* source) const { - return pair_has_edge(target, source); - } - std::vector dependents(Root* source) { - std::vector result; - for (auto& node : list) { - if (depends_on(node.object.get(), source)) result.push_back(&node); - } - return result; - } - std::vector dependents(Root* source) const { - std::vector result; - for (const auto& node : list) { - if (depends_on(node.object.get(), source)) result.push_back(&node); - } - return result; - } - std::vector dependencies(Root* object) const { - std::vector result; - const auto& values = nodes.at(object)->dependencies; - result.reserve(values.size()); - for (const auto dependency : values) result.push_back(dependency.get()); - return result; - } - std::vector find_edges(Root* target, Root* source) const { - std::vector result; - for (const auto& edge : edges) { - if (edge.target.get() == target && edge.source.get() == source) - result.push_back(edge); - } - return result; - } - template Callback> - void for_each_edge(Callback&& callback) const { - for (const auto& edge : edges) std::invoke(callback, edge); - } - // 拓扑结果既用于验证无环,也定义上层按照依赖顺序构图和遍历的稳定入口。 - std::expected, Error> topological_order() const { - enum struct Visit { - none, - visiting, - visited - }; - std::pmr::unordered_map visits{pmr_resource.get()}; - std::vector result; - result.reserve(list.size()); - for (const auto& node : list) visits.emplace(&node, Visit::none); - auto visit = [&](Self&& self, const Node* node) -> std::expected { - auto current = visits.find(node); - if (current == visits.end()) return std::unexpected(Error::missing_dependency); - if (current->second == Visit::visited) return {}; - if (current->second == Visit::visiting) return std::unexpected(Error::cycle); - current->second = Visit::visiting; - for (const auto dependency : node->dependencies) { - if (dependency.get() == node) - return std::unexpected(Error::self_dependency); - auto dependency_state = visits.find(dependency.get()); - if (dependency_state == visits.end()) return std::unexpected(Error::missing_dependency); - if (dependency_state->second == Visit::visiting) return std::unexpected(Error::cycle); - auto check = self(self, dependency.get()); - if (!check) return std::unexpected(check.error()); - } - current->second = Visit::visited; - result.push_back(node); - return {}; - }; - for (const auto& node : list) { - auto check = visit(visit, &node); - if (!check) return std::unexpected(check.error()); - } - return result; - } - template Callback> - void for_each(Callback&& callback) const { - for (const auto& node : list) std::invoke(callback, node); - } - template Callback> - std::expected for_each_topological(Callback&& callback) const { - auto result = topological_order(); - if (!result) return std::unexpected(result.error()); - for (const auto* node : *result) std::invoke(callback, *node); - return {}; - } - template Callback> - std::expected for_each_topological_view(Callback&& callback) const { - View view(*this); - return for_each_topological( - [&](const Node& node) { - std::invoke(callback, view, node); - } - ); - } - template - [[nodiscard]] Typed_View typed_view() const { - return Typed_View{*this}; - } - void emit(Root* source, const void* key) const { - auto [first, last] = dirty_edges.equal_range(Dirty_Source{source, key}); - for (; first != last; ++first) { - const auto& target = first->second; - target.object->mark_dirty_id(target.tag, target.dirty_key); - } - } -}; -inline Root::~Root() { - while (!dependency_graphs.empty()) { - const auto dependency_graph = dependency_graphs.back(); - dependency_graphs.pop_back(); - const_cast(dependency_graph.get())->detach_destroyed(this); - } - delete d; -} -} diff --git a/kernel/src/kernel/double_buffer/Dependency_Graph_Storage.hpp b/kernel/src/kernel/double_buffer/Dependency_Graph_Storage.hpp deleted file mode 100644 index 7d056c9..0000000 --- a/kernel/src/kernel/double_buffer/Dependency_Graph_Storage.hpp +++ /dev/null @@ -1,403 +0,0 @@ -#pragma once -#include "Dependency_Graph.hpp" -namespace double_buffer { -namespace detail { -template -concept Dependency_Graph_Edit_Callback_For = Dependency_Graph_List && Unique_Types::value && std::invocable< - Callback, - Dependency_Graph::Editor>&... ->; -template -concept Dependency_Graph_View_Node_Callback = Root_Derived && std::invocable&, const Dependency_Graph::Node&>; -template -concept Dependency_Graph_Current_Callback = std::invocable; -template -concept Dependency_Graph_Access_Callback_For = Dependency_Graph_List && Unique_Types::value && std::invocable&...>; -} -inline bool Root::mark_dirty_id(const void* tag, const void* dirty_key) { - if (std::find(dirty_tags.begin(), dirty_tags.end(), tag) != dirty_tags.end()) return false; - dirty_tags.push_back(tag); - for (const auto dependency_graph : dependency_graphs) - dependency_graph->emit(this, dirty_key); - return true; -} -inline void Root::emit_dependency(const void* key) { - for (const auto dependency_graph : dependency_graphs) - dependency_graph->emit(this, key); -} -namespace detail { -template -struct Dependency_Graph_Storage; -// Dependency_Graph_Storage 为每个 Dependency_Graph_Type 保存 pending/current 两份图;编辑只落到 pending,advance 后 current 成为执行侧并同步新的编辑基线。 -template -struct Dependency_Graph_Storage> { - using allocator_type = std::pmr::polymorphic_allocator; -private: - template - struct Dependency_Graph_Buffer : Commit_Double_Buffer, Mechanism { - using Base = Commit_Double_Buffer; - using allocator_type = typename Base::allocator_type; - Dependency_Graph_Buffer() = default; - Dependency_Graph_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} - void mark_structure_dirty() noexcept { - this->mark_dirty(); - } - }; - std::tuple...> dependency_graphs; - void pair_buffers() { - std::apply( - [](auto&... buffer) { - (buffer.pending->pair_with(*buffer.current), ...); - }, - dependency_graphs - ); - } - template - static consteval std::size_t index() { - return tag_index(); - } - template - std::expected validate_pending_impl() const { - if constexpr (I == sizeof...(Dependency_Graph_Types)) { - return {}; - } - else { - auto check = std::get(dependency_graphs).pending->topological_order(); - if (!check) return std::unexpected(check.error()); - return validate_pending_impl(); - } - } - template - static void advance_one(Dependency_Graph_Buffer& buffer) { - // pending/current revision 相同表示上次推进后没有结构编辑,无需再次深拷贝整张依赖图。 - if (buffer.pending->structure_revision == buffer.current->structure_revision) return; - buffer.current->unbind(); - buffer.advance(); - buffer.current->bind(); - } -public: - Dependency_Graph_Storage() : Dependency_Graph_Storage(std::allocator_arg, allocator_type{std::pmr::get_default_resource()}) {} - Dependency_Graph_Storage(std::allocator_arg_t, const allocator_type& allocator) : dependency_graphs(std::allocator_arg, allocator) { - pair_buffers(); - } - template > Tag> - auto& get() { - return std::get()>(dependency_graphs); - } - template > Tag> - const auto& get() const { - return std::get()>(dependency_graphs); - } - template >... Tags, Dependency_Graph_Access_Callback_For, Tags...> Callback> - void access(Callback&& callback) { - std::invoke( - std::forward(callback), - static_cast>&>(get())... - ); - } - // 编辑采用临时图事务:先复制 pending,回调修改临时图并统一验证,成功后才替换 pending,失败不会污染现有结构。 - template >... Tags, Dependency_Graph_Edit_Callback_For, Tags...> Callback> - std::expected edit(Callback&& callback) { - std::tuple...> next(*get().pending...); - auto editors = [&](std::index_sequence) { - return std::tuple{ - Dependency_Graph::Editor>>( - std::get(next), - dependency_id(), - dependency_id>(), - true - )... - }; - }(std::index_sequence_for{}); - std::apply( - [&](auto&... values) { - std::invoke(std::forward(callback), values...); - }, - editors - ); - std::expected result; - std::apply( - [&](const auto&... value) { - ( - [&] { - if (!result) return; - auto check = value.topological_order(); - if (!check) result = std::unexpected(check.error()); - }(), - ... - ); - }, - next - ); - if (!result) return result; - auto changed = [&](std::index_sequence) { - return std::tuple{ - (std::get(next).structure_revision != get().pending->structure_revision)... - }; - }(std::index_sequence_for{}); - [&](std::index_sequence) { - ( - [&] { - auto& buffer = get(); - buffer.pending->swap(std::get(next)); - if (std::get(changed)) buffer.mark_structure_dirty(); - }(), - ... - ); - }(std::index_sequence_for{}); - return {}; - } - std::expected validate_pending() const { - return validate_pending_impl(); - } - void advance() { - std::apply( - [](auto&... buffer) { - (advance_one(buffer), ...); - }, - dependency_graphs - ); - } - template - void for_each_current(Callback&& callback) const { - std::apply( - [&](const auto&... buffer) { - (std::invoke(callback, std::as_const(*buffer.current)), ...); - }, - dependency_graphs - ); - } -}; -// Builder 使用独立依赖图完成对象构造期编辑;build 成功时一次性提交到对象的正式 Dependency_Graph_Storage。 -template -struct Dependency_Graph_Build_Storage> { - using allocator_type = std::pmr::polymorphic_allocator; -private: - std::tuple...> dependency_graphs; - template - static consteval std::size_t index() { - return tag_index(); - } - template - std::expected validate_impl() const { - if constexpr (I == sizeof...(Dependency_Graph_Types)) { - return {}; - } - else { - auto result = std::get(dependency_graphs).topological_order(); - if (!result) return std::unexpected(result.error()); - return validate_impl(); - } - } - template - void commit_one(Dependency_Graph_Storage>& storage) { - using Tag = typename Graph_Declaration::Tag_Type; - auto& buffer = storage.template get(); - buffer.pending->swap(get()); - buffer.pending->bind_nodes(); - *buffer.current = *buffer.pending; - buffer.current->bind(); - buffer.mark_structure_dirty(); - } -public: - Dependency_Graph_Build_Storage() : Dependency_Graph_Build_Storage(std::pmr::get_default_resource()) {} - explicit Dependency_Graph_Build_Storage(std::pmr::memory_resource* resource) : dependency_graphs(std::allocator_arg, allocator_type{resource}) {} - template > Tag> - Dependency_Graph& get() { - return std::get()>(dependency_graphs); - } - template > Tag> - const Dependency_Graph& get() const { - return std::get()>(dependency_graphs); - } - template >... Tags, Dependency_Graph_Edit_Callback_For, Tags...> Callback> - void edit(Callback&& callback) { - auto editors = std::tuple{ - Dependency_Graph::Editor>>( - get(), - dependency_id(), - dependency_id>(), - false - )... - }; - std::apply( - [&](auto&... values) { - std::invoke(std::forward(callback), values...); - }, - editors - ); - } - std::expected validate() const { - return validate_impl(); - } - void commit(Dependency_Graph_Storage>& storage) { - (commit_one(storage), ...); - } -}; -} -template -struct Root::Builder { - using Prop = typename Object::Prop; - using Buffer_Build = detail::Buffer_Build_Storage; - using Dependency_Graph_Build = detail::Dependency_Graph_Build_Storage; - using Final_Builder = typename Object::template Builder; - std::unique_ptr object; /* 尚未挂接最终 Private 的对象外壳。 */ - Prop prop{}; /* build() 前暂存的发布属性写入面。 */ - Buffer_Build buffer_storage; /* build() 前暂存的普通 Buffer 初值。 */ - Dependency_Graph_Build dependency_graph_storage; /* build() 前独立编辑并验证的依赖图。 */ - template - explicit Builder(Args&&... args) : object(new Object(std::forward(args)...)) {} - Final_Builder& set_pmr(Pmr pmr) { - object->set_pmr_resource(pmr); - return static_cast(*this); - } - Final_Builder& set_pmr(Pmr::Resource& resource) { - return set_pmr(Pmr{resource}); - } - Final_Builder& set_pmr(Pmr::Resource* resource) { - return set_pmr(Pmr{resource}); - } - template Value> - Final_Builder& set(Member Owner::* member, Value&& value) { - prop.*member = std::forward(value); - return static_cast(*this); - } - template Tag, detail::Buffer_Value_Settable Value> - Final_Builder& set(Value&& value) { - buffer_storage.template set(std::forward(value)); - return static_cast(*this); - } - template ... Dependency_Graph_Tags, detail::Dependency_Graph_Edit_Callback_For Callback> - Final_Builder& edit_dependency_graph(Callback&& callback) { - dependency_graph_storage.template edit(std::forward(callback)); - return static_cast(*this); - } - template Dependency_Graph_Tag, detail::Bound_Dependency_Graph_Target> Node_Object> - Final_Builder& add_dependency_node(Node_Object* node) { - return edit_dependency_graph( - [&](auto& editor) { - editor.add(node); - } - ); - } - template Dependency_Graph_Tag, Root_Derived Node_Object> - Final_Builder& remove_dependency_node(Node_Object* node) { - return edit_dependency_graph( - [&](auto& editor) { - editor.remove(node); - } - ); - } - /* 构造期整体依赖只建立拓扑边,不绑定 dirty 来源。 */ - template Dependency_Graph_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Dependency_Object Source> - Final_Builder& add_dependency(Target* target, Source* source) { - return edit_dependency_graph([&](auto& editor) { editor.add_dependency(target, source); }); - } - template Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target> Target, detail::Prop_Dependency_Source Source> - Final_Builder& add_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph([&](auto& editor) { editor.template add_prop_dependency(target, source); }); - } - template Dependency_Graph_Tag, typename Prop_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Prop_Layer_Dependency_Source Source> - Final_Builder& add_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph([&](auto& editor) { editor.template add_prop_dependency(target, source); }); - } - template Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target> Target, detail::State_Dependency_Source Source> - Final_Builder& add_dependency(Target* target, Source* source) { - return edit_dependency_graph( - [&](auto& editor) { - editor.template add_dependency(target, source); - } - ); - } - /* 在构造期依赖图中添加指定 State Tag 的状态层级依赖。 */ - template Dependency_Graph_Tag, typename State_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::State_Layer_Dependency_Source Source> - Final_Builder& add_state_dependency(Target* target, Source* source) { - return edit_dependency_graph( - [&](auto& editor) { - editor.template add_state_dependency(target, source); - } - ); - } - template Dependency_Graph_Tag, typename Buffer_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Buffer_Dependency_Source Source> - Final_Builder& add_dependency(Target* target, Source* source) { - return edit_dependency_graph( - [&](auto& editor) { - editor.template add_dependency(target, source); - } - ); - } - template Dependency_Graph_Tag, typename Source_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Dependency_Object Source> - Final_Builder& add_dirty_dependency(Target* target, Source* source) { - return edit_dependency_graph( - [&](auto& editor) { - editor.template add_dirty_dependency(target, source); - } - ); - } - template Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target> Target, detail::State_Dependency_Source Source> - Final_Builder& remove_dependency(Target* target, Source* source) { - return edit_dependency_graph( - [&](auto& editor) { - editor.template remove_dependency(target, source); - } - ); - } - template Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target> Target, detail::Prop_Dependency_Source Source> - Final_Builder& remove_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph([&](auto& editor) { editor.template remove_prop_dependency(target, source); }); - } - template Dependency_Graph_Tag, typename Prop_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Prop_Layer_Dependency_Source Source> - Final_Builder& remove_prop_dependency(Target* target, Source* source) { - return edit_dependency_graph([&](auto& editor) { editor.template remove_prop_dependency(target, source); }); - } - /* 从构造期依赖图中移除指定 State Tag 的状态层级依赖。 */ - template Dependency_Graph_Tag, typename State_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::State_Layer_Dependency_Source Source> - Final_Builder& remove_state_dependency(Target* target, Source* source) { - return edit_dependency_graph( - [&](auto& editor) { - editor.template remove_state_dependency(target, source); - } - ); - } - template Dependency_Graph_Tag, typename Buffer_Tag, detail::Bound_Dependency_Graph_Target> Target, detail::Buffer_Dependency_Source Source> - Final_Builder& remove_dependency(Target* target, Source* source) { - return edit_dependency_graph( - [&](auto& editor) { - editor.template remove_dependency(target, source); - } - ); - } - std::expected, Dependency_Graph_Error> build() { - auto validate_result = validate(); - if (!validate_result) return std::unexpected(validate_result.error()); - auto private_data = std::make_unique>( - object.get(), object->memory_resource()); - *private_data->pending = prop; - *private_data->current = prop; - buffer_storage.commit(private_data->buffer_storage); - dependency_graph_storage.commit(private_data->dependency_graph_storage); - object->d = private_data.release(); - // d 已指向最终 Private 后,由 Private 继承链按最终 Object 类型安装公开薄壳所需的 CRTP 分派。 - static_cast(*object->d).bind_private_crtp(object.get()); - object->template bind_object_crtp(); - return std::move(object); - } - std::expected validate() const { - return dependency_graph_storage.validate(); - } -protected: - /* 派生 Builder 在完成 build 后绑定协作对象时,按 Tag 访问其 Private 层;不公开内部指针。 */ - template - [[nodiscard]] static Private_Access private_access(Target* target) noexcept { - return Private_Access{static_cast(*target->d)}; - } - /* Builder 内部读取目标已提交 Prop;不经过对象的外部 pending 读取接口。 */ - template - [[nodiscard]] static const auto& current_prop(const Target* target) noexcept { - using Layer = detail::Prop_Value; - const auto* prop = static_cast( - target->d->runtime_current_prop_data()); - return static_cast(*prop); - } -}; -} diff --git a/kernel/src/kernel/double_buffer/Mpmc_Triple_Buffer.hpp b/kernel/src/kernel/double_buffer/Mpmc_Triple_Buffer.hpp deleted file mode 100644 index bdf3e2d..0000000 --- a/kernel/src/kernel/double_buffer/Mpmc_Triple_Buffer.hpp +++ /dev/null @@ -1,161 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace double_buffer { -/* Def mechanism declaration. Tag selects one independently exchanged MPMC stream. */ -template -struct Mpmc_Triple_Buffer { - using Tag_Type = Tag; - using Value_Type = Value; -}; -namespace detail { -/* - * The three buffers are queues, not snapshots. Producers append to collecting while - * the renderer owns rendering and observers own query. advance() rotates roles as: - * completed rendering -> query, collecting -> rendering, expired query -> collecting. - */ -template -struct Mpmc_Triple_Buffer_Storage { -public: - using Value = typename Declaration::Value_Type; - Mpmc_Triple_Buffer_Storage() = default; - Mpmc_Triple_Buffer_Storage(const Mpmc_Triple_Buffer_Storage&) = delete; - Mpmc_Triple_Buffer_Storage& operator=(const Mpmc_Triple_Buffer_Storage&) = delete; - void submit(Value value) { - std::shared_lock lock(exchange_lock_); - if (!collecting_->enqueue(std::move(value))) throw std::bad_alloc{}; - submission_generation_.fetch_add(1, std::memory_order_release); - } - [[nodiscard]] std::uint64_t submission_generation() const noexcept { - return submission_generation_.load(std::memory_order_acquire); - } - void advance() { - std::unique_lock lock(exchange_lock_); - const auto expired_query = query_; - query_ = rendering_; - rendering_ = collecting_; - collecting_ = expired_query; - clear(*collecting_); - } - /* - * Accumulating streams promote the last published version into the current - * rendering version before appending the newly collected batch. Query keeps - * the previous complete version and producers remain isolated in collecting. - */ - void accumulate(std::size_t capacity) requires std::copy_constructible { - accumulate_with([capacity](std::vector& values) { - if (values.size() <= capacity) return; - values.erase(values.begin(), values.end() - - static_cast(capacity)); - }); - } - template requires std::copy_constructible && - std::predicate - void accumulate(Predicate&& retain) { - accumulate_with([&](std::vector& values) { - std::erase_if(values, [&](const Value& value) { - return !std::invoke(retain, value); - }); - }); - } - template requires std::invocable> - decltype(auto) access_rendering(Callback&& callback) { - std::unique_lock lock(exchange_lock_); - return access(*rendering_, std::forward(callback)); - } - template requires std::invocable> - decltype(auto) access_query(Callback&& callback) const { - std::unique_lock lock(exchange_lock_); - auto adapter = [&](std::span values) -> decltype(auto) { - return std::invoke(callback, std::span{values}); - }; - return access(*query_, adapter); - } -private: - using Queue = moodycamel::ConcurrentQueue; - static void clear(Queue& queue) { - Value value; - while (queue.try_dequeue(value)) {} - } - static void restore(Queue& queue, std::vector& values) { - for (auto& value : values) if (!queue.enqueue(std::move(value))) throw std::bad_alloc{}; - } - template - void accumulate_with(Mutation&& mutate) { - std::unique_lock lock(exchange_lock_); - std::vector published; - published.reserve(query_->size_approx()); - Value value; - while (query_->try_dequeue(value)) published.push_back(std::move(value)); - std::vector accumulated{published.begin(), published.end()}; - restore(*query_, published); - accumulated.reserve(accumulated.size() + rendering_->size_approx()); - while (rendering_->try_dequeue(value)) accumulated.push_back(std::move(value)); - std::invoke(std::forward(mutate), accumulated); - restore(*rendering_, accumulated); - } - template - static decltype(auto) access(Queue& queue, Callback&& callback) { - std::vector values; - values.reserve(queue.size_approx()); - Value value; - while (queue.try_dequeue(value)) values.push_back(std::move(value)); - if constexpr (std::is_void_v>>) { - std::invoke(callback, std::span{values}); - restore(queue, values); - } - else { - static_assert(!std::is_reference_v>>, - "triple-buffer access results must not outlive the locked queue view"); - auto result = std::invoke(callback, std::span{values}); - restore(queue, values); - return result; - } - } - mutable std::shared_mutex exchange_lock_; /* Serializes role exchange and stable role inspection. */ - std::atomic_uint64_t submission_generation_{}; /* Producer wake generation; payload authority remains the three queues. */ - mutable Queue first_{}; /* First physical MPMC queue; role changes only under exchange_lock_. */ - mutable Queue second_{}; /* Second physical MPMC queue; role changes only under exchange_lock_. */ - mutable Queue third_{}; /* Third physical MPMC queue; role changes only under exchange_lock_. */ - aethera::not_null collecting_{&first_}; /* Concurrent producer destination until the next exchange. */ - aethera::not_null rendering_{&second_}; /* Immutable-to-producers queue consumed by the renderer. */ - mutable aethera::not_null query_{&third_}; /* Last completed render input exposed to observers. */ -}; -template -struct Mpmc_Triple_Buffer_Storage_Set; -template -struct Mpmc_Triple_Buffer_Storage_Set> { - template - static consteval std::size_t index_of() { - if constexpr (std::same_as) return 0; - else return 1 + index_of(); - } -public: - template - auto& get() noexcept { - constexpr std::size_t index = index_of(); - return std::get < index > (storage_); - } - template - const auto& get() const noexcept { - return const_cast(this)->template get(); - } -private: - std::tuple...> storage_; -}; -} -} diff --git a/kernel/src/kernel/double_buffer/mechanism.hpp b/kernel/src/kernel/double_buffer/mechanism.hpp deleted file mode 100644 index d97e4e0..0000000 --- a/kernel/src/kernel/double_buffer/mechanism.hpp +++ /dev/null @@ -1,882 +0,0 @@ -#pragma once -#include "Mpmc_Triple_Buffer.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace double_buffer { -template -concept Lockable = requires(Lock& lock) { - { lock.lock() } -> std::same_as; - { lock.unlock() } -> std::same_as; -}; -template -concept Prop_State = std::default_initializable && std::assignable_from; -struct Pmr { - using Resource = std::pmr::memory_resource; -private: - aethera::not_null value; -public: - Pmr() noexcept : value(std::pmr::get_default_resource()) {} - explicit Pmr(Resource& resource) noexcept : value(&resource) {} - explicit Pmr(Resource* resource) noexcept : value(resource ? resource : std::pmr::get_default_resource()) {} - [[nodiscard]] Resource* resource() const noexcept { - return value.get(); - } - template - std::pmr::polymorphic_allocator allocator() const noexcept { - return std::pmr::polymorphic_allocator{value.get()}; - } - static Resource* default_resource() noexcept { - return std::pmr::get_default_resource(); - } - static Resource* set_default_resource(Resource& resource) noexcept { - return std::pmr::set_default_resource(&resource); - } - static Resource* set_default_resource(Resource* resource) noexcept { - return std::pmr::set_default_resource(resource); - } -}; -namespace detail { -struct Pmr_Resource : public std::pmr::memory_resource { -private: - using Resource = std::pmr::memory_resource; - struct Header { - aethera::not_null resource; - aethera::not_null base; - std::size_t bytes; - std::size_t alignment; - }; - aethera::not_null upstream; - void* do_allocate(std::size_t bytes, std::size_t alignment) override { - auto effective_alignment = std::max(alignment, alignof(Header)); - auto total = bytes + sizeof(Header) + effective_alignment - 1; - const auto resource = upstream; - const aethera::not_null base{ - resource->allocate(total, effective_alignment)}; - auto begin = reinterpret_cast(base.get()) + sizeof(Header); - auto aligned = (begin + effective_alignment - 1) & ~(static_cast(effective_alignment) - 1); - auto* header = reinterpret_cast(aligned - sizeof(Header)); - std::construct_at(header, Header{resource, base, total, effective_alignment}); - return reinterpret_cast(aligned); - } - void do_deallocate(void* pointer, std::size_t, std::size_t) override { - auto* header = reinterpret_cast(reinterpret_cast(pointer) - sizeof(Header)); - const auto resource = header->resource; - const auto base = header->base; - auto bytes = header->bytes; - auto alignment = header->alignment; - std::destroy_at(header); - resource->deallocate(base.get(), bytes, alignment); - } - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { - return this == &other; - } -public: - Pmr_Resource() noexcept : upstream(std::pmr::get_default_resource()) {} - explicit Pmr_Resource(Resource* resource) noexcept : upstream(resource ? resource : std::pmr::get_default_resource()) {} - void set_resource(Resource* resource) noexcept { - upstream = resource ? resource : std::pmr::get_default_resource(); - } - Resource* resource() const noexcept { - return upstream.get(); - } -}; -} -struct Pinned { - Pinned() = default; - Pinned(const Pinned&) = delete; - Pinned& operator=(const Pinned&) = delete; - Pinned(Pinned&&) = delete; - Pinned& operator=(Pinned&&) = delete; -}; -// Double_Buffer 只负责推进 pending/current 两个视图,不复制数据;需要推进后同步基线时使用 Commit_Double_Buffer 或 Publish_Double_Buffer。 -template -struct Double_Buffer : Pinned { - using allocator_type = std::pmr::polymorphic_allocator; -private: - std::tuple buf; -public: - aethera::not_null pending; - aethera::not_null current; - Double_Buffer() : Double_Buffer(std::allocator_arg, allocator_type{std::pmr::get_default_resource()}) {} - Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : buf(std::allocator_arg, allocator), - pending(&std::get < 0 > (buf)), - current(&std::get < 1 > (buf)) {} - void advance() { - std::swap(pending, current); - } -}; -// Commit_Double_Buffer 用于外部提交到内部:pending 接收外部写入,推进后 current 成为内部读值,再把 current 回写 pending 作为下一次编辑基线。 -template -struct Commit_Double_Buffer : Double_Buffer { - using Base = Double_Buffer; - using allocator_type = typename Base::allocator_type; - Commit_Double_Buffer() = default; - Commit_Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} - void advance() { - Base::advance(); - *this->pending = *this->current; - } -}; -// Publish_Double_Buffer 用于内部发布到外部:current 接收内部写入,推进后 pending 成为外部读值,再把 pending 回写 current 作为下一次内部写入基线。 -template -struct Publish_Double_Buffer : Double_Buffer { - using Base = Double_Buffer; - using allocator_type = typename Base::allocator_type; - Publish_Double_Buffer() = default; - Publish_Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {} - void advance() { - Base::advance(); - *this->current = *this->pending; - } -}; -namespace detail { -struct Prop_Root { - bool operator==(const Prop_Root&) const = default; -}; -struct State_Root { - bool operator==(const State_Root&) const = default; -}; -} -template -struct Prop_Type : Prev { - using Tag_Type = Tag; - using Prev_Prop = Prev; - bool operator==(const Prop_Type&) const = default; -}; -// State_Type 用 Tag 标记每一层状态,Prev_State 把 CRTP 继承链上的状态按层串起来,供精确回调和类型约束使用。 -template -struct State_Type : Prev { - using Tag_Type = Tag; - using Prev_State = Prev; - bool operator==(const State_Type&) const = default; -}; -template -struct Tagged_Buffer { - using Tag_Type = Tag; - using Value_Type = Value; -}; -struct Root; -// Dependency_Graph_Type 描述一类依赖图及其允许绑定的对象类型;dirty 只表示该依赖阶段需要重新处理,不承担图结构所有权。 -template -struct Dependency_Graph_Type { - using Tag_Type = Tag; - using Object_Type = Object; -private: - std::atomic_flag clean = ATOMIC_FLAG_INIT; -protected: - void mark_dirty() noexcept { - clean.clear(std::memory_order_release); - } -public: - [[nodiscard]] bool dirty() const noexcept { - return !clean.test(std::memory_order_acquire); - } - bool take_dirty() noexcept { - return !clean.test_and_set(std::memory_order_acq_rel); - } -}; -namespace detail { -template -concept Prop_Member_Settable = requires(Prop& prop, Member Owner::* member, Value&& value) { - requires std::derived_from; - prop.*member = std::forward(value); -}; -template -struct Unique_Types : std::true_type {}; -template -struct Unique_Types : std::bool_constant< - ((!std::same_as) && ...) && - Unique_Types::value -> {}; -template -struct Is_Tagged_Buffer : std::false_type {}; -template -struct Is_Tagged_Buffer> : std::true_type {}; -template -struct Is_Dependency_Graph_Type : std::false_type {}; -template -struct Is_Dependency_Graph_Type> : std::true_type {}; -template -struct Is_Mpmc_Triple_Buffer : std::false_type {}; -template -struct Is_Mpmc_Triple_Buffer> : std::true_type {}; -template -struct Is_State_Type : std::false_type {}; -template -struct Is_State_Type> : std::true_type {}; -template -struct Is_Prop_Type : std::false_type {}; -template -struct Is_Prop_Type> : std::true_type {}; -template -concept Buffer_Type = Is_Tagged_Buffer::value; -template -concept Dependency_Graph_Mechanism = Is_Dependency_Graph_Type::value; -template -concept Mpmc_Triple_Buffer_Mechanism = Is_Mpmc_Triple_Buffer::value; -template -concept Mechanism_Type = Buffer_Type || Dependency_Graph_Mechanism || Mpmc_Triple_Buffer_Mechanism; -template -concept Tagged_State = Prop_State && requires { - typename Value::Tag_Type; - typename Value::Prev_State; - requires std::derived_from>; -}; -template -concept Tagged_Prop = Prop_State && requires { - typename Value::Tag_Type; - typename Value::Prev_Prop; - requires std::derived_from>; -}; -template -struct State_Layers { - using Type = std::tuple<>; -}; -template -struct State_Layers> { - using Type = decltype(std::tuple_cat( - std::declval>(), - std::declval < typename State_Layers::Type > () - )); -}; -template -using State_Layers_T = typename State_Layers::Type; -template -struct Prop_Layers { - using Type = std::tuple<>; -}; -template -struct Prop_Layers> { - using Type = decltype(std::tuple_cat(std::declval>(), std::declval < typename Prop_Layers::Type > ())); -}; -template -using Prop_Layers_T = typename Prop_Layers::Type; -template -struct Member_Pointer_Traits; -template -struct Member_Pointer_Traits { - using Owner_Type = Owner; - using Member_Type = Member; -}; -template -concept State_Member = requires { - requires std::is_member_object_pointer_v; - requires std::derived_from< - State, - typename Member_Pointer_Traits::Owner_Type - >; -}; -template -using State_Member_Tag = typename Member_Pointer_Traits::Owner_Type::Tag_Type; -template -concept Prop_Member = requires { - requires std::is_member_object_pointer_v; - requires std::derived_from::Owner_Type>; - requires requires { typename Member_Pointer_Traits::Owner_Type::Prop_Tag; } || Tagged_Prop::Owner_Type>; -}; -template -struct Prop_Owner_Tag { - using Type = typename Owner::Tag_Type; -}; -template -struct Prop_Owner_Tag> { - using Type = typename Owner::Prop_Tag; -}; -template -using Prop_Member_Tag = typename Prop_Owner_Tag::Owner_Type>::Type; -template -concept State_Member_Settable = requires(State& state, Value&& value) { - requires State_Member; - state.*Member = std::forward(value); -}; -template -using Mechanism_Buffer_Tuple = std::conditional_t, std::tuple, std::tuple<>>; -template -using Mechanism_Dependency_Graph_Tuple = std::conditional_t, std::tuple, std::tuple<>>; -template -using Mechanism_Mpmc_Triple_Buffer_Tuple = std::conditional_t, std::tuple, std::tuple<>>; -template -struct Has_Tag : std::false_type {}; -template -struct Has_Tag> : std::bool_constant< - (std::same_as || ...) -> {}; -template -struct Unique_Tags : Unique_Types {}; -template -struct Tagged_List_Check : std::false_type {}; -template -struct Tagged_List_Check : Unique_Tags {}; -template -struct Is_Buffer_List : std::false_type {}; -template -struct Is_Buffer_List> : Tagged_List_Check< - (Buffer_Type && ...), - Buffers... -> {}; -template -struct Is_Dependency_Graph_List : std::false_type {}; -template -struct Is_Dependency_Graph_List> : Tagged_List_Check< - (Dependency_Graph_Mechanism && ...) && (std::derived_from < typename Dependency_Graph_Types::Object_Type, Root > &&...), - Dependency_Graph_Types... -> {}; -template -concept Buffer_List = Is_Buffer_List::value; -template -concept Dependency_Graph_List = Is_Dependency_Graph_List::value; -template -struct Is_Mpmc_Triple_Buffer_List : std::false_type {}; -template -struct Is_Mpmc_Triple_Buffer_List> : Tagged_List_Check< - (Mpmc_Triple_Buffer_Mechanism && ...), Declarations...> {}; -template -concept Mpmc_Triple_Buffer_List = Is_Mpmc_Triple_Buffer_List::value; -template -concept Mpmc_Triple_Buffer_Tag_In = requires { - requires Mpmc_Triple_Buffer_List; - requires Has_Tag::value; -}; -template -concept Buffer_Tag_In = requires { - requires Buffer_List; - requires Has_Tag::value; -}; -template -concept Dependency_Graph_Tag_In = requires { - requires Dependency_Graph_List; - requires Has_Tag::value; -}; -template -struct Dependency_Graph_Declaration_By_Tag; -template -struct Dependency_Graph_Declaration_By_Tag> { -private: - template - struct Find { - using Type = std::conditional_t< - std::same_as, - First, - typename Find::Type - >; - }; - template - struct Find { - using Type = Last; - }; -public: - using Type = typename Find::Type; -}; -template -using Dependency_Graph_Declaration = typename Dependency_Graph_Declaration_By_Tag::Type; -template -using Dependency_Graph_Bound_Object = typename Dependency_Graph_Declaration::Object_Type; -template -struct Is_State_List : std::false_type {}; -template -struct Is_State_List> : Tagged_List_Check< - (Tagged_State && ...), - States... -> {}; -template -concept State_List = Is_State_List::value; -template -struct Is_Prop_List : std::false_type {}; -template -struct Is_Prop_List> : Tagged_List_Check<(Tagged_Prop && ...), Props...> {}; -template -concept Prop_List = Is_Prop_List::value; -template -concept Prop_Chain = Tagged_Prop && Prop_List>; -template -concept Prop_Tag_In = requires { - requires Prop_List; - requires Has_Tag::value; -}; -template -concept State_Chain = Tagged_State && State_List>; -template -concept State_Tag_In = requires { - requires State_List; - requires Has_Tag::value; -}; -template -concept State_Chain_Matches = State_Chain && State_List && [] { - using Layers = State_Layers_T; - if constexpr (std::tuple_size_v != std::tuple_size_v) return false; - else { - return [](std::index_sequence) { - return (std::same_as < - typename std::tuple_element_t < I, Layers > ::Tag_Type, - typename std::tuple_element_t < std::tuple_size_v - I - 1, Tuple > ::Tag_Type - > && ...); - }(std::make_index_sequence>{}); - } -}(); -template -struct Rebind_State; -template -struct Rebind_State, Prev> { - using Type = State_Type; -}; -template -using Rebind_State_T = typename Rebind_State::Type; -// Tag 是否存在由外层 requires 保证;内部只按声明顺序计算位置,避免每层重复检查,也避开 MSVC 对复杂 fold 表达式的解析问题。 -template -consteval std::size_t tag_index() { - if constexpr (std::is_same_v) return 0; - else if constexpr (sizeof...(Rest) == 0) return 0; - else return 1 + tag_index(); -} -template -struct Tag_Index; -template -struct Tag_Index> : std::integral_constant()> {}; -template -inline constexpr std::size_t tag_index_v = Tag_Index::value; -template -struct State_By_Tag { -private: - using Layers = State_Layers_T; -public: - using Type = std::tuple_element_t, Layers>; -}; -template -using State_Value = typename State_By_Tag::Type; -template -struct Prop_By_Tag { -private: - using Layers = Prop_Layers_T; -public: - using Type = std::tuple_element_t, Layers>; -}; -template -using Prop_Value = typename Prop_By_Tag::Type; -template -concept State_Callback_For = State_Tag_In && State_Chain_Matches && std::invocable&>; -template -struct State_Callback_Entry { - std::mutex publish_lock{}; /* 仅串行化该状态层的回调安装、清除与发布。 */ - std::function callback{}; /* 当前状态层唯一的发布回调。 */ -}; -template -struct State_Callback_Tuple; -template -struct State_Callback_Tuple> { - using Type = std::tuple...>; -}; -template -struct State_Callback_Storage { -private: - using Layers = State_Layers_T; - using Callbacks = typename State_Callback_Tuple::Type; - Callbacks callbacks; - template - static consteval std::size_t index() { - return tag_index_v; - } -public: - template Tag, typename Callback> - void set(Callback&& callback) { - constexpr auto value_index = index(); - using Layer = std::tuple_element_t; - auto& entry = std::get(callbacks); - std::lock_guard guard(entry.publish_lock); - entry.callback = std::function{std::forward(callback)}; - } - template Tag> - void clear() { - auto& entry = std::get()>(callbacks); - std::lock_guard guard(entry.publish_lock); - entry.callback = {}; - } - template Tag, typename Publish> - requires std::same_as, State_Value> - void publish(Publish&& publish) { - constexpr auto value_index = index(); - auto& entry = std::get(callbacks); - std::lock_guard guard(entry.publish_lock); - auto state = std::invoke(std::forward(publish)); - if (entry.callback) entry.callback(state); - } -}; -// 每个 State Tag 独立保存回调与发布锁;字段更新不会隐式通知,发布频率由拥有该状态的内部机制显式控制。 -template -struct Buffer_Value_By_Tag; -template -struct Buffer_Value_By_Tag> { - using Type = typename std::tuple_element_t< - tag_index(), - std::tuple - >::Value_Type; -}; -template -using Buffer_Value = typename Buffer_Value_By_Tag::Type; -template -concept Buffer_Value_Settable = requires(Buffer_Value& target, Value&& value) { - requires Buffer_List; - requires Buffer_Tag_In; - target = std::forward(value); -}; -// Buffer_Storage 保存普通 Tagged_Buffer 的双缓冲;它只执行基础推进,不做 State/Prop 那种推进后同步。 -template -struct Buffer_Storage; -template -struct Buffer_Storage> { - using allocator_type = std::pmr::polymorphic_allocator; -private: - std::tuple...> buffers; - template - static consteval std::size_t index() { - return tag_index(); - } -public: - Buffer_Storage() : Buffer_Storage(std::allocator_arg, allocator_type{std::pmr::get_default_resource()}) {} - Buffer_Storage(std::allocator_arg_t, const allocator_type& allocator) : buffers(std::allocator_arg, allocator) {} - template > Tag> - auto& get() { - return std::get < index() > (buffers); - } - template > Tag> - const auto& get() const { - return std::get < index() > (buffers); - } - void advance() { - std::apply( - [](auto&... buffer) { - (buffer.advance(), ...); - }, - buffers - ); - } -}; -/* Builder 在最终 Private 创建前暂存普通 Buffer 初值,并在 build() 时写入正式双缓冲。 */ -template -struct Buffer_Build_Storage; -template -struct Buffer_Build_Storage> { -private: - std::tuple values; /* build() 前暂存的各 Tagged_Buffer 初值。 */ -public: - template > Tag, Buffer_Value_Settable> Value> - void set(Value&& value) { - std::get < tag_index_v> > (values) = std::forward(value); - } - void commit(Buffer_Storage>& storage) const { - [&](std::index_sequence) { - ( - [&] { - using Buffer = std::tuple_element_t>; - auto& target = storage.template get(); - *target.pending = std::get < I > (values); - *target.current = *target.pending; - }(), - ... - ); - }(std::index_sequence_for < Buffers...>{}); - } -}; -} -/* - * State_Access 是 State 继承链的按 Tag 访问视图。 - * State_Type 为 const 时 get() 返回只读层,否则返回可写层;视图不暴露 pending/current 缓冲角色。 - */ -template requires detail::State_Chain> -struct State_Access { -private: - using Value_Type = std::remove_const_t; - aethera::not_null state; - template requires detail::State_Chain> - friend struct State_Access; -public: - explicit State_Access(State_Type& value) noexcept : state(&value) {} - template requires std::convertible_to - State_Access(const State_Access& source) noexcept : state(source.state) {} - template requires detail::State_Tag_In> - [[nodiscard]] decltype(auto) get() const noexcept { - using Layer = detail::State_Value; - if constexpr (std::is_const_v) return static_cast(*state); - else return static_cast(*state); - } -}; -template requires detail::State_Chain> -State_Access(State_Type&) -> State_Access; -/* Prop_Access 按定义层的 Base_Tag 选择 Prop;Prop 与 State 的标签空间彼此隔离。 */ -template requires detail::Prop_Chain> -struct Prop_Access { -private: - using Value_Type = std::remove_const_t; - aethera::not_null prop; - template requires detail::Prop_Chain> - friend struct Prop_Access; -public: - explicit Prop_Access(Prop_Type& value) noexcept : prop(&value) {} - template requires std::convertible_to - Prop_Access(const Prop_Access& source) noexcept : prop(source.prop) {} - template requires detail::Prop_Tag_In> - [[nodiscard]] decltype(auto) get() const noexcept { - using Layer = detail::Prop_Value; - if constexpr (std::is_const_v) return static_cast(*prop); - else return static_cast(*prop); - } -}; -template requires detail::Prop_Chain> -Prop_Access(Prop_Type&) -> Prop_Access; -/* Private_Access 使用同一个定义层 Base_Tag 选择 Private;该标签空间不与 Prop/State 混用。 */ -template -struct Private_Access { -private: - aethera::not_null private_data; - template - friend struct Private_Access; -public: - explicit Private_Access(Private_Type& value) noexcept : private_data(&value) {} - template requires std::convertible_to - Private_Access(const Private_Access& source) noexcept : private_data(source.private_data) {} - template requires requires { typename Tag::Private; } && std::derived_from, typename Tag::Private> - [[nodiscard]] decltype(auto) get() const noexcept { - using Layer = typename Tag::Private; - if constexpr (std::is_const_v) return static_cast(*private_data); - else return static_cast(*private_data); - } -}; -template -Private_Access(Private_Type&) -> Private_Access; -namespace detail { -template -struct Prop_Dependency_Key {}; -template -struct Prop_Layer_Dependency_Key {}; -template -struct State_Dependency_Key {}; -template -struct State_Layer_Dependency_Key {}; -template -struct Buffer_Dependency_Key {}; -template -struct Dirty_Dependency_Key {}; -template -inline char dependency_token; -template -constexpr const void* dependency_id() { - return &dependency_token; -} -} -struct Dependency_Graph; -template -concept Root_Derived = std::derived_from; -template -concept Object_Core = requires { - requires Root_Derived; - typename T::Private; - typename T::States; - requires Prop_State; - requires Prop_State; - requires detail::State_Chain_Matches; - requires detail::Buffer_List; - requires detail::Dependency_Graph_List; - requires detail::Mpmc_Triple_Buffer_List; -}; -namespace detail { -template -struct Dependency_Graph_Storage; -template -struct Dependency_Graph_Build_Storage; -template -struct Attached_Private; -template -struct Public_Access; -struct Internal_Access; -} -enum struct Dependency_Graph_Error { - self_dependency, - missing_dependency, - cycle -}; -struct Root { - /* 所有实现层 Private 的公共析构基类;Root 通过该类型唯一拥有最终 Private。 */ - struct Private { - using Tag_Type = Root; - virtual ~Private() = default; - [[nodiscard]] virtual const void* runtime_current_prop_data() const noexcept = 0; - [[nodiscard]] virtual void* runtime_pending_prop_data() noexcept = 0; - [[nodiscard]] virtual const void* runtime_current_state_data() const noexcept = 0; - [[nodiscard]] virtual void* runtime_pending_state_data() noexcept = 0; - /* CRTP 默认:Builder 挂接最终 Private 后按基类到派生类绑定最终对象类型;Root 层不处理。 */ - template - void bind_private_crtp(Object*) {} - protected: - /* 内部实现按目标类型读取已提交 Prop;不经过对象的外部 pending 读取接口。 */ - template - requires detail::Prop_Tag_In> - [[nodiscard]] static const auto& read_current_prop(const Target* target) noexcept { - using Layer = detail::Prop_Value; - const auto* prop = static_cast( - target->d->runtime_current_prop_data()); - return static_cast(*prop); - } - }; - using Buffers = std::tuple<>; - using Dependency_Graph_Types = std::tuple<>; - using Mpmc_Triple_Buffers = std::tuple<>; - using Base_Tag = Root; - using States = std::tuple>; - struct Prop : Prop_Type { - bool operator==(const Prop&) const = default; - }; - struct State : State_Type { - bool operator==(const State&) const = default; - }; -private: - template - friend struct detail::Attached_Private; - template - friend struct detail::Public_Access; - friend struct detail::Internal_Access; - using Call = void (*)(Root*); - Call advance_call{}; - detail::Pmr_Resource pmr_resource; - std::pmr::vector> dependency_graphs; - std::pmr::vector dirty_tags; -protected: - aethera::owner d{}; /* Builder::build() 挂接的最终 Private;由 Root 析构释放。 */ -private: - void set_pmr_resource(Pmr pmr) noexcept { - pmr_resource.set_resource(pmr.resource()); - } - bool mark_dirty_id(const void* tag, const void* dirty_key); - void emit_dependency(const void* key); - void bind_dependency_graph( - aethera::not_null dependency_graph) { - if (std::find(dependency_graphs.begin(), dependency_graphs.end(), dependency_graph) == dependency_graphs.end()) dependency_graphs.push_back(dependency_graph); - } - void unbind_dependency_graph( - aethera::not_null dependency_graph) { - std::erase(dependency_graphs, dependency_graph); - } - friend struct Dependency_Graph; -protected: - template - void bind_object_crtp() { - advance_call = [](Root* root) { - static_cast(root)->advance(); - }; - } - void emit_dependency_source(const void* key) { - emit_dependency(key); - } -public: - Root() : pmr_resource(), - dependency_graphs(&pmr_resource), - dirty_tags(&pmr_resource) {} - Root(const Root&) = delete; - Root& operator=(const Root&) = delete; - Root(Root&&) = delete; - Root& operator=(Root&&) = delete; - ~Root(); - [[nodiscard]] std::pmr::memory_resource* memory_resource() const noexcept { - return const_cast(&pmr_resource); - } - [[nodiscard]] Pmr pmr() const noexcept { - return Pmr{pmr_resource.resource()}; - } - template - std::pmr::polymorphic_allocator allocator() const noexcept { - return std::pmr::polymorphic_allocator{memory_resource()}; - } - template - [[nodiscard]] bool dirty() const { - return std::find(dirty_tags.begin(), dirty_tags.end(), detail::dependency_id()) != dirty_tags.end(); - } - template - void mark_dirty() { - mark_dirty_id(detail::dependency_id(), detail::dependency_id>()); - } - template - bool take_dirty() { - auto current = std::find(dirty_tags.begin(), dirty_tags.end(), detail::dependency_id()); - if (current == dirty_tags.end()) return false; - dirty_tags.erase(current); - return true; - } - void advance_object() { - advance_call(this); - } - template - struct Builder; -}; -template -concept Object_Root = requires { - requires Object_Core; - typename T::template Builder; -}; -// Attached 是实际运行时对象的编译期标记:只有经过 Attach_Object 包装的对象才拥有 Attached_Object,并允许进入会触发 advance_object 的 Dependency_Graph 图。 -template -concept Attached = Object_Core && requires { - typename T::Attached_Object; - requires std::same_as; -}; -namespace detail { -template -concept Dependency_Object = Attached && requires { - requires Root_Derived; - typename T::Prop; - typename T::State; - typename T::Buffers; - requires Prop_State; - requires Prop_State; - requires Buffer_List; -}; -template -concept Bound_Dependency_Object = Dependency_Object && requires { - typename T::Private; -}; -template -concept Bound_Dependency_Graph_Target = Bound_Dependency_Object && std::derived_from; -template -concept Dependency_Graph_Target = Root_Derived && std::derived_from; -template -concept Prop_Dependency_Source = requires { - requires Dependency_Object; - requires Prop_Member; -}; -template -concept Prop_Layer_Dependency_Source = requires { - requires Dependency_Object; - requires Prop_Tag_In>; -}; -template -concept State_Dependency_Source = requires { - requires Dependency_Object; - requires State_Member; -}; -template -concept State_Layer_Dependency_Source = requires { - requires Dependency_Object; - requires State_Tag_In; -}; -template -concept Buffer_Dependency_Source = requires { - requires Dependency_Object; - requires Buffer_Tag_In; -}; -} -} diff --git a/kernel/src/kernel/double_buffer/model.hpp b/kernel/src/kernel/double_buffer/model.hpp deleted file mode 100644 index d5cb585..0000000 --- a/kernel/src/kernel/double_buffer/model.hpp +++ /dev/null @@ -1,775 +0,0 @@ -#pragma once -#include "Dependency_Graph_Storage.hpp" -namespace double_buffer { -template -concept Object = requires { - requires Object_Root; - typename T::This_Object; - typename T::Prev_Object; - typename T::Base_Tag; - typename T::Prev_Prop; - typename T::Prev_State; - typename T::template Prev_Builder; - typename T::Prev_Private; - typename T::States; - requires std::same_as; - requires std::same_as; - requires Object_Root; - requires std::derived_from; - requires detail::Tagged_Prop; - requires detail::State_Tag_In; - requires detail::State_Chain; - requires std::derived_from; - requires detail::State_Chain_Matches; - requires std::derived_from, typename T::template Prev_Builder>; - requires std::derived_from; -}; -namespace detail { -template -using Def_Buffers = decltype(std::tuple_cat( - std::declval(), - std::declval>()... -)); -template -using Def_Dependency_Graph_Types = decltype(std::tuple_cat( - std::declval(), - std::declval>()... -)); -template -using Def_Mpmc_Triple_Buffers = decltype(std::tuple_cat( - std::declval(), - std::declval>()... -)); -template -using Def_States = decltype(std::tuple_cat( - std::declval(), - std::declval>>() -)); -template -concept Def_Mechanisms = Object_Root && (Mechanism_Type && ...) && requires { - requires Buffer_List>; - requires Dependency_Graph_List>; - requires Mpmc_Triple_Buffer_List>; - requires State_List>; -}; -template -concept Process_Callback_For = requires(Private& private_data, Object_T* object, Callback&& callback) { - private_data.process(object, std::forward(callback)); -}; -template -struct Public_Access { -private: - [[nodiscard]] auto& private_data() noexcept { - return static_cast&>(*static_cast(this)->d); - } - [[nodiscard]] const auto& private_data() const noexcept { - return static_cast&>(*static_cast(this)->d); - } -public: - template Tag> - decltype(auto) pending_buffer() { return private_data().template pending_buffer(); } - template void submit_stream(Input&& input) { private_data().template submit_stream(std::forward(input)); } - template [[nodiscard]] std::uint64_t stream_submission_generation() const noexcept { return private_data().template stream_submission_generation(); } - template void exchange_stream() { private_data().template exchange_stream(); } - template void accumulate_stream(std::size_t capacity) { private_data().template accumulate_stream(capacity); } - template void accumulate_stream(Predicate&& retain) { private_data().template accumulate_stream(std::forward(retain)); } - template decltype(auto) access_rendering_stream(Callback&& callback) { return private_data().template access_rendering_stream(std::forward(callback)); } - template decltype(auto) access_query_stream(Callback&& callback) const { return private_data().template access_query_stream(std::forward(callback)); } - template Tag> - decltype(auto) current_buffer() const { return private_data().template current_buffer(); } - template Tag, typename Value> - void initialize_buffer(Value&& value) { private_data().template initialize_buffer(std::forward(value)); } - template [[nodiscard]] auto pending_dependency_graph() const { return private_data().template pending_dependency_graph(); } - template [[nodiscard]] auto current_dependency_graph() const { return private_data().template current_dependency_graph(); } - template void access_pending_dependency_graph(Callback&& callback) { private_data().template access_pending_dependency_graph(std::forward(callback)); } - template auto edit_dependency_graph(Callback&& callback) { return private_data().template edit_dependency_graph(std::forward(callback)); } - [[nodiscard]] auto validate_dependency_graph() const { return private_data().validate_dependency_graph(); } - template auto for_each_dependency_graph_topological(Callback&& callback) const { return private_data().template for_each_dependency_graph_topological(std::forward(callback)); } - template void for_each_current_dependency_graph(Callback&& callback) const { private_data().for_each_current_dependency_graph(std::forward(callback)); } - template Obj& set(Value&& value) { return private_data().template set(std::forward(value)); } - template [[nodiscard]] auto get() const { return private_data().template get(); } - template [[nodiscard]] const auto& read_prop() const noexcept { return private_data().template read_prop(); } - template void update_prop(Callback&& callback) { private_data().template update_prop(std::forward(callback)); } - template void set_state_callback(Callback&& callback) { private_data().template set_state_callback(std::forward(callback)); } - template void clear_state_callback() { private_data().template clear_state_callback(); } - template void access_state(Callback&& callback) const { private_data().template access_state(std::forward(callback)); } - template [[nodiscard]] const auto& read_state() const noexcept { return private_data().template read_state(); } - template Value> - void update_state(Value&& value) { private_data().template update_state(std::forward(value)); } - template - requires (sizeof...(Members) > 0) && - (detail::State_Member && ...) && - std::invocable> - void update_state(Callback&& callback) { private_data().template update_state(std::forward(callback)); } - template void process(Callback&& callback) { private_data().process(std::forward(callback)); } - void advance() { private_data().advance(); } - template void publish_state() { private_data().template publish_state(); } - template void publish_state(Callback&& callback) { private_data().template publish_state(std::forward(callback)); } - template void advance(Callback&& callback) { private_data().advance(std::forward(callback)); } -}; -} -// Def 在编译期把 Buffer/Dependency_Graph/State 三类机制叠加到继承链;最终具体对象汇总完整能力。 -template requires - detail::Def_Mechanisms -struct Def : Base, detail::Public_Access { - using Access = detail::Public_Access; - using Access::access_pending_dependency_graph; - using Access::access_query_stream; - using Access::access_rendering_stream; - using Access::access_state; - using Access::accumulate_stream; - using Access::advance; - using Access::clear_state_callback; - using Access::current_buffer; - using Access::current_dependency_graph; - using Access::edit_dependency_graph; - using Access::exchange_stream; - using Access::for_each_current_dependency_graph; - using Access::for_each_dependency_graph_topological; - using Access::get; - using Access::initialize_buffer; - using Access::pending_buffer; - using Access::pending_dependency_graph; - using Access::process; - using Access::publish_state; - using Access::read_prop; - using Access::read_state; - using Access::set; - using Access::set_state_callback; - using Access::submit_stream; - using Access::stream_submission_generation; - using Access::update_prop; - using Access::update_state; - using Access::validate_dependency_graph; - using This_Object = Self; - using Prev_Object = Base; - using Base_Tag = Self; - using Prev_Prop = Prop_Type; - template - using Prev_Builder = typename Base::template Builder; - using Prev_State = State_Type; - using Base_Private = typename Base::Private; - using Buffers = detail::Def_Buffers; - using Dependency_Graph_Types = detail::Def_Dependency_Graph_Types; - using Mpmc_Triple_Buffers = detail::Def_Mpmc_Triple_Buffers; - using States = detail::Def_States; - using Attached_Object = Self; - struct Private : Base_Private { - using Tag_Type = Base_Tag; - using Prev_Private = Base_Private; - /* CRTP 默认:继续调用上一 Private 层的最终对象绑定;派生 Private 覆盖时必须先调用此实现。 */ - template - void bind_private_crtp(Object* object) { - Base_Private::bind_private_crtp(object); - } - /* CRTP 可覆盖:写入 Prop 成员前按基类到派生类顺序调用。 */ - template - void before_prop_set(Self* object, Member Owner::* member, Prop_Access props) {} - /* CRTP 可覆盖:写入 Prop 成员后按派生类到基类顺序调用。 */ - template - void after_prop_set(Self* object, Member Owner::* member, Prop_Access props) {} - /* CRTP 可覆盖:写入 State 成员前按基类到派生类顺序调用;pending_states.get() 返回对应可写状态层。 */ - template - void before_state_set(Self* object, Member Owner::* member, State_Access pending_states) {} - /* CRTP 可覆盖:写入 State 成员后按派生类到基类顺序调用;member 是本次写入的成员指针。 */ - template - void after_state_set(Self* object, Member Owner::* member, State_Access pending_states) {} - /* CRTP 可覆盖:缓冲推进前按基类到派生类顺序调用;pending_states 可写,current_states 只读。 */ - template - void before_advance(Self* object, - Prop_T* pending_prop, - State_Access pending_states, - const Prop_T* current_prop, - State_Access current_states) {} - /* CRTP 可覆盖:所有缓冲完成推进后按派生类到基类顺序调用;两侧 State 均通过 Tag 选择状态层。 */ - template - void after_advance(Self* object, - Prop_T* pending_prop, - State_Access pending_states, - const Prop_T* current_prop, - State_Access current_states) {} - }; - using Prev_Private = Private; - template - static void walk_private(Object_T* object, Callback& callback) { - if constexpr (requires { typename Layer::Prev_Object; }) { - if constexpr (!Reverse) walk_private(object, callback); - callback(static_cast(*object->d)); - if constexpr (Reverse) walk_private(object, callback); - } - } -}; -// Attach_Object 是对象运行时唯一数据入口:Prop 向外发布,State 向内提交,普通 Buffer 只推进,Dependency_Graph 负责依赖图同步。 -namespace detail { -template -struct Axis_Runtime_Private : Base {}; - -template - requires requires { typename Base::Axis_Runtime_Interface; } -struct Axis_Runtime_Private : Base { -private: - [[nodiscard]] Attached_Private& runtime() noexcept { - return static_cast&>(*this); - } - [[nodiscard]] const Attached_Private& runtime() const noexcept { - return static_cast&>(*this); - } -public: - [[nodiscard]] typename Base::Axis_Range_Type runtime_coordinate_range() const override { - return static_cast(*this).coordinate_range(runtime().object.get()); - } - [[nodiscard]] typename Base::Axis_Pixel_Position_Type runtime_coordinate_to_pixel( - typename Base::Axis_Coordinate_Type coordinate) const override { - return static_cast(*this).coordinate_to_pixel(runtime().object.get(), coordinate); - } - [[nodiscard]] typename Base::Axis_Coordinate_Type runtime_pixel_to_coordinate( - typename Base::Axis_Pixel_Position_Type pixel) const override { - return static_cast(*this).pixel_to_coordinate(runtime().object.get(), pixel); - } - [[nodiscard]] typename Base::Axis_Coordinate_Type runtime_point_to_coordinate( - typename Base::Axis_Point_Type point) const override { - return static_cast(*this).point_to_coordinate(runtime().object.get(), point); - } - [[nodiscard]] typename Base::Axis_Pixel_Sample_Count_Type runtime_pixel_sample_count( - typename Base::Axis_Range_Type coordinate_range) const override { - return static_cast(*this).pixel_sample_count(runtime().object.get(), coordinate_range); - } - [[nodiscard]] typename Base::Axis_Coordinate_Type runtime_tick_step( - typename Base::Axis_Range_Type coordinate_range) const override { - return static_cast(*this).tick_step(runtime().object.get(), coordinate_range); - } - [[nodiscard]] std::string runtime_tick_label( - typename Base::Axis_Coordinate_Type tick) const override { - return static_cast(*this).tick_label(runtime().object.get(), tick); - } - [[nodiscard]] typename Base::Axis_Tick_Count_Type runtime_sub_tick_count( - typename Base::Axis_Coordinate_Type major_step) const override { - return static_cast(*this).sub_tick_count(runtime().object.get(), major_step); - } -}; - -template > -struct Runtime_Private_Base : Base {}; - -template - requires requires { typename Base::Time_Axis_Runtime_Interface; } -struct Runtime_Private_Base : Base { -private: - [[nodiscard]] Attached_Private& runtime() noexcept { - return static_cast&>(*this); - } - [[nodiscard]] const Attached_Private& runtime() const noexcept { - return static_cast&>(*this); - } -public: - [[nodiscard]] std::size_t runtime_time_point_count() const override { - return static_cast(*this).time_point_count(runtime().object.get()); - } - typename Base::Axis_Time_Tick_Type runtime_append_time( - typename Base::Time_Of_Day_Type time) override { - return static_cast(*this).append_time(runtime().object.get(), time); - } - [[nodiscard]] typename Base::Time_Of_Day_Type runtime_tick_to_time( - typename Base::Axis_Time_Tick_Type tick) const override { - return static_cast(*this).tick_to_time(runtime().object.get(), tick); - } -}; - -template -struct Attached_Private : Runtime_Private_Base, Commit_Double_Buffer { - static_assert(Object_Root); - static_assert(Lockable); - using Prop = typename Obj::Prop; - using State = typename Obj::State; - using Buffers = typename Obj::Buffers; - using Dependency_Graph_Types = typename Obj::Dependency_Graph_Types; - using Mpmc_Triple_Buffers = typename Obj::Mpmc_Triple_Buffers; - using States = typename Obj::States; - /* CRTP 最终 Builder:所有基类流式接口都返回本类型,确保 build() 分派到最派生业务 Builder。 */ - using Private = Attached_Private; - aethera::not_null object; - using Allocator = std::pmr::polymorphic_allocator; - mutable Lock advance_lock; /* 仅保护全部双缓冲在 advance/publish 边界的交换。 */ - Commit_Double_Buffer state; - detail::State_Callback_Storage state_callbacks; - detail::Buffer_Storage buffer_storage; - detail::Dependency_Graph_Storage dependency_graph_storage; - detail::Mpmc_Triple_Buffer_Storage_Set mpmc_triple_buffer_storage; - explicit Attached_Private(Obj* object_value, std::pmr::memory_resource* resource) - : Commit_Double_Buffer(std::allocator_arg, Allocator{resource}), - state(std::allocator_arg, Allocator{resource}), - buffer_storage(std::allocator_arg, Allocator{resource}), - dependency_graph_storage(std::allocator_arg, Allocator{resource}), - object(object_value) {} - [[nodiscard]] const void* runtime_current_prop_data() const noexcept override { - return this->current; - } - [[nodiscard]] void* runtime_pending_prop_data() noexcept override { - return this->pending; - } - [[nodiscard]] const void* runtime_current_state_data() const noexcept override { - return state.current; - } - [[nodiscard]] void* runtime_pending_state_data() noexcept override { - return state.pending; - } - template requires detail::Prop_Tag_In> - [[nodiscard]] const auto& current_prop() const noexcept { - using Layer = detail::Prop_Value; - return static_cast(*this->current); - } - template Tag> - void publish_state_snapshot() { - using Layer = detail::State_Value; - state_callbacks.template publish([&] { - std::lock_guard guard(advance_lock); - state.advance(); - return Layer{static_cast(*state.current)}; - }); - } -private: - [[nodiscard]] Private& data() noexcept { - return *this; - } - [[nodiscard]] const Private& data() const noexcept { - return *this; - } - template - void walk_private(Callback& callback) { - if constexpr (requires { typename Layer::Prev_Object; }) { - if constexpr (!Reverse) walk_private(callback); - callback(static_cast(data())); - if constexpr (Reverse) walk_private(callback); - } - } - template - void before_prop_set(Member Owner::* member) { - Prop_Access props{*data().pending}; - auto callback = [&](auto& private_data) { - private_data.before_prop_set(object.get(), member, props); - }; - Obj::template walk_private(object.get(), callback); - } - template - void after_prop_set(Member Owner::* member) { - Prop_Access props{*data().pending}; - auto callback = [&](auto& private_data) { - private_data.after_prop_set(object.get(), member, props); - }; - Obj::template walk_private(object.get(), callback); - } - template - void emit_prop_dependencies() { - object->emit_dependency_source(detail::dependency_id>()); - using Prop_Tag = detail::Prop_Member_Tag; - object->emit_dependency_source(detail::dependency_id>()); - } - template - void before_state_set(Member Owner::* member) { - State_Access pending_states{*data().state.pending}; - auto callback = [&](auto& private_data) { - private_data.before_state_set(object.get(), member, pending_states); - }; - Obj::template walk_private(object.get(), callback); - } - template - void after_state_set(Member Owner::* member) { - State_Access pending_states{*data().state.pending}; - auto callback = [&](auto& private_data) { - private_data.after_state_set(object.get(), member, pending_states); - }; - Obj::template walk_private(object.get(), callback); - } - /* 同时发布字段级键和字段所属 State Tag 的状态层级键。 */ - template - void emit_state_dependencies() { - object->emit_dependency_source(detail::dependency_id>()); - using State_Tag = detail::State_Member_Tag; - object->emit_dependency_source(detail::dependency_id>()); - } - void before_advance() { - State_Access pending_states{*data().state.pending}; - State_Access current_states{std::as_const(*data().state.current)}; - auto callback = [&](auto& private_data) { - private_data.before_advance( - object.get(), - data().pending.get(), - pending_states, - data().current.get(), - current_states - ); - }; - Obj::template walk_private(object.get(), callback); - } - void after_advance() { - State_Access pending_states{*data().state.pending}; - State_Access current_states{*data().state.current}; - auto callback = [&](auto& private_data) { - private_data.after_advance( - object.get(), - data().pending.get(), - pending_states, - data().current.get(), - current_states - ); - }; - Obj::template walk_private(object.get(), callback); - } - void exchange() { - // Prop 与 State 均从外部 pending 提交到内部 current,交换后 pending 同步为最新编辑基线。 - static_cast&>(data()).advance(); - data().state.advance(); - data().buffer_storage.advance(); - data().dependency_graph_storage.advance(); - } -public: - template Tag> - auto& pending_buffer() { - object->emit_dependency_source(detail::dependency_id>()); - return *data().buffer_storage.template get().pending; - } - template Tag, typename Input> - void submit_stream(Input&& input) { - data().mpmc_triple_buffer_storage.template get().submit(std::forward(input)); - } - template Tag> - [[nodiscard]] std::uint64_t stream_submission_generation() const noexcept { - return data().mpmc_triple_buffer_storage.template get().submission_generation(); - } - template Tag> - void exchange_stream() { - data().mpmc_triple_buffer_storage.template get().advance(); - } - template Tag> - void accumulate_stream(std::size_t capacity) { - data().mpmc_triple_buffer_storage.template get().accumulate(capacity); - } - template Tag, - typename Predicate> - void accumulate_stream(Predicate&& retain) { - data().mpmc_triple_buffer_storage.template get().accumulate( - std::forward(retain)); - } - template Tag, typename Callback> - decltype(auto) access_rendering_stream(Callback&& callback) { - return data().mpmc_triple_buffer_storage.template get().access_rendering( - std::forward(callback)); - } - template Tag, typename Callback> - decltype(auto) access_query_stream(Callback&& callback) const { - return data().mpmc_triple_buffer_storage.template get().access_query( - std::forward(callback)); - } - template Tag> - const auto& current_buffer() const { - return *data().buffer_storage.template get().current; - } - template Tag, detail::Buffer_Value_Settable Value> - void initialize_buffer(Value&& value) { - auto& buffer = data().buffer_storage.template get(); - *buffer.pending = std::forward(value); - *buffer.current = *buffer.pending; - } - template Tag> - [[nodiscard]] auto pending_dependency_graph() const { - using Bound_Object = detail::Dependency_Graph_Bound_Object; - const auto graph = - data().dependency_graph_storage.template get().pending; - return graph->typed_view(); - } - template Tag> - [[nodiscard]] auto current_dependency_graph() const { - using Bound_Object = detail::Dependency_Graph_Bound_Object; - const auto graph = - data().dependency_graph_storage.template get().current; - return graph->typed_view(); - } - template ... Tags, detail::Dependency_Graph_Access_Callback_For Callback> - void access_pending_dependency_graph(Callback&& callback) { - data().dependency_graph_storage.template access(std::forward(callback)); - } - // 依赖图只允许在编辑侧修改;回调完成后先验证 DAG,再把新结构提交到 pending。 - template ... Tags, detail::Dependency_Graph_Edit_Callback_For Callback> - std::expected edit_dependency_graph(Callback&& callback) { - return data().dependency_graph_storage.template edit(std::forward(callback)); - } - std::expected validate_dependency_graph() const { - return data().dependency_graph_storage.validate_pending(); - } - template Tag, detail::Dependency_Graph_View_Node_Callback> Callback> - std::expected for_each_dependency_graph_topological(Callback&& callback) const { - return current_dependency_graph().for_each_topological_view(std::forward(callback)); - } - template - void for_each_current_dependency_graph(Callback&& callback) const { - data().dependency_graph_storage.for_each_current(std::forward(callback)); - } - template requires detail::Prop_Member && requires(Prop& prop, Value&& value) { prop.*Member = std::forward(value); } - Obj& set(Value&& value) { - before_prop_set(Member); - data().pending->*Member = std::forward(value); - after_prop_set(Member); - emit_prop_dependencies(); - return *object; - } - template requires detail::Prop_Dependency_Source - [[nodiscard]] auto get() const { - return data().pending->*Member; - } - template requires detail::Prop_Tag_In> - [[nodiscard]] const auto& read_prop() const noexcept { - using Layer = detail::Prop_Value; - return static_cast(*data().pending); - } - template requires - (sizeof...(Members) > 0) && - (detail::Prop_Member && ...) && - std::invocable> - void update_prop(Callback&& callback) { - (before_prop_set(Members), ...); - std::invoke(std::forward(callback), Prop_Access{*data().pending}); - (after_prop_set(Members), ...); - (emit_prop_dependencies(), ...); - } - template Tag, detail::State_Callback_For Callback> - void set_state_callback(Callback&& callback) { - data().state_callbacks.template set(std::forward(callback)); - } - template Tag> - void clear_state_callback() { - data().state_callbacks.template clear(); - } - template Tag, detail::State_Callback_For Callback> - void access_state(Callback&& callback) const { - using Layer = detail::State_Value; - std::invoke(std::forward(callback), static_cast(*data().state.current)); - } - // 按 Tag 直接读取双缓冲已提交状态层;调用方不得保存引用跨越任一相关对象的下一次 advance/process。 - template Tag> - [[nodiscard]] const auto& read_state() const noexcept { - using Layer = detail::State_Value; - return static_cast(*data().state.current); - } - // State 写入 pending,随后同时发出成员级与状态层级依赖信号;真正进入 current 发生在 advance/process 边界。 - template Value> - void update_state(Value&& value) { - before_state_set(Member); - data().state.pending->*Member = std::forward(value); - after_state_set(Member); - emit_state_dependencies(); - } - // 在同一锁作用域内按 Tag 原子修改一组相关 State 成员,并逐项触发生命周期钩子和依赖通知。 - template requires - (sizeof...(Members) > 0) && - (detail::State_Member && ...) && - std::invocable> - void update_state(Callback&& callback) { - (before_state_set(Members), ...); - std::invoke(std::forward(callback), State_Access{*data().state.pending}); - (after_state_set(Members), ...); - (emit_state_dependencies(), ...); - } - template Callback> - void process(Callback&& callback) { - advance(); - static_cast(*this).process( - object.get(), std::forward(callback)); - } - void advance() { - before_advance(); - { - std::lock_guard guard(data().advance_lock); - exchange(); - } - after_advance(); - // Commit_Double_Buffer::advance() 已为 pending 建立 current 基线;after_advance - // 只向 pending 写本轮派生状态,禁止回写正在供外部查询的 current。 - } - template Tag> - void publish_state() { - data().template publish_state_snapshot(); - } - template Tag, auto... Members, typename Callback> - requires (sizeof...(Members) > 0) && - (detail::State_Member && ...) && - std::invocable> - void publish_state(Callback&& callback) { - (before_state_set(Members), ...); - std::invoke(std::forward(callback), - State_Access{*data().state.pending}); - (after_state_set(Members), ...); - (emit_state_dependencies(), ...); - publish_state(); - } - template Callback> - void advance(Callback&& callback) { - advance(); - std::invoke(std::forward(callback), std::as_const(*data().state.current)); - } -}; -struct Internal_Access { -private: - template - [[nodiscard]] static Attached_Private& runtime(Object* object) noexcept { - return static_cast&>(*object->d); - } - template - [[nodiscard]] static const Attached_Private& runtime(const Object* object) noexcept { - return static_cast&>(*object->d); - } -public: - template - [[nodiscard]] static typename Tag::Private& layer(Object* object) noexcept { - return static_cast(*object->d); - } - template - [[nodiscard]] static const typename Tag::Private& layer(const Object* object) noexcept { - return static_cast(*object->d); - } - template - [[nodiscard]] static const typename Object::Prop& current_prop(const Object* object) noexcept { - return *static_cast(object->d->runtime_current_prop_data()); - } - template - requires detail::Prop_Tag_In> - [[nodiscard]] static const auto& current_prop_layer(const Object* object) noexcept { - using Layer = detail::Prop_Value; - return static_cast(current_prop(object)); - } - template - [[nodiscard]] static typename Object::Prop& pending_prop(Object* object) noexcept { - return *static_cast(object->d->runtime_pending_prop_data()); - } - template - [[nodiscard]] static typename Object::State& pending_state(Object* object) noexcept { - return *static_cast(object->d->runtime_pending_state_data()); - } - template - [[nodiscard]] static const typename Object::State& current_state(const Object* object) noexcept { - return *static_cast(object->d->runtime_current_state_data()); - } - template - requires detail::Buffer_Tag_In - [[nodiscard]] static decltype(auto) current_buffer(const Object* object) { - return runtime(object).template current_buffer(); - } - template - requires detail::Buffer_Tag_In - [[nodiscard]] static decltype(auto) pending_buffer(Object* object) { - return runtime(object).template pending_buffer(); - } - template - static void exchange_stream(Object* object) { - runtime(object).template exchange_stream(); - } - template - static void accumulate_stream(Object* object, std::size_t capacity) { - runtime(object).template accumulate_stream(capacity); - } - template - requires (!std::convertible_to) - static void accumulate_stream(Object* object, Predicate&& retain) { - runtime(object).template accumulate_stream(std::forward(retain)); - } - template - static decltype(auto) access_rendering_stream(Object* object, Callback&& callback) { - return runtime(object).template access_rendering_stream(std::forward(callback)); - } - template - static decltype(auto) access_query_stream(const Object* object, Callback&& callback) { - return runtime(object).template access_query_stream(std::forward(callback)); - } - template - static void submit_stream(Object* object, Input&& input) { - runtime(object).template submit_stream(std::forward(input)); - } - template - static void update_prop(Object* object, Callback&& callback) { - runtime(object).template update_prop(std::forward(callback)); - } - template - static void set(Object* object, Value&& value) { - runtime(object).template set(std::forward(value)); - } - template Value> - static void update_state(Object* object, Value&& value) { - runtime(object).template update_state(std::forward(value)); - } - template - requires (sizeof...(Members) > 0) && - (detail::State_Member && ...) && - std::invocable> - static void update_state(Object* object, Callback&& callback) { - runtime(object).template update_state(std::forward(callback)); - } - template - [[nodiscard]] static auto current_dependency_graph(const Object* object) { - return runtime(object).template current_dependency_graph(); - } - template - [[nodiscard]] static auto pending_dependency_graph(const Object* object) { - return runtime(object).template pending_dependency_graph(); - } - template - static void access_pending_dependency_graph(Object* object, Callback&& callback) { - runtime(object).template access_pending_dependency_graph( - std::forward(callback)); - } - template - static auto edit_dependency_graph(Object* object, Callback&& callback) { - return runtime(object).template edit_dependency_graph( - std::forward(callback)); - } - template - static void for_each_current_dependency_graph(const Object* object, Callback&& callback) { - runtime(object).for_each_current_dependency_graph(std::forward(callback)); - } - template - static void mark_dirty(Object* object) { - object->mark_dirty_id( - detail::dependency_id(), - detail::dependency_id>()); - } - template - [[nodiscard]] static bool dirty(const Object* object) { - return std::find(object->dirty_tags.begin(), object->dirty_tags.end(), - detail::dependency_id()) != object->dirty_tags.end(); - } - template - [[nodiscard]] static bool take_dirty(Object* object) { - const auto current = std::find(object->dirty_tags.begin(), object->dirty_tags.end(), - detail::dependency_id()); - if (current == object->dirty_tags.end()) return false; - object->dirty_tags.erase(current); - return true; - } - template - static void advance(Object* object) { - runtime(object).advance(); - } - template - static void publish_state(Object* object) { - runtime(object).template publish_state(); - } - template - static void access_state(const Object* object, Callback&& callback) { - runtime(object).template access_state(std::forward(callback)); - } - template - static void publish_state(Object* object, Callback&& callback) { - runtime(object).template publish_state(std::forward(callback)); - } - template - [[nodiscard]] static typename Object::Private& get(Object* object) noexcept { - return static_cast(*object->d); - } - template - [[nodiscard]] static const typename Object::Private& get(const Object* object) noexcept { - return static_cast(*object->d); - } -}; -} -} diff --git a/kernel/src/kernel/event.cpp b/kernel/src/kernel/event.cpp deleted file mode 100644 index 290b34a..0000000 --- a/kernel/src/kernel/event.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "event.hpp" -#include - -namespace aethera { -Event_Statistics_Accumulator::Event_Statistics_Accumulator( - std::size_t capacity) { - if (capacity == 0) - throw std::invalid_argument( - "event statistics capacity must be greater than zero"); - for (auto& event : values_) - for (auto& statistic : event) - statistic = Sliding_Statistics{capacity}; -} - -void Event_Statistics_Accumulator::submit( - Event_Type type, std::uint64_t created_steady_ns, - std::uint64_t started_steady_ns, - std::uint64_t completed_steady_ns) { - const auto type_index = static_cast(type); - if (type_index >= event_type_count || created_steady_ns == 0 || - started_steady_ns < created_steady_ns || - completed_steady_ns < started_steady_ns) - return; - const std::array samples{ - static_cast(started_steady_ns - created_steady_ns) / 1'000'000.0, - static_cast(completed_steady_ns - started_steady_ns) / 1'000'000.0, - static_cast(completed_steady_ns - created_steady_ns) / 1'000'000.0}; - for (std::size_t index = 0; index < samples.size(); ++index) - state_.values[type_index][index] = - values_[type_index][index].submit(samples[index]); -} - -const Event_Statistics_State& Event_Statistics_Accumulator::state() const noexcept { - return state_; -} - -void Event_Statistics_Accumulator::reset() noexcept { - for (auto& event : values_) - for (auto& statistic : event) - statistic.reset(); - state_ = {}; -} -} diff --git a/kernel/src/kernel/event.hpp b/kernel/src/kernel/event.hpp deleted file mode 100644 index da74697..0000000 --- a/kernel/src/kernel/event.hpp +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once -#include "frame_statistics.hpp" -#include "ownership.hpp" -#include -#include -#include -#include -#include -#include -namespace aethera { -enum struct Event_Type : std::uint8_t { - resize, - show, - hide, - leave, - pointer_move, - pointer_press, - pointer_release, - wheel, - key_press, - key_release -}; -inline constexpr std::size_t event_type_count = -static_cast(Event_Type::key_release) + 1; -enum struct Event_Statistic : std::uint8_t { - queue_wait_ms, - dispatch_ms, - total_ms, - count -}; -inline constexpr std::size_t event_statistic_count = -static_cast(Event_Statistic::count); -struct Event_Statistics_State { - std::array, - event_type_count> values{}; /* 按事件业务类型发布的定长延迟统计结果。 */ - bool operator==(const Event_Statistics_State&) const = default; -}; -struct Event_Statistics_Accumulator final { -public: - explicit Event_Statistics_Accumulator(std::size_t capacity = 600); - void submit(Event_Type type, std::uint64_t created_steady_ns, - std::uint64_t started_steady_ns, - std::uint64_t completed_steady_ns); - [[nodiscard]] const Event_Statistics_State& state() const noexcept; - void reset() noexcept; -private: - std::array, - event_type_count> values_; /* Scene 单帧准入域内增量维护的统计器。 */ - Event_Statistics_State state_{}; /* 下一次 Scene State 交换时发布的结果。 */ -}; -struct Event_Timeline_Time { - std::uint64_t nanoseconds{}; /* 输入生产者单调时间线上的事件发生时刻。 */ - bool operator==(const Event_Timeline_Time&) const = default; -}; -/* 所有输入事件的公共业务基类。 */ -struct Event { - struct Dispatch_Timing { - std::uint64_t frame_sequence{}; - std::uint64_t created_steady_ns{}; - std::uint64_t started_steady_ns{}; - std::uint64_t completed_steady_ns{}; - }; - explicit Event(Event_Type value); - Event(Event_Type value, Event_Timeline_Time occurred_at); - virtual ~Event(); - void accept() const noexcept; - [[nodiscard]] bool is_accepted() const noexcept; - void mark_dispatch_started(std::uint64_t frame_sequence) const noexcept; - void mark_dispatch_completed() const noexcept; - [[nodiscard]] Dispatch_Timing dispatch_timing() const noexcept; - Event_Type type; /* 事件种类;构造后保持不变。 */ - const Event_Timeline_Time occurred_at; /* 控制器消费的原始事件时间,不参与内核排队耗时统计。 */ -private: - friend struct Scene; - void observe_with( - not_null accumulator) noexcept; - std::uint64_t created_steady_ns_{}; /* 事件对象进入 Scene 前的内核单调时刻。 */ - Event_Statistics_Accumulator* statistics_{}; /* 所属 Scene 的统计器;不拥有且不得跨 Scene。 */ - mutable bool accepted{}; /* 处理链是否已经消费事件。 */ - mutable std::atomic_uint64_t dispatch_frame_sequence_{}; - mutable std::atomic_uint64_t dispatch_started_steady_ns_{}; - mutable std::atomic_uint64_t dispatch_completed_steady_ns_{}; -}; -template -concept Event_Object = std::derived_from, Event>; -template -concept Event_Point = std::default_initializable && requires(T value) { - value.x; - value.y; -}; -template -concept Event_Size = std::default_initializable && requires(T value) { - value.width; - value.height; -}; -enum struct Mouse_Button : std::uint8_t { none, left, right, middle }; -using Mouse_Button_Mask = std::uint8_t; -enum struct Keyboard_Modifier : std::uint8_t { - none = 0, - control = 1 << 0, - shift = 1 << 1, - alt = 1 << 2, - meta = 1 << 3 -}; -[[nodiscard]] constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, Keyboard_Modifier right) noexcept; -/* 不依赖具体坐标类型的指针事件查询能力。 */ -struct Pointer_Event_Capability { - virtual ~Pointer_Event_Capability() = default; - [[nodiscard]] virtual double position_x() const noexcept = 0; - [[nodiscard]] virtual double position_y() const noexcept = 0; - [[nodiscard]] virtual Mouse_Button pointer_button() const noexcept = 0; - [[nodiscard]] virtual Mouse_Button_Mask pointer_buttons() const noexcept = 0; - [[nodiscard]] virtual Keyboard_Modifier keyboard_modifiers() const noexcept = 0; -}; -/* 不依赖具体坐标类型的滚轮增量查询能力。 */ -struct Wheel_Event_Capability { - virtual ~Wheel_Event_Capability() = default; - [[nodiscard]] virtual double pixel_delta_x_value() const noexcept = 0; - [[nodiscard]] virtual double pixel_delta_y_value() const noexcept = 0; - [[nodiscard]] virtual double angle_delta_x_value() const noexcept = 0; - [[nodiscard]] virtual double angle_delta_y_value() const noexcept = 0; -}; -/* 带局部位置、全局位置、按键和修饰键的指针事件。 */ -template -struct Basic_Pointer_Event : Event, Pointer_Event_Capability { - explicit Basic_Pointer_Event(Event_Type value = Event_Type::pointer_move); - Basic_Pointer_Event(Event_Type value, Event_Timeline_Time occurred_at); - Point position{}; /* 事件接收对象局部坐标。 */ - Point global_position{}; /* 全局窗口坐标。 */ - Mouse_Button button{Mouse_Button::none}; /* 本次按下或释放的按键。 */ - Mouse_Button_Mask buttons{}; /* 事件发生时保持按下的按键集合。 */ - Keyboard_Modifier modifiers{Keyboard_Modifier::none}; /* 事件发生时的键盘修饰键集合。 */ - [[nodiscard]] double position_x() const noexcept override; - [[nodiscard]] double position_y() const noexcept override; - [[nodiscard]] Mouse_Button pointer_button() const noexcept override; - [[nodiscard]] Mouse_Button_Mask pointer_buttons() const noexcept override; - [[nodiscard]] Keyboard_Modifier keyboard_modifiers() const noexcept override; -}; -/* 带角度增量和像素增量的滚轮事件。 */ -template -struct Basic_Wheel_Event : Basic_Pointer_Event, Wheel_Event_Capability { - Basic_Wheel_Event(); - explicit Basic_Wheel_Event(Event_Timeline_Time occurred_at); - double angle_delta_x{}; /* 水平方向滚轮角度增量。 */ - double angle_delta_y{}; /* 垂直方向滚轮角度增量。 */ - double pixel_delta_x{}; /* 水平方向高精度像素增量。 */ - double pixel_delta_y{}; /* 垂直方向高精度像素增量。 */ - [[nodiscard]] double pixel_delta_x_value() const noexcept override; - [[nodiscard]] double pixel_delta_y_value() const noexcept override; - [[nodiscard]] double angle_delta_x_value() const noexcept override; - [[nodiscard]] double angle_delta_y_value() const noexcept override; -}; -/* 带旧尺寸和新尺寸的调整事件。 */ -template -struct Basic_Resize_Event : Event { - Basic_Resize_Event(); - Size old_size{}; /* 调整前尺寸。 */ - Size new_size{}; /* 调整后尺寸。 */ -}; -enum struct Key : std::uint16_t { - unknown, - escape, - enter, - space, - delete_key, - backspace, - left, - right, - up, - down, - home -}; -/* 键盘按下或释放事件。 */ -struct Key_Event : Event { - explicit Key_Event(Event_Type value); - Key_Event(Event_Type value, Event_Timeline_Time occurred_at); - Key key{Key::unknown}; /* 标准化按键。 */ - std::uint32_t native_key{}; /* 平台原生按键编码。 */ - Keyboard_Modifier modifiers{Keyboard_Modifier::none}; /* 事件发生时的修饰键集合。 */ - bool auto_repeat{}; /* 是否由系统自动重复产生。 */ -}; -} -#include "event.ipp" diff --git a/kernel/src/kernel/event.ipp b/kernel/src/kernel/event.ipp deleted file mode 100644 index 5f57a02..0000000 --- a/kernel/src/kernel/event.ipp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include -namespace aethera { -namespace detail { -inline std::uint64_t event_steady_time_ns() noexcept { - return static_cast(std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count()); -} -} -inline Event::Event(Event_Type value) - : Event(value, Event_Timeline_Time{detail::event_steady_time_ns()}) {} -inline Event::Event(Event_Type value, Event_Timeline_Time value_occurred_at) - : type(value), occurred_at(value_occurred_at), - created_steady_ns_(detail::event_steady_time_ns()) {} -inline Event::~Event() = default; -inline void Event::accept() const noexcept { accepted = true; } -inline bool Event::is_accepted() const noexcept { return accepted; } -inline void Event::mark_dispatch_started(std::uint64_t frame_sequence) const noexcept { - dispatch_frame_sequence_.store(frame_sequence, std::memory_order_relaxed); - dispatch_completed_steady_ns_.store(0, std::memory_order_relaxed); - dispatch_started_steady_ns_.store(detail::event_steady_time_ns(), std::memory_order_release); -} -inline void Event::mark_dispatch_completed() const noexcept { - const auto completed = detail::event_steady_time_ns(); - dispatch_completed_steady_ns_.store(completed, std::memory_order_release); - if (statistics_) - statistics_->submit( - type, created_steady_ns_, - dispatch_started_steady_ns_.load(std::memory_order_acquire), - completed); -} -inline Event::Dispatch_Timing Event::dispatch_timing() const noexcept { - return {dispatch_frame_sequence_.load(std::memory_order_relaxed), - created_steady_ns_, - dispatch_started_steady_ns_.load(std::memory_order_acquire), - dispatch_completed_steady_ns_.load(std::memory_order_acquire)}; -} -inline void Event::observe_with( - not_null accumulator) noexcept { - statistics_ = accumulator; -} -constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, Keyboard_Modifier right) noexcept { return static_cast(static_cast(left) | static_cast(right)); } -template Basic_Pointer_Event::Basic_Pointer_Event(Event_Type value) : Event(value) {} -template Basic_Pointer_Event::Basic_Pointer_Event(Event_Type value, Event_Timeline_Time value_occurred_at) : Event(value, value_occurred_at) {} -template double Basic_Pointer_Event::position_x() const noexcept { return static_cast(position.x); } -template double Basic_Pointer_Event::position_y() const noexcept { return static_cast(position.y); } -template Mouse_Button Basic_Pointer_Event::pointer_button() const noexcept { return button; } -template Mouse_Button_Mask Basic_Pointer_Event::pointer_buttons() const noexcept { return buttons; } -template Keyboard_Modifier Basic_Pointer_Event::keyboard_modifiers() const noexcept { return modifiers; } -template Basic_Wheel_Event::Basic_Wheel_Event() : Basic_Pointer_Event(Event_Type::wheel) {} -template Basic_Wheel_Event::Basic_Wheel_Event(Event_Timeline_Time value_occurred_at) : Basic_Pointer_Event(Event_Type::wheel, value_occurred_at) {} -template double Basic_Wheel_Event::pixel_delta_x_value() const noexcept { return pixel_delta_x; } -template double Basic_Wheel_Event::pixel_delta_y_value() const noexcept { return pixel_delta_y; } -template double Basic_Wheel_Event::angle_delta_x_value() const noexcept { return angle_delta_x; } -template double Basic_Wheel_Event::angle_delta_y_value() const noexcept { return angle_delta_y; } -template Basic_Resize_Event::Basic_Resize_Event() : Event(Event_Type::resize) {} -inline Key_Event::Key_Event(Event_Type value) : Event(value) {} -inline Key_Event::Key_Event(Event_Type value, Event_Timeline_Time value_occurred_at) : Event(value, value_occurred_at) {} -} diff --git a/kernel/src/kernel/frame.cpp b/kernel/src/kernel/frame.cpp deleted file mode 100644 index 2db730f..0000000 --- a/kernel/src/kernel/frame.cpp +++ /dev/null @@ -1,543 +0,0 @@ -#include "frame.hpp" -#include "frame_statistics.hpp" -#include "Taskflow_Frame_Access.hpp" -#include "Task_Graph_Internal.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace aethera { -namespace { -constexpr std::size_t marker_count = static_cast(Frame_Trace_Marker::count); -constexpr std::size_t measurement_count = static_cast(Frame_Trace_Measurement::count); -std::uint64_t encode_present_value(std::uint64_t value) noexcept { return value == std::numeric_limits::max() ? value : value + 1; } -std::uint64_t decode_present_value(std::uint64_t value) noexcept { return value == std::numeric_limits::max() ? value : value - 1; } -} -struct Render_Frame::Private { - struct Worker_Taskflow_Trace { - std::vector tasks{}; /* 仅对应 worker 写入,捕获结束后统一读取。 */ - }; - Frame_Identity identity{}; /* 外部帧管理器提供且终生不变的帧身份。 */ - Frame_Request_Source request_source{}; /* 当前逻辑帧的策略触发来源。 */ - std::chrono::steady_clock::time_point created_at{}; /* 所有 elapsed_ns 使用的单调时钟原点。 */ - std::uint64_t created_time_unix_ns{}; /* 用于跨进程展示的创建 Unix 时间,单位为纳秒。 */ - std::array markers{}; /* 每种时间点首次出现时的 elapsed_ns 加一编码。 */ - std::array measurements{}; /* 每种原始耗时首次记录值的加一编码。 */ - std::atomic_bool taskflow_trace_requested{}; /* 本次逻辑帧是否请求原生 Taskflow 捕获。 */ - std::vector taskflow_workers{}; /* 按 Executor worker 隔离的单写者时间线。 */ - std::vector taskflow_graphs{}; /* 内部流水线追加完成后,由外部一次性消费。 */ -}; -Render_Frame::Render_Frame(Frame_Identity identity) : d(std::make_unique()) { - begin(identity); -} -void Render_Frame::begin(Frame_Identity identity, - Frame_Request_Source source) noexcept { - d->identity = identity; - d->request_source = source; - d->created_at = std::chrono::steady_clock::now(); - const auto system_now = std::chrono::system_clock::now().time_since_epoch(); - d->created_time_unix_ns = static_cast(std::chrono::duration_cast(system_now).count()); - for (auto& marker : d->markers) - marker.store(0, std::memory_order_relaxed); - for (auto& measurement : d->measurements) - measurement.store(0, std::memory_order_relaxed); - d->taskflow_trace_requested.store(false, std::memory_order_relaxed); - for (auto& worker : d->taskflow_workers) worker.tasks.clear(); - d->taskflow_graphs.clear(); - d->markers[static_cast(Frame_Trace_Marker::created)].store(encode_present_value(0), std::memory_order_relaxed); -} -Render_Frame::~Render_Frame() = default; -Frame_Identity Render_Frame::identity() const noexcept { return d->identity; } -std::uint64_t Render_Frame::created_time_unix_ns() const noexcept { return d->created_time_unix_ns; } -void Render_Frame::mark(Frame_Trace_Marker marker) noexcept { - const auto index = static_cast(marker); - if (index >= marker_count) return; - const auto elapsed = std::chrono::steady_clock::now() - d->created_at; - const auto elapsed_ns = static_cast(std::max(0, std::chrono::duration_cast(elapsed).count())); - std::uint64_t expected{}; - d->markers[index].compare_exchange_strong(expected, encode_present_value(elapsed_ns), std::memory_order_release, std::memory_order_relaxed); -} -void Render_Frame::record(Frame_Trace_Measurement measurement, std::uint64_t value_ns) noexcept { - const auto index = static_cast(measurement); - if (index >= measurement_count) return; - std::uint64_t expected{}; - d->measurements[index].compare_exchange_strong(expected, encode_present_value(value_ns), std::memory_order_release, std::memory_order_relaxed); -} -Frame_Statistics_Sample Render_Frame::statistics() const { - std::array, marker_count> markers{}; - for (std::size_t index = 0; index < marker_count; ++index) { - const auto encoded = d->markers[index].load(std::memory_order_acquire); - if (encoded) - markers[index] = static_cast(decode_present_value(encoded)) / - 1'000'000.0; - } - std::array measurements{}; - for (std::size_t index = 0; index < measurement_count; ++index) { - const auto encoded = d->measurements[index].load(std::memory_order_acquire); - if (encoded) - measurements[index] = static_cast(decode_present_value(encoded)) / - 1'000'000.0; - } - const auto marker = [&](Frame_Trace_Marker value) { - return markers[static_cast(value)].value_or(0.0); - }; - const auto interval = [&](Frame_Trace_Marker first, Frame_Trace_Marker last) { - const auto start = markers[static_cast(first)]; - const auto finish = markers[static_cast(last)]; - return start && finish ? std::max(0.0, *finish - *start) : 0.0; - }; - const auto measurement = [&](Frame_Trace_Measurement value) { - return measurements[static_cast(value)]; - }; - - Frame_Statistics_Sample result{}; - result.set(Frame_Statistic::plot_tick_queue_ms, - measurement(Frame_Trace_Measurement::plot_tick_queue_ns)); - result.set(Frame_Statistic::plot_update_ms, - measurement(Frame_Trace_Measurement::plot_update_ns)); - result.set(Frame_Statistic::plot_publish_ms, - measurement(Frame_Trace_Measurement::plot_publish_ns)); - result.set(Frame_Statistic::server_completion_ms, - marker(Frame_Trace_Marker::frame_ready)); - result.set(Frame_Statistic::scene_render_ms, interval( - Frame_Trace_Marker::scene_render_started, - Frame_Trace_Marker::scene_render_finished)); - result.set(Frame_Statistic::event_dispatch_ms, interval( - Frame_Trace_Marker::event_dispatch_started, - Frame_Trace_Marker::event_dispatch_finished)); - result.set(Frame_Statistic::backend_queue_ms, interval( - Frame_Trace_Marker::backend_queue_entered, Frame_Trace_Marker::backend_queue_left)); - result.set(Frame_Statistic::gpu_submission_ms, interval( - Frame_Trace_Marker::gpu_submitted, Frame_Trace_Marker::gpu_completed)); - - constexpr std::array measurement_statistics{ - Frame_Statistic::backend_apply_ms, - Frame_Statistic::backend_plan_ms, - Frame_Statistic::backend_execute_ms, - Frame_Statistic::backend_target_acquire_ms, - Frame_Statistic::backend_structure_check_ms, - Frame_Statistic::backend_query_ms, - Frame_Statistic::backend_runtime_plan_ms, - Frame_Statistic::backend_runtime_plan_cpu_ms, - Frame_Statistic::backend_runtime_execute_ms, - Frame_Statistic::backend_runtime_execute_cpu_ms, - Frame_Statistic::backend_mvp_update_ms, - Frame_Statistic::backend_frame_begin_ms, - Frame_Statistic::backend_frame_plan_ms, - Frame_Statistic::backend_frame_plan_cpu_ms, - Frame_Statistic::backend_emit_replay_dirty_ms, - Frame_Statistic::backend_emit_layout_ms, - Frame_Statistic::backend_emit_prepare_ms, - Frame_Statistic::backend_emit_plan_build_ms, - Frame_Statistic::backend_emit_contract_ms, - Frame_Statistic::backend_emit_stream_ms, - Frame_Statistic::backend_emit_stream_freeze_ms, - Frame_Statistic::backend_emit_commit_ms, - Frame_Statistic::backend_emit_plan_reset_ms, - Frame_Statistic::backend_emit_artifact_create_ms, - Frame_Statistic::backend_emit_artifact_freeze_ms, - Frame_Statistic::backend_emit_packet_encode_ms, - Frame_Statistic::backend_external_register_ms, - Frame_Statistic::backend_frame_attach_ms, - Frame_Statistic::backend_frame_execute_ms, - Frame_Statistic::backend_frame_execute_cpu_ms, - Frame_Statistic::backend_frame_finish_ms, - Frame_Statistic::backend_drp_validation_ms, - Frame_Statistic::backend_drp_state_ms, - Frame_Statistic::backend_drp_buffer_create_ms, - Frame_Statistic::backend_drp_texture_create_ms, - Frame_Statistic::backend_drp_shader_create_ms, - Frame_Statistic::backend_drp_shader_compile_ms, - Frame_Statistic::backend_drp_shader_module_create_ms, - Frame_Statistic::backend_drp_pipeline_create_ms, - Frame_Statistic::backend_drp_binding_create_ms, - Frame_Statistic::backend_drp_upload_ms, - Frame_Statistic::backend_drp_upload_decode_ms, - Frame_Statistic::backend_drp_upload_vulkan_allocate_ms, - Frame_Statistic::backend_drp_upload_host_copy_ms, - Frame_Statistic::backend_drp_upload_command_allocate_ms, - Frame_Statistic::backend_drp_upload_command_record_ms, - Frame_Statistic::backend_drp_upload_fence_create_ms, - Frame_Statistic::backend_drp_upload_submit_enqueue_ms, - Frame_Statistic::backend_drp_upload_submit_queue_wait_ms, - Frame_Statistic::backend_drp_upload_queue_submit_ms, - Frame_Statistic::backend_drp_upload_fence_wait_ms, - Frame_Statistic::backend_drp_upload_retire_ms, - Frame_Statistic::backend_drp_transfer_ms, - Frame_Statistic::backend_drp_record_ms, - Frame_Statistic::backend_submit_queue_ms, - Frame_Statistic::backend_submit_ms, - Frame_Statistic::gpu_completion_observation_ms, - Frame_Statistic::completion_task_queue_ms, - Frame_Statistic::gpu_render_ms, - Frame_Statistic::gpu_transition_ms, - Frame_Statistic::gpu_copy_ms, - Frame_Statistic::gpu_total_ms, - Frame_Statistic::readback_ms}; - constexpr std::array measurement_keys{ - Frame_Trace_Measurement::backend_apply_ns, - Frame_Trace_Measurement::backend_plan_ns, - Frame_Trace_Measurement::backend_execute_ns, - Frame_Trace_Measurement::backend_target_acquire_ns, - Frame_Trace_Measurement::backend_structure_check_ns, - Frame_Trace_Measurement::backend_query_ns, - Frame_Trace_Measurement::backend_runtime_plan_ns, - Frame_Trace_Measurement::backend_runtime_plan_cpu_ns, - Frame_Trace_Measurement::backend_runtime_execute_ns, - Frame_Trace_Measurement::backend_runtime_execute_cpu_ns, - Frame_Trace_Measurement::backend_mvp_update_ns, - Frame_Trace_Measurement::backend_frame_begin_ns, - Frame_Trace_Measurement::backend_frame_plan_ns, - Frame_Trace_Measurement::backend_frame_plan_cpu_ns, - Frame_Trace_Measurement::backend_emit_replay_dirty_ns, - Frame_Trace_Measurement::backend_emit_layout_ns, - Frame_Trace_Measurement::backend_emit_prepare_ns, - Frame_Trace_Measurement::backend_emit_plan_build_ns, - Frame_Trace_Measurement::backend_emit_contract_ns, - Frame_Trace_Measurement::backend_emit_stream_ns, - Frame_Trace_Measurement::backend_emit_stream_freeze_ns, - Frame_Trace_Measurement::backend_emit_commit_ns, - Frame_Trace_Measurement::backend_emit_plan_reset_ns, - Frame_Trace_Measurement::backend_emit_artifact_create_ns, - Frame_Trace_Measurement::backend_emit_artifact_freeze_ns, - Frame_Trace_Measurement::backend_emit_packet_encode_ns, - Frame_Trace_Measurement::backend_external_register_ns, - Frame_Trace_Measurement::backend_frame_attach_ns, - Frame_Trace_Measurement::backend_frame_execute_ns, - Frame_Trace_Measurement::backend_frame_execute_cpu_ns, - Frame_Trace_Measurement::backend_frame_finish_ns, - Frame_Trace_Measurement::backend_drp_validation_ns, - Frame_Trace_Measurement::backend_drp_state_ns, - Frame_Trace_Measurement::backend_drp_buffer_create_ns, - Frame_Trace_Measurement::backend_drp_texture_create_ns, - Frame_Trace_Measurement::backend_drp_shader_create_ns, - Frame_Trace_Measurement::backend_drp_shader_compile_ns, - Frame_Trace_Measurement::backend_drp_shader_module_create_ns, - Frame_Trace_Measurement::backend_drp_pipeline_create_ns, - Frame_Trace_Measurement::backend_drp_binding_create_ns, - Frame_Trace_Measurement::backend_drp_upload_ns, - Frame_Trace_Measurement::backend_drp_upload_decode_ns, - Frame_Trace_Measurement::backend_drp_upload_vulkan_allocate_ns, - Frame_Trace_Measurement::backend_drp_upload_host_copy_ns, - Frame_Trace_Measurement::backend_drp_upload_command_allocate_ns, - Frame_Trace_Measurement::backend_drp_upload_command_record_ns, - Frame_Trace_Measurement::backend_drp_upload_fence_create_ns, - Frame_Trace_Measurement::backend_drp_upload_submit_enqueue_ns, - Frame_Trace_Measurement::backend_drp_upload_submit_queue_wait_ns, - Frame_Trace_Measurement::backend_drp_upload_queue_submit_ns, - Frame_Trace_Measurement::backend_drp_upload_fence_wait_ns, - Frame_Trace_Measurement::backend_drp_upload_retire_ns, - Frame_Trace_Measurement::backend_drp_transfer_ns, - Frame_Trace_Measurement::backend_drp_record_ns, - Frame_Trace_Measurement::backend_submit_queue_ns, - Frame_Trace_Measurement::backend_submit_ns, - Frame_Trace_Measurement::gpu_completion_observation_ns, - Frame_Trace_Measurement::completion_task_queue_ns, - Frame_Trace_Measurement::gpu_render_ns, - Frame_Trace_Measurement::gpu_transition_ns, - Frame_Trace_Measurement::gpu_copy_ns, - Frame_Trace_Measurement::gpu_total_ns, - Frame_Trace_Measurement::readback_ns}; - for (std::size_t index = 0; index < measurement_statistics.size(); ++index) - result.set(measurement_statistics[index], measurement(measurement_keys[index])); - - return result; -} - -void Render_Frame::request_taskflow_trace() noexcept { - d->taskflow_trace_requested.store(true, std::memory_order_release); -} - -bool Render_Frame::taskflow_trace_requested() const noexcept { - return d->taskflow_trace_requested.load(std::memory_order_acquire); -} - -Taskflow_Frame_Trace Render_Frame::take_taskflow_trace() { - Taskflow_Frame_Trace result{}; - result.identity = d->identity; - result.request_source = d->request_source; - result.created_time_unix_ns = d->created_time_unix_ns; - result.worker_count = d->taskflow_workers.size(); - for (std::size_t index = 0; index < marker_count; ++index) { - const auto encoded = d->markers[index].load(std::memory_order_acquire); - if (!encoded) continue; - result.markers.push_back(Frame_Trace_Point{ - static_cast(index), decode_present_value(encoded)}); - } - for (std::size_t index = 0; index < measurement_count; ++index) { - const auto encoded = d->measurements[index].load(std::memory_order_acquire); - if (!encoded) continue; - result.measurements.push_back(Frame_Trace_Value{ - static_cast(index), - decode_present_value(encoded)}); - } - result.graphs.reserve(d->taskflow_graphs.size()); - for (auto& graph : d->taskflow_graphs) - result.graphs.push_back(std::move(graph)); - d->taskflow_graphs.clear(); - for (auto& worker : d->taskflow_workers) { - for (auto& task : worker.tasks) - result.tasks.push_back(std::move(task)); - worker.tasks.clear(); - } - std::ranges::sort(result.tasks, {}, &Taskflow_Task_Trace::started_ms); - - std::erase_if(result.tasks, [&](const Taskflow_Task_Trace& task) { - return std::ranges::none_of(result.graphs, [&](const Taskflow_Graph_Trace& graph) { - return std::ranges::any_of(graph.nodes, [&](const Taskflow_Graph_Trace::Node& node) { - return node.native_id == task.native_id; - }); - }); - }); - - struct Node_Context { - not_null graph; - not_null node; - }; - std::unordered_map nodes; - std::unordered_map native_id_by_node_id; - std::unordered_map> children_by_parent_id; - for (const auto& graph : result.graphs) { - for (const auto& node : graph.nodes) { - nodes.try_emplace( - node.native_id, - Node_Context{not_null{&graph}, not_null{&node}}); - native_id_by_node_id.try_emplace(node.node_id, node.native_id); - if (!node.parent_node_id.empty()) - children_by_parent_id[node.parent_node_id].push_back(node.native_id); - } - } - std::unordered_map observed_finished; - for (const auto& task : result.tasks) - observed_finished[task.native_id] = std::max( - observed_finished[task.native_id], task.completed_ms); - - std::unordered_map ready_cache; - std::unordered_map finished_cache; - std::unordered_set resolving_ready; - std::unordered_set resolving_finished; - std::function ready_of; - std::function finished_of; - ready_of = [&](std::uint64_t native_id) -> double { - if (const auto cached = ready_cache.find(native_id); - cached != ready_cache.end()) return cached->second; - const auto context = nodes.find(native_id); - if (context == nodes.end() || !resolving_ready.insert(native_id).second) - return 0.0; - double ready = context->second.graph->submitted_ms; - const auto& node = *context->second.node; - if (!node.parent_node_id.empty()) { - if (const auto parent = native_id_by_node_id.find(node.parent_node_id); - parent != native_id_by_node_id.end()) - ready = std::max(ready, ready_of(parent->second)); - } - for (const auto predecessor : node.predecessors) - ready = std::max(ready, finished_of(predecessor)); - resolving_ready.erase(native_id); - ready_cache.emplace(native_id, ready); - return ready; - }; - finished_of = [&](std::uint64_t native_id) -> double { - if (const auto cached = finished_cache.find(native_id); - cached != finished_cache.end()) return cached->second; - if (!resolving_finished.insert(native_id).second) return ready_of(native_id); - double finished = ready_of(native_id); - if (const auto observed = observed_finished.find(native_id); - observed != observed_finished.end()) - finished = std::max(finished, observed->second); - if (const auto context = nodes.find(native_id); context != nodes.end()) { - if (const auto children = children_by_parent_id.find( - context->second.node->node_id); - children != children_by_parent_id.end()) - for (const auto child : children->second) - finished = std::max(finished, finished_of(child)); - } - resolving_finished.erase(native_id); - finished_cache.emplace(native_id, finished); - return finished; - }; - - std::unordered_map previous_execution_finished; - for (auto& task : result.tasks) { - double ready = ready_of(task.native_id); - if (const auto previous = previous_execution_finished.find(task.native_id); - previous != previous_execution_finished.end()) - ready = std::max(ready, previous->second); - task.ready_ms = ready; - task.queue_wait_ms = std::max(0.0, task.entered_ms - ready); - previous_execution_finished[task.native_id] = task.completed_ms; - } - std::unordered_set used_native_ids; - for (const auto& graph : result.graphs) - for (const auto& node : graph.nodes) used_native_ids.insert(node.native_id); - std::uint64_t diagnostic_native_id = std::numeric_limits::max(); - const auto allocate_diagnostic_id = [&]() { - while (used_native_ids.contains(diagnostic_native_id)) --diagnostic_native_id; - const auto value = diagnostic_native_id--; - used_native_ids.insert(value); - return value; - }; - for (auto& graph : result.graphs) { - if (!graph.completed || graph.finished_ms <= 0.0) continue; - std::unordered_set graph_native_ids; - for (const auto& node : graph.nodes) graph_native_ids.insert(node.native_id); - double last_completed = graph.submitted_ms; - for (const auto& task : result.tasks) - if (graph_native_ids.contains(task.native_id)) - last_completed = std::max(last_completed, task.completed_ms); - std::vector predecessors; - for (const auto& node : graph.nodes) - if (node.successors.empty()) predecessors.push_back(node.native_id); - struct Diagnostic_Phase { - const char* name; - const char* label; - double finished_ms; - }; - const Diagnostic_Phase phases[] = { - {"taskflow.executor.finalize", "Executor topology 收尾", graph.executor_finished_ms}, - {"taskflow.runtime.bookkeeping", "运行计数更新", graph.runtime_bookkeeping_finished_ms}, - {"taskflow.runtime.publish_state", "运行时状态发布", graph.runtime_publish_finished_ms}, - {"taskflow.observer.unbind_frame", "Observer 帧绑定清除", graph.observer_unbind_finished_ms}, - {"taskflow.graph.finish", "Graph 完成记录", graph.finished_ms} - }; - double started_ms = last_completed; - for (const auto& phase : phases) { - if (phase.finished_ms <= 0.0) continue; - const auto finished_ms = std::max(started_ms, phase.finished_ms); - const auto native_id = allocate_diagnostic_id(); - Taskflow_Graph_Trace::Node node{}; - node.native_id = native_id; - node.node_id = (graph.taskflow_name.empty() ? graph.stage : graph.taskflow_name) + - "/@diagnostic/" + phase.name; - node.name = phase.name; - node.type = "diagnostic"; - node.predecessors = predecessors; - node.attributes.emplace_back("diagnostic", "runtime_tail"); - node.attributes.emplace_back("label", phase.label); - for (const auto predecessor : predecessors) { - const auto found = std::ranges::find( - graph.nodes, predecessor, &Taskflow_Graph_Trace::Node::native_id); - if (found != graph.nodes.end()) found->successors.push_back(native_id); - } - graph.nodes.push_back(std::move(node)); - Taskflow_Task_Trace task{}; - task.native_id = native_id; - task.worker_id = result.worker_count; - task.ready_ms = started_ms; - task.entered_ms = started_ms; - task.started_ms = started_ms; - task.finished_ms = finished_ms; - task.completed_ms = finished_ms; - task.duration_ms = std::max(0.0, finished_ms - started_ms); - result.tasks.push_back(std::move(task)); - predecessors.assign(1, native_id); - started_ms = finished_ms; - } - } - std::ranges::sort(result.tasks, {}, &Taskflow_Task_Trace::started_ms); - return result; -} - -void detail::Taskflow_Frame_Access::begin_capture(Render_Frame& frame, std::size_t workers) { - auto& data = *frame.d; - data.taskflow_workers.resize(workers); - for (auto& worker : data.taskflow_workers) { - worker.tasks.clear(); - if (worker.tasks.capacity() < 64) worker.tasks.reserve(64); - } - data.taskflow_graphs.clear(); -} - -void detail::Taskflow_Frame_Access::finish_capture(Render_Frame& frame) noexcept { - (void)frame; - /* run completion 已保证最后一个 Observer on_exit 返回;这里只标记消费边界,不同步等待。 */ -} - -std::size_t detail::Taskflow_Frame_Access::append_task( - Render_Frame& frame, std::size_t worker, std::uint64_t native_id, - std::size_t queue_size, std::size_t queue_capacity, - Clock::time_point entered, Clock::time_point started, - Clock::time_point finished, std::uint64_t cooperative_wait_ns, - std::span> - cooperative_waits) { - auto& data = *frame.d; - if (worker >= data.taskflow_workers.size()) - return std::numeric_limits::max(); - const auto elapsed_ms = [&](Clock::time_point value) { - return std::chrono::duration(value - data.created_at).count(); - }; - Taskflow_Task_Trace trace{}; - trace.native_id = native_id; - trace.worker_id = worker; - trace.worker_queue_size = queue_size; - trace.worker_queue_capacity = queue_capacity; - trace.entered_ms = elapsed_ms(entered); - trace.started_ms = elapsed_ms(started); - trace.finished_ms = elapsed_ms(finished); - trace.completed_ms = trace.finished_ms; - trace.duration_ms = std::max(0.0, trace.finished_ms - trace.started_ms); - trace.cooperative_wait_ms = static_cast(cooperative_wait_ns) / 1'000'000.0; - trace.cooperative_waits.reserve(cooperative_waits.size()); - for (const auto& [wait_started, wait_finished] : cooperative_waits) { - trace.cooperative_waits.push_back({ - elapsed_ms(wait_started), elapsed_ms(wait_finished)}); - } - trace.observer_entry_ms = std::max(0.0, trace.started_ms - trace.entered_ms); - data.taskflow_workers[worker].tasks.push_back(std::move(trace)); - return data.taskflow_workers[worker].tasks.size() - 1; -} - -void detail::Taskflow_Frame_Access::finish_task_observer( - Render_Frame& frame, std::size_t worker, std::size_t task, - Clock::time_point completed) noexcept { - auto& data = *frame.d; - if (worker >= data.taskflow_workers.size() || - task >= data.taskflow_workers[worker].tasks.size()) return; - auto& trace = data.taskflow_workers[worker].tasks[task]; - trace.completed_ms = std::chrono::duration( - completed - data.created_at).count(); - trace.observer_exit_ms = std::max( - 0.0, trace.completed_ms - trace.finished_ms); -} - -detail::Taskflow_Graph_Token detail::Taskflow_Frame_Access::begin_graph( - Render_Frame& frame, Task_Graph& taskflow, std::string_view stage) { - Taskflow_Graph_Trace graph{}; - graph.stage = stage; - graph.taskflow_name = taskflow.name(); - graph.nodes = detail::Task_Graph_Access::nodes(taskflow); - for (auto& worker : frame.d->taskflow_workers) { - const auto required = worker.tasks.size() + graph.nodes.size(); - if (worker.tasks.capacity() < required) worker.tasks.reserve(required); - } - graph.submitted_ms = std::chrono::duration( - Clock::now() - frame.d->created_at).count(); - frame.d->taskflow_graphs.push_back(std::move(graph)); - return {not_null{&frame}, frame.d->taskflow_graphs.size() - 1}; -} - -void detail::Taskflow_Frame_Access::finish_graph( - detail::Taskflow_Graph_Token token, - const detail::Taskflow_Graph_Timing& timing) noexcept { - auto& data = *token.frame->d; - const auto elapsed_ms = [&](Clock::time_point value) { - return value == Clock::time_point{} ? 0.0 - : std::chrono::duration(value - data.created_at).count(); - }; - if (token.index >= data.taskflow_graphs.size()) return; - const auto finished = Clock::now(); - auto& graph = data.taskflow_graphs[token.index]; - graph.executor_finished_ms = elapsed_ms(timing.executor_finished); - graph.runtime_bookkeeping_finished_ms = elapsed_ms(timing.runtime_bookkeeping_finished); - graph.runtime_publish_finished_ms = elapsed_ms(timing.runtime_publish_finished); - graph.observer_unbind_finished_ms = elapsed_ms(timing.observer_unbind_finished); - graph.finished_ms = elapsed_ms(finished); - graph.completed = true; -} -} diff --git a/kernel/src/kernel/frame.hpp b/kernel/src/kernel/frame.hpp deleted file mode 100644 index 03c86f6..0000000 --- a/kernel/src/kernel/frame.hpp +++ /dev/null @@ -1,211 +0,0 @@ -#pragma once -#include "double_buffer/mechanism.hpp" -#include -#include -#include -#include -#include -namespace aethera { -enum struct Frame_Request_Source : std::uint8_t { - unspecified, - periodic, - immediate, - maximum_rate -}; -namespace detail { -struct Taskflow_Frame_Access; -} -struct Frame_Statistics_Sample; -enum struct Frame_Trace_Marker : std::uint8_t { - created, - plot_update_started, - plot_update_finished, - scene_render_entered, - scene_advance_started, - scene_advance_finished, - scene_render_requested, - scene_render_started, - event_dispatch_started, - event_dispatch_finished, - prepare_started, - prepare_finished, - paint_started, - paint_frame_target_started, - paint_frame_target_finished, - paint_background_started, - paint_background_finished, - paint_cache_targets_started, - paint_cache_targets_finished, - paint_taskflow_started, - paint_taskflow_finished, - paint_finished, - backend_queue_entered, - backend_prepare_started, - backend_prepare_finished, - backend_submit_queued, - backend_queue_left, - gpu_submitted, - gpu_completed, - readback_started, - readback_finished, - scene_render_finished, - frame_ready, - count -}; -enum struct Frame_Trace_Measurement : std::uint8_t { - plot_tick_queue_ns, - plot_update_ns, - plot_publish_ns, - backend_apply_ns, - backend_plan_ns, - backend_execute_ns, - backend_target_acquire_ns, - backend_structure_check_ns, - backend_query_ns, - backend_runtime_plan_ns, - backend_runtime_plan_cpu_ns, - backend_runtime_execute_ns, - backend_runtime_execute_cpu_ns, - backend_mvp_update_ns, - backend_frame_begin_ns, - backend_frame_plan_ns, - backend_frame_plan_cpu_ns, - backend_emit_replay_dirty_ns, - backend_emit_layout_ns, - backend_emit_prepare_ns, - backend_emit_plan_build_ns, - backend_emit_contract_ns, - backend_emit_stream_ns, - backend_emit_stream_freeze_ns, - backend_emit_commit_ns, - backend_emit_plan_reset_ns, - backend_emit_artifact_create_ns, - backend_emit_artifact_freeze_ns, - backend_emit_packet_encode_ns, - backend_external_register_ns, - backend_frame_attach_ns, - backend_frame_execute_ns, - backend_frame_execute_cpu_ns, - backend_frame_finish_ns, - backend_drp_validation_ns, - backend_drp_state_ns, - backend_drp_buffer_create_ns, - backend_drp_texture_create_ns, - backend_drp_shader_create_ns, - backend_drp_shader_compile_ns, - backend_drp_shader_module_create_ns, - backend_drp_pipeline_create_ns, - backend_drp_binding_create_ns, - backend_drp_upload_ns, - backend_drp_upload_decode_ns, - backend_drp_upload_vulkan_allocate_ns, - backend_drp_upload_host_copy_ns, - backend_drp_upload_command_allocate_ns, - backend_drp_upload_command_record_ns, - backend_drp_upload_fence_create_ns, - backend_drp_upload_submit_enqueue_ns, - backend_drp_upload_submit_queue_wait_ns, - backend_drp_upload_queue_submit_ns, - backend_drp_upload_fence_wait_ns, - backend_drp_upload_retire_ns, - backend_drp_transfer_ns, - backend_drp_record_ns, - backend_submit_queue_ns, - backend_submit_ns, - gpu_completion_observation_ns, - completion_task_queue_ns, - gpu_render_ns, - gpu_transition_ns, - gpu_copy_ns, - gpu_total_ns, - readback_ns, - count -}; -struct Frame_Identity { - friend bool operator==(const Frame_Identity&, const Frame_Identity&) = default; - std::uint64_t sequence{}; /* 外部帧管理器分配的单调帧序号。 */ - std::uint64_t correlation_id{}; /* 与调用方请求关联的标识;零值表示未关联。 */ -}; -struct Frame_Trace_Point { - Frame_Trace_Marker marker{}; /* 本时间点在端到端帧流水线中的固定语义。 */ - std::uint64_t elapsed_ns{}; /* 相对 Frame 创建时刻的单调时钟偏移,单位为纳秒。 */ -}; -struct Frame_Trace_Value { - Frame_Trace_Measurement measurement{}; /* 无法表达为公共时间点的原始后端测量类型。 */ - std::uint64_t value_ns{}; /* 后端直接记录的持续时间,单位为纳秒。 */ -}; -struct Taskflow_Graph_Trace { - std::string stage{}; /* 本帧中执行该原生 Taskflow 的业务阶段。 */ - std::string taskflow_name{}; /* Taskflow 原生图名称。 */ - struct Node { - std::uint64_t native_id{}; /* Taskflow Node 的 native hash。 */ - std::string node_id{}; /* 图路径、业务名和同名序号组成的稳定帧内 ID。 */ - std::string parent_node_id{}; /* 模块子图所属的业务节点;顶层为空。 */ - std::string name{}; /* Taskflow 节点业务名称。 */ - std::string type{}; /* Taskflow 原生 TaskType。 */ - std::vector predecessors{}; /* 原生直接前驱 hash。 */ - std::vector successors{}; /* 原生直接后继 hash。 */ - std::vector> attributes{}; - }; - std::vector nodes{}; /* DAG 构造时登记的节点与依赖元信息。 */ - double submitted_ms{}; /* 相对帧创建时刻的 run 提交时间。 */ - double executor_finished_ms{}; /* Executor 同步返回或异步 completion 进入的时间。 */ - double runtime_bookkeeping_finished_ms{}; /* Taskflow 运行计数更新完成时间。 */ - double runtime_publish_finished_ms{}; /* Taskflow 运行时状态发布完成时间。 */ - double observer_unbind_finished_ms{}; /* Node 帧绑定清除完成时间。 */ - double finished_ms{}; /* 同步返回或异步 topology 完成时间。 */ - bool completed{}; /* 对应 topology 是否已经结束。 */ -}; -struct Taskflow_Task_Trace { - struct Cooperative_Wait { - double started_ms{}; /* 相对帧创建时刻的协作让出起点。 */ - double finished_ms{}; /* 相对帧创建时刻的协作让出终点。 */ - }; - std::uint64_t native_id{}; /* TaskView::hash_value() 返回的原生 Node 身份。 */ - std::size_t worker_id{}; /* 执行该任务的 Executor worker。 */ - std::size_t worker_queue_size{}; /* on_entry 时的原生 worker queue_size。 */ - std::size_t worker_queue_capacity{}; /* on_entry 时的原生 worker queue_capacity。 */ - double entered_ms{}; /* Observer on_entry 开始,即 Executor 真正选中任务的时间。 */ - double started_ms{}; /* Observer on_entry 返回前,即任务体即将执行的时间。 */ - double finished_ms{}; /* Observer on_exit 进入,即任务体已经结束的时间。 */ - double completed_ms{}; /* Observer on_exit 与按帧追踪写入全部结束的时间。 */ - double duration_ms{}; /* 仅任务体 started 到 finished 的持续时间。 */ - double cooperative_wait_ms{}; /* Task_Graph::corun/corun_until 主动让出 Worker 的墙钟时间。 */ - std::vector cooperative_waits{}; /* 时间线中可直接覆盖到任务体上的让出区间。 */ - double observer_entry_ms{}; /* on_entry 诊断本身的耗时。 */ - double observer_exit_ms{}; /* on_exit 诊断与按帧追踪写入的耗时。 */ - double ready_ms{}; /* 前驱完成或根 run 提交后的估算就绪时间。 */ - double queue_wait_ms{}; /* ready 到 entered 的估算 Executor 排队时间。 */ -}; -struct Taskflow_Frame_Trace { - Frame_Identity identity{}; /* 该执行图所属逻辑渲染帧。 */ - Frame_Request_Source request_source{}; /* 该逻辑帧由周期、手动或最大吞吐策略触发。 */ - std::uint64_t created_time_unix_ns{}; /* 帧创建 Unix 时间,单位纳秒。 */ - std::size_t worker_count{}; /* 捕获时全局 Executor 的 worker 数。 */ - std::vector markers{}; /* 与该帧 DAG 共用时间原点的原始流水线时间点。 */ - std::vector measurements{}; /* 本帧原始耗时测量;不另建统计副本。 */ - std::vector graphs{}; /* 本帧主动执行的业务 DAG 元信息。 */ - std::vector tasks{}; /* 本帧窗口内原生 Observer 完成的任务执行。 */ -}; -struct Render_Frame : public double_buffer::Pinned { -public: - explicit Render_Frame(Frame_Identity identity); - virtual ~Render_Frame(); - [[nodiscard]] Frame_Identity identity() const noexcept; - [[nodiscard]] std::uint64_t created_time_unix_ns() const noexcept; - void mark(Frame_Trace_Marker marker) noexcept; - void record(Frame_Trace_Measurement measurement, std::uint64_t value_ns) noexcept; - [[nodiscard]] Frame_Statistics_Sample statistics() const; - void request_taskflow_trace() noexcept; - [[nodiscard]] bool taskflow_trace_requested() const noexcept; - [[nodiscard]] Taskflow_Frame_Trace take_taskflow_trace(); -protected: - /* 物理帧槽再次承载新逻辑帧时,重建其唯一身份和诊断时间原点。 */ - void begin(Frame_Identity identity, - Frame_Request_Source source = Frame_Request_Source::unspecified) noexcept; -private: - struct Private; - friend struct detail::Taskflow_Frame_Access; - std::unique_ptr d; /* 帧身份、时钟原点与原子诊断槽位的唯一所有权。 */ -}; -} diff --git a/kernel/src/kernel/frame_statistics.cpp b/kernel/src/kernel/frame_statistics.cpp deleted file mode 100644 index 5ff3a5e..0000000 --- a/kernel/src/kernel/frame_statistics.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "frame_statistics.hpp" -#include -#include -#include - -namespace aethera { -void Frame_Statistics_Sample::set(Frame_Statistic statistic, double value) noexcept { - const auto index = static_cast(statistic); - if (index >= values.size() || !std::isfinite(value)) return; - values[index] = value; - present.set(index); -} - -Sliding_Statistics::Sliding_Statistics() : Sliding_Statistics(600) {} - -Sliding_Statistics::Sliding_Statistics(std::size_t capacity) - : values_(capacity), trimmed_values_(capacity), - minimum_(capacity, true), maximum_(capacity, false) { - if (capacity == 0) - throw std::invalid_argument("statistics capacity must be positive"); -} - -Sliding_Statistics::Extremum_Queue::Extremum_Queue( - std::size_t capacity, bool minimum) - : nodes_(capacity + 1U), minimum_(minimum) {} - -void Sliding_Statistics::Extremum_Queue::submit( - double sample, std::uint64_t sequence, std::size_t window) noexcept { - const auto count = nodes_.size(); - while (head_ != tail_ && - nodes_[head_].sequence + window <= sequence) - head_ = (head_ + 1U) % count; - while (head_ != tail_) { - const auto previous = (tail_ + count - 1U) % count; - const bool superseded = minimum_ ? nodes_[previous].value >= sample - : nodes_[previous].value <= sample; - if (!superseded) break; - tail_ = previous; - } - nodes_[tail_] = Node{sample, sequence}; - tail_ = (tail_ + 1U) % count; -} - -double Sliding_Statistics::Extremum_Queue::value() const noexcept { - return head_ == tail_ ? 0.0 : nodes_[head_].value; -} - -void Sliding_Statistics::Extremum_Queue::reset() noexcept { - head_ = 0; - tail_ = 0; -} - -Sliding_Statistics::Quantile_Estimator::Quantile_Estimator( - double probability) noexcept : probability_(probability) {} - -void Sliding_Statistics::Quantile_Estimator::submit(double sample) noexcept { - if (count_ < initial_.size()) { - initial_[count_++] = sample; - if (count_ != initial_.size()) return; - std::ranges::sort(initial_); - heights_ = initial_; - positions_ = {1.0, 2.0, 3.0, 4.0, 5.0}; - desired_ = {1.0, 1.0 + 2.0 * probability_, - 1.0 + 4.0 * probability_, - 3.0 + 2.0 * probability_, 5.0}; - increments_ = {0.0, probability_ / 2.0, probability_, - (1.0 + probability_) / 2.0, 1.0}; - return; - } - ++count_; - std::size_t bucket{}; - if (sample < heights_[0]) { - heights_[0] = sample; - } else if (sample >= heights_[4]) { - heights_[4] = sample; - bucket = 3; - } else { - while (bucket < 3 && sample >= heights_[bucket + 1]) ++bucket; - } - for (std::size_t index = bucket + 1; index < positions_.size(); ++index) - positions_[index] += 1.0; - for (std::size_t index = 0; index < desired_.size(); ++index) - desired_[index] += increments_[index]; - for (std::size_t index = 1; index < 4; ++index) { - const double distance = desired_[index] - positions_[index]; - const double direction = distance >= 1.0 ? 1.0 : distance <= -1.0 ? -1.0 : 0.0; - if (direction == 0.0 || - (direction > 0.0 && positions_[index + 1] - positions_[index] <= 1.0) || - (direction < 0.0 && positions_[index - 1] - positions_[index] >= -1.0)) - continue; - const double left = positions_[index] - positions_[index - 1]; - const double right = positions_[index + 1] - positions_[index]; - const double estimate = heights_[index] + direction / (left + right) * - ((left + direction) * (heights_[index + 1] - heights_[index]) / right + - (right - direction) * (heights_[index] - heights_[index - 1]) / left); - if (heights_[index - 1] < estimate && estimate < heights_[index + 1]) - heights_[index] = estimate; - else { - const auto neighbor = static_cast( - static_cast(index) + - static_cast(direction)); - heights_[index] += direction * - (heights_[neighbor] - heights_[index]) / - (positions_[neighbor] - positions_[index]); - } - positions_[index] += direction; - } -} - -double Sliding_Statistics::Quantile_Estimator::value() const noexcept { - if (count_ == 0) return 0.0; - if (count_ >= initial_.size()) return heights_[2]; - auto ordered = initial_; - std::sort(ordered.begin(), ordered.begin() + static_cast(count_)); - const auto index = std::min(count_ - 1U, static_cast( - std::ceil(probability_ * static_cast(count_))) - 1U); - return ordered[index]; -} - -void Sliding_Statistics::Quantile_Estimator::reset() noexcept { - initial_ = {}; - heights_ = {}; - positions_ = {}; - desired_ = {}; - increments_ = {}; - count_ = 0; -} - -Statistic_State Sliding_Statistics::submit(double value) { - if (!std::isfinite(value)) return state_; - if (size_ == values_.size()) { - if (!trimmed_window_initialized_) { - double lower = p05_.value(); - double upper = p95_.value(); - if (lower > upper) std::swap(lower, upper); - trimmed_sum_ = 0.0; - for (std::size_t index = 0; index < size_; ++index) { - trimmed_values_[index] = std::clamp(values_[index], lower, upper); - trimmed_sum_ += trimmed_values_[index]; - } - trimmed_window_initialized_ = true; - } - const double discarded = values_[next_]; - sum_ -= discarded; - squared_sum_ -= discarded * discarded; - trimmed_sum_ -= trimmed_values_[next_]; - } - p05_.submit(value); - p50_.submit(value); - p95_.submit(value); - p99_.submit(value); - minimum_.submit(value, sequence_, values_.size()); - maximum_.submit(value, sequence_, values_.size()); - ++sequence_; - double trim_lower = p05_.value(); - double trim_upper = p95_.value(); - if (trim_lower > trim_upper) std::swap(trim_lower, trim_upper); - const double trimmed = trimmed_window_initialized_ - ? std::clamp(value, trim_lower, trim_upper) : value; - values_[next_] = value; - trimmed_values_[next_] = trimmed; - sum_ += value; - squared_sum_ += value * value; - trimmed_sum_ += trimmed; - next_ = (next_ + 1U) % values_.size(); - size_ = std::min(size_ + 1U, values_.size()); - const double mean = sum_ / static_cast(size_); - const double quantile_50 = p50_.value(); - const double quantile_95 = std::max(quantile_50, p95_.value()); - const double quantile_99 = std::max(quantile_95, p99_.value()); - state_ = Statistic_State{ - size_, value, minimum_.value(), maximum_.value(), mean, - trimmed_sum_ / static_cast(size_), - std::sqrt(std::max(0.0, - squared_sum_ / static_cast(size_) - mean * mean)), - quantile_50, quantile_95, quantile_99}; - return state_; -} - -const Statistic_State& Sliding_Statistics::state() const noexcept { return state_; } - -void Sliding_Statistics::reset() noexcept { - size_ = 0; - next_ = 0; - sum_ = 0.0; - squared_sum_ = 0.0; - trimmed_sum_ = 0.0; - trimmed_window_initialized_ = false; - sequence_ = 0; - minimum_.reset(); - maximum_.reset(); - p05_.reset(); - p50_.reset(); - p95_.reset(); - p99_.reset(); - state_ = {}; -} - -Frame_Statistics_Accumulator::Frame_Statistics_Accumulator(std::size_t capacity) { - values_.reserve(frame_statistic_count); - for (std::size_t index = 0; index < frame_statistic_count; ++index) - values_.emplace_back(capacity); -} - -const Frame_Statistics_State& Frame_Statistics_Accumulator::submit( - const Render_Frame& frame) { - auto sample = frame.statistics(); - const auto now = std::chrono::steady_clock::now(); - const auto frame_identity = frame.identity(); - if (previous_completion_ != std::chrono::steady_clock::time_point{}) { - sample.set(Frame_Statistic::frame_interval_ms, - std::chrono::duration( - now - previous_completion_).count()); - if (frame_identity.sequence > previous_sequence_ + 1U) - state_.dropped_sequences += frame_identity.sequence - previous_sequence_ - 1U; - } - previous_completion_ = now; - previous_sequence_ = frame_identity.sequence; - state_.identity = frame_identity; - state_.created_time_unix_ns = frame.created_time_unix_ns(); - for (std::size_t index = 0; index < sample.values.size(); ++index) { - if (!sample.present.test(index)) continue; - state_.values[index] = values_[index].submit(sample.values[index]); - } - return state_; -} - -void Frame_Statistics_Accumulator::reset() noexcept { - for (auto& value : values_) value.reset(); - state_ = {}; - previous_completion_ = {}; - previous_sequence_ = 0; -} -} diff --git a/kernel/src/kernel/frame_statistics.hpp b/kernel/src/kernel/frame_statistics.hpp deleted file mode 100644 index 20995fb..0000000 --- a/kernel/src/kernel/frame_statistics.hpp +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once -#include "frame.hpp" -#include -#include -#include -#include -#include -#include -namespace aethera { -enum struct Frame_Statistic : std::uint8_t { - plot_tick_queue_ms, - plot_update_ms, - plot_publish_ms, - server_completion_ms, - scene_render_ms, - event_dispatch_ms, - backend_queue_ms, - gpu_submission_ms, - backend_apply_ms, - backend_plan_ms, - backend_execute_ms, - backend_target_acquire_ms, - backend_structure_check_ms, - backend_query_ms, - backend_runtime_plan_ms, - backend_runtime_plan_cpu_ms, - backend_runtime_execute_ms, - backend_runtime_execute_cpu_ms, - backend_mvp_update_ms, - backend_frame_begin_ms, - backend_frame_plan_ms, - backend_frame_plan_cpu_ms, - backend_emit_replay_dirty_ms, - backend_emit_layout_ms, - backend_emit_prepare_ms, - backend_emit_plan_build_ms, - backend_emit_contract_ms, - backend_emit_stream_ms, - backend_emit_stream_freeze_ms, - backend_emit_commit_ms, - backend_emit_plan_reset_ms, - backend_emit_artifact_create_ms, - backend_emit_artifact_freeze_ms, - backend_emit_packet_encode_ms, - backend_external_register_ms, - backend_frame_attach_ms, - backend_frame_execute_ms, - backend_frame_execute_cpu_ms, - backend_frame_finish_ms, - backend_drp_validation_ms, - backend_drp_state_ms, - backend_drp_buffer_create_ms, - backend_drp_texture_create_ms, - backend_drp_shader_create_ms, - backend_drp_shader_compile_ms, - backend_drp_shader_module_create_ms, - backend_drp_pipeline_create_ms, - backend_drp_binding_create_ms, - backend_drp_upload_ms, - backend_drp_upload_decode_ms, - backend_drp_upload_vulkan_allocate_ms, - backend_drp_upload_host_copy_ms, - backend_drp_upload_command_allocate_ms, - backend_drp_upload_command_record_ms, - backend_drp_upload_fence_create_ms, - backend_drp_upload_submit_enqueue_ms, - backend_drp_upload_submit_queue_wait_ms, - backend_drp_upload_queue_submit_ms, - backend_drp_upload_fence_wait_ms, - backend_drp_upload_retire_ms, - backend_drp_transfer_ms, - backend_drp_record_ms, - backend_submit_queue_ms, - backend_submit_ms, - gpu_completion_observation_ms, - completion_task_queue_ms, - gpu_render_ms, - gpu_transition_ms, - gpu_copy_ms, - gpu_total_ms, - readback_ms, - frame_interval_ms, - count -}; -inline constexpr std::size_t frame_statistic_count = -static_cast(Frame_Statistic::count); -struct Frame_Statistics_Sample { - std::array values{}; - std::bitset present{}; - void set(Frame_Statistic statistic, double value) noexcept; -}; -struct Statistic_State { - std::size_t count{}; - double latest{}; - double minimum{}; - double maximum{}; - double average{}; - double trimmed_average{}; - double variability{}; - double p50{}; - double p95{}; - double p99{}; - bool operator==(const Statistic_State&) const = default; -}; -struct Sliding_Statistics final { -public: - Sliding_Statistics(); - explicit Sliding_Statistics(std::size_t capacity); - [[nodiscard]] Statistic_State submit(double value); - [[nodiscard]] const Statistic_State& state() const noexcept; - void reset() noexcept; -private: - /* P² 只维护五个标记点,分位数是从 reset 起的在线估计,不保存样本。 */ - struct Quantile_Estimator final { - public: - explicit Quantile_Estimator(double probability) noexcept; - void submit(double value) noexcept; - [[nodiscard]] double value() const noexcept; - void reset() noexcept; - private: - double probability_{}; - std::array initial_{}; - std::array heights_{}; - std::array positions_{}; - std::array desired_{}; - std::array increments_{}; - std::size_t count_{}; - }; - /* 预分配单调队列,O(1) 摊还维护精确滑动窗口极值。 */ - struct Extremum_Queue final { - public: - Extremum_Queue(std::size_t capacity, bool minimum); - void submit(double value, std::uint64_t sequence, - std::size_t window) noexcept; - [[nodiscard]] double value() const noexcept; - void reset() noexcept; - private: - struct Node { - double value{}; - std::uint64_t sequence{}; - }; - std::vector nodes_; - std::size_t head_{}; - std::size_t tail_{}; - bool minimum_{}; - }; - /* 原始值和截尾值只为精确滑动均值/方差服务,构造后不再分配。 */ - std::vector values_; - std::vector trimmed_values_; - std::size_t size_{}; - std::size_t next_{}; - double sum_{}; - double squared_sum_{}; - double trimmed_sum_{}; - bool trimmed_window_initialized_{}; - std::uint64_t sequence_{}; - Extremum_Queue minimum_; - Extremum_Queue maximum_; - Quantile_Estimator p05_{0.05}; - Quantile_Estimator p50_{0.50}; - Quantile_Estimator p95_{0.95}; - Quantile_Estimator p99_{0.99}; - Statistic_State state_{}; -}; -struct Frame_Statistics_State { - /* 可直接通过 Scene State 双缓冲发布的定长结果;不含统计器内部样本。 */ - std::array values{}; - Frame_Identity identity{}; - std::uint64_t created_time_unix_ns{}; - std::uint64_t dropped_sequences{}; - bool operator==(const Frame_Statistics_State&) const = default; -}; -struct Frame_Statistics_Accumulator final { -public: - explicit Frame_Statistics_Accumulator(std::size_t capacity = 600); - [[nodiscard]] const Frame_Statistics_State& submit(const Render_Frame& frame); - void reset() noexcept; -private: - std::vector values_; - Frame_Statistics_State state_{}; - std::chrono::steady_clock::time_point previous_completion_{}; - std::uint64_t previous_sequence_{}; -}; -} diff --git a/kernel/src/kernel/ownership.hpp b/kernel/src/kernel/ownership.hpp deleted file mode 100644 index 295bb1d..0000000 --- a/kernel/src/kernel/ownership.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include - -namespace aethera { -using gsl::not_null; -using gsl::owner; -} diff --git a/kernel/src/kernel/plot/Axis.cpp b/kernel/src/kernel/plot/Axis.cpp deleted file mode 100644 index 64f127f..0000000 --- a/kernel/src/kernel/plot/Axis.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include "Axis.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace aethera::plot { -namespace { - -std::string trim_number(std::string value) { - const auto exponent = value.find_first_of("eE"); - const auto fraction_end = exponent == std::string::npos ? value.size() : exponent; - const auto decimal = value.find('.'); - if (decimal == std::string::npos || decimal >= fraction_end) return value; - auto last = fraction_end; - while (last > decimal + 1 && value[last - 1] == '0') --last; - if (last == decimal + 1) --last; - value.erase(last, fraction_end - last); - return value; -} - -std::string numeric_label(double value, int precision) { - std::ostringstream stream; - const auto magnitude = std::abs(value); - std::string suffix; - if (magnitude >= 1.0e3 && magnitude < 1.0e6) { - value /= 1.0e3; - suffix = "k"; - stream << std::fixed; - } else if ((magnitude >= 1.0e6 || (magnitude > 0.0 && magnitude < 1.0e-4))) - stream << std::scientific; - else - stream << std::fixed; - stream << std::setprecision(std::clamp(precision, 0, 12)) << value; - auto result = trim_number(stream.str()) + suffix; - return result; -} - -bool ascii_iequals(std::string_view left, std::string_view right) { - return left.size() == right.size() && - std::ranges::equal(left, right, [](char first, char second) { - return std::tolower(static_cast(first)) == - std::tolower(static_cast(second)); - }); -} - -std::string time_label(double seconds, int precision, - std::string_view unit) { - if (!unit.empty() && !ascii_iequals(unit, "s") && - !ascii_iequals(unit, "sec") && !ascii_iequals(unit, "second") && - !ascii_iequals(unit, "seconds")) - return numeric_label(seconds, precision) + " " + std::string(unit); - const auto sign = seconds < 0.0 ? "-" : ""; - const auto absolute = std::abs(seconds); - if (absolute == 0.0) return "0 s"; - if (absolute < 1.0) - return std::string(sign) + - numeric_label(absolute * 1'000.0, precision) + " ms"; - if (absolute < 60.0) - return std::string(sign) + numeric_label(absolute, precision) + " s"; - - const auto hours = static_cast(absolute / 3'600.0); - const auto minutes = static_cast(absolute / 60.0) % 60U; - const auto remaining = absolute - - static_cast(hours * 3'600U + minutes * 60U); - std::ostringstream stream; - stream << sign; - if (hours != 0U) stream << hours << ':' << std::setfill('0') << std::setw(2); - stream << minutes << ':' << std::setfill('0') - << std::setw(precision > 0 ? precision + 3 : 2) - << std::fixed << std::setprecision(std::clamp(precision, 0, 6)) - << remaining; - return stream.str(); -} - -std::string frequency_label(double frequency, int precision) { - const auto magnitude = std::abs(frequency); - double divisor{1.0}; - std::string_view unit{"Hz"}; - if (magnitude >= 1.0e9) { - divisor = 1.0e9; - unit = "GHz"; - } - else if (magnitude >= 1.0e6) { - divisor = 1.0e6; - unit = "MHz"; - } - else if (magnitude >= 1.0e3) { - divisor = 1.0e3; - unit = "kHz"; - } - return numeric_label(frequency / divisor, precision) + " " + - std::string(unit); -} - -std::string axis_tick_label(const Axis_Descriptor& axis, double coordinate) { - if (axis.scale == Axis_Scale::time) - return time_label(coordinate, axis.precision, axis.unit); - if (ascii_iequals(axis.unit, "hz")) - return frequency_label(coordinate, axis.precision); - return numeric_label(coordinate, axis.precision); -} - -std::vector linear_ticks(const Axis_Descriptor& axis) { - const auto step = nice_tick_step(axis.range, axis.target_tick_count); - if (!(step > 0.0) || !std::isfinite(step)) return {}; - const auto [minimum, maximum] = std::minmax(axis.range.origin, axis.range.target); - const auto first = std::ceil(minimum / step) * step; - std::vector ticks; - const auto maximum_count = std::max(2, axis.target_tick_count * 4 + 4); - for (std::size_t index = 0; index < maximum_count; ++index) { - const auto coordinate = first + static_cast(index) * step; - if (coordinate > maximum + step * 1.0e-9) break; - ticks.push_back({coordinate, axis_tick_label(axis, coordinate)}); - } - if (axis.range.length() < 0.0) std::ranges::reverse(ticks); - return ticks; -} - -std::vector logarithmic_ticks(const Axis_Descriptor& axis) { - const auto [minimum, maximum] = std::minmax(axis.range.origin, axis.range.target); - if (!(minimum > 0.0) || !std::isfinite(maximum)) return {}; - const auto first_power = static_cast(std::floor(std::log10(minimum))); - const auto last_power = static_cast(std::ceil(std::log10(maximum))); - const auto decade_count = static_cast(last_power - first_power + 1); - const auto multiples = axis.target_tick_count <= decade_count - ? std::vector{1.0} - : axis.target_tick_count <= decade_count * 2 - ? std::vector{1.0, 5.0} - : std::vector{1.0, 2.0, 5.0}; - std::vector ticks; - for (int power = first_power; power <= last_power; ++power) { - const auto decade = std::pow(10.0, static_cast(power)); - for (const double multiple : multiples) { - const auto coordinate = multiple * decade; - if (coordinate >= minimum * (1.0 - 1.0e-12) && - coordinate <= maximum * (1.0 + 1.0e-12)) - ticks.push_back({coordinate, axis_tick_label(axis, coordinate)}); - } - } - const auto add_boundary = [&](double coordinate) { - const auto tolerance = coordinate * 1.0e-9; - if (std::ranges::none_of(ticks, [&](const Axis_Tick& tick) { - return std::abs(tick.coordinate - coordinate) <= tolerance; - })) { - const auto logarithmic_span = std::log(maximum / minimum); - const auto minimum_spacing = 0.5 / - static_cast(std::max(2, axis.target_tick_count) - 1); - if (logarithmic_span > 0.0) { - const auto nearest = std::ranges::min_element( - ticks, {}, [&](const Axis_Tick& tick) { - return std::abs(std::log(tick.coordinate / coordinate)) / - logarithmic_span; - }); - if (nearest != ticks.end() && - std::abs(std::log(nearest->coordinate / coordinate)) / - logarithmic_span < minimum_spacing) - ticks.erase(nearest); - } - ticks.push_back({coordinate, axis_tick_label(axis, coordinate)}); - } - }; - add_boundary(minimum); - add_boundary(maximum); - std::ranges::sort(ticks, {}, &Axis_Tick::coordinate); - if (axis.range.length() < 0.0) std::ranges::reverse(ticks); - return ticks; -} - -} // namespace - -Axis_Coordinate Axis_Range::size() const noexcept { - return std::abs(length()); -} - -Axis_Coordinate Axis_Range::length() const noexcept { - return target - origin; -} - -Axis_Coordinate Axis_Range::center() const noexcept { - return origin + length() * 0.5; -} - -bool Axis_Range::contains(Axis_Coordinate coordinate) const noexcept { - const auto [minimum, maximum] = std::minmax(origin, target); - return coordinate >= minimum - 1.0e-9 && coordinate <= maximum + 1.0e-9; -} - -Axis_Coordinate nice_tick_step(Axis_Range range, std::size_t target_tick_count) { - if (!std::isfinite(range.origin) || !std::isfinite(range.target) || range.size() <= 0.0) - throw std::invalid_argument("axis range must be finite and non-empty"); - const auto count = std::max(2, target_tick_count); - const auto raw = range.size() / static_cast(count - 1); - const auto magnitude = std::pow(10.0, std::floor(std::log10(raw))); - const auto normalized = raw / magnitude; - const auto nice = normalized <= 1.0 ? 1.0 : normalized <= 2.0 ? 2.0 : - normalized <= 5.0 ? 5.0 : 10.0; - return nice * magnitude; -} - -std::vector axis_ticks(const Axis_Descriptor& axis) { - if (!axis.visible) return {}; - if (axis.scale == Axis_Scale::logarithmic) return logarithmic_ticks(axis); - return linear_ticks(axis); -} - -} // namespace aethera::plot diff --git a/kernel/src/kernel/plot/Axis.hpp b/kernel/src/kernel/plot/Axis.hpp deleted file mode 100644 index 462d289..0000000 --- a/kernel/src/kernel/plot/Axis.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -#include -#include -#include -#include -namespace aethera::plot { -using Axis_Coordinate = double; -enum struct Axis_Scale : std::uint8_t { - linear, - logarithmic, - time -}; -struct Axis_Range { - Axis_Coordinate origin{}; - Axis_Coordinate target{}; - [[nodiscard]] Axis_Coordinate size() const noexcept; - [[nodiscard]] Axis_Coordinate length() const noexcept; - [[nodiscard]] Axis_Coordinate center() const noexcept; - [[nodiscard]] bool contains(Axis_Coordinate coordinate) const noexcept; - bool operator==(const Axis_Range&) const = default; -}; -struct Axis_Point { - Axis_Coordinate horizontal{}; - Axis_Coordinate vertical{}; - bool operator==(const Axis_Point&) const = default; -}; -struct Axis_Rectangle { - Axis_Range horizontal{}; - Axis_Range vertical{}; - bool operator==(const Axis_Rectangle&) const = default; -}; -struct Axis_Descriptor { - Axis_Range range{}; - Axis_Scale scale{Axis_Scale::linear}; - std::string label{}; - std::string unit{}; - std::size_t target_tick_count{6}; - int precision{2}; - bool visible{true}; - bool grid_visible{true}; - bool labels_visible{true}; - bool operator==(const Axis_Descriptor&) const = default; -}; -struct Axis_Tick { - Axis_Coordinate coordinate{}; - std::string label{}; - bool operator==(const Axis_Tick&) const = default; -}; -[[nodiscard]] Axis_Coordinate nice_tick_step(Axis_Range range, - std::size_t target_tick_count = 6); -[[nodiscard]] std::vector axis_ticks(const Axis_Descriptor& axis); -} // namespace aethera::plot diff --git a/kernel/src/kernel/plot/Time_Domain.hpp b/kernel/src/kernel/plot/Time_Domain.hpp deleted file mode 100644 index 2f2eb8c..0000000 --- a/kernel/src/kernel/plot/Time_Domain.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Axis.hpp" - -#include -#include - -namespace aethera::plot { - -struct Time_Domain { - Axis_Range seconds{}; - std::size_t visible_sample_count{}; - bool realtime{}; - bool newest_at_start{}; - bool operator==(const Time_Domain&) const = default; -}; - -} // namespace aethera::plot diff --git a/kernel/src/kernel/render_common.cpp b/kernel/src/kernel/render_common.cpp deleted file mode 100644 index 793f9e0..0000000 --- a/kernel/src/kernel/render_common.cpp +++ /dev/null @@ -1,1043 +0,0 @@ -#include "render_common.hpp" -#include "Taskflow_Frame_Access.hpp" -#include "Task_Graph_Internal.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(_WIN32) -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#elif defined(CLOCK_THREAD_CPUTIME_ID) -#include -#endif -namespace aethera { -namespace { -struct Task_Name_Registry { - std::mutex mutex; - std::unordered_map> names; -}; -Task_Name_Registry& task_name_registry() { - static Task_Name_Registry value; - return value; -} -tf::Taskflow& native_taskflow(Task_Graph& graph) noexcept { - return *static_cast( - detail::Task_Graph_Access::native_storage(graph)); -} -Render_Frame* task_frame( - const tf::TaskView& task) noexcept { - /* - * Taskflow v4.1.0 ABI: TaskView 的 offset 0 是唯一的 const Node&,Task 的 - * offset 0 是唯一的 Node*。这里把同一 Node 转成公开 Task 句柄,只读取 - * Task::data(),避免修改第三方代码。版本升级导致句柄尺寸变化时立即编译失败。 - */ - static_assert(sizeof(tf::TaskView) == sizeof(tf::Node*)); - static_assert(alignof(tf::TaskView) == alignof(tf::Node*)); - static_assert(sizeof(tf::Task) == sizeof(tf::Node*)); - static_assert(alignof(tf::Task) == alignof(tf::Node*)); - auto* node = *reinterpret_cast(std::addressof(task)); - tf::Task handle{}; - *reinterpret_cast(std::addressof(handle)) = node; - auto* frame = static_cast(handle.data()); - return frame; -} -#if defined(_WIN32) -std::uint64_t thread_cpu_ns(HANDLE thread) noexcept { - FILETIME created{}, exited{}, kernel{}, user{}; - if (!thread || - !GetThreadTimes(thread, &created, &exited, &kernel, &user)) - return 0; - const auto ticks = [](FILETIME value) noexcept { - ULARGE_INTEGER result{}; - result.LowPart = value.dwLowDateTime; - result.HighPart = value.dwHighDateTime; - return result.QuadPart; - }; - return (ticks(kernel) + ticks(user)) * 100ULL; -} -std::uint64_t thread_cpu_cycles(HANDLE thread) noexcept { - ULONG64 cycles{}; - return thread && QueryThreadCycleTime(thread, &cycles) - ? static_cast(cycles) - : 0; -} -#endif -struct Task_Observer : public tf::ObserverInterface { -private: - using Clock = std::chrono::steady_clock; - struct Task_Statistics { - std::atomic_size_t count{}; - std::atomic_uint64_t total_time_ns{}; - std::atomic_uint64_t min_time_ns{std::numeric_limits::max()}; - std::atomic_uint64_t max_time_ns{}; - }; - struct alignas(64) Worker_Statistics { - std::array task_types{}; - std::atomic_size_t task_count{}; - std::atomic_size_t named_task_count{}; - std::atomic_size_t active_depth{}; - std::atomic_size_t peak_active_depth{}; - std::atomic_bool executing_task_segment{}; - std::atomic_size_t current_queue_size{}; - std::atomic_size_t current_queue_capacity{}; - std::atomic_size_t peak_queue_size{}; - std::atomic_size_t max_queue_capacity{}; - std::atomic_uint64_t active_task_hash{}; - std::atomic_uint64_t active_task_started_ns{}; - std::atomic_uint64_t active_segment_started_ns{}; -#if defined(_WIN32) - std::atomic_uintptr_t native_thread_handle{}; /* Watchdog 只读的真实 Worker 线程句柄。 */ - std::atomic_uint64_t cpu_time_origin_ns{}; /* 首次进入 Worker 时的累计线程 CPU,读取侧据此计算观测期增量。 */ - std::atomic_uint64_t active_cpu_cycles{}; /* 连续片段最近一次采样的 Worker CPU 周期。 */ - std::atomic_uint64_t active_cpu_progress_ns{}; /* CPU 周期最后前进的墙钟时刻。 */ -#endif - std::atomic active_task_type{tf::TaskType::UNDEFINED}; - std::atomic_bool active_task_reported{}; - std::atomic_uint64_t task_time_ns{}; - std::atomic_uint64_t busy_time_ns{}; - std::atomic_uint64_t cooperative_wait_count{}; - std::atomic_uint64_t cooperative_wait_time_ns{}; - std::atomic_uint64_t min_task_time_ns{std::numeric_limits::max()}; - std::atomic_uint64_t max_task_time_ns{}; - std::atomic_uint64_t first_task_time_ns{}; - std::atomic_uint64_t last_task_time_ns{}; - std::atomic_uint64_t longest_task_time_ns{}; - std::atomic_size_t longest_task_hash{}; - std::atomic longest_task_type{tf::TaskType::UNDEFINED}; - }; - struct Start_Record { - using Wait_Record = std::pair; - Clock::time_point entered{}; /* Observer on_entry 进入时间。 */ - Clock::time_point started{}; /* on_entry 完成、任务体即将执行的时间。 */ - Clock::time_point segment_started{}; /* 当前连续独占 Worker 片段的起点。 */ - std::uint64_t active_time_ns{}; /* 已结束独占 Worker 片段的累计墙钟。 */ - Clock::time_point cooperative_wait_started{}; /* 主动 corun 让出 Worker 的墙钟起点。 */ - std::uint64_t cooperative_wait_ns{}; /* 已累计 cooperative wait 墙钟。 */ - std::array cooperative_waits{}; /* 按帧时间线的固定容量让出区间。 */ - std::uint64_t cooperative_wait_count{}; - std::size_t traced_cooperative_wait_count{}; - Render_Frame* frame{}; /* 进入任务时唯一活动的按帧捕获。 */ - std::size_t queue_size{}; /* 进入任务时 worker 队列深度。 */ - std::size_t queue_capacity{}; /* 进入任务时 worker 队列容量。 */ - std::uint64_t native_id{}; /* 嵌套 corun 返回外层任务时恢复其原生身份。 */ - tf::TaskType type{tf::TaskType::UNDEFINED}; /* 嵌套 corun 返回外层任务时恢复其原生类型。 */ - bool cooperatively_suspended{}; /* 外层任务是否主动让出 Worker 执行子图。 */ - }; - std::vector> starts; - std::unique_ptr worker_statistics; - std::size_t worker_statistics_count{}; - std::atomic_size_t active_task_count{}; - std::atomic_size_t peak_active_task_count{}; - std::atomic_size_t active_worker_count{}; - std::atomic_size_t peak_active_worker_count{}; - std::uint64_t worker_occupation_limit_ns{}; /* 单节点连续非 CPU 等待 Worker 的上限。 */ - Task_Overrun_Action worker_overrun_action{}; /* 节点超过占用上限后的处置策略。 */ - std::atomic_bool watchdog_stopping{}; /* Watchdog 生命周期停止标志。 */ - std::mutex watchdog_mutex{}; /* 仅用于 Watchdog 条件休眠。 */ - std::condition_variable watchdog_wake{}; /* Observer 销毁时唤醒 Watchdog。 */ - std::thread watchdog_thread{}; /* 不占用 Executor Worker 的超时检查线程。 */ - static std::uint64_t clock_ns(Clock::time_point value) noexcept { - return static_cast(std::chrono::duration_cast(value.time_since_epoch()).count()); - } - template - static void update_max(std::atomic& value, T next) noexcept { - auto current = value.load(std::memory_order_relaxed); - while (current < next && !value.compare_exchange_weak(current, next, std::memory_order_relaxed)) {} - } - static void update_min(std::atomic_uint64_t& value, std::uint64_t next) noexcept { - auto current = value.load(std::memory_order_relaxed); - while (next < current && !value.compare_exchange_weak(current, next, std::memory_order_relaxed)) {} - } - static void update_first(std::atomic_uint64_t& value, std::uint64_t next) noexcept { - auto current = value.load(std::memory_order_relaxed); - while ((current == 0 || next < current) && !value.compare_exchange_weak(current, next, std::memory_order_relaxed)) {} - } - void set_worker_execution(Worker_Statistics& worker, bool executing) noexcept { - const auto previous = worker.executing_task_segment.exchange( - executing, std::memory_order_acq_rel); - if (previous == executing) return; - if (!executing) { - active_worker_count.fetch_sub(1, std::memory_order_relaxed); - return; - } - const auto active = active_worker_count.fetch_add( - 1, std::memory_order_relaxed) + 1; - update_max(peak_active_worker_count, active); - } - static std::size_t task_type_index(tf::TaskType type) noexcept { - return static_cast(type); - } - void report_active_overrun(std::size_t worker, - const Worker_Statistics& state, - std::uint64_t elapsed_ns, - std::uint64_t stalled_ns) const noexcept { - const auto type = state.active_task_type.load( - std::memory_order_relaxed); - const auto native_id = state.active_task_hash.load( - std::memory_order_relaxed); - const auto name = detail::taskflow_node_name(native_id); - std::fprintf( - stderr, - "Taskflow worker occupation limit exceeded while node is active\n" - " node: %s\n" - " hash: %llu\n" - " type: %s\n" - " worker: %zu\n" - " segment-wall: %.3f ms\n" - " no-cpu-progress: %.3f ms\n" - " limit: %.3f ms\n" - " queue: %zu/%zu\n", - name ? name->c_str() : "", - static_cast(native_id), - tf::to_string(type), worker, - static_cast(elapsed_ns) / 1'000'000.0, - static_cast(stalled_ns) / 1'000'000.0, - static_cast(worker_occupation_limit_ns) / 1'000'000.0, - state.current_queue_size.load(std::memory_order_relaxed), - state.current_queue_capacity.load(std::memory_order_relaxed)); - std::fflush(stderr); - if (worker_overrun_action == Task_Overrun_Action::fast_fail) std::abort(); - } - void run_watchdog() noexcept { - const auto interval = std::chrono::nanoseconds( - std::min(worker_occupation_limit_ns / 4, - 10'000'000ULL)); - for (;;) { - { - std::unique_lock lock(watchdog_mutex); - watchdog_wake.wait_for(lock, interval, [this] { - return watchdog_stopping.load(std::memory_order_acquire); - }); - } - if (watchdog_stopping.load(std::memory_order_acquire)) return; - const auto now = clock_ns(Clock::now()); - for (std::size_t worker = 0; - worker < worker_statistics_count; ++worker) { - auto& state = worker_statistics[worker]; - const auto started = state.active_segment_started_ns.load( - std::memory_order_acquire); - if (!started || now <= started || - now - started <= worker_occupation_limit_ns) - continue; -#if defined(_WIN32) - const auto handle = state.native_thread_handle.load( - std::memory_order_acquire); - const auto wall = now - started; - const auto cycles = thread_cpu_cycles( - reinterpret_cast(handle)); - if (!cycles) continue; - const auto previous = state.active_cpu_cycles.exchange( - cycles, std::memory_order_acq_rel); - if (cycles != previous) { - state.active_cpu_progress_ns.store( - now, std::memory_order_release); - continue; - } - const auto progress = state.active_cpu_progress_ns.load( - std::memory_order_acquire); - if (!progress || now <= progress || - now - progress <= worker_occupation_limit_ns) - continue; - const auto stalled = now - progress; -#else - const auto wall = now - started; - const auto stalled = wall; -#endif - if (state.active_task_reported.exchange( - true, std::memory_order_acq_rel)) - continue; - report_active_overrun(worker, state, wall, stalled); - } - } - } - void pause_worker(std::size_t worker) noexcept { - if (worker >= starts.size() || starts[worker].empty()) return; - const auto now = Clock::now(); - auto& active = starts[worker].back(); - if (active.segment_started != Clock::time_point{}) { - const auto elapsed = static_cast( - std::chrono::duration_cast( - now - active.segment_started).count()); - active.active_time_ns += elapsed; - } - active.segment_started = {}; - const bool begins_wait = - active.cooperative_wait_started == Clock::time_point{}; - if (begins_wait) - active.cooperative_wait_started = now; - if (begins_wait) ++active.cooperative_wait_count; - if (begins_wait && active.frame != nullptr && - active.traced_cooperative_wait_count < active.cooperative_waits.size()) { - active.cooperative_waits[active.traced_cooperative_wait_count++] = {now, {}}; - } - active.cooperatively_suspended = true; - worker_statistics[worker].active_segment_started_ns.store( - 0, std::memory_order_release); - set_worker_execution(worker_statistics[worker], false); -#if defined(_WIN32) - worker_statistics[worker].active_cpu_cycles.store( - 0, std::memory_order_release); - worker_statistics[worker].active_cpu_progress_ns.store( - 0, std::memory_order_release); -#endif - } - void resume_worker(std::size_t worker) noexcept { - if (worker >= starts.size() || starts[worker].empty()) return; - const auto now = Clock::now(); - auto& active = starts[worker].back(); - if (active.cooperative_wait_started != Clock::time_point{}) { - active.cooperative_wait_ns += static_cast( - std::chrono::duration_cast( - now - active.cooperative_wait_started).count()); - active.cooperative_wait_started = {}; - if (active.traced_cooperative_wait_count != 0) { - auto& wait = active.cooperative_waits[ - active.traced_cooperative_wait_count - 1]; - if (wait.second == Clock::time_point{}) wait.second = now; - } - } - active.cooperatively_suspended = false; - active.segment_started = now; - worker_statistics[worker].active_segment_started_ns.store( - clock_ns(now), std::memory_order_release); - set_worker_execution(worker_statistics[worker], true); -#if defined(_WIN32) - worker_statistics[worker].active_cpu_cycles.store(0, - std::memory_order_release); - worker_statistics[worker].active_cpu_progress_ns.store( - clock_ns(now), std::memory_order_release); -#endif - worker_statistics[worker].active_task_reported.store( - false, std::memory_order_relaxed); - } - friend struct Task_Resource; -public: - Task_Observer(std::chrono::milliseconds worker_occupation_limit, - Task_Overrun_Action worker_overrun_action_value) : worker_occupation_limit_ns(static_cast( - std::chrono::duration_cast( - worker_occupation_limit).count())), - worker_overrun_action(worker_overrun_action_value) {} - ~Task_Observer() override { - watchdog_stopping.store(true, std::memory_order_release); - watchdog_wake.notify_one(); - if (watchdog_thread.joinable()) watchdog_thread.join(); -#if defined(_WIN32) - for (std::size_t worker = 0; - worker < worker_statistics_count; ++worker) { - const auto handle = worker_statistics[worker] - .native_thread_handle.exchange(0, std::memory_order_acq_rel); - if (handle) CloseHandle(reinterpret_cast(handle)); - } -#endif - } - void set_up(std::size_t workers) override { - starts.resize(workers); - worker_statistics = std::make_unique(workers); - worker_statistics_count = workers; - for (auto& worker : starts) worker.reserve(32); - watchdog_thread = std::thread([this] { - run_watchdog(); - }); - } - void on_entry(tf::WorkerView worker, tf::TaskView task) override { - auto now = Clock::now(); - auto& worker_starts = starts[worker.id()]; - auto& worker_state = worker_statistics[worker.id()]; -#if defined(_WIN32) - if (worker_state.native_thread_handle.load( - std::memory_order_acquire) == 0) { - HANDLE duplicated{}; - if (DuplicateHandle( - GetCurrentProcess(), GetCurrentThread(), - GetCurrentProcess(), &duplicated, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - worker_state.cpu_time_origin_ns.store( - thread_cpu_ns(duplicated), std::memory_order_relaxed); - worker_state.native_thread_handle.store( - reinterpret_cast(duplicated), - std::memory_order_release); - } - } -#endif - if (!worker_starts.empty()) { - auto& parent = worker_starts.back(); - if (parent.segment_started != Clock::time_point{}) { - const auto elapsed = static_cast( - std::chrono::duration_cast( - now - parent.segment_started).count()); - parent.active_time_ns += elapsed; - parent.segment_started = {}; - } - } - const auto queue_size = worker.queue_size(); - const auto queue_capacity = worker.queue_capacity(); - Start_Record record{}; - record.entered = now; - record.queue_size = queue_size; - record.queue_capacity = queue_capacity; - record.native_id = static_cast(task.hash_value()); - record.type = task.type(); - worker_starts.push_back(std::move(record)); - const auto frame = task_frame(task); - worker_starts.back().frame = frame; - const auto previous_depth = worker_state.active_depth.fetch_add( - 1, std::memory_order_relaxed); - const auto active_depth = previous_depth + 1; - update_max(worker_state.peak_active_depth, active_depth); - const auto active_tasks = active_task_count.fetch_add( - 1, std::memory_order_relaxed) + 1; - update_max(peak_active_task_count, active_tasks); - worker_state.current_queue_size.store(queue_size, std::memory_order_relaxed); - worker_state.current_queue_capacity.store(queue_capacity, std::memory_order_relaxed); - worker_state.active_task_hash.store(task.hash_value(), std::memory_order_relaxed); - worker_state.active_task_started_ns.store(clock_ns(now), std::memory_order_relaxed); - worker_state.active_segment_started_ns.store(clock_ns(now), - std::memory_order_release); -#if defined(_WIN32) - worker_state.active_cpu_cycles.store(0, std::memory_order_release); - worker_state.active_cpu_progress_ns.store( - clock_ns(now), std::memory_order_release); -#endif - worker_state.active_task_type.store(task.type(), std::memory_order_relaxed); - worker_state.active_task_reported.store(false, std::memory_order_relaxed); - update_max(worker_state.peak_queue_size, queue_size); - update_max(worker_state.max_queue_capacity, queue_capacity); - if (!task.name().empty()) worker_state.named_task_count.fetch_add(1, std::memory_order_relaxed); - update_first(worker_state.first_task_time_ns, clock_ns(now)); - worker_starts.back().started = Clock::now(); - worker_starts.back().segment_started = worker_starts.back().started; - set_worker_execution(worker_state, true); - } - void on_exit(tf::WorkerView worker, tf::TaskView task) override { - const auto finished = Clock::now(); - auto& worker_starts = starts[worker.id()]; - auto& active = worker_starts.back(); - if (active.cooperative_wait_started != Clock::time_point{}) { - active.cooperative_wait_ns += static_cast( - std::chrono::duration_cast( - finished - active.cooperative_wait_started).count()); - active.cooperative_wait_started = {}; - if (active.traced_cooperative_wait_count != 0) { - auto& wait = active.cooperative_waits[ - active.traced_cooperative_wait_count - 1]; - if (wait.second == Clock::time_point{}) wait.second = finished; - } - } - auto start = active; - worker_starts.pop_back(); - const auto elapsed = static_cast( - std::chrono::duration_cast( - finished - start.started).count()); - if (start.segment_started != Clock::time_point{}) { - const auto segment = static_cast( - std::chrono::duration_cast( - finished - start.segment_started).count()); - start.active_time_ns += segment; - } - const auto occupied = elapsed >= start.cooperative_wait_ns - ? elapsed - start.cooperative_wait_ns - : 0; - auto& worker_state = worker_statistics[worker.id()]; - worker_state.active_task_reported.store(true, - std::memory_order_release); - worker_state.task_count.fetch_add(1, std::memory_order_relaxed); - worker_state.task_time_ns.fetch_add(occupied, std::memory_order_relaxed); - worker_state.busy_time_ns.fetch_add( - start.active_time_ns, std::memory_order_relaxed); - worker_state.cooperative_wait_count.fetch_add( - start.cooperative_wait_count, std::memory_order_relaxed); - worker_state.cooperative_wait_time_ns.fetch_add( - start.cooperative_wait_ns, std::memory_order_relaxed); - update_min(worker_state.min_task_time_ns, occupied); - update_max(worker_state.max_task_time_ns, occupied); - auto type_index = task_type_index(task.type()); - if (type_index < worker_state.task_types.size()) { - auto& type = worker_state.task_types[type_index]; - type.count.fetch_add(1, std::memory_order_relaxed); - type.total_time_ns.fetch_add(occupied, std::memory_order_relaxed); - update_min(type.min_time_ns, occupied); - update_max(type.max_time_ns, occupied); - } - auto longest = worker_state.longest_task_time_ns.load( - std::memory_order_relaxed); - if (longest < occupied && worker_state.longest_task_time_ns.compare_exchange_strong( - longest, occupied, std::memory_order_relaxed)) { - worker_state.longest_task_hash.store(task.hash_value(), std::memory_order_relaxed); - worker_state.longest_task_type.store(task.type(), std::memory_order_relaxed); - } - worker_state.active_depth.fetch_sub(1, std::memory_order_relaxed); - active_task_count.fetch_sub(1, std::memory_order_relaxed); - std::optional trace_task; - if (start.frame) { - try { - trace_task = detail::Taskflow_Frame_Access::append_task( - *start.frame, worker.id(), - static_cast(task.hash_value()), - start.queue_size, start.queue_capacity, start.entered, - start.started, finished, start.cooperative_wait_ns, - std::span{start.cooperative_waits}.first( - start.traced_cooperative_wait_count)); - } - catch (...) { - /* Observer 不能让按需诊断分配失败改变渲染任务的完成语义。 */ - } - } - const auto completed = Clock::now(); - if (start.frame) { - if (trace_task) - detail::Taskflow_Frame_Access::finish_task_observer( - *start.frame, worker.id(), *trace_task, completed); - } - if (worker_starts.empty()) { - set_worker_execution(worker_state, false); - worker_state.active_task_hash.store(0, std::memory_order_relaxed); - worker_state.active_task_started_ns.store(0, std::memory_order_relaxed); - worker_state.active_segment_started_ns.store(0, - std::memory_order_release); -#if defined(_WIN32) - worker_state.active_cpu_cycles.store(0, std::memory_order_release); - worker_state.active_cpu_progress_ns.store(0, - std::memory_order_release); -#endif - worker_state.active_task_type.store(tf::TaskType::UNDEFINED, - std::memory_order_relaxed); - worker_state.current_queue_size.store(0, std::memory_order_relaxed); - worker_state.current_queue_capacity.store(0, std::memory_order_relaxed); - } - else { - auto& parent = worker_starts.back(); - worker_state.active_task_hash.store(parent.native_id, std::memory_order_relaxed); - worker_state.active_task_started_ns.store(clock_ns(parent.entered), - std::memory_order_relaxed); - if (parent.cooperatively_suspended) { - set_worker_execution(worker_state, false); - worker_state.active_segment_started_ns.store( - 0, std::memory_order_release); -#if defined(_WIN32) - worker_state.active_cpu_cycles.store(0, - std::memory_order_release); - worker_state.active_cpu_progress_ns.store(0, - std::memory_order_release); -#endif - } - else { - set_worker_execution(worker_state, true); - const auto parent_resumed = Clock::now(); - parent.segment_started = parent_resumed; - worker_state.active_segment_started_ns.store( - clock_ns(parent_resumed), std::memory_order_release); -#if defined(_WIN32) - worker_state.active_cpu_cycles.store(0, - std::memory_order_release); - worker_state.active_cpu_progress_ns.store( - clock_ns(parent_resumed), std::memory_order_release); -#endif - } - worker_state.active_task_type.store(parent.type, std::memory_order_relaxed); - worker_state.active_task_reported.store(false, - std::memory_order_relaxed); - worker_state.current_queue_size.store(parent.queue_size, - std::memory_order_relaxed); - worker_state.current_queue_capacity.store(parent.queue_capacity, - std::memory_order_relaxed); - } - update_max(worker_state.last_task_time_ns, clock_ns(completed)); - } - bool begin_trace(Render_Frame& frame, std::size_t workers) { - /* Frame 自己保存捕获缓冲;多个 Frame 可以同时处于捕获窗口。 */ - detail::Taskflow_Frame_Access::begin_capture(frame, workers); - return true; - } - void finish_trace(Render_Frame& frame) noexcept { - detail::Taskflow_Frame_Access::finish_capture(frame); - } - void write_state(Task_Runtime_State& state, std::size_t workers, std::size_t active_topologies) const { - state.worker_count = workers; - state.active_topology_count = active_topologies; - state.active_task_count = 0; - state.peak_active_task_count = 0; - state.active_worker_count = 0; - state.observed_task_count = 0; - state.named_task_count = 0; - state.peak_observed_worker_queue_size = 0; - state.max_observed_worker_queue_capacity = 0; - state.total_task_time_ns = 0; - state.worker_busy_time_ns = 0; - state.worker_cpu_time_ns = 0; - state.cooperative_wait_count = 0; - state.cooperative_wait_time_ns = 0; - state.longest_task_time_ns = 0; - state.longest_task_hash = 0; - state.longest_task_name.clear(); - state.longest_task_type.clear(); - std::uint64_t first{}; - std::uint64_t last{}; - state.task_types.assign(tf::TASK_TYPES.size(), {}); - for (std::size_t type = 0; type < tf::TASK_TYPES.size(); ++type) - state.task_types[type].name = - std::string(tf::to_string(tf::TASK_TYPES[type])); - state.workers.resize(worker_statistics_count); - const auto read_time_ns = clock_ns(Clock::now()); - for (std::size_t i = 0; i < worker_statistics_count; ++i) { - const auto& source = worker_statistics[i]; - auto& target = state.workers[i]; - target.id = i; - target.task_count = source.task_count.load(std::memory_order_relaxed); - target.current_queue_size = source.current_queue_size.load(std::memory_order_relaxed); - target.current_queue_capacity = source.current_queue_capacity.load(std::memory_order_relaxed); - target.peak_observed_queue_size = source.peak_queue_size.load(std::memory_order_relaxed); - target.max_observed_queue_capacity = source.max_queue_capacity.load(std::memory_order_relaxed); - target.active_task_hash = source.active_task_hash.load(std::memory_order_relaxed); - const auto active_started = source.active_task_started_ns.load(std::memory_order_relaxed); - const auto active_segment = source.active_segment_started_ns.load( - std::memory_order_acquire); - target.active_task_time_ns = active_segment && read_time_ns >= active_segment - ? read_time_ns - active_segment - : 0; - const auto active_type = source.active_task_type.load(std::memory_order_relaxed); - target.active_task_type = active_started - ? std::string(tf::to_string(active_type)) - : std::string{}; - target.task_time_ns = source.task_time_ns.load(std::memory_order_relaxed); - target.busy_time_ns = source.busy_time_ns.load(std::memory_order_relaxed); - target.cooperative_wait_count = source.cooperative_wait_count.load( - std::memory_order_relaxed); - target.cooperative_wait_time_ns = source.cooperative_wait_time_ns.load( - std::memory_order_relaxed); -#if defined(_WIN32) - const auto native_handle = source.native_thread_handle.load( - std::memory_order_acquire); - const auto cpu_origin = source.cpu_time_origin_ns.load( - std::memory_order_acquire); - const auto cpu_now = thread_cpu_ns( - reinterpret_cast(native_handle)); - target.cpu_time_ns = cpu_now >= cpu_origin ? cpu_now - cpu_origin : 0; -#else - target.cpu_time_ns = 0; -#endif - target.idle_time_ns = state.observed_wall_time_ns > target.busy_time_ns ? state.observed_wall_time_ns - target.busy_time_ns : 0; - auto min = source.min_task_time_ns.load(std::memory_order_relaxed); - target.min_task_time_ns = target.task_count ? min : 0; - target.max_task_time_ns = source.max_task_time_ns.load(std::memory_order_relaxed); - target.utilization = state.observed_wall_time_ns ? static_cast(target.busy_time_ns) * 100.0 / static_cast(state.observed_wall_time_ns) : 0.0; - target.cpu_utilization = state.observed_wall_time_ns - ? static_cast(target.cpu_time_ns) * 100.0 / - static_cast(state.observed_wall_time_ns) - : 0.0; - state.observed_task_count += target.task_count; - state.named_task_count += source.named_task_count.load( - std::memory_order_relaxed); - state.peak_observed_worker_queue_size = std::max( - state.peak_observed_worker_queue_size, - target.peak_observed_queue_size); - state.max_observed_worker_queue_capacity = std::max( - state.max_observed_worker_queue_capacity, - target.max_observed_queue_capacity); - state.total_task_time_ns += target.task_time_ns; - state.worker_busy_time_ns += target.busy_time_ns; - state.worker_cpu_time_ns += target.cpu_time_ns; - state.cooperative_wait_count += target.cooperative_wait_count; - state.cooperative_wait_time_ns += target.cooperative_wait_time_ns; - const auto worker_first = source.first_task_time_ns.load( - std::memory_order_relaxed); - if (worker_first && (!first || worker_first < first)) first = worker_first; - last = std::max(last, source.last_task_time_ns.load( - std::memory_order_relaxed)); - for (std::size_t type = 0; type < tf::TASK_TYPES.size(); ++type) { - const auto& source_type = source.task_types[type]; - auto& target_type = state.task_types[type]; - const auto count = source_type.count.load( - std::memory_order_relaxed); - target_type.count += count; - target_type.total_time_ns += source_type.total_time_ns.load( - std::memory_order_relaxed); - const auto minimum = source_type.min_time_ns.load( - std::memory_order_relaxed); - if (count && (!target_type.min_time_ns || - minimum < target_type.min_time_ns)) - target_type.min_time_ns = minimum; - target_type.max_time_ns = std::max( - target_type.max_time_ns, - source_type.max_time_ns.load(std::memory_order_relaxed)); - } - const auto longest = source.longest_task_time_ns.load( - std::memory_order_relaxed); - if (longest > state.longest_task_time_ns) { - state.longest_task_time_ns = longest; - state.longest_task_hash = source.longest_task_hash.load( - std::memory_order_relaxed); - const auto name = detail::taskflow_node_name( - state.longest_task_hash); - state.longest_task_name = name ? *name : std::string{}; - state.longest_task_type = std::string(tf::to_string( - source.longest_task_type.load(std::memory_order_relaxed))); - } - } - state.observed_wall_time_ns = first && last >= first ? last - first : 0; - state.active_task_count = active_task_count.load(std::memory_order_relaxed); - state.peak_active_task_count = peak_active_task_count.load(std::memory_order_relaxed); - state.active_worker_count = active_worker_count.load(std::memory_order_relaxed); - state.peak_active_worker_count = peak_active_worker_count.load(std::memory_order_relaxed); - state.worker_utilization = workers && state.observed_wall_time_ns - ? static_cast(state.worker_busy_time_ns) * 100.0 / - static_cast(state.observed_wall_time_ns) / - static_cast(workers) - : 0.0; - state.worker_cpu_utilization = workers && state.observed_wall_time_ns - ? static_cast(state.worker_cpu_time_ns) * 100.0 / - static_cast(state.observed_wall_time_ns) / - static_cast(workers) - : 0.0; - for (auto& target : state.workers) { - target.idle_time_ns = state.observed_wall_time_ns > target.busy_time_ns - ? state.observed_wall_time_ns - target.busy_time_ns - : 0; - target.utilization = state.observed_wall_time_ns - ? static_cast(target.busy_time_ns) * 100.0 / - static_cast(state.observed_wall_time_ns) - : 0.0; - target.cpu_utilization = state.observed_wall_time_ns - ? static_cast(target.cpu_time_ns) * 100.0 / - static_cast(state.observed_wall_time_ns) - : 0.0; - } - } -}; -struct Task_Resource : Pinned { -private: - std::unique_ptr executor; - std::shared_ptr observer; - not_null memory; - std::atomic_size_t active_taskflows{}; - std::atomic_size_t peak_active_taskflows{}; - std::atomic_size_t completed_taskflows{}; - std::atomic_size_t failed_taskflows{}; - Task_Runtime_State state; - double_buffer::detail::State_Callback_Storage state_callbacks; - std::recursive_mutex state_mutex; - std::atomic_bool state_callback_enabled{}; - std::atomic_bool executor_ready{}; - Task_Runtime_Configuration configuration{}; - void create_executor(std::size_t workers, std::shared_ptr worker_interface) { - executor_ready.store(false, std::memory_order_relaxed); - executor = std::make_unique(workers, std::move(worker_interface)); - observer = executor->make_observer( - configuration.worker_occupation_limit, - configuration.worker_overrun_action); - state = {}; - executor_ready.store(true, std::memory_order_release); - } - void ensure_executor() { - /* 正常渲染热路径只读取一次原子位;互斥量仅处理首次惰性初始化。 */ - if (executor_ready.load(std::memory_order_acquire)) return; - std::lock_guard guard(state_mutex); - if (executor) return; - executor = std::make_unique(); - observer = executor->make_observer( - configuration.worker_occupation_limit, - configuration.worker_overrun_action); - executor_ready.store(true, std::memory_order_release); - } - void publish_state() { - if (!state_callback_enabled.load(std::memory_order_acquire)) return; - Task_Runtime_State published; - { - std::lock_guard guard(state_mutex); - observer->write_state(state, executor->num_workers(), executor->num_topologies()); - state.active_taskflow_count = active_taskflows.load(std::memory_order_relaxed); - state.peak_active_taskflow_count = peak_active_taskflows.load(std::memory_order_relaxed); - state.completed_taskflow_count = completed_taskflows.load(std::memory_order_relaxed); - state.failed_taskflow_count = failed_taskflows.load(std::memory_order_relaxed); - published = state; - } - state_callbacks.template publish( - [published = std::move(published)] { return published; }); - } - std::uint64_t run_synchronous( - Task_Graph& taskflow, detail::Taskflow_Graph_Timing* timing) { - ensure_executor(); - auto active = active_taskflows.fetch_add(1, std::memory_order_relaxed) + 1; - auto peak = peak_active_taskflows.load(std::memory_order_relaxed); - while (peak < active && !peak_active_taskflows.compare_exchange_weak(peak, active, std::memory_order_relaxed)) {} - const auto start = std::chrono::steady_clock::now(); - try { - if (const auto* worker = executor->this_worker()) { - observer->pause_worker(worker->id()); - try { - executor->corun(native_taskflow(taskflow)); - } - catch (...) { - observer->resume_worker(worker->id()); - throw; - } - observer->resume_worker(worker->id()); - } - else executor->run(native_taskflow(taskflow)).get(); - } - catch (...) { - const auto executor_finished = std::chrono::steady_clock::now(); - active_taskflows.fetch_sub(1, std::memory_order_relaxed); - failed_taskflows.fetch_add(1, std::memory_order_relaxed); - if (timing) timing->runtime_bookkeeping_finished = std::chrono::steady_clock::now(); - publish_state(); - if (timing) { - timing->executor_finished = executor_finished; - timing->runtime_publish_finished = std::chrono::steady_clock::now(); - } - throw; - } - const auto executor_finished = std::chrono::steady_clock::now(); - const auto elapsed = static_cast( - std::chrono::duration_cast( - executor_finished - start).count()); - active_taskflows.fetch_sub(1, std::memory_order_relaxed); - completed_taskflows.fetch_add(1, std::memory_order_relaxed); - if (timing) timing->runtime_bookkeeping_finished = std::chrono::steady_clock::now(); - publish_state(); - if (timing) { - timing->executor_finished = executor_finished; - timing->runtime_publish_finished = std::chrono::steady_clock::now(); - } - return elapsed; - } - void run_asynchronous( - Task_Graph& taskflow, bool capture_timing, - std::function completion) { - ensure_executor(); - auto active = active_taskflows.fetch_add(1, std::memory_order_relaxed) + 1; - auto peak = peak_active_taskflows.load(std::memory_order_relaxed); - while (peak < active && !peak_active_taskflows.compare_exchange_weak( - peak, active, std::memory_order_relaxed)) {} - executor->run(native_taskflow(taskflow), [this, capture_timing, completion = std::move(completion)]() mutable { - detail::Taskflow_Graph_Timing timing{}; - if (capture_timing) timing.executor_finished = std::chrono::steady_clock::now(); - active_taskflows.fetch_sub(1, std::memory_order_relaxed); - completed_taskflows.fetch_add(1, std::memory_order_relaxed); - if (capture_timing) timing.runtime_bookkeeping_finished = std::chrono::steady_clock::now(); - publish_state(); - if (capture_timing) timing.runtime_publish_finished = std::chrono::steady_clock::now(); - completion(std::move(timing)); - }); - } -public: - Task_Resource() : memory(std::pmr::get_default_resource()) {} - static Task_Resource& instance() { - static Task_Resource value; - return value; - } - void initialize(Task_Runtime_Configuration value, - std::shared_ptr worker_interface) { - if (!value.workers) - throw std::invalid_argument( - "Taskflow runtime requires at least one worker"); - if (value.worker_occupation_limit <= std::chrono::milliseconds::zero()) - throw std::invalid_argument( - "Taskflow worker occupation limit must be positive"); - std::lock_guard guard(state_mutex); - configuration = value; - memory = not_null{configuration.pmr.resource()}; - create_executor(configuration.workers, std::move(worker_interface)); - active_taskflows.store(0, std::memory_order_relaxed); - peak_active_taskflows.store(0, std::memory_order_relaxed); - completed_taskflows.store(0, std::memory_order_relaxed); - failed_taskflows.store(0, std::memory_order_relaxed); - } - std::uint64_t run(Task_Graph& taskflow) { - detail::Task_Graph_Access::bind_frame(taskflow, nullptr); - return run_synchronous(taskflow, nullptr); - } - void run(Task_Graph& taskflow, std::function completion) { - if (!completion) throw std::invalid_argument("Taskflow completion is empty"); - detail::Task_Graph_Access::bind_frame(taskflow, nullptr); - run_asynchronous(taskflow, false, [completion = std::move(completion)]( - detail::Taskflow_Graph_Timing) mutable { - completion(); - }); - } - std::uint64_t run(Task_Graph& taskflow, Render_Frame& frame, - std::string_view stage) { - const auto token = detail::Taskflow_Frame_Access::begin_graph( - frame, taskflow, stage); - /* - * 真无锁 Node::data -> Frame 路由的成立条件: - * 1. 同一 DAG 的同一批 Node 不能同时绑定不同帧并发执行;_data 只有一个普通 void* 槽位。 - * 2. Frame 必须覆盖整个 DAG 执行以及全部 Observer on_entry/on_exit 回调的生命周期。 - * 3. 多 worker 会并发附加 trace;Frame 必须按 worker 隔离单写者,不能共享写同一容器。 - */ - detail::Task_Graph_Access::bind_frame( - taskflow, frame.taskflow_trace_requested() ? &frame : nullptr); - detail::Taskflow_Graph_Timing timing{}; - try { - const auto elapsed = run_synchronous(taskflow, &timing); - detail::Task_Graph_Access::bind_frame(taskflow, nullptr); - timing.observer_unbind_finished = std::chrono::steady_clock::now(); - detail::Taskflow_Frame_Access::finish_graph(token, timing); - return elapsed; - } - catch (...) { - detail::Task_Graph_Access::bind_frame(taskflow, nullptr); - timing.observer_unbind_finished = std::chrono::steady_clock::now(); - detail::Taskflow_Frame_Access::finish_graph(token, timing); - throw; - } - } - void run(Task_Graph& taskflow, Render_Frame& frame, std::string_view stage, - std::function completion) { - if (!completion) throw std::invalid_argument("Taskflow completion is empty"); - const auto token = detail::Taskflow_Frame_Access::begin_graph( - frame, taskflow, stage); - try { - detail::Task_Graph_Access::bind_frame( - taskflow, frame.taskflow_trace_requested() ? &frame : nullptr); - run_asynchronous(taskflow, true, [this, &taskflow, &frame, token, - completion = std::move(completion)]( - detail::Taskflow_Graph_Timing timing) mutable { - detail::Task_Graph_Access::bind_frame(taskflow, nullptr); - timing.observer_unbind_finished = std::chrono::steady_clock::now(); - detail::Taskflow_Frame_Access::finish_graph(token, timing); - completion(); - }); - } - catch (...) { - detail::Taskflow_Graph_Timing timing{}; - detail::Task_Graph_Access::bind_frame(taskflow, nullptr); - timing.observer_unbind_finished = std::chrono::steady_clock::now(); - detail::Taskflow_Frame_Access::finish_graph(token, timing); - throw; - } - } - void schedule(std::string name, std::function task) { - if (!task) throw std::invalid_argument("Taskflow scheduled task is empty"); - if (name.empty()) throw std::invalid_argument("Taskflow scheduled task name is empty"); - ensure_executor(); - executor->silent_async(std::move(name), std::move(task)); - } - void corun_until(std::function predicate) { - ensure_executor(); - const auto* worker = executor->this_worker(); - if (!worker) - throw std::logic_error( - "Task graph cooperative wait requires a Kernel Executor worker"); - observer->pause_worker(worker->id()); - try { - executor->corun_until(std::move(predicate)); - } - catch (...) { - observer->resume_worker(worker->id()); - throw; - } - observer->resume_worker(worker->id()); - } - std::pmr::memory_resource* memory_resource() const noexcept { - return memory.get(); - } - void set_state_callback(std::function callback) { - ensure_executor(); - std::lock_guard guard(state_mutex); - state_callbacks.template set(std::move(callback)); - state_callback_enabled.store(true, std::memory_order_release); - } - void clear_state_callback() { - std::lock_guard guard(state_mutex); - state_callbacks.template clear(); - state_callback_enabled.store(false, std::memory_order_release); - } - bool begin_trace(Render_Frame& frame) { - ensure_executor(); - return observer->begin_trace(frame, executor->num_workers()); - } - void finish_trace(Render_Frame& frame) noexcept { - if (observer) observer->finish_trace(frame); - } - Task_Runtime_State runtime_state() { - ensure_executor(); - std::lock_guard guard(state_mutex); - Task_Runtime_State result{}; - observer->write_state(result, executor->num_workers(), executor->num_topologies()); - result.active_taskflow_count = active_taskflows.load(std::memory_order_relaxed); - result.peak_active_taskflow_count = peak_active_taskflows.load(std::memory_order_relaxed); - result.completed_taskflow_count = completed_taskflows.load(std::memory_order_relaxed); - result.failed_taskflow_count = failed_taskflows.load(std::memory_order_relaxed); - return result; - } -}; -} -namespace detail { -void register_taskflow_node(std::uint64_t native_id, - std::string_view name) { - if (!native_id || name.empty()) return; - auto& registry = task_name_registry(); - std::lock_guard lock(registry.mutex); - registry.names.insert_or_assign( - native_id, std::make_shared(name)); -} -std::shared_ptr taskflow_node_name( - std::uint64_t native_id) noexcept { - try { - auto& registry = task_name_registry(); - std::lock_guard lock(registry.mutex); - const auto found = registry.names.find(native_id); - return found == registry.names.end() ? nullptr : found->second; - } - catch (...) { - return {}; - } -} -void corun_taskflow_until(std::function predicate) { - Task_Resource::instance().corun_until(std::move(predicate)); -} -} -void initialize_runtime(Task_Runtime_Configuration configuration) { - Task_Resource::instance().initialize(std::move(configuration), nullptr); -} -void schedule_task(std::string name, std::function task) { - Task_Resource::instance().schedule(std::move(name), std::move(task)); -} -Task_Runtime_State task_runtime_state() { - return Task_Resource::instance().runtime_state(); -} -namespace detail { -void set_runtime_state_callback_impl(std::function < void(const Task_Runtime_State &) > callback) { - Task_Resource::instance().set_state_callback(std::move(callback)); -} -void clear_runtime_state_callback_impl() { - Task_Resource::instance().clear_state_callback(); -} -std::uint64_t run_taskflow(Task_Graph& taskflow) { - return Task_Resource::instance().run(taskflow); -} -void run_taskflow(Task_Graph& taskflow, std::function completion) { - Task_Resource::instance().run(taskflow, std::move(completion)); -} -std::uint64_t run_taskflow(Task_Graph& taskflow, Render_Frame& frame, - std::string_view stage) { - return Task_Resource::instance().run(taskflow, frame, stage); -} -void run_taskflow(Task_Graph& taskflow, Render_Frame& frame, - std::string_view stage, std::function completion) { - Task_Resource::instance().run(taskflow, frame, stage, std::move(completion)); -} -bool begin_taskflow_trace(Render_Frame& frame) { - return !frame.taskflow_trace_requested() || - Task_Resource::instance().begin_trace(frame); -} -void finish_taskflow_trace(Render_Frame& frame) noexcept { - if (frame.taskflow_trace_requested()) Task_Resource::instance().finish_trace(frame); -} -std::pmr::memory_resource* task_memory_resource() noexcept { - return Task_Resource::instance().memory_resource(); -} -} -} diff --git a/kernel/src/kernel/render_common.hpp b/kernel/src/kernel/render_common.hpp deleted file mode 100644 index 291b485..0000000 --- a/kernel/src/kernel/render_common.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once -#include "double_buffer/model.hpp" -#include "frame.hpp" -#include "Task_Graph.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace aethera { -using double_buffer::Pinned; -using double_buffer::Def; -using double_buffer::State_Type; -using double_buffer::State_Access; -using double_buffer::Prop_Access; -using double_buffer::Pmr; -using double_buffer::Root; -using double_buffer::Tagged_Buffer; -using double_buffer::Mpmc_Triple_Buffer; -using double_buffer::Attached; -using double_buffer::Dependency_Graph_Type; -using double_buffer::Dependency_Graph; -using double_buffer::Dependency_Graph_Error; -/* Renderable 业务图在 Scene 依赖图中的唯一标签。 */ -struct Render_Graph_Tag {}; -/* 全局 Taskflow 运行时状态标签,用于注册状态回调。 */ -struct Task_Runtime_State_Tag {}; -/* 单一 Taskflow 任务类型的累计统计。 */ -struct Task_Type_State { - std::string name{}; - std::size_t count{}; - std::uint64_t total_time_ns{}; - std::uint64_t min_time_ns{}; - std::uint64_t max_time_ns{}; - bool operator==(const Task_Type_State&) const = default; -}; -/* 单一 Taskflow Worker 的累计统计。 */ -struct Task_Worker_State { - std::size_t id{}; - std::size_t task_count{}; - std::size_t current_queue_size{}; /* 当前活跃任务进入时观察到的 Worker 队列深度。 */ - std::size_t current_queue_capacity{}; /* 当前活跃任务进入时观察到的 Worker 队列容量。 */ - std::size_t peak_observed_queue_size{}; - std::size_t max_observed_queue_capacity{}; - std::uint64_t active_task_hash{}; /* 当前最内层原生任务身份;空闲时为 0。 */ - std::uint64_t active_task_time_ns{}; /* 当前任务连续占用 Worker 片段已经持续的时间;协作让出时为 0。 */ - std::string active_task_type{}; /* 当前任务的 Taskflow 原生 TaskType;空闲时为空。 */ - std::uint64_t task_time_ns{}; /* 已完成任务体墙钟之和,不含 cooperative wait。 */ - std::uint64_t busy_time_ns{}; /* Worker 实际执行任务体片段的墙钟并集,不含 cooperative wait。 */ - std::uint64_t cpu_time_ns{}; /* 从首次观察任务起,Worker 原生线程的累计 CPU 时间。 */ - std::uint64_t cooperative_wait_count{}; /* 当前 Worker 上任务主动 corun/corun_until 让出的累计次数。 */ - std::uint64_t cooperative_wait_time_ns{}; /* 外层任务协作挂起区间之和;期间 Worker 可执行其他任务。 */ - std::uint64_t idle_time_ns{}; - std::uint64_t min_task_time_ns{}; - std::uint64_t max_task_time_ns{}; - double utilization{}; - double cpu_utilization{}; - bool operator==(const Task_Worker_State&) const = default; -}; -/* - * 全局 Taskflow Executor 的累计运行状态。 - * 状态在每次 taskflow 执行完成或异常结束后发布,适合用于性能统计和调试面板。 - */ -struct Task_Runtime_State : State_Type { - std::size_t worker_count{}; - std::size_t active_topology_count{}; - std::size_t active_taskflow_count{}; - std::size_t peak_active_taskflow_count{}; - std::size_t completed_taskflow_count{}; - std::size_t failed_taskflow_count{}; - std::size_t active_task_count{}; - std::size_t peak_active_task_count{}; - std::size_t active_worker_count{}; - std::size_t peak_active_worker_count{}; - std::size_t observed_task_count{}; - std::size_t named_task_count{}; - std::size_t peak_observed_worker_queue_size{}; - std::size_t max_observed_worker_queue_capacity{}; - std::size_t longest_task_hash{}; - std::string longest_task_name; - std::string longest_task_type; - std::uint64_t longest_task_time_ns{}; - std::uint64_t total_task_time_ns{}; - std::uint64_t worker_busy_time_ns{}; - std::uint64_t worker_cpu_time_ns{}; - std::uint64_t cooperative_wait_count{}; - std::uint64_t cooperative_wait_time_ns{}; - std::uint64_t observed_wall_time_ns{}; - double worker_utilization{}; - double worker_cpu_utilization{}; - std::vector task_types; - std::vector workers; - bool operator==(const Task_Runtime_State&) const = default; -}; -enum struct Task_Overrun_Action : std::uint8_t { - warning, - fast_fail -}; -struct Task_Runtime_Configuration { - std::size_t workers{std::thread::hardware_concurrency()}; /* 全局 Executor 的 Worker 数量。 */ - std::chrono::milliseconds worker_occupation_limit{100}; /* 单个节点连续非 CPU 等待 Worker 的上限;协作让出区间不计。 */ - Task_Overrun_Action worker_overrun_action{ - /* 节点超过占用上限后的处置策略。 */ - Task_Overrun_Action::warning - }; - Pmr pmr{}; /* Task Graph 临时结构使用的内存资源。 */ -}; -/* - * 初始化全局 Taskflow 运行时。 - * workers 为 Executor Worker 数;worker_interface 可自定义 Worker;pmr 作为渲染内核临时图结构的统一内存资源。 - * 未主动调用时运行时会在第一次执行 Taskflow 时按 Taskflow 默认配置惰性创建 Executor。 - */ -void initialize_runtime(Task_Runtime_Configuration configuration = {}); -/* 把具名独立业务任务提交给全局 Taskflow worker;任务不得执行阻塞式设备等待。 */ -void schedule_task(std::string name, std::function task); -/* 读取全局 Executor 的累计状态;计算只读取原子计数,不触发逐任务导出。 */ -[[nodiscard]] Task_Runtime_State task_runtime_state(); -/* 为全局 Taskflow 运行时状态注册回调;Tag 目前只接受 Task_Runtime_State_Tag。 */ -template Tag, std::invocable Callback> -void set_runtime_state_callback(Callback && callback); -/* 清除全局 Taskflow 运行时状态回调;Tag 目前只接受 Task_Runtime_State_Tag。 */ -template Tag> -void clear_runtime_state_callback(); -} -#include "render_common.ipp" diff --git a/kernel/src/kernel/render_common.ipp b/kernel/src/kernel/render_common.ipp deleted file mode 100644 index 91bebe6..0000000 --- a/kernel/src/kernel/render_common.ipp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -namespace aethera::detail { -void set_runtime_state_callback_impl(std::function callback); -void clear_runtime_state_callback_impl(); -std::uint64_t run_taskflow(Task_Graph& taskflow); -void run_taskflow(Task_Graph& taskflow, std::function completion); -std::uint64_t run_taskflow(Task_Graph& taskflow, Render_Frame& frame, std::string_view stage); -void run_taskflow(Task_Graph& taskflow, Render_Frame& frame, std::string_view stage, - std::function completion); -bool begin_taskflow_trace(Render_Frame& frame); -void finish_taskflow_trace(Render_Frame& frame) noexcept; -std::pmr::memory_resource* task_memory_resource() noexcept; -} -namespace aethera { -template Tag, std::invocable Callback> -void set_runtime_state_callback(Callback&& callback) { - detail::set_runtime_state_callback_impl(std::function{std::forward(callback)}); -} -template Tag> -void clear_runtime_state_callback() { - detail::clear_runtime_state_callback_impl(); -} -} diff --git a/kernel/src/kernel/renderable.cpp b/kernel/src/kernel/renderable.cpp deleted file mode 100644 index 4943659..0000000 --- a/kernel/src/kernel/renderable.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "renderable.hpp" -namespace aethera { -std::optional Renderable::event_routing_distance(const Event& event) const { - const auto& data = static_cast(*d); - return data.event_routing_distance_run ? data.event_routing_distance_run(this, event) : std::nullopt; -} -Task_Graph& Renderable::taskflow() { - return static_cast(*d).extension; -} -} diff --git a/kernel/src/kernel/renderable.hpp b/kernel/src/kernel/renderable.hpp deleted file mode 100644 index 801bfe5..0000000 --- a/kernel/src/kernel/renderable.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "event.hpp" -#include "render_common.hpp" -#include -namespace aethera { -/* Renderable 派生实现声明颜色缓存时使用的业务标签。 */ -struct Color_Cache { - virtual ~Color_Cache() = default; -}; -/* Renderable 状态标签,用于访问和订阅 Renderable::State。 */ -struct Renderable; -/* 最终 Private 提供 handle_event(T*, const Event&) 时具备事件处理能力。 */ -template -concept Event_Renderable = Attached && requires(typename T::Private& private_data, T* object, const Event& event) { - { private_data.handle_event(object, event) } -> std::same_as; -}; -/* 最终 Private 提供 build_graph(T*, const Prop&) 时使用其自定义业务图。 */ -template -concept Graph_Renderable = Attached && requires(typename T::Private& private_data, - T* object, - const typename T::Prop& prop) { - { private_data.build_graph(object, prop) } -> std::same_as; -}; -/* 最终可交给 Scene 执行的 Renderable 契约。 */ -template -concept Renderable_Object = Attached && std::derived_from; -/* - * Renderable 定义 Scene 可调度对象的公共机制。 - * 用户继续派生该定义,在最终具体类型的 Private 中定义自己的业务图。 - */ -struct Renderable : Def { - /* Renderable 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */ - struct Prop : Prev_Prop {}; - /* 任务拓扑和执行状态由逐帧 Taskflow trace 发布,不进入业务 State。 */ - struct State : Prev_State {}; - /* 完整声明、内部派发以及 Prepare/Paint CRTP 能力契约见 renderable.ipp 中的 Renderable::Private。 */ - struct Private; - /* 返回该对象参与当前事件竞争时的几何距离;无值表示沿用普通绘制层级路由。 */ - [[nodiscard]] std::optional event_routing_distance(const Event& event) const; - /* - * 返回 Prepare 阶段完成后执行的业务 DAG 扩展端口。 - * Task_Graph 只能在该 Renderable 所属 Scene 没有运行时修改。 - */ - [[nodiscard]] Task_Graph& taskflow(); -private: - /* 依赖图绑定时为最终对象建立 CRTP 类型擦除入口,并标记首次执行。 */ - void bind_dependency_graph_object(Attached auto* object); - friend struct Dependency_Graph; -}; -} -#include "renderable.ipp" diff --git a/kernel/src/kernel/renderable.ipp b/kernel/src/kernel/renderable.ipp deleted file mode 100644 index 9141b42..0000000 --- a/kernel/src/kernel/renderable.ipp +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once -#include -#include -#include -#include -namespace aethera { -struct Renderable::Private : Prev_Private { - using Event_Run = void (*)(Root*, const Event&); - using Event_Routing_Distance_Run = std::optional (*)(const Root*, const Event&); - using Color_Cache_Visitor = void (*)(void*, const Color_Cache&); - using Color_Cache_Visit = void (*)(Root*, void*, Color_Cache_Visitor); - using Build_Graph_Run = std::optional (*)(Root*); - using Graph_Rebuild_Run = bool (*)(Root*); - using Render_Predicate_Run = bool (*)(Root*, bool); - using Publish_State_Run = void (*)(Root*); - - std::string business_name_value{}; - std::string diagnostic_component_id{}; /* 按需诊断使用的组件语义 ID;不属于业务状态。 */ - Build_Graph_Run build_graph_run{}; - Graph_Rebuild_Run graph_rebuild_run{}; - Render_Predicate_Run render_predicate_run{}; - Publish_State_Run publish_state_run{}; - Event_Run event_run{}; - Event_Routing_Distance_Run event_routing_distance_run{}; - Color_Cache_Visit color_cache_visit{}; - std::optional graph{}; - Task_Graph extension{"renderable.extension"}; - bool render_graph_executed{}; /* 当前 Scene 帧的条件节点是否选择了本对象业务图。 */ - - [[nodiscard]] std::string_view business_name() const noexcept { - return business_name_value; - } - [[nodiscard]] std::optional build_runtime_graph(Root* object) const { - return build_graph_run ? build_graph_run(object) : std::nullopt; - } - [[nodiscard]] bool should_rebuild_runtime_graph(Root* object) const { - return graph_rebuild_run && graph_rebuild_run(object); - } - [[nodiscard]] bool should_render(Root* object, bool dirty) const { - return render_predicate_run ? render_predicate_run(object, dirty) : dirty; - } - void publish_renderable_state(Root* object) const { - publish_state_run(object); - } - - template - void bind_private_crtp(Object* object); -}; - -template -void Renderable::Private::bind_private_crtp(Object* object) { - Prev_Private::bind_private_crtp(object); - auto& data = static_cast(*this); - data.business_name_value = [] { - std::string name = typeid(Object).name(); - for (const std::string_view prefix : {"struct ", "class "}) - if (name.starts_with(prefix)) name.erase(0, prefix.size()); - if (const auto separator = name.rfind("::"); separator != std::string::npos) - name.erase(0, separator + 2); - return name; - }(); - data.build_graph_run = [](Root* root) -> std::optional { - auto* value = static_cast(root); - auto& private_data = double_buffer::detail::Internal_Access::get(value); - const auto& prop = double_buffer::detail::Internal_Access::current_prop(value); - if constexpr (requires { private_data.build_graph(value, prop); }) { - return private_data.build_graph(value, prop); - } - else if constexpr (requires { private_data.prepare_data(value); } || - requires { private_data.paint(value); }) { - Task_Graph graph{"renderable.graph"}; - graph.add("render", [value] { - auto& runtime_data = double_buffer::detail::Internal_Access::get(value); - if constexpr (requires { runtime_data.prepare_data(value); }) - runtime_data.prepare_data(value); - if constexpr (requires { runtime_data.paint(value); }) - runtime_data.paint(value); - }); - return graph; - } - else { - return std::nullopt; - } - }; - data.graph_rebuild_run = [](Root* root) { - auto* value = static_cast(root); - auto& private_data = double_buffer::detail::Internal_Access::get(value); - const auto& prop = double_buffer::detail::Internal_Access::current_prop(value); - if constexpr (requires { private_data.should_rebuild_graph(value, prop); }) - return private_data.should_rebuild_graph(value, prop); - return false; - }; - data.render_predicate_run = [](Root* root, bool dirty) { - auto* value = static_cast(root); - auto& private_data = double_buffer::detail::Internal_Access::get(value); - const auto& state = static_cast( - double_buffer::detail::Internal_Access::current_state(value)); - if constexpr (requires { private_data.should_render(value, state, dirty); }) - return private_data.should_render(value, state, dirty); - return dirty; - }; - data.publish_state_run = [](Root* root) { - auto* value = static_cast(root); - double_buffer::detail::Internal_Access::publish_state(value); - }; - if constexpr (Event_Renderable) { - data.event_run = [](Root* root, const Event& event) { - auto* value = static_cast(root); - auto& private_data = double_buffer::detail::Internal_Access::get(value); - private_data.handle_event(value, event); - }; - } - if constexpr (requires { - double_buffer::detail::Internal_Access::pending_buffer(object); - }) { - using Cache = std::remove_reference_t(object))>; - if constexpr (std::derived_from) { - data.color_cache_visit = [](Root* root, void* context, Color_Cache_Visitor visitor) { - auto* value = static_cast(root); - if (double_buffer::detail::Internal_Access::get(value) - .render_graph_executed) - visitor(context, double_buffer::detail::Internal_Access::pending_buffer(value)); - else - visitor(context, double_buffer::detail::Internal_Access::current_buffer(value)); - }; - } - } - double_buffer::detail::Internal_Access::mark_dirty(object); -} - -inline void Renderable::bind_dependency_graph_object(Attached auto* object) { - using Object = std::remove_pointer_t; - static_assert(Renderable_Object); - double_buffer::detail::Internal_Access::mark_dirty(object); -} -} diff --git a/kernel/src/kernel/scene.cpp b/kernel/src/kernel/scene.cpp deleted file mode 100644 index cfcd4ed..0000000 --- a/kernel/src/kernel/scene.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "scene.hpp" /* 后端共有 Prepare Scene 实现。 */ -namespace aethera { -Scene::Private::Private() = default; -Scene::Private::~Private() = default; -Task_Graph& Scene::completion_taskflow() { - return static_cast(*d).completion_graph; -} -} diff --git a/kernel/src/kernel/scene.hpp b/kernel/src/kernel/scene.hpp deleted file mode 100644 index 084f8f6..0000000 --- a/kernel/src/kernel/scene.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include "frame.hpp" -#include "renderable.hpp" -#include -#include -#include -#include -namespace aethera { -enum struct Render_Frame_Result : std::uint8_t { - submitted, - frame_pipeline_busy, - view_inactive, - empty_viewport, - backend_unavailable, - incompatible_frame -}; -struct Render_Frame_Callbacks { - std::function, bool)> submitted{}; /* Scene 已提交该借用帧;bool 表示是否允许并行准备下一帧。 */ - std::function)> completed{}; /* Scene 清除全部借用并完成帧数据附加。 */ -}; -struct Render_Frame_Scene { - virtual ~Render_Frame_Scene() = default; - [[nodiscard]] virtual Render_Frame_Result render_frame( - not_null frame, Render_Frame_Callbacks callbacks) = 0; -}; -struct Scene_Event_Stream_Tag {}; -/* Scene 状态标签,用于访问和订阅 Scene::State。 */ -/* - * Scene 只汇总跨渲染后端共有的 Prepare 数据依赖图并构建 Taskflow。 - * 最终具体 Scene 直接持有其 Private;编辑 Dependency_Graph 后提交结构变化,再执行当前场景。 - */ -struct Scene : Def, - Mpmc_Triple_Buffer>> { - using Event_Pointer = std::shared_ptr; - using Event_Batch = std::pmr::vector; - using Event_Report_Pointer = std::shared_ptr; - using Event_Report_Batch = std::pmr::vector; - /* Scene 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */ - struct Prop : Prev_Prop {}; - /* Scene 只发布输入事件业务统计;任务图结构与执行数据来自逐帧 trace。 */ - struct State : Prev_State { - Event_Statistics_State event_statistics{}; /* 输入进入 Scene 到 Renderable 消费完成的分类统计。 */ - bool operator==(const State&) const = default; - }; - /* 完整声明、字段及可覆盖 CRTP hook 见 scene.ipp 中的 Scene::Private。 */ - struct Private; - template - [[nodiscard]] std::shared_ptr make_event(Arguments&&... arguments); - [[nodiscard]] Task_Graph& completion_taskflow(); -protected: - void observe_event(not_null event); -}; -} -#include "scene.ipp" diff --git a/kernel/src/kernel/scene.ipp b/kernel/src/kernel/scene.ipp deleted file mode 100644 index 73a9449..0000000 --- a/kernel/src/kernel/scene.ipp +++ /dev/null @@ -1,244 +0,0 @@ -#pragma once -#include -#include "Task_Graph_Internal.hpp" -#include -#include -#include -#include -#include -namespace aethera { -struct Scene::Private : Prev_Private { - struct Result {}; /* process(...) 完成回调的结果类型;当前仅表示完成。 */ - struct Runtime; /* Scene 的 Taskflow 构建产物;完整定义位于本文件下方。 */ - std::unique_ptr runtime; /* Scene 唯一运行时构建产物的所有权。 */ - Task_Graph completion_graph{"scene.completion"}; - Event_Statistics_Accumulator event_statistics{}; /* Scene 单帧域内按事件类型增量计算。 */ - Private(); - ~Private(); - template - [[nodiscard]] Event_Batch take_events(Object* object); - template void bind_private_crtp(Object* object); - /* Def CRTP hook:所有缓冲推进后重建必要的总 Taskflow,并更新 Scene_State_Tag 状态层。 */ - template - void after_advance(Object* object, - Prop* pending_prop, - State_Access pending_states, - const Prop* current_prop, - State_Access current_states); - /* 写入事件统计字段表示开始新的统计窗口;累加器与发布 State 在同一入口归零。 */ - template - void before_state_set(Object* object, Member Owner::* member, - State_Access pending_states); - /* Private CRTP 入口:执行当前总 Taskflow;派生 Private 只有在替换完整 Scene 处理语义时才应覆盖。 */ - template - void process(Object* object, Callback&& callback) requires std::invocable; - /* 派生渲染 Scene 使用:把调用方外部帧贯穿公共 Prepare 阶段并追加诊断标记。 */ - template - void process( - Object* object, - Render_Frame* frame, - Callback&& callback) - requires std::invocable; - /* 派生 Scene 的 Private 还可覆盖 Def::Private 的四个 State 生命周期 hook,并通过 State_Access::get() 访问状态层。 */ -}; - -template -void Scene::Private::before_state_set( - Object* object, Member Owner::* member, - State_Access pending_states) { - if constexpr (std::same_as) - if (member == &State::event_statistics) event_statistics.reset(); -} -struct Scene::Private::Runtime { - std::unique_ptr taskflow; /* 当前已构建的总业务 DAG;为空表示尚未构建。 */ -}; -template -void Scene::Private::bind_private_crtp(Object* object) { - Prev_Private::bind_private_crtp(object); - runtime = std::make_unique(); -} -template -Scene::Event_Batch Scene::Private::take_events(Object* object) { - double_buffer::detail::Internal_Access::exchange_stream(object); - return double_buffer::detail::Internal_Access::access_rendering_stream(object, - [&](std::span events) { - Event_Batch result{object->memory_resource()}; - result.reserve(events.size()); - result.insert(result.end(), events.begin(), events.end()); - return result; - }); -} -template -std::shared_ptr Scene::make_event(Arguments&&... arguments) { - auto event = std::allocate_shared( - allocator(), std::forward(arguments)...); - observe_event(not_null{event.get()}); - return event; -} -inline void Scene::observe_event(not_null event) { - event->observe_with(&static_cast(*d).event_statistics); -} -template -void Scene::Private::process(Object* object, Callback&& callback) requires std::invocable { - process(object, nullptr, std::forward(callback)); -} -template -void Scene::Private::process( - Object* object, - Render_Frame* frame, - Callback&& callback) - requires std::invocable { - if (frame) frame->mark(Frame_Trace_Marker::prepare_started); - if (runtime->taskflow && !runtime->taskflow->empty()) - if (frame && frame->taskflow_trace_requested()) - (void)detail::run_taskflow( - *runtime->taskflow, *frame, "scene.renderables"); - else - (void)detail::run_taskflow(*runtime->taskflow); - if (frame) frame->mark(Frame_Trace_Marker::prepare_finished); - double_buffer::detail::Internal_Access::current_dependency_graph(object).for_each_bound( - [](Renderable* renderable, Renderable::Private& data) { - data.publish_renderable_state(renderable); - }); - auto& state = static_cast( - double_buffer::detail::Internal_Access::pending_state(object)); - state.event_statistics = event_statistics.state(); - double_buffer::detail::Internal_Access::publish_state(object); - Result result; - std::invoke(std::forward(callback), std::as_const(result)); -} -template -void Scene::Private::after_advance(Object* object, - Prop* pending_prop, - State_Access pending_states, - const Prop* current_prop, - State_Access current_states) { - auto* resource = detail::task_memory_resource(); - std::pmr::unordered_set advanced_objects{resource}; - advanced_objects.insert(object); - double_buffer::detail::Internal_Access::for_each_current_dependency_graph(object, - [&](const Dependency_Graph& dependency_graph) { - dependency_graph.for_each( - [&](const Dependency_Graph::Node& node) { - if (advanced_objects.insert(node.object).second) { - node.object->advance_object(); - } - } - ); - } - ); - auto render_dependencies = - double_buffer::detail::Internal_Access::current_dependency_graph(object); - bool taskflow_dirty = !runtime->taskflow; - double_buffer::detail::Internal_Access::access_pending_dependency_graph(object, - [&](auto& graph_state) { taskflow_dirty = taskflow_dirty || graph_state.dirty(); }); - render_dependencies.for_each_bound( - [&](Renderable* renderable, Renderable::Private& data) { - if (!data.graph || data.should_rebuild_runtime_graph(renderable)) { - data.graph = data.build_runtime_graph(renderable); - if (data.graph) { - data.graph->describe("owner_kind", "renderable"); - if (!data.diagnostic_component_id.empty()) - data.graph->describe("owner_component", - data.diagnostic_component_id); - } - double_buffer::detail::Internal_Access::mark_dirty( - renderable); - } - }); - if (!taskflow_dirty) return; - if (!runtime->taskflow) runtime->taskflow = std::make_unique("scene.render"); - auto& taskflow = *runtime->taskflow; - std::pmr::unordered_set renderables{resource}; - render_dependencies.for_each_bound( - [&](Renderable* renderable, Renderable::Private&) { - renderables.insert(renderable); - } - ); - taskflow.clear(); - struct Render_Tasks { - Task_Node entry; - Task_Node exit; - }; - std::pmr::unordered_map render_tasks{resource}; - /* - * Render_Graph_Tag 是对象间执行顺序的唯一权威来源。对象内部节点和 - * 对其他 Private 图的直接访问都属于具体实现,公共层不定义阶段端口。 - */ - for (auto* renderable : renderables) { - Root* root = renderable; - auto* data = render_dependencies.private_data(root); - if (!data) continue; - const auto task_prefix = std::string(data->business_name()) + ".render"; - auto render_if = taskflow.add_condition(task_prefix + ".condition", [data, root] { - const bool dirty = - double_buffer::detail::Internal_Access::dirty(root); - data->render_graph_executed = data->should_render(root, dirty); - return data->render_graph_executed ? 0 : 1; - }); - Task_Node render_run = data->graph - ? taskflow.compose(task_prefix + ".graph", *data->graph) - : taskflow.add(task_prefix + ".empty", [] {}); - auto render_done = taskflow.add(task_prefix + ".complete", [data, root] { - if (data->render_graph_executed) - (void)double_buffer::detail::Internal_Access::take_dirty(root); - }); - const auto identify_renderable_task = [data](Task_Node& node) { - node.describe("owner_kind", "renderable"); - if (!data->diagnostic_component_id.empty()) - node.describe("owner_component", data->diagnostic_component_id); - }; - identify_renderable_task(render_if); - identify_renderable_task(render_run); - identify_renderable_task(render_done); - render_if.precede(render_run); - render_if.precede(render_done); - if (data->extension.empty()) { - render_run.precede(render_done); - } - else { - auto extension = taskflow.compose( - task_prefix + ".extension", data->extension); - identify_renderable_task(extension); - render_run.precede(extension); - extension.precede(render_done); - } - render_tasks.emplace(root, Render_Tasks{render_if, render_done}); - } - auto connect_dependencies = [&](const auto& dependency_graph) { - dependency_graph.for_each( - [&](const Dependency_Graph::Node& dependency_node) { - if (!dependency_graph.private_data(dependency_node)) return; - auto target = render_tasks.find(dependency_node.object.get()); - if (target == render_tasks.end()) return; - std::pmr::unordered_set visited{resource}; - std::pmr::vector> pending{ - resource}; - for (const auto dependency : dependency_node.dependencies) - pending.push_back(dependency); - while (!pending.empty()) { - const auto dependency = pending.back(); - pending.pop_back(); - if (!visited.insert(dependency->object.get()).second) - continue; - if (dependency_graph.private_data(*dependency)) { - auto source = render_tasks.find( - dependency->object.get()); - if (source != render_tasks.end()) { - source->second.exit.precede(target->second.entry); - } - continue; - } - for (const auto next : dependency->dependencies) - pending.push_back(next); - } - } - ); - }; - connect_dependencies(render_dependencies); - double_buffer::detail::Internal_Access::access_pending_dependency_graph(object, - [](auto& graph_state) { if (graph_state.dirty()) graph_state.take_dirty(); }); -} -} diff --git a/kernel/src/kernel/visual_common.hpp b/kernel/src/kernel/visual_common.hpp deleted file mode 100644 index 07e13bf..0000000 --- a/kernel/src/kernel/visual_common.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include -namespace aethera { -/* 与绘制维度和后端无关的非预乘 RGBA 颜色。 */ -struct Color { - std::uint8_t red{}; /* 红色通道,范围为 0 到 255。 */ - std::uint8_t green{}; /* 绿色通道,范围为 0 到 255。 */ - std::uint8_t blue{}; /* 蓝色通道,范围为 0 到 255。 */ - std::uint8_t alpha{255}; /* 不透明度通道,范围为 0 到 255。 */ - [[nodiscard]] static constexpr Color transparent() noexcept; - [[nodiscard]] static constexpr Color black() noexcept; - [[nodiscard]] static constexpr Color white() noexcept; - [[nodiscard]] static constexpr Color red_color() noexcept; - [[nodiscard]] static constexpr Color green_color() noexcept; - [[nodiscard]] static constexpr Color yellow() noexcept; - bool operator==(const Color&) const = default; -}; -using Duration_Milliseconds = std::uint64_t; -/* 返回进程内单调时钟的毫秒刻度,只用于计算持续时间。 */ -[[nodiscard]] Duration_Milliseconds monotonic_milliseconds() noexcept; -/* 一天内的墙钟时间;不携带时区和日期。 */ -struct Time_Of_Day { - std::int64_t milliseconds{-1}; /* 自当天零点起的毫秒数;负值表示无效。 */ - [[nodiscard]] bool valid() const noexcept; - bool operator==(const Time_Of_Day&) const = default; -}; -} -#include "visual_common.ipp" diff --git a/kernel/src/kernel/visual_common.ipp b/kernel/src/kernel/visual_common.ipp deleted file mode 100644 index d8c8828..0000000 --- a/kernel/src/kernel/visual_common.ipp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include -namespace aethera { -constexpr Color Color::transparent() noexcept { return {0, 0, 0, 0}; } -constexpr Color Color::black() noexcept { return {0, 0, 0, 255}; } -constexpr Color Color::white() noexcept { return {255, 255, 255, 255}; } -constexpr Color Color::red_color() noexcept { return {255, 0, 0, 255}; } -constexpr Color Color::green_color() noexcept { return {0, 255, 0, 255}; } -constexpr Color Color::yellow() noexcept { return {255, 255, 0, 255}; } -inline Duration_Milliseconds monotonic_milliseconds() noexcept { return static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); } -inline bool Time_Of_Day::valid() const noexcept { return milliseconds >= 0 && milliseconds < 24LL * 60LL * 60LL * 1000LL; } -} diff --git a/kernel/src/test/Axis_Formatting_Test.cpp b/kernel/src/test/Axis_Formatting_Test.cpp deleted file mode 100644 index 3845be6..0000000 --- a/kernel/src/test/Axis_Formatting_Test.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include - -#include -#include -#include - -namespace aethera::plot { -namespace { - -TEST(Axis_Formatting, Time_Scale_Uses_Elapsed_Time_Labels) { - const Axis_Descriptor axis{ - {0.0, 4.0}, Axis_Scale::time, "Time", "s", - 5, 1, true, true, true}; - - const auto ticks = axis_ticks(axis); - ASSERT_EQ(ticks.size(), 5U); - EXPECT_EQ(ticks.front().label, "0 s"); - EXPECT_EQ(ticks[1].label, "1 s"); - EXPECT_EQ(ticks.back().label, "4 s"); -} - -TEST(Axis_Formatting, Time_Scale_Formats_Long_Durations) { - const Axis_Descriptor axis{ - {0.0, 4'000.0}, Axis_Scale::time, "Elapsed", "s", - 5, 0, true, true, true}; - - const auto ticks = axis_ticks(axis); - ASSERT_FALSE(ticks.empty()); - EXPECT_EQ(ticks.front().label, "0 s"); - EXPECT_EQ(ticks.back().label, "1:06:40"); -} - -TEST(Axis_Formatting, Hertz_Unit_Uses_Frequency_Prefixes) { - const Axis_Descriptor axis{ - {10.0, 20'000.0}, Axis_Scale::logarithmic, "Frequency", "Hz", - 5, 0, true, true, true}; - - const auto ticks = axis_ticks(axis); - const std::vector labels = [&] { - std::vector result; - result.reserve(ticks.size()); - for (const auto& tick : ticks) result.push_back(tick.label); - return result; - }(); - EXPECT_NE(std::ranges::find(labels, "10 Hz"), labels.end()); - EXPECT_NE(std::ranges::find(labels, "1 kHz"), labels.end()); - EXPECT_NE(std::ranges::find(labels, "20 kHz"), labels.end()); -} - -} // namespace -} // namespace aethera::plot diff --git a/kernel/src/test/Dependency_Graph_Test.cpp b/kernel/src/test/Dependency_Graph_Test.cpp deleted file mode 100644 index eceaf91..0000000 --- a/kernel/src/test/Dependency_Graph_Test.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "double_buffer/model.hpp" -#include -namespace { -struct Graph_Tag {}; -struct Node_Object : double_buffer::Def { - struct Prop : Prev_Prop { int value{}; int other{}; }; - struct State : Prev_State { - int value{}; /* 字段级依赖测试使用的状态值。 */ - int other{}; /* 状态层级依赖测试使用的同层其他值。 */ - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private {}; -}; -using Node = Node_Object; -struct Graph_Object : double_buffer::Def> { - struct Prop : Prev_Prop {}; - struct State : Prev_State { - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private {}; - auto& data_for_test(); -}; -using Graph = Graph_Object; -inline auto& Graph_Object::data_for_test() { - return static_cast&>(*d); -} -template -std::unique_ptr build_object() { - auto result = typename Object_Type::template Builder{}.build(); - if (!result) std::terminate(); - return std::move(result).value(); -} -static_assert(double_buffer::detail::Dependency_Object); -static_assert(double_buffer::detail::Dependency_Object); -static_assert(double_buffer::detail::State_Dependency_Source); -static_assert(double_buffer::detail::State_Layer_Dependency_Source); -static_assert(double_buffer::detail::Prop_Layer_Dependency_Source); -} -TEST(dependency_graph_storage, edited_graph_commits_and_stays_synchronized) { - auto first = build_object(); - auto second = build_object(); - auto graph = build_object(); - auto result = graph->edit_dependency_graph( - [&](auto& editor) { - editor.add(first.get()); - editor.add(second.get()); - editor.template add_dependency<&Node_Object::State::value>(second.get(), first.get()); - } - ); - ASSERT_TRUE(result.has_value()); - auto& buffer = graph->data_for_test().dependency_graph_storage.get(); - auto pending_before = buffer.pending; - auto current_before = buffer.current; - graph->advance(); - EXPECT_EQ(buffer.current, pending_before); - EXPECT_EQ(buffer.pending, current_before); - EXPECT_EQ(buffer.current->size(), 2u); - EXPECT_EQ(buffer.pending->size(), 2u); - EXPECT_TRUE(buffer.current->depends_on(second.get(), first.get())); - EXPECT_TRUE(buffer.pending->depends_on(second.get(), first.get())); -} -TEST(dependency_graph_storage, unchanged_graph_does_not_advance_again) { - auto node = build_object(); - auto graph = build_object(); - ASSERT_TRUE(graph->edit_dependency_graph([&](auto& editor) { editor.add(node.get()); }).has_value()); - graph->advance(); - auto& buffer = graph->data_for_test().dependency_graph_storage.get(); - auto pending = buffer.pending; - auto current = buffer.current; - graph->advance(); - EXPECT_EQ(buffer.pending, pending); - EXPECT_EQ(buffer.current, current); -} -TEST(dependency_graph, topological_order_and_state_dirty_propagation) { - auto source = build_object(); - auto target = build_object(); - auto graph = build_object(); - ASSERT_TRUE(graph->edit_dependency_graph( - [&](auto& editor) { - editor.add(source.get()); - editor.add(target.get()); - editor.template add_dependency<&Node_Object::State::value>(target.get(), source.get()); - } - ).has_value()); - graph->advance(); - auto view = graph->current_dependency_graph(); - std::vector order; - auto result = view.for_each_topological_view( - [&](const auto&, const auto& node) { - order.push_back(node.object); - } - ); - ASSERT_TRUE(result.has_value()); - ASSERT_EQ(order.size(), 2u); - EXPECT_EQ(order[0], source.get()); - EXPECT_EQ(order[1], target.get()); - source->update_state<&Node_Object::State::value>(31); - EXPECT_TRUE(target->dirty()); -} -TEST(dependency_graph, whole_object_dependency_only_controls_topology) { - auto source = build_object(); - auto target = build_object(); - auto graph = build_object(); - ASSERT_TRUE(graph->edit_dependency_graph([&](auto& editor) { editor.add_dependency(target.get(), source.get()); }).has_value()); - graph->advance(); - std::vector order; - ASSERT_TRUE(graph->current_dependency_graph().for_each_topological_view([&](const auto&, const auto& node) { order.push_back(node.object); }).has_value()); - ASSERT_EQ(order.size(), 2u); - EXPECT_EQ(order[0], source.get()); - EXPECT_EQ(order[1], target.get()); - source->set<&Node_Object::Prop::value>(1); - source->update_state<&Node_Object::State::value>(1); - source->mark_dirty(); - EXPECT_FALSE(target->dirty()); -} -TEST(dependency_graph, state_dependency_granularity_is_selectable) { - auto member_source = build_object(); - auto member_target = build_object(); - auto layer_source = build_object(); - auto layer_target = build_object(); - auto graph = build_object(); - ASSERT_TRUE(graph->edit_dependency_graph( - [&](auto& editor) { - editor.add(member_source.get()); - editor.add(member_target.get()); - editor.add(layer_source.get()); - editor.add(layer_target.get()); - editor.template add_dependency<&Node_Object::State::value>(member_target.get(), member_source.get()); - editor.template add_state_dependency(layer_target.get(), layer_source.get()); - } - ).has_value()); - graph->advance(); - member_source->update_state<&Node_Object::State::other>(1); - EXPECT_FALSE(member_target->dirty()); - member_source->update_state<&Node_Object::State::value>(2); - EXPECT_TRUE(member_target->dirty()); - layer_source->update_state<&Node_Object::State::other>(3); - EXPECT_TRUE(layer_target->dirty()); -} -TEST(dependency_graph, prop_dependency_granularity_is_selectable) { - auto member_source = build_object(); - auto member_target = build_object(); - auto layer_source = build_object(); - auto layer_target = build_object(); - auto graph = build_object(); - ASSERT_TRUE(graph->edit_dependency_graph([&](auto& editor) { - editor.add(member_source.get()); - editor.add(member_target.get()); - editor.add(layer_source.get()); - editor.add(layer_target.get()); - editor.template add_prop_dependency<&Node_Object::Prop::value>(member_target.get(), member_source.get()); - editor.template add_prop_dependency(layer_target.get(), layer_source.get()); - }).has_value()); - graph->advance(); - member_source->set<&Node_Object::Prop::other>(1); - EXPECT_FALSE(member_target->dirty()); - member_source->set<&Node_Object::Prop::value>(2); - EXPECT_TRUE(member_target->dirty()); - layer_source->set<&Node_Object::Prop::other>(3); - EXPECT_TRUE(layer_target->dirty()); -} -TEST(dependency_graph, cycle_is_rejected_before_pending_graph_commit) { - auto first = build_object(); - auto second = build_object(); - auto graph = build_object(); - auto result = graph->edit_dependency_graph( - [&](auto& editor) { - editor.add(first.get()); - editor.add(second.get()); - editor.template add_dependency<&Node_Object::State::value>(first.get(), second.get()); - editor.template add_dependency<&Node_Object::State::value>(second.get(), first.get()); - } - ); - ASSERT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), double_buffer::Dependency_Graph_Error::cycle); - EXPECT_TRUE(graph->pending_dependency_graph().empty()); -} -TEST(dependency_graph_lifetime, referenced_object_can_be_destroyed_before_graph_owner) { - auto graph = build_object(); - auto node = build_object(); - ASSERT_TRUE(graph->edit_dependency_graph([&](auto& editor) { editor.add(node.get()); }).has_value()); - graph->advance(); - ASSERT_EQ(graph->current_dependency_graph().size(), 1u); - node.reset(); - EXPECT_TRUE(graph->current_dependency_graph().empty()); - EXPECT_TRUE(graph->pending_dependency_graph().empty()); -} diff --git a/kernel/src/test/Event_Timeline_Test.cpp b/kernel/src/test/Event_Timeline_Test.cpp deleted file mode 100644 index 550deb9..0000000 --- a/kernel/src/test/Event_Timeline_Test.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - -namespace aethera::tests { - -struct Event_Test_Point { - double x{}; - double y{}; -}; - -TEST(Event_Timeline, Explicit_Producer_Time_Is_Authoritative) { - constexpr Event_Timeline_Time occurred_at{16'666'667}; - const Basic_Pointer_Event event{ - Event_Type::pointer_move, occurred_at}; - EXPECT_EQ(event.occurred_at, occurred_at); -} - -TEST(Event_Timeline, Zero_Is_A_Valid_Producer_Timeline_Origin) { - const Basic_Wheel_Event event{Event_Timeline_Time{0}}; - EXPECT_EQ(event.occurred_at.nanoseconds, 0U); -} - -TEST(Event_Timeline, Internal_Event_Uses_Its_Creation_Timeline) { - const Event event{Event_Type::show}; - EXPECT_NE(event.occurred_at.nanoseconds, 0U); -} - -} diff --git a/kernel/src/test/Frame_Policy_Lifecycle_Test.cpp b/kernel/src/test/Frame_Policy_Lifecycle_Test.cpp deleted file mode 100644 index d066e2e..0000000 --- a/kernel/src/test/Frame_Policy_Lifecycle_Test.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include "Frame_Policy/Fixed_Rate_Frame_Policy.hpp" -#include "Frame_Policy/Manual_Frame_Policy.hpp" -#include "Frame_Policy/Maximum_Rate_Frame_Policy.hpp" -#include -#include -#include -#include -#include - -namespace { - -struct Test_Frame final : aethera::Render_Frame { - explicit Test_Frame(aethera::Frame_Identity identity) - : Render_Frame(identity) {} -}; - -struct Test_Scene final : aethera::Render_Frame_Scene { - struct Borrow { - aethera::Render_Frame* frame{}; /* Policy 拥有、Scene 临时借用。 */ - aethera::Render_Frame_Callbacks callbacks{}; /* 对应借用帧的直接回调。 */ - }; - std::array borrows{}; - std::atomic_size_t submitted{}; - bool submit_during_render{true}; - bool complete_during_render{}; - - aethera::Render_Frame_Result render_frame( - aethera::not_null frame, - aethera::Render_Frame_Callbacks callbacks) override { - const auto index = submitted.load(std::memory_order_relaxed); - borrows[index] = Borrow{frame.get(), std::move(callbacks)}; - submitted.store(index + 1, std::memory_order_release); - if (submit_during_render && borrows[index].callbacks.submitted) - borrows[index].callbacks.submitted(frame, true); - if (complete_during_render && borrows[index].callbacks.completed) - borrows[index].callbacks.completed(frame); - return aethera::Render_Frame_Result::submitted; - } -}; - -struct Policy_Probe { - Test_Scene scene{}; - std::atomic_size_t created{}; - std::atomic_size_t prepared{}; - std::atomic_size_t published{}; - std::atomic_bool failed{}; - - aethera::Frame_Policy::Dependencies dependencies() { - return { - .scene = &scene, - .create_frame = [this] { - created.fetch_add(1, std::memory_order_relaxed); - return std::make_unique(aethera::Frame_Identity{}); - }, - .prepare_frame = [this]( - aethera::not_null, - const aethera::Frame_Production&) { - prepared.fetch_add(1, std::memory_order_release); - }, - .publish_frame = [this]( - aethera::not_null, - const aethera::Frame_Production&) { - published.fetch_add(1, std::memory_order_release); - const auto now = std::chrono::steady_clock::now(); - return aethera::Frame_Publication_Dispatch{ - .started_at = now, - .completed_at = now, - .succeeded = true}; - }, - .retain_owner = [] { return std::make_shared(1); }, - .report_failure = [this](std::exception_ptr) { - failed.store(true, std::memory_order_release); - }, - .frame_capacity = 3}; - } -}; - -template -void cooperate_until(Predicate predicate) { - aethera::Task_Graph driver{"test.frame_policy.cooperate"}; - driver.add("cooperate", [predicate = std::move(predicate)]() mutable { - aethera::Task_Graph::corun_until(std::move(predicate)); - }); - static_cast(aethera::detail::run_taskflow(driver)); -} - -[[nodiscard]] aethera::Frame_Policy::State policy_state( - const aethera::Frame_Policy& policy) { - aethera::Frame_Policy::State result{}; - policy.access_state( - [&](const aethera::Frame_Policy::State& state) { result = state; }); - return result; -} - -TEST(frame_policy_lifecycle, concrete_policy_types_expose_only_their_mode) { - Policy_Probe manual_probe; - Policy_Probe fixed_probe; - Policy_Probe maximum_probe; - auto manual = std::make_shared( - manual_probe.dependencies()); - auto fixed = std::make_shared( - fixed_probe.dependencies(), 47.5); - auto maximum = std::make_shared( - maximum_probe.dependencies()); - - EXPECT_EQ(manual->type(), aethera::Frame_Policy_Type::manual); - EXPECT_EQ(fixed->type(), aethera::Frame_Policy_Type::fixed_rate); - EXPECT_EQ(maximum->type(), aethera::Frame_Policy_Type::maximum_rate); - EXPECT_DOUBLE_EQ(fixed->frame_rate(), 47.5); - EXPECT_THROW(fixed->set_frame_rate(0.0), std::invalid_argument); - EXPECT_EQ(manual_probe.created.load(), 3u); - EXPECT_EQ(fixed_probe.created.load(), 3u); - EXPECT_EQ(maximum_probe.created.load(), 3u); -} - -TEST(frame_policy_lifecycle, manual_policy_owns_reuses_and_publishes_frame) { - aethera::initialize_runtime({.workers = 4}); - Policy_Probe probe; - probe.scene.complete_during_render = true; - auto policy = std::make_shared( - probe.dependencies()); - policy->start(); - policy->request_frame(aethera::Frame_Request{ - .issued_at = std::chrono::steady_clock::now(), - .time_milliseconds = 1.0}); - cooperate_until([&] { - return policy_state(*policy).observation.published_frame_count == 1; - }); - - aethera::Frame_Policy_State state{}; - policy->access_state( - [&](const aethera::Frame_Policy::State& current) { - state = current.observation; - }); - EXPECT_EQ(state.completed_frame_count, 1u); - EXPECT_EQ(state.published_frame_count, 1u); - EXPECT_EQ(policy->read_state(). - observation.active_frame_count, 0u); - EXPECT_FALSE(probe.failed.load(std::memory_order_acquire)); -} - -TEST(frame_policy_lifecycle, - maximum_rate_fills_three_slots_and_stop_callback_waits_for_all_frames) { - aethera::initialize_runtime({.workers = 4}); - Policy_Probe probe; - auto policy = std::make_shared( - probe.dependencies()); - policy->start(); - cooperate_until([&] { - return probe.scene.submitted.load(std::memory_order_acquire) == 3; - }); - EXPECT_EQ(policy->read_state(). - observation.active_frame_count, 3u); - - std::atomic_bool stopped{}; - policy->stop([&] { stopped.store(true, std::memory_order_release); }); - cooperate_until([&] { - return policy_state(*policy).lifecycle == - aethera::Frame_Policy_Lifecycle::stopping; - }); - EXPECT_FALSE(stopped.load(std::memory_order_acquire)); - for (std::size_t index = 0; index < 3; ++index) { - auto& borrow = probe.scene.borrows[index]; - ASSERT_NE(borrow.frame, nullptr); - borrow.callbacks.completed(aethera::not_null{borrow.frame}); - } - cooperate_until([&] { - return stopped.load(std::memory_order_acquire) && - policy_state(*policy).lifecycle == - aethera::Frame_Policy_Lifecycle::stopped; - }); - EXPECT_TRUE(stopped.load(std::memory_order_acquire)); - EXPECT_EQ(policy_state(*policy).observation.active_frame_count, 0u); - EXPECT_EQ(probe.published.load(std::memory_order_acquire), 3u); -} - -TEST(frame_policy_lifecycle, - burst_requests_are_coalesced_while_scene_owns_cpu_production) { - aethera::initialize_runtime({.workers = 4}); - Policy_Probe probe; - probe.scene.submit_during_render = false; - auto policy = std::make_shared( - probe.dependencies()); - policy->start(); - policy->request_frame(aethera::Frame_Request{ - .issued_at = std::chrono::steady_clock::now(), - .time_milliseconds = 1.0}); - cooperate_until([&] { - return probe.scene.submitted.load(std::memory_order_acquire) == 1; - }); - - for (std::uint64_t index = 0; index < 64; ++index) { - policy->request_frame(aethera::Frame_Request{ - .issued_at = std::chrono::steady_clock::now(), - .sequence = index + 2, - .time_milliseconds = static_cast(index + 2)}); - } - std::atomic_bool burst_dispatched{}; - aethera::schedule_task("test.frame_policy.burst.dispatched", [&] { - burst_dispatched.store(true, std::memory_order_release); - }); - cooperate_until([&] { - return burst_dispatched.load(std::memory_order_acquire); - }); - EXPECT_EQ(probe.scene.submitted.load(std::memory_order_acquire), 1u); - EXPECT_EQ(probe.prepared.load(std::memory_order_acquire), 1u); - - auto& first = probe.scene.borrows[0]; - ASSERT_NE(first.frame, nullptr); - first.callbacks.submitted(aethera::not_null{first.frame}, false); - first.callbacks.completed(aethera::not_null{first.frame}); - cooperate_until([&] { - return probe.scene.submitted.load(std::memory_order_acquire) == 2; - }); - EXPECT_EQ(probe.prepared.load(std::memory_order_acquire), 2u); - - auto& second = probe.scene.borrows[1]; - ASSERT_NE(second.frame, nullptr); - second.callbacks.submitted(aethera::not_null{second.frame}, false); - second.callbacks.completed(aethera::not_null{second.frame}); - cooperate_until([&] { - return probe.published.load(std::memory_order_acquire) == 2; - }); - std::atomic_bool stopped{}; - policy->stop([&] { stopped.store(true, std::memory_order_release); }); - cooperate_until([&] { return stopped.load(std::memory_order_acquire); }); - EXPECT_TRUE(stopped.load(std::memory_order_acquire)); -} - -TEST(frame_policy_lifecycle, idle_stop_calls_completion_once) { - Policy_Probe probe; - auto policy = std::make_shared( - probe.dependencies()); - std::atomic_size_t calls{}; - policy->stop([&] { calls.fetch_add(1, std::memory_order_release); }); - cooperate_until([&] { return calls.load(std::memory_order_acquire) == 1; }); - EXPECT_EQ(calls.load(std::memory_order_acquire), 1u); - EXPECT_EQ(policy_state(*policy).lifecycle, - aethera::Frame_Policy_Lifecycle::stopped); -} - -} diff --git a/kernel/src/test/Task_Runtime_Configuration_Test.cpp b/kernel/src/test/Task_Runtime_Configuration_Test.cpp deleted file mode 100644 index 8f672c2..0000000 --- a/kernel/src/test/Task_Runtime_Configuration_Test.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include - -#include "render_common.hpp" - -namespace aethera::test { - -TEST(Task_Runtime_Configuration_Test, Watchdog_Default_Action_Is_Warning) { - const Task_Runtime_Configuration configuration{}; - - EXPECT_EQ(configuration.worker_overrun_action, Task_Overrun_Action::warning); - EXPECT_EQ(configuration.worker_occupation_limit, std::chrono::milliseconds{100}); -} - -} // namespace aethera::test diff --git a/kernel/src/test/main.cpp b/kernel/src/test/main.cpp deleted file mode 100644 index 51aa7e6..0000000 --- a/kernel/src/test/main.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/kernel/src/test/model_test.cpp b/kernel/src/test/model_test.cpp deleted file mode 100644 index 72832d8..0000000 --- a/kernel/src/test/model_test.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "double_buffer/mechanism.hpp" -#include -namespace { -struct Value { - int first{}; - int second{}; - bool operator==(const Value&) const = default; -}; -} -TEST(double_buffer, advance_only_swaps_sides) { - double_buffer::Double_Buffer buffer; - buffer.pending->first = 1; - buffer.current->first = 2; - auto pending = buffer.pending; - auto current = buffer.current; - buffer.advance(); - EXPECT_EQ(buffer.pending, current); - EXPECT_EQ(buffer.current, pending); - EXPECT_EQ(buffer.pending->first, 2); - EXPECT_EQ(buffer.current->first, 1); -} -TEST(commit_publish_double_buffer, commit_preserves_incremental_pending_state) { - double_buffer::Commit_Double_Buffer buffer; - buffer.pending->first = 7; - buffer.advance(); - EXPECT_EQ(buffer.current->first, 7); - EXPECT_EQ(buffer.pending->first, 7); - buffer.pending->second = 9; - buffer.advance(); - EXPECT_EQ(buffer.current->first, 7); - EXPECT_EQ(buffer.current->second, 9); - EXPECT_EQ(*buffer.pending, *buffer.current); -} -TEST(commit_publish_double_buffer, publish_preserves_incremental_current_prop) { - double_buffer::Publish_Double_Buffer buffer; - buffer.current->first = 11; - buffer.advance(); - EXPECT_EQ(buffer.pending->first, 11); - EXPECT_EQ(buffer.current->first, 11); - buffer.current->second = 13; - buffer.advance(); - EXPECT_EQ(buffer.pending->first, 11); - EXPECT_EQ(buffer.pending->second, 13); - EXPECT_EQ(*buffer.pending, *buffer.current); -} diff --git a/kernel/src/test/object_test.cpp b/kernel/src/test/object_test.cpp deleted file mode 100644 index 9b993f2..0000000 --- a/kernel/src/test/object_test.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "double_buffer/model.hpp" -#include -#include -namespace { -struct Object_Buffer_Tag {}; -struct Stream_Buffer_Tag {}; -struct Stream_Object : double_buffer::Def< - Stream_Object, double_buffer::Root, - double_buffer::Mpmc_Triple_Buffer> { - struct Prop : Prev_Prop {}; - struct State : Prev_State { bool operator==(const State&) const = default; }; - struct Private : Prev_Private {}; -}; -using Stream = Stream_Object; -struct Test_Object : double_buffer::Def> { - struct Prop : Prev_Prop { - int first{}; - int second{}; - }; - struct State : Prev_State { - int first{}; - int second{}; - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private {}; - auto& data_for_test(); -}; -using Object = Test_Object; -inline auto& Test_Object::data_for_test() { - return static_cast&>(*d); -} -struct Derived_Object : double_buffer::Def { - struct Prop : Prev_Prop {}; - struct State : Prev_State { - int derived{}; - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private {}; -}; -using Derived = Derived_Object; -struct Lifetime_Object : double_buffer::Def { - struct Prop : Prev_Prop {}; - struct State : Prev_State { - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private { - inline static int destruction_count{}; /* 测试最终 Private 是否经公共虚析构链释放。 */ - ~Private() override { - ++destruction_count; - } - }; -}; -using Lifetime = Lifetime_Object; -template -std::unique_ptr build_object() { - auto result = typename Object_Type::template Builder{}.build(); - if (!result) std::terminate(); - return std::move(result).value(); -} -} -TEST(object_lifetime, root_destroys_attached_private_through_virtual_base) { - Lifetime_Object::Private::destruction_count = 0; - { - auto object = build_object(); - EXPECT_EQ(Lifetime_Object::Private::destruction_count, 0); - } - EXPECT_EQ(Lifetime_Object::Private::destruction_count, 1); -} -TEST(object_builder, build_attaches_private_and_commits_staged_values) { - Object::Builder builder; - builder.set(&Test_Object::Prop::first, 29).set(31); - auto result = builder.build(); - ASSERT_TRUE(result.has_value()); - auto object = std::move(result).value(); - EXPECT_EQ(object->data_for_test().current->first, 29); - EXPECT_EQ(object->data_for_test().pending->first, 29); - EXPECT_EQ(object->current_buffer(), 31); - EXPECT_EQ(object->pending_buffer(), 31); -} -TEST(object_buffer, state_commits_and_keeps_incremental_baseline) { - auto object = build_object(); - object->update_state<&Test_Object::State::first>(3); - object->advance(); - EXPECT_EQ(object->data_for_test().state.current->first, 3); - EXPECT_EQ(object->data_for_test().state.pending->first, 3); - object->update_state<&Test_Object::State::second>(5); - object->advance(); - EXPECT_EQ(object->data_for_test().state.current->first, 3); - EXPECT_EQ(object->data_for_test().state.current->second, 5); -} -TEST(object_buffer, prop_commits_and_keeps_incremental_baseline) { - auto object = build_object(); - object->set<&Test_Object::Prop::first>(17); - EXPECT_EQ(object->data_for_test().current->first, 0); - EXPECT_EQ(object->data_for_test().pending->first, 17); - object->advance(); - EXPECT_EQ(object->data_for_test().pending->first, 17); - EXPECT_EQ(object->data_for_test().current->first, 17); - object->set<&Test_Object::Prop::second>(19); - object->advance(); - EXPECT_EQ(object->data_for_test().pending->first, 17); - EXPECT_EQ(object->data_for_test().pending->second, 19); -} -TEST(object_buffer, mpmc_triple_buffer_rotates_three_queue_roles) { - auto object = build_object(); - object->submit_stream(11); - object->submit_stream(13); - object->exchange_stream(); - object->access_rendering_stream([](std::span rendering) { - ASSERT_EQ(rendering.size(), 2); - EXPECT_EQ(rendering[0], 11); - EXPECT_EQ(rendering[1], 13); - }); - EXPECT_EQ(object->access_query_stream( - [](std::span query) { return query.size(); }), 0); - object->exchange_stream(); - object->access_query_stream([](std::span query) { - ASSERT_EQ(query.size(), 2); - EXPECT_EQ(query[0], 11); - EXPECT_EQ(query[1], 13); - }); -} -TEST(object_buffer, mpmc_triple_buffer_accepts_multiple_producers) { - auto object = build_object(); - std::vector producers; - for (int producer = 0; producer < 4; ++producer) - producers.emplace_back([&, producer] { - for (int index = 0; index < 100; ++index) - object->submit_stream(producer * 100 + index); - }); - for (auto& producer : producers) producer.join(); - object->exchange_stream(); - EXPECT_EQ(object->access_rendering_stream( - [](std::span rendering) { return rendering.size(); }), 400); - EXPECT_EQ(object->access_query_stream( - [](std::span query) { return query.size(); }), 0); - object->exchange_stream(); - EXPECT_EQ(object->access_query_stream( - [](std::span query) { return query.size(); }), 400); -} -TEST(object_buffer, mpmc_triple_buffer_accumulates_published_history_with_capacity) { - auto object = build_object(); - object->submit_stream(11); - object->submit_stream(13); - object->exchange_stream(); - object->accumulate_stream(3); - object->submit_stream(17); - object->submit_stream(19); - object->exchange_stream(); - object->accumulate_stream(3); - object->access_query_stream([](std::span query) { - ASSERT_EQ(query.size(), 2); - EXPECT_EQ(query[0], 11); - EXPECT_EQ(query[1], 13); - }); - object->access_rendering_stream([](std::span rendering) { - ASSERT_EQ(rendering.size(), 3); - EXPECT_EQ(rendering[0], 13); - EXPECT_EQ(rendering[1], 17); - EXPECT_EQ(rendering[2], 19); - }); -} -TEST(state_tag, callback_publishes_only_requested_layer) { - auto object = build_object(); - int calls = 0; - object->set_state_callback( - [&](const auto& state) { - ++calls; - EXPECT_EQ(state.first, 23); - EXPECT_EQ(object->read_state().first, 23); - } - ); - object->update_state<&Test_Object::State::first>(23); - object->publish_state(); - EXPECT_EQ(calls, 1); -} -static_assert(requires(Object& object) { - object.template access_state([](const auto&) {}); -}); -static_assert(std::same_as< - decltype(std::declval().template read_state()), - const Test_Object::State& ->); -struct Missing_State_Tag {}; -static_assert(!double_buffer::detail::State_Tag_In); -TEST(state_tag, inherited_tags_remain_independently_addressable) { - auto object = build_object(); - int base_calls = 0; - int derived_calls = 0; - object->set_state_callback([&](const auto&) { ++base_calls; }); - object->set_state_callback([&](const auto&) { ++derived_calls; }); - object->publish_state(); - EXPECT_EQ(base_calls, 1); - EXPECT_EQ(derived_calls, 0); - object->publish_state(); - EXPECT_EQ(base_calls, 1); - EXPECT_EQ(derived_calls, 1); -} -TEST(state_tag, committed_layer_is_directly_readable_by_tag) { - auto object = build_object(); - object->update_state<&Test_Object::State::first>(41); - object->update_state<&Derived_Object::State::derived>(43); - object->advance(); - EXPECT_EQ(object->read_state().first, 41); - EXPECT_EQ(object->read_state().derived, 43); -} -static_assert(double_buffer::detail::State_Tag_In); -static_assert(double_buffer::detail::State_Tag_In); -static_assert(double_buffer::detail::State_Tag_In); -static_assert(std::same_as< - decltype(std::declval>().template get()), - Test_Object::State& ->); -static_assert(std::same_as< - decltype(std::declval>().template get()), - const Derived_Object::State& ->); -TEST(state_tag, state_access_selects_mutable_and_const_layers_by_tag) { - Derived::State state; - double_buffer::State_Access states{state}; - states.get().first = 31; - states.get().derived = 47; - double_buffer::State_Access base_states = states; - EXPECT_EQ(base_states.get().first, 31); - const auto& const_state = state; - double_buffer::State_Access current_states{const_state}; - EXPECT_EQ(current_states.get().first, 31); - EXPECT_EQ(current_states.get().derived, 47); -} -TEST(state_tag, state_chain_keeps_default_equality_usable) { - Test_Object::State first; - Test_Object::State second; - EXPECT_TRUE(first == second); - second.first = 1; - EXPECT_FALSE(first == second); -} -static_assert(std::same_as< - decltype(std::declval>().template get()), - Test_Object::Prop& ->); -static_assert(std::same_as< - decltype(std::declval>().template get()), - Test_Object::Private& ->); -TEST(base_tag, prop_state_and_private_domains_are_independently_addressable) { - Derived::Prop prop; - double_buffer::Prop_Access props{prop}; - props.get().first = 53; - EXPECT_EQ(props.get().first, 53); - struct Test_Private final : Derived_Object::Private { - [[nodiscard]] const void* runtime_current_prop_data() const noexcept override { return nullptr; } - [[nodiscard]] void* runtime_pending_prop_data() noexcept override { return nullptr; } - [[nodiscard]] const void* runtime_current_state_data() const noexcept override { return nullptr; } - [[nodiscard]] void* runtime_pending_state_data() noexcept override { return nullptr; } - } private_data; - double_buffer::Private_Access private_layers{private_data}; - EXPECT_EQ(&private_layers.get(), static_cast(&private_data)); - EXPECT_EQ(&private_layers.get(), static_cast(&private_data)); -} diff --git a/kernel/src/test/render_test.cpp b/kernel/src/test/render_test.cpp deleted file mode 100644 index 78951bf..0000000 --- a/kernel/src/test/render_test.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include "scene.hpp" -#include "Frame_Policy/Frame_Policy.hpp" -#include -#include -#include -#include -namespace { -struct Direct_Renderable : double_buffer::Def { - struct Prop : Prev_Prop {}; - struct State : Prev_State { - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private { - int prepare_calls{}; - int paint_calls{}; - void prepare_data(double_buffer::Attached auto*) { - ++prepare_calls; - } - void paint(double_buffer::Attached auto*) { - ++paint_calls; - } - }; - Private& data_for_test(); -}; -struct Graph_Renderable : double_buffer::Def { - struct Prop : Prev_Prop {}; - struct State : Prev_State { - bool operator==(const State&) const = default; - }; - struct Private : Prev_Private { - int prepare_calls{}; - int paint_calls{}; - int prepare_builds{}; - bool rebuild_prepare{}; - aethera::Task_Graph build_graph(double_buffer::Attached auto*, const Prop&) { - ++prepare_builds; - aethera::Task_Graph graph{"test.render.graph"}; - auto prepare = graph.add("prepare", [this] { ++prepare_calls; }); - auto paint = graph.add("paint", [this] { ++paint_calls; }); - prepare.precede(paint); - return graph; - } - bool should_rebuild_graph(double_buffer::Attached auto*, const Prop&) { - return std::exchange(rebuild_prepare, false); - } - }; - Private& data_for_test(); -}; -using Direct = Direct_Renderable; -using Graph = Graph_Renderable; -using Scene = aethera::Scene; -inline Direct_Renderable::Private& Direct_Renderable::data_for_test() { - return static_cast(*d); -} - -inline Graph_Renderable::Private& Graph_Renderable::data_for_test() { - return static_cast(*d); -} -template -std::unique_ptr build_object() { - auto result = typename Object_Type::template Builder{}.build(); - if (!result) std::terminate(); - return std::move(result).value(); -} -template -void add_renderable(Scene& scene, Renderable* renderable) { - ASSERT_TRUE(scene.edit_dependency_graph([&](auto& prepare) { prepare.add(renderable); }).has_value()); -} -} -TEST(renderable_capability, direct_stages_do_not_allocate_subgraphs) { - aethera::initialize_runtime({.workers = 2}); - auto renderable = build_object(); - auto scene = build_object(); - add_renderable(*scene, renderable.get()); - int prepare_extension_calls{}; - renderable->taskflow().add( - "test.render.extension", [&] { ++prepare_extension_calls; }); - scene->process([](const auto&) {}); - auto& data = renderable->data_for_test(); - auto& base = static_cast(data); - EXPECT_EQ(data.prepare_calls, 1); - EXPECT_EQ(data.paint_calls, 1); - EXPECT_EQ(prepare_extension_calls, 1); - EXPECT_TRUE(base.graph.has_value()); - scene->process([](const auto&) {}); - EXPECT_EQ(data.prepare_calls, 1); - EXPECT_EQ(data.paint_calls, 1); - EXPECT_EQ(prepare_extension_calls, 1); -} -TEST(renderable_capability, graph_stage_builds_lazily_and_rebuilds_inside_condition) { - aethera::initialize_runtime({.workers = 2}); - auto renderable = build_object(); - auto scene = build_object(); - add_renderable(*scene, renderable.get()); - auto& data = renderable->data_for_test(); - auto& base = static_cast(data); - scene->process([](const auto&) {}); - ASSERT_TRUE(base.graph.has_value()); - EXPECT_EQ(data.prepare_builds, 1); - EXPECT_EQ(data.prepare_calls, 1); - scene->process([](const auto&) {}); - EXPECT_EQ(data.prepare_builds, 1); - EXPECT_EQ(data.prepare_calls, 1); - data.rebuild_prepare = true; - renderable->mark_dirty(); - scene->process([](const auto&) {}); - EXPECT_EQ(data.prepare_builds, 2); - EXPECT_EQ(data.prepare_calls, 2); -} -TEST(renderable_state, scene_and_renderable_callbacks_publish_at_stage_boundaries) { - aethera::initialize_runtime({.workers = 2}); - auto renderable = build_object(); - auto scene = build_object(); - add_renderable(*scene, renderable.get()); - int renderable_updates = 0; - int scene_updates = 0; - int runtime_updates = 0; - renderable->set_state_callback([&](const auto& state) { - (void)state; - ++renderable_updates; - }); - scene->set_state_callback([&](const auto& state) { - (void)state; - ++scene_updates; - }); - aethera::set_runtime_state_callback([&](const auto& state) { - ++runtime_updates; - EXPECT_GE(state.completed_taskflow_count, 1u); - EXPECT_GT(state.observed_task_count, 0u); - }); - scene->process([](const auto&) {}); - EXPECT_EQ(renderable_updates, 1); - EXPECT_EQ(scene_updates, 1); - EXPECT_EQ(runtime_updates, 1); - aethera::clear_runtime_state_callback(); -} -TEST(scene_condition, render_order_does_not_create_a_false_data_dependency) { - aethera::initialize_runtime({.workers = 2}); - auto source = build_object(); - auto target = build_object(); - auto scene = build_object(); - ASSERT_TRUE((scene->edit_dependency_graph( - [&](auto& graph) { - graph.add(source.get()); - graph.add(target.get()); - graph.add_dependency(target.get(), source.get()); - }).has_value())); - - scene->process([](const auto&) {}); - EXPECT_EQ(source->data_for_test().prepare_calls, 1); - EXPECT_EQ(target->data_for_test().prepare_calls, 1); - - scene->process([](const auto&) {}); - EXPECT_EQ(source->data_for_test().prepare_calls, 1); - EXPECT_EQ(target->data_for_test().prepare_calls, 1); - - source->mark_dirty(); - scene->process([](const auto&) {}); - EXPECT_EQ(source->data_for_test().prepare_calls, 2); - EXPECT_EQ(target->data_for_test().prepare_calls, 1); -} - -TEST(task_graph_observer, business_dag_and_native_execution_share_node_identity) { - aethera::initialize_runtime({.workers = 2}); - std::atomic_int completed{}; - std::atomic_bool cooperative_ready{}; - aethera::Task_Graph child{"test.visual"}; - child.describe("owner_kind", "renderable") - .describe("owner_component", "spectrum"); - auto prepare = child.add("prepare.samples", [&] { completed.fetch_add(1); }); - auto paint = child.add("paint.visual", [&] { completed.fetch_add(1); }); - prepare.precede(paint); - aethera::Task_Graph frame_graph{"test.frame"}; - auto module_task = frame_graph.compose("spectrum", child); - auto cooperative = frame_graph.add("wait.cooperative", [&] { - aethera::schedule_task("test.cooperative.release", [&] { - cooperative_ready.store(true, std::memory_order_release); - }); - aethera::Task_Graph::corun_until([&] { - return cooperative_ready.load(std::memory_order_acquire); - }); - completed.fetch_add(1); - }); - auto publish = frame_graph.add("publish.state", [&] { completed.fetch_add(1); }); - module_task.precede(cooperative); - cooperative.precede(publish); - - aethera::Render_Frame frame{{41, 73}}; - frame.mark(aethera::Frame_Trace_Marker::paint_started); - frame.mark(aethera::Frame_Trace_Marker::paint_frame_target_started); - frame.mark(aethera::Frame_Trace_Marker::paint_frame_target_finished); - frame.mark(aethera::Frame_Trace_Marker::paint_finished); - frame.record(aethera::Frame_Trace_Measurement::plot_update_ns, 125'000); - frame.request_taskflow_trace(); - ASSERT_TRUE(aethera::detail::begin_taskflow_trace(frame)); - aethera::detail::run_taskflow(frame_graph, frame, "test.scene.paint"); - aethera::detail::finish_taskflow_trace(frame); - - EXPECT_EQ(completed.load(), 4); - const auto trace = frame.take_taskflow_trace(); - ASSERT_EQ(trace.graphs.size(), 1u); - EXPECT_EQ(trace.identity, (aethera::Frame_Identity{41, 73})); - EXPECT_TRUE(std::ranges::contains( - trace.markers, - aethera::Frame_Trace_Marker::paint_frame_target_started, - &aethera::Frame_Trace_Point::marker)); - const auto plot_update = std::ranges::find( - trace.measurements, - aethera::Frame_Trace_Measurement::plot_update_ns, - &aethera::Frame_Trace_Value::measurement); - ASSERT_NE(plot_update, trace.measurements.end()); - EXPECT_EQ(plot_update->value_ns, 125'000u); - EXPECT_EQ(trace.graphs.front().stage, "test.scene.paint"); - EXPECT_TRUE(trace.graphs.front().completed); - EXPECT_EQ(std::ranges::count_if(trace.graphs.front().nodes, [](const auto& node) { - return node.type != "diagnostic"; - }), 5); - EXPECT_EQ(std::ranges::count_if(trace.graphs.front().nodes, [](const auto& node) { - return node.type == "diagnostic"; - }), 5); - const auto module = std::ranges::find( - trace.graphs.front().nodes, "spectrum", - &aethera::Taskflow_Graph_Trace::Node::name); - ASSERT_NE(module, trace.graphs.front().nodes.end()); - const auto child_prepare = std::ranges::find( - trace.graphs.front().nodes, "prepare.samples", - &aethera::Taskflow_Graph_Trace::Node::name); - ASSERT_NE(child_prepare, trace.graphs.front().nodes.end()); - EXPECT_EQ(child_prepare->parent_node_id, module->node_id); - EXPECT_EQ(child_prepare->node_id, "test.frame/spectrum/prepare.samples"); - EXPECT_TRUE(std::ranges::contains( - child_prepare->attributes, - std::pair{"owner_kind", "renderable"})); - EXPECT_TRUE(std::ranges::contains( - child_prepare->attributes, - std::pair{"owner_component", "spectrum"})); - const auto cooperative_node = std::ranges::find( - trace.graphs.front().nodes, "wait.cooperative", - &aethera::Taskflow_Graph_Trace::Node::name); - ASSERT_NE(cooperative_node, trace.graphs.front().nodes.end()); - const auto cooperative_execution = std::ranges::find( - trace.tasks, cooperative_node->native_id, - &aethera::Taskflow_Task_Trace::native_id); - ASSERT_NE(cooperative_execution, trace.tasks.end()); - ASSERT_EQ(cooperative_execution->cooperative_waits.size(), 1u); - EXPECT_GE(cooperative_execution->cooperative_wait_ms, 0.0); - EXPECT_GE(cooperative_execution->cooperative_waits.front().finished_ms, - cooperative_execution->cooperative_waits.front().started_ms); - const auto runtime = aethera::task_runtime_state(); - EXPECT_GE(runtime.cooperative_wait_count, 1U); - EXPECT_LE(runtime.active_worker_count, runtime.worker_count); - EXPECT_EQ(runtime.cooperative_wait_count, - std::accumulate(runtime.workers.begin(), runtime.workers.end(), - std::uint64_t{}, [](std::uint64_t total, - const auto& worker) { - return total + worker.cooperative_wait_count; - })); - - std::unordered_set metadata_ids; - for (const auto& node : trace.graphs.front().nodes) - metadata_ids.insert(node.native_id); - ASSERT_FALSE(trace.tasks.empty()); - for (const auto& task : trace.tasks) - EXPECT_TRUE(metadata_ids.contains(task.native_id)); - const auto child_paint = std::ranges::find( - trace.graphs.front().nodes, "paint.visual", - &aethera::Taskflow_Graph_Trace::Node::name); - const auto publish_node = std::ranges::find( - trace.graphs.front().nodes, "publish.state", - &aethera::Taskflow_Graph_Trace::Node::name); - ASSERT_NE(child_paint, trace.graphs.front().nodes.end()); - ASSERT_NE(publish_node, trace.graphs.front().nodes.end()); - const auto child_paint_execution = std::ranges::find( - trace.tasks, child_paint->native_id, - &aethera::Taskflow_Task_Trace::native_id); - const auto publish_execution = std::ranges::find( - trace.tasks, publish_node->native_id, - &aethera::Taskflow_Task_Trace::native_id); - ASSERT_NE(child_paint_execution, trace.tasks.end()); - ASSERT_NE(publish_execution, trace.tasks.end()); - EXPECT_NEAR(publish_execution->ready_ms, - child_paint_execution->finished_ms, 0.05); - EXPECT_LT(publish_execution->queue_wait_ms, 1.0); - for (const auto name : {"taskflow.executor.finalize", "taskflow.runtime.bookkeeping", - "taskflow.runtime.publish_state", "taskflow.observer.unbind_frame", - "taskflow.graph.finish"}) { - const auto diagnostic = std::ranges::find( - trace.graphs.front().nodes, name, &aethera::Taskflow_Graph_Trace::Node::name); - ASSERT_NE(diagnostic, trace.graphs.front().nodes.end()); - EXPECT_NE(std::ranges::find(trace.tasks, diagnostic->native_id, - &aethera::Taskflow_Task_Trace::native_id), - trace.tasks.end()); - } -}