galaxy 1.0.0
Real-Time C++23 Game Programming Framework. Built on data-driven design principles and agile software engineering.
Loading...
Searching...
No Matches
Application.cpp
Go to the documentation of this file.
1
7
8#define BS_THREAD_POOL_NATIVE_EXTENSIONS
9
10#include <format>
11
12#include <BS_thread_pool.hpp>
13#include <entt/signal/dispatcher.hpp>
14#include <glad/glad.h>
15#include <mimalloc.h>
16#include <SDL3/SDL.h>
17#include <sol/sol.hpp>
18
29#include "galaxy/lua/Lua.hpp"
33#include "galaxy/time/Time.hpp"
34
35#include "Application.hpp"
36
37using namespace std::chrono_literals;
38
39namespace galaxy
40{
41 App::App(const std::string& config_file)
42 {
43 SDL_SetMemoryFunctions(&mi_malloc, &mi_calloc, &mi_realloc, &mi_free);
44
47 setup_config(config_file);
49 setup_fs();
53 // setup_nuklear();
54 // setup_loader();
55 setup_meta();
58
59 // Load game assets.
60 // core::entt::locator<core::Loader>::ref().load_all();
61 }
62
64 {
65 entt::locator<VirtualFileSystem>::reset();
66 entt::locator<Config>::reset();
67
68 GALAXY_LOG(GALAXY_INFO, "Application closed.");
69 entt::locator<BS::priority_thread_pool>::value().wait();
70
71 entt::locator<Log>::reset();
72 entt::locator<BS::priority_thread_pool>::reset();
73
74 SDL_Quit();
75 }
76
77 /*void App::load()
78 {
79 const auto path = Settings::root_dir() / Settings::asset_pack();
80
81 auto& sm = entt::locator<scene::SceneManager>::value();
82 sm.load_app(path.string());
83 }*/
84
85 void App::run()
86 {
87 // https://stackoverflow.com/a/59446610
88 // We dont need 't' or 'alpha/render' sections.
89
90 auto& window = entt::locator<Window>::value();
91 auto& scenes = entt::locator<SceneManager>::value();
92
93 // The expression dt/1s simply converts the double-based chrono seconds
94 // into a double so it can participate in the physics computation.
95 constexpr const auto dt = std::chrono::duration<long long, std::ratio<1, 60>> {1};
96 time::dt(dt / 1.0s);
97
98 using clock = std::chrono::steady_clock;
99 using duration = decltype(clock::duration {} + dt);
100 using time_point = std::chrono::time_point<clock, duration>;
101
102 duration accum = 0s;
103 time_point prev = clock::now();
104 time_point now = clock::now();
105
106 auto updates = 0u;
107 auto frames = 0u;
108 duration perf = 0s;
109
110 while (window.is_open())
111 {
112 now = clock::now();
113 auto elapsed = now - prev;
114
115 // 250ms is the limit put in place on the frame time to cope with the spiral of death.
116 // It doesn't have to be 250ms exactly but it should be sufficiently high enough to deal with spikes in load.
117 if (elapsed > 250ms)
118 {
119 elapsed = 250ms;
120 }
121
122 prev = now;
123 accum += elapsed;
124
125 while (accum >= dt)
126 {
127 perf += dt;
128 accum -= dt;
129
130 while (SDL_PollEvent(&m_events))
131 {
132 switch (m_events.type)
133 {
134 case SDL_EVENT_QUIT:
135 window.close();
136 break;
137
138 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
139 if (m_events.window.windowID == SDL_GetWindowID(window.handle()))
140 {
141 window.close();
142 }
143 break;
144
145 default:
146 scenes.on_event(m_events);
147 break;
148 }
149 }
150
151 scenes.update();
152
153 updates++;
154 }
155
156 scenes.render();
157 window.swap();
158
159 frames++;
160
161 if (perf >= 1s)
162 {
163 window.append_title(std::format(" | UPS: {0}, FPS: {1}", updates, frames));
164
165 frames = 0;
166 updates = 0;
167 perf = 0s;
168 }
169 }
170 }
171
173 {
174 // Configure threadpool.
175
176 // Calc threads.
177 // We optimize for 6: 1 for audio, 1 for main, 4 for tasks.
178 auto system_cores = std::thread::hardware_concurrency();
179 if (system_cores < 6)
180 {
181 system_cores = std::thread::hardware_concurrency();
182 }
183
184 // Check for highest available priority.
185 BS::set_os_process_priority(BS::os_process_priority::high);
186 entt::locator<BS::priority_thread_pool>::emplace(system_cores, [](const std::size_t idx) {
187 BS::this_thread::set_os_thread_priority(BS::os_thread_priority::highest);
188 });
189 }
190
192 {
193 platform::configure_terminal();
194 if (!std::filesystem::exists(Settings::log_dir()))
195 {
196 std::filesystem::create_directory(Settings::log_dir());
197 }
198 entt::locator<Log>::emplace();
199
200 const auto path = std::format("{0}{1}{2}", Settings::log_dir(), std::format("{0:%d-%m-%Y-[%H-%M]}", time::now()), ".log");
202
204 GALAXY_LOG(GALAXY_INFO, "App started.");
205 }
206
207 void App::setup_config(std::string_view config_file)
208 {
209 auto& config = entt::locator<Config>::emplace(config_file);
212 }
213
215 {
217
218 platform::set_metadata(SDL_PROP_APP_METADATA_NAME_STRING, Settings::title().c_str());
219 platform::set_metadata(SDL_PROP_APP_METADATA_VERSION_STRING, Settings::version().c_str());
220 platform::set_metadata(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, Settings::identifier().c_str());
221 platform::set_metadata(SDL_PROP_APP_METADATA_CREATOR_STRING, Settings::creator().c_str());
222 platform::set_metadata(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, Settings::copyright().c_str());
223 platform::set_metadata(SDL_PROP_APP_METADATA_URL_STRING, Settings::website().c_str());
224 platform::set_metadata(SDL_PROP_APP_METADATA_TYPE_STRING, "game");
225
226 platform::set_hint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "1");
227 platform::set_hint(SDL_HINT_ASSERT, "break");
228 platform::set_hint(SDL_HINT_AUDIO_CATEGORY, "playback");
229 platform::set_hint(SDL_HINT_AUDIO_CHANNELS, "2");
230 platform::set_hint(SDL_HINT_AUDIO_INCLUDE_MONITORS, "0");
231 platform::set_hint(SDL_HINT_AUTO_UPDATE_JOYSTICKS, "1");
232 platform::set_hint(SDL_HINT_AUTO_UPDATE_SENSORS, "1");
233 platform::set_hint(SDL_HINT_LOGGING, "warn");
234 platform::set_hint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0");
235 platform::set_hint(SDL_HINT_EVENT_LOGGING, "0");
236 platform::set_hint(SDL_HINT_FRAMEBUFFER_ACCELERATION, "opengl");
237 platform::set_hint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
238 platform::set_hint(SDL_HINT_GPU_DRIVER, "vulkan");
239 platform::set_hint(SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS, "1");
240 platform::set_hint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");
241 platform::set_hint(SDL_HINT_JOYSTICK_DIRECTINPUT, "1");
242 platform::set_hint(SDL_HINT_TV_REMOTE_AS_JOYSTICK, "1");
243 platform::set_hint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
244 platform::set_hint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
245 platform::set_hint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0");
246 platform::set_hint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1");
247 platform::set_hint(SDL_HINT_MOUSE_DPI_SCALE_CURSORS, "1");
248 platform::set_hint(SDL_HINT_PEN_MOUSE_EVENTS, "1");
249 platform::set_hint(SDL_HINT_PEN_TOUCH_EVENTS, "1");
250 platform::set_hint(SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE, "1");
251 platform::set_hint(SDL_HINT_RENDER_DRIVER, "opengl");
252 platform::set_hint(SDL_HINT_RENDER_GPU_DEBUG, "0");
253 platform::set_hint(SDL_HINT_RENDER_GPU_LOW_POWER, "0");
254 platform::set_hint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1");
255 platform::set_hint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, "0");
256 platform::set_hint(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, "0");
257 platform::set_hint(SDL_HINT_XINPUT_ENABLED, "1");
258 platform::set_hint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "0");
259 platform::set_hint(SDL_HINT_VIDEO_DUMMY_SAVE_FRAMES, "0");
260 platform::set_hint(SDL_HINT_VIDEO_MATCH_EXCLUSIVE_MODE_ON_MOVE, "1");
261 platform::set_hint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
262 platform::set_hint(SDL_HINT_VIDEO_OFFSCREEN_SAVE_FRAMES, "0");
263 platform::set_hint(SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS, "0");
264 platform::set_hint(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, "1");
265 platform::set_hint(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, "1");
266 platform::set_hint(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, "0");
267 platform::set_hint(SDL_HINT_WINDOWS_GAMEINPUT, "1");
268 platform::set_hint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "0");
269 // TODO: platform::set_hint(SDL_HINT_IME_IMPLEMENTED_UI, "0");
270
271 const auto vsync = Settings::vsync() ? "1" : "0";
272 platform::set_hint(SDL_HINT_RENDER_VSYNC, vsync);
273
274 const auto audio_freq = std::to_string(Settings::audio_freq());
275 platform::set_hint(SDL_HINT_AUDIO_FREQUENCY, audio_freq.c_str());
276
277 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS | SDL_INIT_HAPTIC | SDL_INIT_SENSOR))
278 {
279 GALAXY_LOG(GALAXY_FATAL, SDL_GetError());
280 }
281 }
282
284 {
285 entt::locator<VirtualFileSystem>::emplace();
286 }
287
289 {
290 auto& window = entt::locator<Window>::emplace();
291
292 window.set_icon(Settings::window_icon());
293 window.show();
294
295 auto& sampler = entt::locator<Sampler>::emplace();
296
297 // Need to create our default texture sampler object.
298 if (Settings::mipmap())
299 {
300 switch (Settings::texture_filter())
301 {
303 sampler.set(GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
304 sampler.set(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
305 break;
307 sampler.set(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
308 sampler.set(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
309 break;
311 sampler.set(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
312 sampler.set(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
313 break;
314 }
315 }
316 else
317 {
319 {
320 sampler.set(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
321 sampler.set(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
322 }
323 else
324 {
325 sampler.set(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
326 sampler.set(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
327 }
328 }
329
330 auto ansio = std::clamp(Settings::ansiotrophy(), 1, 16);
331 if (ansio == 3)
332 {
333 ansio = 4;
334 }
335 else if (ansio > 4 && ansio < 8)
336 {
337 ansio = 8;
338 }
339 else
340 {
341 ansio = 16;
342 }
343 sampler.setf(GL_TEXTURE_MAX_ANISOTROPY, static_cast<float>(ansio));
344
345 sampler.set(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
346 sampler.set(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
347 sampler.set(GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
348 sampler.set(GL_TEXTURE_LOD_BIAS, GL_NONE);
349 }
350
352 {
353 SDL_zero(m_events);
354 }
355
357 {
358 auto& keyboard = entt::locator<Keyboard>::emplace();
359 auto& mouse = entt::locator<Mouse>::emplace();
360
361 mouse.set_cursor_custom(Settings::cursor_icon(), Settings::cursor_hotspot());
363 {
364 mouse.show_cursor();
365 }
366 else
367 {
368 mouse.hide_cursor();
369 }
370 }
371
372 // void App::setup_nuklear()
373 //{
374 // auto& nui = ServiceLocator<ui::NuklearUI>::make();
375 // }
376
377 // void App::setup_loader()
378 //{
379 // // entt::locator<Loader>::make();
380 // }
381
383 {
384 auto& sf = entt::locator<SystemFactory>::emplace();
385 // auto& ef = entt::locator<meta::EntityFactory>::emplace();
386
387 /*ef.register_component<components::Tag>("Tag");
388
389 em.register_component<components::Animated>("Animated");
390 em.register_component<components::Circle>("Circle");
391 em.register_component<components::Ellipse>("Ellipse");
392 em.register_component<components::Point>("Point");
393 em.register_component<components::Polygon>("Polygon");
394 em.register_component<components::Polyline>("Polyline");
395 em.register_component<components::RigidBody>("RigidBody");
396 em.register_component<components::Script>("Script");
397 em.register_component<components::Sprite>("Sprite");
398 em.register_component<components::Text>("Text");
399 em.register_component<components::TileMap>("TileMap");
400 em.register_component<components::Transform>("Transform");
401 em.register_component<flags::DenySerialization>("DenySerialization");
402 em.register_component<flags::Disabled>("Disabled");
403
404 em.register_dependencies<components::Animated, components::Sprite>();
405 em.register_dependencies<components::Circle, components::Transform>();
406 em.register_dependencies<components::Ellipse, components::Transform>();
407 em.register_dependencies<components::Point, components::Transform>();
408 em.register_dependencies<components::Polygon, components::Transform>();
409 em.register_dependencies<components::Polyline, components::Transform>();
410 em.register_dependencies<components::RigidBody, components::Transform>();
411 em.register_dependencies<components::Sprite, components::Transform>();
412 em.register_dependencies<components::Text, components::Transform>();
413 */
414 }
415
417 {
418 // entt::locator<media::SoundEngine>::make(listener_count);
419 // entt::locator<media::MusicEngine>::make(listener_count);
420 // entt::locator<media::VoiceEngine>::make(listener_count);
421 // entt::locator<resource::SoundCache>::make();
422 // entt::locator<resource::MusicCache>::make();
423 // entt::locator<resource::VoiceCache>::make();
424 // entt::locator<resource::VideoCache>::make();
425 // entt::locator<resource::Animations>::make();
426 // entt::locator<resource::Shaders>::make();
427 // entt::locator<resource::Fonts>::make();
428 // entt::locator<resource::Textures>::make();
429 // entt::locator<resource::Prefabs>::make();
430 // entt::locator<resource::Scripts>::make();
431 entt::locator<SceneManager>::emplace();
432 }
433
435 {
436 auto& lua = entt::locator<sol::state>::emplace();
437 lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::coroutine, sol::lib::string, sol::lib::os, sol::lib::math, sol::lib::table, sol::lib::io, sol::lib::utf8);
438
439 //
440 // Add external libraries to Lua.
441 // Inject all configured galaxy into Lua.
442 // Add engine services to lua.
443 //
444 Lua::inject();
445 }
446} // namespace galaxy
#define GALAXY_ADD_SINK(sink,...)
Definition Log.hpp:28
#define GALAXY_INFO
Log.hpp galaxy.
Definition Log.hpp:23
#define GALAXY_LOG(level, msg,...)
Definition Log.hpp:29
#define GALAXY_FATAL
Definition Log.hpp:26
void setup_config(std::string_view config_file)
void run()
Loads the default appdata file.
App(const std::string &config_file="config.json")
Default constructor.
SDL_Event m_events
Core event data.
void setup_logging()
void setup_async()
void setup_events()
void setup_input()
void setup_rendering()
void setup_meta()
void setup_services()
void setup_scripting()
void setup_platform()
~App()
Destructor.
Logs a message to the console.
static void inject() noexcept
Inject everything into Lua.
Definition Lua.cpp:12
void seed_random() noexcept
Seed the cstdlib rng algos.
Definition Platform.cpp:20
void set_metadata(const char *type, const char *value) noexcept
Sets metadata.
Definition Platform.cpp:25
void set_hint(const char *hint, const char *value) noexcept
Sets SDL hints.
Definition Platform.cpp:30
double dt() noexcept
Get galaxy delta time.
Definition Time.cpp:26
auto now() noexcept -> std::chrono::local_time< std::chrono::system_clock::duration >
Current local time.
Definition Time.cpp:16
Application.hpp galaxy.
@ NEAREST
Nearest-neighbour.
static auto window_icon() noexcept -> const std::string &
Window icon file in vfs.
Definition Settings.cpp:139
static auto log_dir() noexcept -> const std::string &
Current root directory of application, unless it has been changed.
Definition Settings.cpp:244
static auto ansiotrophy() noexcept -> int
Ansiotropic filtering level.
Definition Settings.cpp:194
static auto copyright() noexcept -> const std::string &
Copyright message.
Definition Settings.cpp:234
static auto vsync() noexcept -> bool
Vsync control.
Definition Settings.cpp:154
static auto mipmap() noexcept -> bool
Mipmapping.
Definition Settings.cpp:199
static auto set_settings_from_config() -> void
Set all our settings using the provided config file.
Definition Settings.cpp:76
static auto identifier() noexcept -> const std::string &
Game identifier i.e. com.galaxy.app.
Definition Settings.cpp:224
static auto texture_filter() noexcept -> GLTextureFilter
Texture filtering type.
Definition Settings.cpp:204
static auto title() noexcept -> const std::string &
Game title.
Definition Settings.cpp:214
static auto cursor_show() noexcept -> bool
Is the mouse cursor visible or not.
Definition Settings.cpp:174
static auto cursor_hotspot() noexcept -> const glm::ivec2 &
Cursor selector point (hotspot).
Definition Settings.cpp:184
static auto version() noexcept -> const std::string &
Game semver.
Definition Settings.cpp:219
static auto audio_freq() noexcept -> int
Set audio frequency to use with SDL.
Definition Settings.cpp:189
static auto website() noexcept -> const std::string &
Website URL.
Definition Settings.cpp:239
static auto set_config_to_default() -> void
Restore all config settings to default.
Definition Settings.cpp:23
static auto cursor_icon() noexcept -> const std::string &
Cursor texture file in vfs.
Definition Settings.cpp:179
static auto creator() noexcept -> const std::string &
Owner.
Definition Settings.cpp:229