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) {
 | 
					RT_DLLEXPORT void rtShutdownAssetCompiler(void) {
 | 
				
			||||||
    _keep_running = false;
 | 
					    _keep_running = false;
 | 
				
			||||||
 | 
					    /* Wake the threads */
 | 
				
			||||||
 | 
					    rtLockConditionVar(_processing_queue.lock);
 | 
				
			||||||
 | 
					    rtUnlockConditionVar(_processing_queue.lock, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rtJoinThread(_compiler_thread);
 | 
					    rtJoinThread(_compiler_thread);
 | 
				
			||||||
    for (int i = 0; i < rt_AssetProcessingThreads.i; ++i)
 | 
					    for (int i = 0; i < rt_AssetProcessingThreads.i; ++i)
 | 
				
			||||||
        rtJoinThread(_processing_threads[i]);
 | 
					        rtJoinThread(_processing_threads[i]);
 | 
				
			||||||
 | 
				
			|||||||
@ -4,8 +4,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "asset_compiler/asset_compiler.h"
 | 
					#include "asset_compiler/asset_compiler.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static rt_framegraph *_framegraph;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RegisterCVars(void) {
 | 
					void RegisterCVars(void) {
 | 
				
			||||||
    rtRegisterAssetCompilerCVars();
 | 
					    rtRegisterAssetCompilerCVars();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -15,73 +13,10 @@ void Init(void) {
 | 
				
			|||||||
    rtLog("GAME", "Init");
 | 
					    rtLog("GAME", "Init");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rtInitAssetCompiler();
 | 
					    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 */
 | 
					/* Called after exiting the main-loop and before the runtime starts its shutdown */
 | 
				
			||||||
void Shutdown(void) {
 | 
					void Shutdown(void) {
 | 
				
			||||||
    rtLog("GAME", "Shutdown");
 | 
					    rtLog("GAME", "Shutdown");
 | 
				
			||||||
    rtShutdownAssetCompiler();
 | 
					    rtShutdownAssetCompiler();
 | 
				
			||||||
    rtDestroyFramegraph(_framegraph);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -4,6 +4,7 @@
 | 
				
			|||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#include "gfx.h"
 | 
					#include "gfx.h"
 | 
				
			||||||
#include "renderer_api.h"
 | 
					#include "renderer_api.h"
 | 
				
			||||||
 | 
					#include "main_loop.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -97,6 +98,10 @@ rtWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int n
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    app_callbacks.Init();
 | 
					    app_callbacks.Init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (rtInitMainLoop() != RT_SUCCESS) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Main Loop */
 | 
					    /* Main Loop */
 | 
				
			||||||
    bool keep_running = true;
 | 
					    bool keep_running = true;
 | 
				
			||||||
    while (keep_running) {
 | 
					    while (keep_running) {
 | 
				
			||||||
@ -113,6 +118,8 @@ rtWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int n
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    app_callbacks.Shutdown();
 | 
					    app_callbacks.Shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rtShutdownMainLoop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rtShutdownGFX();
 | 
					    rtShutdownGFX();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DestroyWindow(wnd);
 | 
					    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',
 | 
					  'handles.h',
 | 
				
			||||||
  'hashing.h',
 | 
					  'hashing.h',
 | 
				
			||||||
  'jobs.h',
 | 
					  'jobs.h',
 | 
				
			||||||
 | 
					  'main_loop.h',
 | 
				
			||||||
  'mem_arena.h',
 | 
					  'mem_arena.h',
 | 
				
			||||||
  'renderer_api.h',
 | 
					  'renderer_api.h',
 | 
				
			||||||
  'resources.h',
 | 
					  'resources.h',
 | 
				
			||||||
@ -38,6 +39,7 @@ runtime_lib = library('rt',
 | 
				
			|||||||
  'hashing.c',
 | 
					  'hashing.c',
 | 
				
			||||||
  'init.c',
 | 
					  'init.c',
 | 
				
			||||||
  'jobs.c',
 | 
					  'jobs.c',
 | 
				
			||||||
 | 
					  'main_loop.c',
 | 
				
			||||||
  'mem_arena.c',
 | 
					  'mem_arena.c',
 | 
				
			||||||
  'resource_manager.c',
 | 
					  'resource_manager.c',
 | 
				
			||||||
  'sprint.c',
 | 
					  'sprint.c',
 | 
				
			||||||
@ -45,6 +47,7 @@ runtime_lib = library('rt',
 | 
				
			|||||||
  'threading_cond.c',
 | 
					  'threading_cond.c',
 | 
				
			||||||
  'threading_mutex.c',
 | 
					  'threading_mutex.c',
 | 
				
			||||||
  'threading_rwlock.c',
 | 
					  'threading_rwlock.c',
 | 
				
			||||||
 | 
					  'threading_semaphore.c',
 | 
				
			||||||
  'threading_thread.c',
 | 
					  'threading_thread.c',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Contrib Sources
 | 
					  # Contrib Sources
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user