move opengl library loading to a thread that starts as soon as possible and is awaited as late as we can allow

This commit is contained in:
veclavtalica 2025-01-15 07:54:45 +03:00
parent 8233f31269
commit 449d4d3c32
2 changed files with 116 additions and 95 deletions

View File

@ -28,6 +28,7 @@ typedef struct EngineContext {
char **argv; char **argv;
/* where the app was run from, used as the root for packs */ /* where the app was run from, used as the root for packs */
char *base_dir; char *base_dir;
char *title;
Vec2 window_dims; Vec2 window_dims;
Rect viewport_rect; Rect viewport_rect;

View File

@ -4,7 +4,6 @@
#include "twn_util.h" #include "twn_util.h"
#include "twn_util_c.h" #include "twn_util_c.h"
#include "twn_game_object_c.h" #include "twn_game_object_c.h"
#include "twn_audio_c.h"
#include "twn_textures_c.h" #include "twn_textures_c.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
@ -20,6 +19,9 @@
#define PACKAGE_EXTENSION "btw" #define PACKAGE_EXTENSION "btw"
static SDL_Thread *opengl_load_thread;
static int event_callback(void *userdata, SDL_Event *event) { static int event_callback(void *userdata, SDL_Event *event) {
(void)userdata; (void)userdata;
@ -342,14 +344,16 @@ ERR_PACK_MANIFEST_PATH_ALLOC_FAIL:
} }
static bool initialize(void) { static int opengl_load_thread_fn(void *data) {
profile_start("SDL initialization"); (void)data;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) == -1) {
CRY_SDL("SDL initialization failed.");
return false;
}
profile_end("SDL initialization");
SDL_GL_LoadLibrary(NULL);
return 0;
}
static bool initialize(void) {
/* first things first, most things here will be loaded from the config file */ /* first things first, most things here will be loaded from the config file */
/* it's expected to be present in the data directory, no matter what */ /* it's expected to be present in the data directory, no matter what */
/* that is why PhysicsFS is initialized before anything else */ /* that is why PhysicsFS is initialized before anything else */
@ -446,91 +450,46 @@ static bool initialize(void) {
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
/* init got far enough to create a window */ toml_datum_t datum_title = toml_string_in(about, "title");
{ if (!datum_title.ok)
toml_datum_t datum_title = toml_string_in(about, "title"); datum_title.u.s = SDL_strdup("townengine project");
if (!datum_title.ok)
datum_title.u.s = "townengine project";
/* not yet used ctx.title = datum_title.u.s;
toml_datum_t datum_developer = toml_string_in(about, "developer");
if (!datum_developer.ok) { /* not yet used
CRY("Initialization failed", "Valid about.developer expected in configuration file"); toml_datum_t datum_developer = toml_string_in(about, "developer");
if (!datum_developer.ok) {
CRY("Initialization failed", "Valid about.developer expected in configuration file");
goto fail;
}
*/
toml_array_t *datum_resolution = toml_array_in(game, "resolution");
if (datum_resolution) {
toml_datum_t datum_base_render_width = toml_int_at(datum_resolution, 0);
if (!datum_base_render_width.ok) {
CRY("Initialization failed", "Valid game.resolution expected in configuration file");
goto fail; goto fail;
} }
*/
toml_array_t *datum_resolution = toml_array_in(game, "resolution"); toml_datum_t datum_base_render_height = toml_int_at(datum_resolution, 1);
if (datum_resolution) { if (!datum_base_render_height.ok) {
toml_datum_t datum_base_render_width = toml_int_at(datum_resolution, 0); CRY("Initialization failed", "Valid game.resolution expected in configuration file");
if (!datum_base_render_width.ok) { goto fail;
CRY("Initialization failed", "Valid game.resolution expected in configuration file");
goto fail;
}
toml_datum_t datum_base_render_height = toml_int_at(datum_resolution, 1);
if (!datum_base_render_height.ok) {
CRY("Initialization failed", "Valid game.resolution expected in configuration file");
goto fail;
}
ctx.base_render_width = datum_base_render_width.u.i;
ctx.base_render_height = datum_base_render_height.u.i;
} else {
ctx.base_render_width = 640;
ctx.base_render_height = 360;
} }
ctx.game.resolution.x = (float)ctx.base_render_width; ctx.base_render_width = datum_base_render_width.u.i;
ctx.game.resolution.y = (float)ctx.base_render_height; ctx.base_render_height = datum_base_render_height.u.i;
/* TODO: investigate viability of detached thread driver and window creation, as it's the worst load time offender */ } else {
profile_start("window creation"); ctx.base_render_width = 640;
ctx.window = SDL_CreateWindow(datum_title.u.s, ctx.base_render_height = 360;
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
(int)ctx.base_render_width,
(int)ctx.base_render_height,
//SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_RESIZABLE |
SDL_WINDOW_OPENGL);
profile_end("window creation");
if (datum_title.ok)
SDL_free(datum_title.u.s);
//SDL_free(datum_developer.u.s);
} }
if (ctx.window == NULL) { ctx.game.resolution.x = (float)ctx.base_render_width;
CRY_SDL("Window creation failed."); ctx.game.resolution.y = (float)ctx.base_render_height;
goto fail;
}
profile_start("opengl context creation"); //SDL_free(datum_developer.u.s);
ctx.gl_context = SDL_GL_CreateContext(ctx.window);
if (!ctx.gl_context) {
CRY_SDL("GL context creation failed.");
goto fail;
}
if (SDL_GL_MakeCurrent(ctx.window, ctx.gl_context)) {
CRY_SDL("GL context binding failed.");
goto fail;
}
/* TODO: figure out what we ultimately prefer */
SDL_GL_SetSwapInterval(0);
if (!render_init())
goto fail;
setup_viewport(0, 0, (int)ctx.base_render_width, (int)ctx.base_render_height);
profile_end("opengl context creation");
/* might need this to have multiple windows */
ctx.window_id = SDL_GetWindowID(ctx.window);
/* TODO: */ /* TODO: */
// SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); // SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
@ -628,11 +587,6 @@ static bool initialize(void) {
ctx.render_queue_2d = NULL; ctx.render_queue_2d = NULL;
ctx.uncolored_mesh_batches = NULL; ctx.uncolored_mesh_batches = NULL;
profile_start("texture and text cache initialization");
textures_cache_init(&ctx.texture_cache, ctx.window);
text_cache_init(&ctx.text_cache);
profile_end("texture and text cache initialization");
/* input */ /* input */
toml_datum_t datum_keybind_slots = toml_int_in(engine, "keybind_slots"); toml_datum_t datum_keybind_slots = toml_int_in(engine, "keybind_slots");
if (!datum_keybind_slots.ok) { if (!datum_keybind_slots.ok) {
@ -652,6 +606,63 @@ static bool initialize(void) {
ctx.game.fog_color = (Color){ 255, 255, 255, 255 }; /* TODO: pick some grey? */ ctx.game.fog_color = (Color){ 255, 255, 255, 255 }; /* TODO: pick some grey? */
ctx.game.fog_end = 1.0f; ctx.game.fog_end = 1.0f;
update_viewport();
profile_start("game object load");
/* now we can actually start doing stuff */
game_object_load();
profile_end("game object load");
/* delayed as further as possible so that more work is done before we have to wait */
SDL_WaitThread(opengl_load_thread, NULL);
profile_end("opengl loading");
/* TODO: investigate viability of detached thread driver and window creation, as it's the worst load time offender */
profile_start("window creation");
ctx.window = SDL_CreateWindow(ctx.title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
(int)ctx.base_render_width,
(int)ctx.base_render_height,
//SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_RESIZABLE |
SDL_WINDOW_OPENGL);
profile_end("window creation");
if (ctx.window == NULL) {
CRY_SDL("Window creation failed.");
goto fail;
}
profile_start("opengl context creation");
ctx.gl_context = SDL_GL_CreateContext(ctx.window);
if (!ctx.gl_context) {
CRY_SDL("GL context creation failed.");
goto fail;
}
if (SDL_GL_MakeCurrent(ctx.window, ctx.gl_context)) {
CRY_SDL("GL context binding failed.");
goto fail;
}
/* TODO: figure out what we ultimately prefer */
SDL_GL_SetSwapInterval(0);
if (!render_init())
goto fail;
setup_viewport(0, 0, (int)ctx.base_render_width, (int)ctx.base_render_height);
profile_end("opengl context creation");
/* might need this to have multiple windows */
ctx.window_id = SDL_GetWindowID(ctx.window);
profile_start("texture and text cache initialization");
textures_cache_init(&ctx.texture_cache, ctx.window);
text_cache_init(&ctx.text_cache);
profile_end("texture and text cache initialization");
return true; return true;
fail: fail:
@ -674,7 +685,9 @@ static void clean_up(void) {
PHYSFS_deinit(); PHYSFS_deinit();
SDL_free(ctx.base_dir); SDL_free(ctx.base_dir);
SDL_free(ctx.title);
SDL_GL_DeleteContext(ctx.gl_context); SDL_GL_DeleteContext(ctx.gl_context);
SDL_GL_UnloadLibrary();
SDL_Quit(); SDL_Quit();
} }
@ -688,6 +701,20 @@ static void reset_state(void) {
int enter_loop(int argc, char **argv) { int enter_loop(int argc, char **argv) {
profile_start("startup"); profile_start("startup");
profile_start("SDL initialization");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) == -1) {
CRY_SDL("SDL initialization failed.");
return EXIT_FAILURE;
}
profile_end("SDL initialization");
profile_start("opengl loading");
opengl_load_thread = SDL_CreateThread(opengl_load_thread_fn, "opengl loader", NULL);
if (!opengl_load_thread) {
CRY_SDL("Cannot create opengl loading thread: ");
return EXIT_FAILURE;
}
ctx.argc = argc; ctx.argc = argc;
ctx.argv = argv; ctx.argv = argv;
ctx.base_dir = SDL_GetBasePath(); ctx.base_dir = SDL_GetBasePath();
@ -763,13 +790,6 @@ int enter_loop(int argc, char **argv) {
ctx.game.debug = false; ctx.game.debug = false;
} }
update_viewport();
profile_start("game object load");
/* now we can actually start doing stuff */
game_object_load();
profile_end("game object load");
ctx.was_successful = true; ctx.was_successful = true;
ctx.game.initialization_needed = true; ctx.game.initialization_needed = true;