Now we need to draw something and also have the correct semaphore waits to establish dependencies.
109 lines
3.8 KiB
C
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);
|
|
} |