Add single-threaded framegraph execute function

This commit is contained in:
Kevin Trogant 2024-02-09 00:07:35 +01:00
parent abe05f3780
commit c23f03e622
8 changed files with 167 additions and 21 deletions

View File

@ -1,11 +1,17 @@
#include "runtime/app.h" #include "runtime/app.h"
extern void RegisterCVars(void);
extern void Init(void); extern void Init(void);
extern void Shutdown(void); extern void Shutdown(void);
extern void Update(void);
extern void Render(void);
static rt_app_callbacks _callbacks = { static rt_app_callbacks _callbacks = {
.Init = Init, .RegisterCVars = RegisterCVars,
.Shutdown = Shutdown, .Init = Init,
.Shutdown = Shutdown,
.Update = Update,
.Render = Render,
}; };
#ifdef _WIN32 #ifdef _WIN32

View File

@ -1,6 +1,7 @@
#include "runtime/gfx.h" #include "runtime/gfx.h"
#include "runtime/resources.h"
#include "runtime/mem_arena.h" #include "runtime/mem_arena.h"
#include "runtime/resources.h"
#include "runtime/threading.h"
#include "asset_compiler/asset_compiler.h" #include "asset_compiler/asset_compiler.h"
@ -8,15 +9,66 @@ void RegisterCVars(void) {
rtRegisterAssetCompilerCVars(); rtRegisterAssetCompilerCVars();
} }
static rt_framegraph *_framegraph;
static void PassPrepare(rt_render_pass_id pass,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count) {
rtLog("GAME", "Prepare pass %x", pass);
}
static void PassExecute(rt_render_pass_id pass,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count) {
rtLog("GAME", "Execute pass %x", pass);
}
static void PassFinalize(rt_render_pass_id pass,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count) {
rtLog("GAME", "Finalize pass %x", pass);
}
/* Called after the runtime has finished its initialization and before entering the main-loop*/ /* Called after the runtime has finished its initialization and before entering the main-loop*/
void Init(void) { void Init(void) {
rtLog("GAME", "Init"); rtLog("GAME", "Init");
rtInitAssetCompiler(); rtInitAssetCompiler();
rt_resource_id resid = rtGetResourceID("assets/test.framegraph");
while (rtGetResourceSize(resid) == 0)
rtSleep(10);
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
size_t size = rtGetResourceSize(resid);
rt_resource *res = rtArenaPush(temp.arena, size);
rtGetResource(resid, res);
_framegraph = rtCreateFramegraph(res->data);
rt_render_pass_bind_fns bind = {.Execute = PassExecute,
.Prepare = PassPrepare,
.Finalize = PassFinalize};
rtBindRenderPass(_framegraph, rtCalculateRenderPassID("pass0", sizeof("pass0")-1), &bind);
rtBindRenderPass(_framegraph, rtCalculateRenderPassID("pass1", sizeof("pass1")-1), &bind);
} }
/* 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);
}
void Update(void) {
}
void Render(void) {
rtExecuteFramegraph(_framegraph);
} }

View File

@ -98,7 +98,7 @@ rtWin32Entry(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int n
app_callbacks.Init(); app_callbacks.Init();
if (rtInitMainLoop() != RT_SUCCESS) { if (rtInitMainLoop(app_callbacks.Update, app_callbacks.Render) != RT_SUCCESS) {
return 1; return 1;
} }

View File

@ -4,6 +4,7 @@
/* Platform specific application entry point */ /* Platform specific application entry point */
#include "runtime.h" #include "runtime.h"
#include "main_loop.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -23,6 +24,12 @@ typedef struct {
/* Called after the main-loop and before the runtime starts its shutdown. */ /* Called after the main-loop and before the runtime starts its shutdown. */
rt_app_shutdown_fn *Shutdown; rt_app_shutdown_fn *Shutdown;
/* Called by the update thread for each frame */
rt_main_loop_update_fn *Update;
/* Called by the render thread for each frame */
rt_main_loop_render_fn *Render;
} rt_app_callbacks; } rt_app_callbacks;
#ifdef _WIN32 #ifdef _WIN32

View File

