Add basic infrastructure for running update and render threads

Communicate progress via a pair of semaphores
This commit is contained in:
Kevin Trogant 2024-02-08 23:06:23 +01:00
parent 4f27819fa2
commit abe05f3780
6 changed files with 139 additions and 65 deletions

View File

@ -134,6 +134,10 @@ RT_DLLEXPORT rt_result rtInitAssetCompiler(void) {
RT_DLLEXPORT void rtShutdownAssetCompiler(void) {
_keep_running = false;
/* Wake the threads */
rtLockConditionVar(_processing_queue.lock);
rtUnlockConditionVar(_processing_queue.lock, true);
rtJoinThread(_compiler_thread);
for (int i = 0; i < rt_AssetProcessingThreads.i; ++i)
rtJoinThread(_processing_threads[i]);

View File

@ -4,8 +4,6 @@
#include "asset_compiler/asset_compiler.h"
static rt_framegraph *_framegraph;
void RegisterCVars(void) {
rtRegisterAssetCompilerCVars();
}
@ -15,73 +13,10 @@ void Init(void) {
rtLog("GAME", "Init");
rtInitAssetCompiler();
#if 0
rt_render_target_id rt_ids[4] = {rtCalculateRenderTargetID("rt0", sizeof("rt0")),
rtCalculateRenderTargetID("rt1", sizeof("rt1")),
rtCalculateRenderTargetID("rt2", sizeof("rt2")),
rtCalculateRenderTargetID("rt3", sizeof("rt3")),
};
rt_render_target_info rts[4] = {
{.id = rt_ids[0], .width = 1024, .height = 768, .format = RT_PIXEL_FORMAT_R8G8B8A8_SRGB, .sample_count = 4},
{.id = rt_ids[1], .width = 1024, .height = 768, .format = RT_PIXEL_FORMAT_R8G8B8A8_SRGB, .sample_count = 4},
{.id = rt_ids[2], .width = 1024, .height = 768, .format = RT_PIXEL_FORMAT_R8G8B8A8_SRGB, .sample_count = 4},
{.id = rt_ids[3], .width = 1024, .height = 768, .format = RT_PIXEL_FORMAT_R8G8B8A8_SRGB, .sample_count = 4}
};
rt_render_target_write pass0_writes[] = {{.render_target = rt_ids[0]},
{.render_target = rt_ids[1]}};
rt_render_target_read pass1_reads[] = {{.render_target = rt_ids[0]},
{.render_target = rt_ids[1]}};
rt_render_target_write pass1_writes[] = {{.render_target = rt_ids[2]}};
rt_render_target_read pass2_reads[] = {{.render_target = rt_ids[2]},
{.render_target = rt_ids[1]}};
rt_render_target_write pass2_writes[] = {{.render_target = rt_ids[3]}};
rt_render_target_write pass3_writes[] = {{.render_target = rt_ids[2]}};
rt_render_pass_info passes[4];
rtSetRelptr(&passes[0].read_render_targets, NULL);
rtSetRelptr(&passes[0].write_render_targets, pass0_writes);
passes[0].read_render_target_count = 0;
passes[0].write_render_target_count = RT_ARRAY_COUNT(pass0_writes);
rtSetRelptr(&passes[1].read_render_targets, pass1_reads);
rtSetRelptr(&passes[1].write_render_targets, pass1_writes);
passes[1].read_render_target_count = RT_ARRAY_COUNT(pass1_reads);
passes[1].write_render_target_count = RT_ARRAY_COUNT(pass1_writes);
rtSetRelptr(&passes[2].read_render_targets, pass2_reads);
rtSetRelptr(&passes[2].write_render_targets, pass2_writes);
passes[2].read_render_target_count = RT_ARRAY_COUNT(pass2_reads);
passes[2].write_render_target_count = RT_ARRAY_COUNT(pass2_writes);
rtSetRelptr(&passes[3].read_render_targets, NULL);
rtSetRelptr(&passes[3].write_render_targets, pass3_writes);
passes[3].read_render_target_count = 0;
passes[3].write_render_target_count = RT_ARRAY_COUNT(pass3_writes);
rt_framegraph_info info = {
.render_pass_count = 4,
.render_target_count = 4,
};
rtSetRelptr(&info.render_passes, passes);
rtSetRelptr(&info.render_targets, rts);
#endif
rt_resource_id id = rtGetResourceID("assets/test.framegraph");
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
while (rtGetResourceSize(id) == 0)
;
rt_resource *resource =
rtArenaPush(temp.arena, rtGetResourceSize(id));
rtGetResource(id, resource);
_framegraph = rtCreateFramegraph(resource->data);
}
/* Called after exiting the main-loop and before the runtime starts its shutdown */
void Shutdown(void) {
rtLog("GAME", "Shutdown");
rtShutdownAssetCompiler();
rtDestroyFramegraph(_framegraph);
}

View File

@ -4,6 +4,7 @@
#include "config.h"
#include "gfx.h"
#include "renderer_api.h"
#include "main_loop.h"
#include <stdbool.h>
@ -97,6 +98,10 @@ rtWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int n
app_callbacks.Init();
if (rtInitMainLoop() != RT_SUCCESS) {
return 1;
}
/* Main Loop */
bool keep_running = true;
while (keep_running) {
@ -113,6 +118,8 @@ rtWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int n
app_callbacks.Shutdown();
rtShutdownMainLoop();
rtShutdownGFX();
DestroyWindow(wnd);

98
src/runtime/main_loop.c Normal file
View File

@ -0,0 +1,98 @@
#include "main_loop.h"
#include "runtime.h"
#include "config.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 %d", g_main_loop.u_frame_id);
rtSleep(250);
rtLog("UT", "Finished %d", 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 %d", g_main_loop.r_frame_id);
rtSleep(500);
rtLog("RT", "Finished %d", 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(void) {
RT_ASSERT(g_main_loop.render_thread == NULL && g_main_loop.update_thread == NULL,
"InitMainLoop called multiple times.");
g_main_loop.r_frame_id = 0;
g_main_loop.u_frame_id = 0;
g_main_loop.shutdown = 0;
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);
}

27
src/runtime/main_loop.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef RT_MAIN_LOOP_H
#define RT_MAIN_LOOP_H
#include "runtime.h"
#include "threading.h"
typedef struct {
int u_frame_id;
int r_frame_id;
rt_semaphore update_proceed;
rt_semaphore render_proceed;
rt_thread *update_thread;
rt_thread *render_thread;
volatile int shutdown;
} rt_main_loop;
/* The applications main-loop */
extern RT_DLLIMPORT rt_main_loop g_main_loop;
RT_DLLEXPORT rt_result rtInitMainLoop(void);
RT_DLLEXPORT void rtShutdownMainLoop(void);
#endif

View File

@ -16,6 +16,7 @@ runtime_lib = library('rt',
'handles.h',
'hashing.h',
'jobs.h',
'main_loop.h',
'mem_arena.h',
'renderer_api.h',
'resources.h',
@ -38,6 +39,7 @@ runtime_lib = library('rt',
'hashing.c',
'init.c',
'jobs.c',
'main_loop.c',
'mem_arena.c',
'resource_manager.c',
'sprint.c',
@ -45,6 +47,7 @@ runtime_lib = library('rt',
'threading_cond.c',
'threading_mutex.c',
'threading_rwlock.c',
'threading_semaphore.c',
'threading_thread.c',
# Contrib Sources