Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
scheduler.hpp
Go to the documentation of this file.
1#pragma once
2
8
9#include <atomic>
10#include <cstddef>
11#include <functional>
12#include <optional>
13#include <vector>
14
15namespace helios::app {
16
17class App;
18
19/**
20 * @brief Orchestrates the main sub-app schedule loop and sub-app updates.
21 * @details Each frame runs the main sub-app @ref kUpdateStage, then @ref
22 * kExtractStage, then sub-app updates. Blocking sub-apps are joined each frame.
23 * Overlapping sub-apps may skip extraction while an update is in flight, up to
24 * `kMaxOverlappingUpdates` consecutive frames (`0` = unlimited). Async sub-apps
25 * run updates on a background loop; extraction runs every main frame.
26 */
27class Scheduler {
28public:
29 Scheduler() = default;
30 Scheduler(const Scheduler&) = delete;
31 Scheduler(Scheduler&& other) noexcept;
32 ~Scheduler() = default;
33
34 Scheduler& operator=(const Scheduler&) = delete;
35 Scheduler& operator=(Scheduler&& other) noexcept;
36
37 /**
38 * @brief Builds schedulers and precomputes sub-app task graphs.
39 * @param app Owning application
40 */
41 void Build(App& app);
42
43 /**
44 * @brief Runs startup on the main sub-app, then startup on all sub-apps in
45 * parallel, then starts async sub-app update loops.
46 * @param app Owning application
47 */
48 void RunStartup(App& app);
49
50 /**
51 * @brief Runs one full application frame.
52 * @param app Owning application
53 */
54 void RunFrame(App& app);
55
56 /**
57 * @brief Stops async loops, waits for in-flight updates, shuts down
58 * sub-apps, then the main sub-app.
59 * @param app Owning application
60 */
61 void Shutdown(App& app);
62
63 /// @brief Waits until blocking sub-apps finish their frame update.
64 void WaitForSubApps();
65
66 /// @brief Clears cached task graphs and pending sub-app update state.
67 void Clear();
68
69private:
70 enum class SubAppMode : uint8_t {
71 kBlocking = 0,
72 kOverlapping = 1,
73 kAsync = 2,
74 };
75
76 struct SubAppFrameState {
77 std::reference_wrapper<SubApp> sub_app;
78 SubAppMode mode = SubAppMode::kBlocking;
79 size_t consecutive_extract_skips = 0;
80 bool fresh_extract_this_frame = false;
81 };
82
83 static void RunMainStartup(SubApp& main, async::Executor& executor);
84 static void RunUpdateStage(SubApp& main, async::Executor& executor);
85 void RunExtractStage(SubApp& main, async::Executor& executor);
86 static void RunMainShutdown(SubApp& main, async::Executor& executor);
87
88 void LaunchSubAppUpdates(App& app, async::Executor& executor);
89 void StartAsyncUpdateLoops(App& app, async::Executor& executor);
90 void StopAsyncUpdateLoops();
91
92 static void ExtractSubApp(SubAppFrameState& state,
93 const ecs::World& main_world);
94
95 [[nodiscard]] static SubAppMode ClassifySubApp(
96 const SubApp& sub_app) noexcept;
97
98 async::TaskGraph startup_graph_{"SubAppStartup"};
99 async::TaskGraph blocking_update_graph_{"SubAppBlockingUpdate"};
100 async::TaskGraph shutdown_graph_{"SubAppShutdown"};
101 std::vector<SubAppFrameState> sub_app_states_;
102 std::optional<async::Future<void>> blocking_update_future_;
103 std::atomic<size_t> async_loops_running_{0};
104};
105
106inline Scheduler::Scheduler(Scheduler&& other) noexcept
107 : startup_graph_(std::move(other.startup_graph_)),
108 blocking_update_graph_(std::move(other.blocking_update_graph_)),
109 shutdown_graph_(std::move(other.shutdown_graph_)),
110 sub_app_states_(std::move(other.sub_app_states_)),
111 blocking_update_future_(std::move(other.blocking_update_future_)),
112 async_loops_running_(
113 other.async_loops_running_.load(std::memory_order_relaxed)) {}
114
115inline Scheduler& Scheduler::operator=(Scheduler&& other) noexcept {
116 if (this == &other) [[unlikely]] {
117 return *this;
118 }
119
120 startup_graph_ = std::move(other.startup_graph_);
121 blocking_update_graph_ = std::move(other.blocking_update_graph_);
122 shutdown_graph_ = std::move(other.shutdown_graph_);
123 sub_app_states_ = std::move(other.sub_app_states_);
124 blocking_update_future_ = std::move(other.blocking_update_future_);
125 async_loops_running_.store(
126 other.async_loops_running_.load(std::memory_order_relaxed),
127 std::memory_order_release);
128
129 return *this;
130}
131
132inline void Scheduler::Clear() {
133 startup_graph_.Clear();
134 blocking_update_graph_.Clear();
135 shutdown_graph_.Clear();
136 sub_app_states_.clear();
137 blocking_update_future_.reset();
138 async_loops_running_.store(0, std::memory_order_release);
139}
140
141} // namespace helios::app
Application class.
void RunStartup(App &app)
Runs startup on the main sub-app, then startup on all sub-apps in parallel, then starts async sub-app...
Definition scheduler.cpp:62
Scheduler & operator=(const Scheduler &)=delete
void RunFrame(App &app)
Runs one full application frame.
Definition scheduler.cpp:76
void Shutdown(App &app)
Stops async loops, waits for in-flight updates, shuts down sub-apps, then the main sub-app.
Definition scheduler.cpp:92
void Build(App &app)
Builds schedulers and precomputes sub-app task graphs.
Definition scheduler.cpp:17
void Clear()
Clears cached task graphs and pending sub-app update state.
void WaitForSubApps()
Waits until blocking sub-apps finish their frame update.
Scheduler(const Scheduler &)=delete
Manages worker threads and executes task graphs using work-stealing scheduling.
Definition executor.hpp:32
Represents a task dependency graph that can be executed by an Executor.
The World class manages entities with their components and systems.
Definition world.hpp:39