@ -28,7 +28,7 @@ typedef union {
} rt_color; } rt_color;
/* NOTE(kevin): When you add a value here, you need to handle them in /* NOTE(kevin): When you add a value here, you need to handle them in
* framegraph_processor.c : ParseFramegraph * framegraph_processor.c : ParseFramegraph
* and in the render target and texture functions of all renderers. */ * and in the render target and texture functions of all renderers. */
typedef enum { typedef enum {
@ -62,8 +62,8 @@ RT_DLLEXPORT rt_result rtInitGFX(rt_renderer_init_info *renderer_info);
RT_DLLEXPORT void rtShutdownGFX(void); RT_DLLEXPORT void rtShutdownGFX(void);
/* ********************************************************************* /* *********************************************************************
* Framegraph API * Framegraph API
* *
* The framegraph is used to organize and schedule the work for a frame. * The framegraph is used to organize and schedule the work for a frame.
* *********************************************************************/ * *********************************************************************/
@ -131,9 +131,21 @@ typedef struct {
uint32_t render_pass_count; uint32_t render_pass_count;
} rt_framegraph_info; } rt_framegraph_info;
typedef void rt_render_pass_prepare_fn(rt_render_pass_id id); typedef void rt_render_pass_prepare_fn(rt_render_pass_id id,
typedef void rt_render_pass_execute_fn(rt_render_pass_id id); const rt_render_target_write *writes,
typedef void rt_render_pass_finalize_fn(rt_render_pass_id id); uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count);
typedef void rt_render_pass_execute_fn(rt_render_pass_id id,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count);
typedef void rt_render_pass_finalize_fn(rt_render_pass_id id,
const rt_render_target_write *writes,
uint32_t write_count,
const rt_render_target_read *reads,
uint32_t read_count);
typedef struct { typedef struct {
rt_render_pass_prepare_fn *Prepare; rt_render_pass_prepare_fn *Prepare;
@ -141,14 +153,17 @@ typedef struct {
rt_render_pass_finalize_fn *Finalize; rt_render_pass_finalize_fn *Finalize;
} rt_render_pass_bind_fns; } rt_render_pass_bind_fns;
typedef struct rt_framegraph_s rt_framegraph; typedef struct rt_framegraph_s rt_framegraph;
RT_DLLEXPORT rt_framegraph *rtCreateFramegraph(const rt_framegraph_info *info); RT_DLLEXPORT rt_framegraph *rtCreateFramegraph(const rt_framegraph_info *info);
RT_DLLEXPORT void rtDestroyFramegraph(rt_framegraph *framegraph); RT_DLLEXPORT void rtDestroyFramegraph(rt_framegraph *framegraph);
RT_DLLEXPORT void rtBindRenderPass(rt_framegraph *framegraph, rt_render_pass_id pass, const rt_render_pass_bind_fns *bind_fns); RT_DLLEXPORT void rtBindRenderPass(rt_framegraph *framegraph,
rt_render_pass_id pass,
const rt_render_pass_bind_fns *bind_fns);
RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph);
/* Utility to turn a string into a usable render target id. */ /* Utility to turn a string into a usable render target id. */
RT_DLLEXPORT rt_render_target_id rtCalculateRenderTargetID(const char *name, size_t len); RT_DLLEXPORT rt_render_target_id rtCalculateRenderTargetID(const char *name, size_t len);

View File

@ -18,6 +18,7 @@ RT_CVAR_I(rt_MaxFramegraphs, "Maximum number of framegraphs. Default 16", 16);
#define RT_RENDERPASS_MAX_WRITES 8 #define RT_RENDERPASS_MAX_WRITES 8
typedef struct { typedef struct {
rt_render_target_id id;
rt_pixel_format format; rt_pixel_format format;
unsigned int width; unsigned int width;
unsigned int height; unsigned int height;
@ -26,6 +27,7 @@ typedef struct {
} rt_render_target; } rt_render_target;
typedef struct { typedef struct {
rt_render_pass_id id;
int execution_level; int execution_level;
unsigned int read_count; unsigned int read_count;
unsigned int write_count; unsigned int write_count;
@ -40,10 +42,8 @@ struct rt_framegraph_s {
rt_framegraph *next_free; rt_framegraph *next_free;
rt_render_pass_id pass_ids[RT_FRAMEGRAPH_MAX_PASSES];
rt_render_pass passes[RT_FRAMEGRAPH_MAX_PASSES]; rt_render_pass passes[RT_FRAMEGRAPH_MAX_PASSES];
rt_render_target_id render_target_ids[RT_FRAMEGRAPH_MAX_RENDER_TARGETS];
rt_render_target render_targets[RT_FRAMEGRAPH_MAX_RENDER_TARGETS]; rt_render_target render_targets[RT_FRAMEGRAPH_MAX_RENDER_TARGETS];
}; };
@ -224,6 +224,7 @@ CreateRenderPasses(rt_framegraph *graph, const rt_framegraph_info *info, rt_aren
pass_info[i].read_render_target_count * sizeof(rt_render_target_read)); pass_info[i].read_render_target_count * sizeof(rt_render_target_read));
graph->passes[i].write_count = pass_info[i].write_render_target_count; graph->passes[i].write_count = pass_info[i].write_render_target_count;
graph->passes[i].read_count = pass_info[i].read_render_target_count; graph->passes[i].read_count = pass_info[i].read_render_target_count;
graph->passes[i].id = pass_info[i].id;
} }
/* Sort by execution level */ /* Sort by execution level */
@ -244,7 +245,7 @@ CreateRenderTargets(rt_framegraph *graph, const rt_framegraph_info *info, rt_are
/* TODO(Kevin): determine aliasing opportunities */ /* TODO(Kevin): determine aliasing opportunities */
const rt_render_target_info *render_targets = rtResolveConstRelptr(&info->render_targets); const rt_render_target_info *render_targets = rtResolveConstRelptr(&info->render_targets);
for (uint32_t i = 0; i < info->render_target_count; ++i) { for (uint32_t i = 0; i < info->render_target_count; ++i) {
graph->render_target_ids[i] = render_targets[i].id; graph->render_targets[i].id = render_targets[i].id;
graph->render_targets[i].format = render_targets[i].format; graph->render_targets[i].format = render_targets[i].format;
graph->render_targets[i].width = render_targets[i].width; graph->render_targets[i].width = render_targets[i].width;
graph->render_targets[i].height = render_targets[i].height; graph->render_targets[i].height = render_targets[i].height;
@ -375,7 +376,7 @@ RT_DLLEXPORT void rtBindRenderPass(rt_framegraph *framegraph,
rt_render_pass_id id, rt_render_pass_id id,
const rt_render_pass_bind_fns *bind_fns) { const rt_render_pass_bind_fns *bind_fns) {
for (uint32_t i = 0; i < framegraph->pass_count; ++i) { for (uint32_t i = 0; i < framegraph->pass_count; ++i) {
if (framegraph->pass_ids[i] == id) { if (framegraph->passes[i].id == id) {
if (framegraph->passes[i].bound_fns.Execute) if (framegraph->passes[i].bound_fns.Execute)
rtLog("GFX", "Rebound pass %x to new functions", id); rtLog("GFX", "Rebound pass %x to new functions", id);
framegraph->passes[i].bound_fns = *bind_fns; framegraph->passes[i].bound_fns = *bind_fns;
@ -385,6 +386,58 @@ RT_DLLEXPORT void rtBindRenderPass(rt_framegraph *framegraph,
rtLog("GFX", "Tried to bind functions to unknown render pass %x", id); rtLog("GFX", "Tried to bind functions to unknown render pass %x", id);
} }
RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) {
int execution_level = framegraph->passes[0].execution_level;
uint32_t level_start = 0;
for (uint32_t i = 0; i <= framegraph->pass_count && level_start < framegraph->pass_count; ++i) {
if ((i == framegraph->pass_count) ||
(framegraph->passes[i].execution_level > execution_level)) {
/* Dispatch all passes in the current execution level */
for (uint32_t pass_idx = level_start; pass_idx < i; ++pass_idx) {
bool pass_bound = framegraph->passes[pass_idx].bound_fns.Prepare != NULL &&
framegraph->passes[pass_idx].bound_fns.Execute != NULL &&
framegraph->passes[pass_idx].bound_fns.Finalize != NULL;
if (!pass_bound) {
rtLog("GFX",
"Framegraph pass %u (%x) is not bound to any function.",
pass_idx,
framegraph->passes[pass_idx].id);
continue;
}
rt_render_pass_id id = framegraph->passes[pass_idx].id;
const rt_render_target_write *writes = framegraph->passes[pass_idx].writes;
const rt_render_target_read *reads = framegraph->passes[pass_idx].reads;
uint32_t write_count = framegraph->passes[pass_idx].write_count;
uint32_t read_count = framegraph->passes[pass_idx].read_count;
/* TODO(Kevin): Every one of these should be a job-dispatch*/
framegraph->passes[pass_idx].bound_fns.Prepare(id,
writes,
write_count,
reads,
read_count);
framegraph->passes[pass_idx].bound_fns.Execute(id,
writes,
write_count,
reads,
read_count);
framegraph->passes[pass_idx].bound_fns.Finalize(id,
writes,
write_count,
reads,
read_count);
}
/* Start next level */
level_start = i;
if (i < framegraph->pass_count)
execution_level = framegraph->passes[i].execution_level;
}
}
}
RT_DLLEXPORT rt_render_target_id rtCalculateRenderTargetID(const char *name, size_t len) { RT_DLLEXPORT rt_render_target_id rtCalculateRenderTargetID(const char *name, size_t len) {
rt_render_target_id id = rtHashBytes32(name, len); rt_render_target_id id = rtHashBytes32(name, len);
if (id == 0) if (id == 0)

View File

@ -17,7 +17,7 @@ void UpdateThreadEntry(void *param) {
rtWaitOnSemaphore(&g_main_loop.update_proceed); rtWaitOnSemaphore(&g_main_loop.update_proceed);
rtLog("UT", "Processing %d", g_main_loop.u_frame_id); rtLog("UT", "Processing %d", g_main_loop.u_frame_id);
rtSleep(250); (g_main_loop.GameUpdate)();
rtLog("UT", "Finished %d", g_main_loop.u_frame_id); rtLog("UT", "Finished %d", g_main_loop.u_frame_id);
g_main_loop.u_frame_id += 1; g_main_loop.u_frame_id += 1;
@ -36,7 +36,7 @@ void RenderThreadEntry(void *param) {
rtWaitOnSemaphore(&g_main_loop.render_proceed); rtWaitOnSemaphore(&g_main_loop.render_proceed);
rtLog("RT", "Processing %d", g_main_loop.r_frame_id); rtLog("RT", "Processing %d", g_main_loop.r_frame_id);
rtSleep(500); (g_main_loop.GameRender)();
rtLog("RT", "Finished %d", g_main_loop.r_frame_id); rtLog("RT", "Finished %d", g_main_loop.r_frame_id);
g_main_loop.r_frame_id += 1; g_main_loop.r_frame_id += 1;
@ -45,12 +45,18 @@ void RenderThreadEntry(void *param) {
} }
} }
RT_DLLEXPORT rt_result rtInitMainLoop(void) { 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, RT_ASSERT(g_main_loop.render_thread == NULL && g_main_loop.update_thread == NULL,
"InitMainLoop called multiple times."); "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.r_frame_id = 0;
g_main_loop.u_frame_id = 0; g_main_loop.u_frame_id = 0;
g_main_loop.shutdown = 0; g_main_loop.shutdown = 0;
g_main_loop.GameUpdate = update_cb;
g_main_loop.GameRender = render_cb;
int frame_latency = rt_MaxFrameLatency.i; int frame_latency = rt_MaxFrameLatency.i;
if (frame_latency < 2) { if (frame_latency < 2) {

View File

@ -4,6 +4,9 @@
#include "runtime.h" #include "runtime.h"
#include "threading.h" #include "threading.h"
typedef void rt_main_loop_update_fn(void);
typedef void rt_main_loop_render_fn(void);
typedef struct { typedef struct {
int u_frame_id; int u_frame_id;
int r_frame_id; int r_frame_id;
@ -14,13 +17,17 @@ typedef struct {
rt_thread *update_thread; rt_thread *update_thread;
rt_thread *render_thread; rt_thread *render_thread;
rt_main_loop_update_fn *GameUpdate;
rt_main_loop_render_fn *GameRender;
volatile int shutdown; volatile int shutdown;
} rt_main_loop; } rt_main_loop;
/* The applications main-loop */ /* The applications main-loop */
extern RT_DLLIMPORT rt_main_loop g_main_loop; extern RT_DLLIMPORT rt_main_loop g_main_loop;
RT_DLLEXPORT rt_result rtInitMainLoop(void); RT_DLLEXPORT rt_result rtInitMainLoop(rt_main_loop_update_fn *update_cb,
rt_main_loop_render_fn *render_cb);
RT_DLLEXPORT void rtShutdownMainLoop(void); RT_DLLEXPORT void rtShutdownMainLoop(void);