rtengine/src/app_framework/main_loop.c
Kevin Trogant bc6076b786 Seems like a valid chain of image transitions.
Now we need to draw something and also have the correct semaphore waits
to establish dependencies.
2024-02-20 13:47:47 +01:00

109 lines
3.8 KiB
C

#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);
}