#define RT_DONT_DEFINE_MAIN_LOOP_GLOBAL #include "main_loop.h" #include "runtime/runtime.h" #include "runtime/config.h" #include "gfx/gfx.h" RT_CVAR_I(rt_MaxFrameLatency, "Maximum latency between update and rendering. Default: 2", 2); RT_DLLEXPORT rt_main_loop g_main_loop; /* **************** * Update Thread * ****************/ void UpdateThreadEntry(void *param) { rtLog("UT", "UpdateThread Entry"); while (!g_main_loop.shutdown) { /* Wait until the render thread has catched up */ rtWaitOnSemaphore(&g_main_loop.update_proceed); rtLog("UT", "Processing %u", g_main_loop.u_frame_id); (g_main_loop.GameUpdate)(); rtLog("UT", "Finished %u", g_main_loop.u_frame_id); g_main_loop.u_frame_id += 1; /* Signal the render thread that data is available */ rtSignalSemaphore(&g_main_loop.render_proceed); } } /* **************** * Render Thread * ****************/ void RenderThreadEntry(void *param) { rtLog("RT", "RenderThread Entry"); while (!g_main_loop.shutdown) { rtWaitOnSemaphore(&g_main_loop.render_proceed); rtLog("RT", "Processing %u", g_main_loop.r_frame_id); rtBeginGFXFrame(g_main_loop.r_frame_id); (g_main_loop.GameRender)(); rtEndGFXFrame(g_main_loop.r_frame_id); rtLog("RT", "Finished %u", g_main_loop.r_frame_id); g_main_loop.r_frame_id += 1; /* Signal the update thread that we have finished and it can proceed */ rtSignalSemaphore(&g_main_loop.update_proceed); } } RT_DLLEXPORT rt_result rtInitMainLoop(rt_main_loop_update_fn *update_cb, rt_main_loop_render_fn *render_cb) { RT_ASSERT(g_main_loop.render_thread == NULL && g_main_loop.update_thread == NULL, "InitMainLoop called multiple times."); RT_ASSERT(update_cb != NULL && render_cb != NULL, "Valid update and render functions must be provided."); g_main_loop.r_frame_id = 0; g_main_loop.u_frame_id = 0; g_main_loop.shutdown = 0; g_main_loop.GameUpdate = update_cb; g_main_loop.GameRender = render_cb; int frame_latency = rt_MaxFrameLatency.i; if (frame_latency < 2) { rtLog("ML", "Forcing rt_MaxFrameLatency to minimum value 2."); frame_latency = 2; } else if (frame_latency > 3) { rtLog("ML", "Warning: rt_MaxFrameLatency is larger than 3 (%d)", frame_latency); } rt_create_semaphore_result update_proceed_create = rtCreateSemaphore(frame_latency, frame_latency); if (!update_proceed_create.ok) { rtReportError("ML", "Failed to create the update proceed semaphore."); return RT_UNKNOWN_ERROR; } g_main_loop.update_proceed = update_proceed_create.semaphore; rt_create_semaphore_result render_proceed_create = rtCreateSemaphore(0, frame_latency); if (!render_proceed_create.ok) { rtReportError("ML", "Failed to create the update proceed semaphore."); return RT_UNKNOWN_ERROR; } g_main_loop.render_proceed = render_proceed_create.semaphore; g_main_loop.update_thread = rtSpawnThread(UpdateThreadEntry, NULL, "UpdateThread"); if (!g_main_loop.update_thread) { rtReportError("ML", "Failed to spawn the update thread."); return RT_UNKNOWN_ERROR; } g_main_loop.render_thread = rtSpawnThread(RenderThreadEntry, NULL, "RenderThread"); if (!g_main_loop.render_thread) { rtReportError("ML", "Failed to spawn the render thread."); return RT_UNKNOWN_ERROR; } return RT_SUCCESS; } RT_DLLEXPORT void rtShutdownMainLoop(void) { g_main_loop.shutdown = 1; rtJoinThread(g_main_loop.update_thread); rtJoinThread(g_main_loop.render_thread); rtDestroySemaphore(&g_main_loop.update_proceed); rtDestroySemaphore(&g_main_loop.render_proceed); }