sandbox
Loading...
Searching...
No Matches
engine.hpp
1#ifndef LIBSBX_CORE_ENGINE_HPP_
2#define LIBSBX_CORE_ENGINE_HPP_
3
4#include <map>
5#include <vector>
6#include <typeindex>
7#include <memory>
8#include <span>
9#include <string_view>
10#include <cmath>
11#include <chrono>
12#include <ranges>
13
14#include <range/v3/all.hpp>
15
16#include <easy/profiler.h>
17
18#include <libsbx/utility/concepts.hpp>
19#include <libsbx/utility/noncopyable.hpp>
20#include <libsbx/utility/assert.hpp>
21#include <libsbx/utility/type_name.hpp>
22#include <libsbx/utility/timer.hpp>
23
24#include <libsbx/units/time.hpp>
25
26#include <libsbx/core/module.hpp>
27#include <libsbx/core/application.hpp>
28#include <libsbx/core/cli.hpp>
29#include <libsbx/core/profiler.hpp>
30#include <libsbx/core/settings.hpp>
31
32namespace sbx::core {
33
35
36 using stage = module_manager::stage;
37 using module_base = module_manager::module_base;
38 using module_factory = module_manager::module_factory;
39
40public:
41
42 engine(std::span<std::string_view> args)
43 : _cli{args} {
44 utility::assert_that(_instance == nullptr, "Engine already exists.");
45
46 _instance = this;
47
48 for (auto&& [type, factory] : module_manager::_factories() | ranges::views::filter([](const auto& entry) { return entry.has_value(); }) | ranges::views::enumerate) {
49 _create_module(type, *factory);
50 }
51 }
52
53 ~engine() {
54 for (auto&& [type, entry] : _modules | ranges::views::enumerate | std::views::reverse) {
55 _destroy_module(type);
56 }
57
58 _instance = nullptr;
59 }
60
61 static auto delta_time() -> units::second {
62 return _instance->_delta_time;
63 }
64
65 static auto fixed_delta_time() -> units::second {
66 return units::second{1.0f / 60.0f};
67 }
68
69 static auto time() -> units::second {
70 return _instance->_time;
71 }
72
73 static auto quit() -> void {
74 _instance->_is_running = false;
75 }
76
77 static auto cli() noexcept -> core::cli& {
78 return _instance->_cli;
79 }
80
81 // static auto profiler() noexcept -> core::profiler& {
82 // return _instance->_profiler;
83 // }
84
85 static auto settings() noexcept -> core::settings& {
86 return _instance->_settings;
87 }
88
89 template<typename Module>
90 requires (std::is_base_of_v<module_base, Module>)
91 [[nodiscard]] static auto get_module() -> Module& {
92 const auto type = type_id<Module>::value();
93
94 auto& modules = _instance->_modules;
95
96 if (type >= modules.size() || !modules[type]) {
97 throw std::runtime_error{fmt::format("Failed to find module '{}'", utility::type_name<Module>())};
98 }
99
100 return *static_cast<Module*>(modules[type]);
101 }
102
103 auto run(std::unique_ptr<application> application) -> void {
104 if (_is_running) {
105 return;
106 }
107
108 using clock_type = std::chrono::high_resolution_clock;
109
110 _is_running = true;
111
112 auto last = clock_type::now();
113
114 auto fixed_accumulator = units::second{};
115
116 while (_is_running) {
117 const auto now = clock_type::now();
118 const auto delta_time = std::chrono::duration_cast<std::chrono::duration<std::float_t>>(now - last).count();
119 last = now;
120
121
122 _instance->_delta_time = units::second{delta_time};
123 _instance->_time += _instance->_delta_time;
124
125 fixed_accumulator += _instance->_delta_time;
126
127 EASY_BLOCK("stage pre");
128 _update_stage(stage::pre);
129 EASY_END_BLOCK;
130
131 EASY_BLOCK("application update");
132 application->update();
133 EASY_END_BLOCK;
134
135 EASY_BLOCK("stage normal");
136 _update_stage(stage::normal);
137 EASY_END_BLOCK;
138
139 EASY_BLOCK("stage post");
140 _update_stage(stage::post);
141 EASY_END_BLOCK;
142
143 EASY_BLOCK("stage pre_fixed");
144 _update_stage(stage::pre_fixed);
145 EASY_END_BLOCK;
146
147 while (fixed_accumulator >= fixed_delta_time()) {
148 EASY_BLOCK("stage fixed");
149 application->fixed_update();
150 _update_stage(stage::fixed);
151 fixed_accumulator -= fixed_delta_time();
152 EASY_END_BLOCK;
153 }
154
155 EASY_BLOCK("stage post_fixed");
156 _update_stage(stage::post_fixed);
157 EASY_END_BLOCK;
158
159 EASY_BLOCK("stage rendering");
160 _update_stage(stage::rendering);
161 EASY_END_BLOCK;
162 }
163 }
164
165private:
166
167 auto _create_module(const std::uint32_t type, const module_factory& factory) -> void {
168 if (type < _modules.size() && _modules[type]) {
169 return;
170 }
171
172 for (const auto& dependency : factory.dependencies) {
173 _create_module(dependency, *module_manager::_factories().at(dependency));
174 }
175
176 if (type >= _modules.size()) {
177 _modules.resize(std::max(_modules.size(), static_cast<std::size_t>(type + 1u)));
178 }
179
180 _modules[type] = std::invoke(factory.create);
181 _module_by_stage[factory.stage].push_back(type);
182 }
183
184 auto _destroy_module(const std::uint32_t type) -> void {
185 if (type >= _modules.size() || !_modules.at(type)) {
186 return;
187 }
188
189 auto& factory = module_manager::_factories().at(type);
190
191 for (const auto& dependency : factory->dependencies) {
192 _destroy_module(dependency);
193 }
194
195 auto* module_instance = _modules.at(type);
196 std::invoke(factory->destroy, module_instance);
197 _modules.at(type) = nullptr;
198 }
199
200 auto _update_stage(stage stage) -> void {
201 if (auto entry = _module_by_stage.find(stage); entry != _module_by_stage.end()) {
202 for (const auto& type : entry->second) {
203 _modules.at(type)->update();
204 }
205 }
206 }
207
208 static engine* _instance;
209
210 units::second _delta_time;
211 units::second _time;
212
213 bool _is_running{};
214 // std::vector<std::string_view> _args{};
215 core::cli _cli;
216 // core::profiler _profiler;
217 core::settings _settings;
218
219 std::vector<module_base*> _modules{};
220 std::map<stage, std::vector<std::uint32_t>> _module_by_stage{};
221
222}; // class engine
223
224} // namespace sbx::core
225
226#endif // LIBSBX_CORE_ENGINE_HPP_
Definition: application.hpp:6
Definition: cli.hpp:23
Definition: engine.hpp:34
Definition: settings.hpp:17
Definition: quantity.hpp:65
Definition: noncopyable.hpp:6
static auto value() noexcept -> std::uint32_t
Generates a unique ID for the type.
Definition: type_id.hpp:40