diff --git a/meson.build b/meson.build index 26a9922..6cc23ea 100644 --- a/meson.build +++ b/meson.build @@ -66,15 +66,21 @@ if build_machine.system() == 'linux' and host_machine.system() == 'windows' endif fs = import('fs') +cmake = import('cmake') # Gather dependencies thread_dep = dependency('threads') m_dep = compiler.find_library('m', required : false) # Subprojects installed via wraps -meshoptimizer_proj = subproject('meshoptimizer', default_options: ['warning_level=0', 'werror=false'] ) +meshoptimizer_opts = cmake.subproject_options() +meshoptimizer_opts.add_cmake_defines({'CMAKE_POSITION_INDEPENDENT_CODE': true}) +meshoptimizer_proj = cmake.subproject('meshoptimizer', options: meshoptimizer_opts) meshoptimizer_dep = meshoptimizer_proj.get_variable('meshoptimizer_dep') +glfw_proj = subproject('glfw', default_options: ['default_library=shared', 'warning_level=0', 'werror=false']) +glfw_dep = glfw_proj.get_variable('glfw_dep') + if host_machine.system() == 'linux' and get_option('use_xlib') add_project_arguments(['-DRT_USE_XLIB'], language : ['c', 'cpp']) elif host_machine.system() == 'linux' and get_option('use_wayland') diff --git a/src/experimental/meshlets/main.cpp b/src/experimental/meshlets/main.cpp index a07ece7..7c047e6 100644 --- a/src/experimental/meshlets/main.cpp +++ b/src/experimental/meshlets/main.cpp @@ -1,18 +1,17 @@ #include #include #include +#include #include #include #include +#include + #include "meshlet_generator.hpp" #include "meshlet_renderer.hpp" -static void GlfwErrorCallback(int errnum, const char *desc) { - rtReportError("GLFW", "Error %d: %s", errnum, desc); -} - static void GLDebugCallback(GLenum source, GLenum type, GLuint id, @@ -144,26 +143,18 @@ struct file_picker { int m_active_input = 0; }; -int main() { - if (rtInitRuntime() != RT_SUCCESS) - return -1; - glfwSetErrorCallback(GlfwErrorCallback); - if (!glfwInit()) - return -1; +struct meshlets_exp { + file_picker picker; + meshlet_generator gen; + meshlet_renderer ren; +}; +void MeshletsRegisterCVARs(rt_launcher_api *api) {} + +rt_result MeshletsInit(rt_launcher_api *api) { rtLog("EXP", "Meshlets experiment starting."); - - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); - GLFWwindow *window = glfwCreateWindow(1280, 720, "MESHLETS!", NULL, NULL); - if (!window) - return -1; - glfwMakeContextCurrent(window); if (!gladLoadGL()) - return -1; + return RT_UNKNOWN_ERROR; glfwSwapInterval(1); int flags; @@ -175,6 +166,10 @@ int main() { glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); } + rt_window window = api->GetWindow(); + if (window.type != RT_WINDOW_TYPE_GLFW) + return RT_INVALID_VALUE; + // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -183,44 +178,63 @@ int main() { io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls ImGui::StyleColorsDark(); - ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplGlfw_InitForOpenGL(window.glfw, true); ImGui_ImplOpenGL3_Init("#version 130"); - file_picker picker; - meshlet_generator gen; - meshlet_renderer ren; - ren.Initialize(); + void *game_obj = api->AllocGameObject(sizeof(meshlets_exp)); + meshlets_exp *exp = new (game_obj) meshlets_exp; + exp->ren.Initialize(); - while (!glfwWindowShouldClose(window)) { - glfwPollEvents(); - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); + return RT_SUCCESS; +} - if (picker.RunFlat()) { - gen.LoadObj(picker.GetPicked()); - gen.RunFlat(); - } - ren.SettingMenu(); +void MeshletsShutdown(rt_launcher_api *api, void *game_obj) {} - ImGui::Render(); - int display_w, display_h; - glfwGetFramebufferSize(window, &display_w, &display_h); - glViewport(0, 0, display_w, display_h); - glClearColor(0.f, 0.f, 0.f, 1.f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +void MeshletsUpdate(rt_launcher_api *api, rt_time_delta delta, void *game_obj) { + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); - if (picker.HasPickedFile()) { - ren.m_aspect = (float)display_w / (float)display_h; - ren.RenderFlat(gen.m_meshlets, gen.m_num_meshlets); - } + meshlets_exp *exp = (meshlets_exp *)game_obj; + if (exp->picker.RunFlat()) { + exp->gen.LoadObj(exp->picker.GetPicked()); + exp->gen.RunFlat(); + } + exp->ren.SettingMenu(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - glfwSwapBuffers(window); + ImGui::Render(); +} + +void MeshletsRender(rt_launcher_api *api, void *game_obj) { + rt_window window = api->GetWindow(); + if (window.type != RT_WINDOW_TYPE_GLFW) + return; + + meshlets_exp *exp = (meshlets_exp *)game_obj; + + int display_w, display_h; + glfwGetFramebufferSize(window.glfw, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (exp->picker.HasPickedFile()) { + exp->ren.m_aspect = (float)display_w / (float)display_h; + exp->ren.RenderFlat(exp->gen.m_meshlets, exp->gen.m_num_meshlets); } - glfwDestroyWindow(window); - glfwTerminate(); - rtShutdownRuntime(); - return 0; + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); +} + +LOAD_GAME_API_FUNC { + return (rt_game_api){ + .RegisterCVARs = MeshletsRegisterCVARs, + .Init = MeshletsInit, + .Shutdown = MeshletsShutdown, + .Update = MeshletsUpdate, + .Render = MeshletsRender, + .OnGameObjectFree = NULL, + .OnReload = NULL, + .OnUnload = NULL, + }; } diff --git a/src/experimental/meshlets/meson.build b/src/experimental/meshlets/meson.build index b7958ab..29fa855 100644 --- a/src/experimental/meshlets/meson.build +++ b/src/experimental/meshlets/meson.build @@ -1,11 +1,9 @@ -glfw_proj = subproject('glfw', default_options: ['default_library=static', 'b_sanitize=none']) -glfw_dep = glfw_proj.get_variable('glfw_dep') imgui_proj = subproject('imgui', default_options: ['warning_level=0', 'werror=false']) imgui_dep = imgui_proj.get_variable('imgui_dep') glm_proj = subproject('glm') glm_dep = glm_proj.get_variable('glm_dep') -executable('meshlet_experiment', +shared_library('meshlet_experiment', 'main.cpp', 'meshlet_generator.hpp', 'meshlet_generator.cpp', @@ -14,5 +12,5 @@ executable('meshlet_experiment', contrib_dir / 'glad/glad.c', extra_files: ['flat_cull.glsl', 'hierarchical_cull.glsl'], include_directories: [engine_incdir, contrib_incdir], - dependencies: [m_dep, meshoptimizer_dep, glfw_dep, imgui_dep, glm_dep], + dependencies: [m_dep, meshoptimizer_dep, imgui_dep, glfw_dep, glm_dep], link_with: runtime_lib) diff --git a/src/launcher/game_api.h b/src/launcher/game_api.h index a692fef..adf7115 100644 --- a/src/launcher/game_api.h +++ b/src/launcher/game_api.h @@ -4,21 +4,79 @@ #include #include -typedef void rt_game_register_cvars_fn(void); -typedef rt_result rt_game_initialize_fn(void); -typedef void rt_game_shutdown_fn(void); -typedef void rt_game_update_fn(rt_time_delta delta); -typedef void rt_game_render_fn(void); +/* Functions offered by the launcher */ + +struct GLFWwindow; + +typedef enum { + RT_WINDOW_TYPE_GLFW, +} rt_window_type; typedef struct { + rt_window_type type; + union { + GLFWwindow *glfw; + }; +} rt_window; + +typedef rt_window rt_launcher_get_window_fn(void); +typedef void *rt_launcher_alloc_game_object_fn(size_t sz); +typedef void *rt_launcher_get_game_object_fn(void); + +typedef struct { + /* Returns the window used by the game */ + rt_launcher_get_window_fn *GetWindow; + + /* Allocates storage for the "game" object passed to the game functions. */ + rt_launcher_alloc_game_object_fn *AllocGameObject; + + /* Returns the object allocated via AllocGameObject */ + rt_launcher_get_game_object_fn *GetGameObject; +} rt_launcher_api; + +/* Functions called by the launcher */ + +typedef void rt_game_register_cvars_fn(rt_launcher_api *api); +typedef rt_result rt_game_initialize_fn(rt_launcher_api *api); +typedef void rt_game_shutdown_fn(rt_launcher_api *api, void *game_obj); +typedef void rt_game_update_fn(rt_launcher_api *api, rt_time_delta delta, void *game_obj); +typedef void rt_game_render_fn(rt_launcher_api *api, void *game_obj); +typedef void rt_game_on_game_object_free_fn(rt_launcher_api *api, void *game_obj); +typedef void rt_game_on_reload_fn(rt_launcher_api *api, void *game_obj); +typedef void rt_game_on_unload_fn(rt_launcher_api *api, void *game_obj); + +typedef struct { + /* Called before initialization and before configs are read */ rt_game_register_cvars_fn *RegisterCVARs; + + /* Called before entering the game-loop. + * It is expected that this is where the game object is created. */ rt_game_initialize_fn *Init; + + /* Called after exiting the game-loop. */ rt_game_shutdown_fn *Shutdown; + + /* Update game state */ rt_game_update_fn *Update; + + /* Render the game */ rt_game_render_fn *Render; + + /* These are optional and may be NULL */ + + /* Called by rt_laucher_api::AllocGameObject, if an old object is freed. */ + rt_game_on_game_object_free_fn *OnGameObjectFree; + + /* Called after the game was reloaded during live re-compilation */ + rt_game_on_reload_fn *OnReload; + + /* Called before the game gets unloaded during live re-compilation */ + rt_game_on_unload_fn *OnUnload; } rt_game_api; +/* This is the function retrieved from the game library to get the + * game_api struct used by the launcher. */ typedef rt_game_api rt_load_game_api_fn(void); #ifdef __cplusplus diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index c3c15a0..9b12bc7 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "game_api.h" @@ -121,12 +122,11 @@ static char *ParseCommandLineGameLib(int argc, char **argv) { return game_lib_cmdline; } -static void __NullGame_RegisterCVARs(void) {} -static rt_result __NullGame_Init(void) { return RT_SUCCESS; } -static void __NullGame_Shutdown(void) {} -static void __NullGame_Update(rt_time_delta delta) { RT_UNUSED(delta); } -static void __NullGame_Render(void) {} - +static void __NullGame_RegisterCVARs(rt_launcher_api *api) {} +static rt_result __NullGame_Init(rt_launcher_api *api) { return RT_SUCCESS; } +static void __NullGame_Shutdown(rt_launcher_api *api, void *game_obj) {} +static void __NullGame_Update(rt_launcher_api *api, rt_time_delta delta, void *game_obj) { RT_UNUSED(delta); } +static void __NullGame_Render(rt_launcher_api *api, void *game_obj) {} static rt_game_api LoadGame(const char *cmdline_gamelib) { const char *game_lib = RT_GAME_LIB_PATH; @@ -140,6 +140,9 @@ static rt_game_api LoadGame(const char *cmdline_gamelib) { #endif if (strcmp(game_lib, "(null)") != 0) { +#if defined(RT_DEBUG) && !defined(RT_DISABLE_LIVE_RECOMPILATION) + /* Copy to temporary location to enable rebuilds */ +#endif _game_lib = rtOpenLib(game_lib); if (!_game_lib) { rtReportError("LAUNCHER", "Failed to open game library: %s", game_lib); @@ -158,14 +161,41 @@ static rt_game_api LoadGame(const char *cmdline_gamelib) { out: /* Fall back to null implementation. */ return (rt_game_api){ - .RegisterCVARs = __NullGame_RegisterCVARs, - .Init = __NullGame_Init, - .Shutdown = __NullGame_Shutdown, - .Update = __NullGame_Update, - .Render = __NullGame_Render, + .RegisterCVARs = __NullGame_RegisterCVARs, + .Init = __NullGame_Init, + .Shutdown = __NullGame_Shutdown, + .Update = __NullGame_Update, + .Render = __NullGame_Render, + .OnGameObjectFree = NULL, + .OnReload = NULL, + .OnUnload = NULL, }; } +static rt_game_api _game; +static rt_window _window; +static void *_game_obj = NULL; +static rt_launcher_api _launcher_api; + +static rt_window LauncherAPIGetWindow(void) { + return _window; +} + +static void *LauncherAPIAllocGameObj(size_t sz) { + if (_game_obj) { + /* Free the old one */ + if (_game.OnGameObjectFree) + _game.OnGameObjectFree(&_launcher_api, _game_obj); + free(_game_obj); + } + _game_obj = malloc(sz); + return _game_obj; +} + +static void *LauncherAPIGetGameObject(void) { + return _game_obj; +} + static void DisplayMonitors(void) { int count = 0; GLFWmonitor **monitors = glfwGetMonitors(&count); @@ -220,10 +250,16 @@ static int Entry(int argc, char **argv) { } g_renderer.RegisterCVARs(); + _launcher_api = (rt_launcher_api){ + .GetWindow = LauncherAPIGetWindow, + .AllocGameObject = LauncherAPIAllocGameObj, + .GetGameObject = LauncherAPIGetGameObject, + }; + /* Load the game */ const char *game_lib_cmdline = ParseCommandLineGameLib(argc, argv); - rt_game_api game = LoadGame(game_lib_cmdline); - game.RegisterCVARs(); + _game = LoadGame(game_lib_cmdline); + _game.RegisterCVARs(&_launcher_api); LoadGameAndRendererConfig(); ParseCommandLineCVARs(argc, argv); @@ -291,6 +327,8 @@ static int Entry(int argc, char **argv) { rtShutdownRuntime(); return -1; } + _window.type = RT_WINDOW_TYPE_GLFW; + _window.glfw = window; if (rt_LauncherCreateGLContext.i) { glfwMakeContextCurrent(window); @@ -316,7 +354,7 @@ static int Entry(int argc, char **argv) { return -1; } - if (game.Init() != RT_SUCCESS) { + if (_game.Init(&_launcher_api) != RT_SUCCESS) { rtReportError("LAUNCHER", "Failed to initialize the renderer."); if (_game_lib) rtCloseLib(_game_lib); @@ -329,6 +367,10 @@ static int Entry(int argc, char **argv) { rt_timestamp previous = rtTimeNow(); rt_time_delta lag = time_per_update; while (!glfwWindowShouldClose(window)) { +#ifdef RT_DEBUG + +#endif + glfwPollEvents(); rt_timestamp current = rtTimeNow(); @@ -339,18 +381,21 @@ static int Entry(int argc, char **argv) { /* TODO: Process input */ while (lag >= time_per_update) { - game.Update(time_per_update); + _game.Update(&_launcher_api, time_per_update, _game_obj); lag -= time_per_update; } - game.Render(); + int disp_w, disp_h; + glfwGetFramebufferSize(window, &disp_w, &disp_h); + rtLog("LAUNCHER", "%d %d", disp_w, disp_h); + _game.Render(&_launcher_api, _game_obj); if (rt_LauncherCreateGLContext.i) { glfwSwapBuffers(window); } } - game.Shutdown(); + _game.Shutdown(&_launcher_api, _game_obj); g_renderer.Shutdown(); glfwDestroyWindow(window); diff --git a/src/launcher/meson.build b/src/launcher/meson.build index b88f771..6284248 100644 --- a/src/launcher/meson.build +++ b/src/launcher/meson.build @@ -1,9 +1,4 @@ -launcher_deps = [thread_dep, m_dep] - -glfw_proj = subproject('glfw', default_options: ['default_library=static', 'warning_level=0', 'werror=false']) -glfw_dep = glfw_proj.get_variable('glfw_dep') - -launcher_deps += glfw_dep +launcher_deps = [thread_dep, glfw_dep, m_dep] launcher_link_libs = [runtime_lib] if get_option('default_library') == 'static'