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