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