Add basic infrastructure for running update and render threads
Communicate progress via a pair of semaphores
This commit is contained in:
parent
4f27819fa2
commit
abe05f3780
@ -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]);
|
||||
|
@ -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);
|
||||
}
|
@ -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
98
src/runtime/main_loop.c
Normal 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
27
src/runtime/main_loop.h
Normal 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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user