44#include " helper/message_box.hpp"
55#include " helper/sleep.hpp"
66#include " input/input.hpp"
7+ #include " manager/music_manager.hpp"
8+ #include " scenes/loading_screen/loading_screen.hpp"
79#include " scenes/scene.hpp"
10+ #include " ui/layout.hpp"
811
912#include < chrono>
13+ #include < fmt/chrono.h>
14+ #include < future>
1015#include < memory>
1116#include < ranges>
1217#include < stdexcept>
@@ -35,9 +40,6 @@ Application::Application(std::shared_ptr<Window>&& window, const std::vector<std
3540 m_window{ std::move (window) },
3641 m_renderer{ *m_window, m_command_line_arguments.target_fps .has_value () ? Renderer::VSync::Disabled
3742 : Renderer::VSync::Enabled },
38- m_music_manager{ this , num_audio_channels },
39- m_input_manager{ std::make_shared<input::InputManager>(m_window) },
40- m_settings_manager{ this },
4143 m_target_framerate{ m_command_line_arguments.target_fps } {
4244 initialize ();
4345} catch (const helper::GeneralError& general_error) {
@@ -156,7 +158,7 @@ void Application::handle_event(const SDL_Event& event) {
156158
157159 // this global event handlers (atm only one) are special cases, they receive all inputs if they are not handled by the scenes explicitly
158160
159- if (m_music_manager. handle_event (m_input_manager, event)) {
161+ if (m_music_manager-> handle_event (m_input_manager, event)) {
160162 return ;
161163 }
162164}
@@ -249,32 +251,121 @@ void Application::render() const {
249251
250252void Application::initialize () {
251253
252- load_resources ();
253- push_scene (scenes::create_scene (*this , SceneId::MainMenu, ui::FullScreenLayout{ *m_window }));
254+ auto loading_screen = scenes::LoadingScreen{ this };
255+
256+ const auto start_time = SDL_GetTicks64 ();
257+
258+ const std::future<void > load_everything = std::async (std::launch::async, [this ] {
259+ this ->m_music_manager = std::make_unique<MusicManager>(this , num_audio_channels);
260+
261+ this ->m_input_manager = std::make_shared<input::InputManager>(this ->m_window );
262+
263+ this ->m_settings_manager = std::make_unique<SettingsManager>(this );
264+
265+ this ->m_font_manager = std::make_unique<FontManager>();
266+
267+ this ->load_resources ();
254268
255269#ifdef DEBUG_BUILD
256- m_fps_text = std::make_unique<ui::Label>(
257- this , " FPS: ?" , font_manager ().get (FontId::Default), Color::white (),
258- std::pair<double , double >{ 0.95 , 0.95 },
259- ui::Alignment{ ui::AlignmentHorizontal::Middle, ui::AlignmentVertical::Center },
260- ui::RelativeLayout{ window (), 0.0 , 0.0 , 0.1 , 0.05 }, false
261- );
270+ m_fps_text = std::make_unique<ui::Label>(
271+ this , " FPS: ?" , font_manager ().get (FontId::Default), Color::white (),
272+ std::pair<double , double >{ 0.95 , 0.95 },
273+ ui::Alignment{ ui::AlignmentHorizontal::Middle, ui::AlignmentVertical::Center },
274+ ui::RelativeLayout{ window (), 0.0 , 0.0 , 0.1 , 0.05 }, false
275+ );
262276#endif
263277
264278#if defined(_HAVE_DISCORD_SDK)
265- if (m_settings_manager.settings ().discord ) {
266- auto discord_instance = DiscordInstance::initialize ();
267- if (not discord_instance.has_value ()) {
268- spdlog::warn (
269- " Error initializing the discord instance, it might not be running: {}" , discord_instance.error ()
270- );
271- } else {
272- m_discord_instance = std::move (discord_instance.value ());
273- m_discord_instance->after_setup ();
279+ if (m_settings_manager->settings ().discord ) {
280+ auto discord_instance = DiscordInstance::initialize ();
281+ if (not discord_instance.has_value ()) {
282+ spdlog::warn (
283+ " Error initializing the discord instance, it might not be running: {}" , discord_instance.error ()
284+ );
285+ } else {
286+ m_discord_instance = std::move (discord_instance.value ());
287+ m_discord_instance->after_setup ();
288+ }
274289 }
275- }
276290
277291#endif
292+ });
293+
294+
295+ using namespace std ::chrono_literals;
296+
297+ const auto sleep_time = m_target_framerate.has_value () ? std::chrono::duration_cast<std::chrono::nanoseconds>(1s)
298+ / m_target_framerate.value ()
299+ : 0s;
300+ auto start_execution_time = std::chrono::steady_clock::now ();
301+
302+
303+ bool finished_loading = false ;
304+
305+ // this is a duplicate of below in some cases, but it's just for the loading screen and can't be factored out easily
306+ // this also only uses a subset of all things, the real event loop uses, so that nothing breaks while doing multithreading
307+ // the only things usable are: (since NOT accessed (writing) via the loading thread and already initialized):
308+ // - m_command_line_arguments
309+ // - m_window
310+ // - m_renderer
311+ // - m_target_framerate
312+
313+ while ((not finished_loading) and m_is_running
314+ #if defined(__CONSOLE__)
315+ and console::inMainLoop ()
316+ #endif
317+ ) {
318+
319+ // we can't use the normal event loop, so we have to do it manually
320+ SDL_Event event;
321+ while (SDL_PollEvent (&event) != 0 ) {
322+ if (event.type == SDL_QUIT) {
323+ m_is_running = false ;
324+ }
325+ }
326+
327+ loading_screen.update ();
328+ // this service_provider only guarantees the renderer + the window to be accessible without race conditions
329+ loading_screen.render (*this );
330+
331+ // present and wait (depending if vsync is on or not, this has to be done manually)
332+ m_renderer.present ();
333+
334+ if (m_target_framerate.has_value ()) {
335+
336+ const auto now = std::chrono::steady_clock::now ();
337+ const auto runtime = (now - start_execution_time);
338+ if (runtime < sleep_time) {
339+ // TODO(totto): use SDL_DelayNS in sdl >= 3.0
340+ helper::sleep_nanoseconds (sleep_time - runtime);
341+ start_execution_time = std::chrono::steady_clock::now ();
342+ } else {
343+ start_execution_time = now;
344+ }
345+ }
346+ // end waiting
347+
348+ // wait until is faster, since it just compares two time_points instead of getting now() and than adding the wait-for argument
349+ finished_loading =
350+ load_everything.wait_until (std::chrono::system_clock::time_point::min ()) == std::future_status::ready;
351+ }
352+
353+
354+ const auto duration = std::chrono::milliseconds (SDL_GetTicks64 () - start_time);
355+
356+ // we can reach this via SDL_QUIT or (not console::inMainLoop())
357+ if (not finished_loading) {
358+
359+ spdlog::debug (" Aborted loading after {}" , duration);
360+
361+ // just exit immediately, without cleaning up, since than we would have to cancel the loading thread somehow, which is way rto complicated, let the OS clean up our mess we create her xD
362+ std::exit (0 );
363+ }
364+
365+
366+ spdlog::debug (" Took {} to load" , duration);
367+
368+ push_scene (scenes::create_scene (*this , SceneId::MainMenu, ui::FullScreenLayout{ *m_window }));
278369}
279370
280371void Application::load_resources () {
@@ -292,7 +383,7 @@ void Application::load_resources() {
292383 };
293384 for (const auto & [font_id, path] : fonts) {
294385 const auto font_path = utils::get_assets_folder () / " fonts" / path;
295- m_font_manager. load (font_id, font_path, fonts_size);
386+ m_font_manager-> load (font_id, font_path, fonts_size);
296387 }
297388}
298389
0 commit comments