From cc49a017f99255d51de74e58bf576a01e0026694 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Tue, 6 Feb 2024 17:57:14 +0100 Subject: [PATCH] Load framegraphs from resource files --- assets/test.framegraph | 64 ++++++++++++++++++++++++++++++++ meson.build | 3 ++ src/game/main.c | 13 ++++++- src/renderer/vk/helper.c | 18 +++++++++ src/renderer/vk/render_targets.c | 2 +- src/runtime/asset_compiler.c | 4 +- src/runtime/gfx.h | 57 ++++++++++++++++++++-------- src/runtime/gfx_framegraph.c | 2 + src/runtime/resource_manager.c | 57 +++++++++++++++++++++++++--- src/runtime/resources.h | 6 +++ src/runtime/threading.h | 20 ++++++++++ src/runtime/threading_rwlock.c | 4 +- 12 files changed, 222 insertions(+), 28 deletions(-) create mode 100644 assets/test.framegraph diff --git a/assets/test.framegraph b/assets/test.framegraph new file mode 100644 index 0000000..d05a895 --- /dev/null +++ b/assets/test.framegraph @@ -0,0 +1,64 @@ +render_targets { + color0 { + format R8G8B8A8_SRGB; + width 1024; + height 768; + sample_count 4; + } + + swapchain_out { + format SWAPCHAIN; + width SWAPCHAIN; + height SWAPCHAIN; + sample_count 1; + } + + depth0 { + format DEPTH24_STENCIL8; + width 512; + height 384; + sample_count 4; + } +} + +passes { + pass0 { + writes { + color0 { + clear_value { + r 1.0; + g 1.0; + b 1.0; + a 1.0; + } + + clear YES; + discard NO; + } + + depth0 { + clear_value { + d 0.0; + s 0; + } + + clear YES; + discard YES; + } + } + } + + pass1 { + reads { + color0 { + mode SAMPLED; + } + } + writes { + swapchain_out { + clear NO; + discard NO; + } + } + } +} diff --git a/meson.build b/meson.build index b572bfc..9350b5a 100644 --- a/meson.build +++ b/meson.build @@ -28,6 +28,8 @@ elif compiler.get_argument_syntax() == 'msvc' if buildtype == 'debug' add_project_arguments(['/RTCsu'], language : ['c', 'cpp']) endif + # Allow nameless struct/unions (standard in C11) + add_project_arguments(['/wd4201'], language : 'cpp') endif if get_option('default_library') == 'static' @@ -98,6 +100,7 @@ if get_option('build_asset_compiler') 'src/runtime/asset_compiler.c', 'src/runtime/description_parser.c', + 'src/runtime/framegraph_processor.c', 'src/runtime/pipeline_processor.c', 'src/runtime/shader_compiler.c', ] diff --git a/src/game/main.c b/src/game/main.c index 06065e1..a583190 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -1,4 +1,6 @@ #include "runtime/gfx.h" +#include "runtime/resources.h" +#include "runtime/mem_arena.h" static rt_framegraph *_framegraph; @@ -6,6 +8,7 @@ static rt_framegraph *_framegraph; void Init(void) { rtLog("GAME", "Init"); + #if 0 rt_render_target_id rt_ids[4] = {rtCalculateRenderTargetID("rt0", sizeof("rt0")), rtCalculateRenderTargetID("rt1", sizeof("rt1")), rtCalculateRenderTargetID("rt2", sizeof("rt2")), @@ -54,8 +57,16 @@ void Init(void) { }; rtSetRelptr(&info.render_passes, passes); rtSetRelptr(&info.render_targets, rts); + #endif - _framegraph = rtCreateFramegraph(&info); + rt_resource_id id = rtGetResourceID("assets/test.framegraph"); + rt_temp_arena temp = rtGetTemporaryArena(NULL, 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 */ diff --git a/src/renderer/vk/helper.c b/src/renderer/vk/helper.c index 1afdc4d..81fbf2d 100644 --- a/src/renderer/vk/helper.c +++ b/src/renderer/vk/helper.c @@ -2,10 +2,26 @@ VkFormat rtPixelFormatToVkFormat(rt_pixel_format format) { switch (format) { + case RT_PIXEL_FORMAT_R8G8B8A8_UNORM: + return VK_FORMAT_R8G8B8A8_UNORM; + case RT_PIXEL_FORMAT_B8G8R8A8_UNORM: + return VK_FORMAT_B8G8R8A8_UNORM; case RT_PIXEL_FORMAT_R8G8B8A8_SRGB: return VK_FORMAT_R8G8B8A8_SRGB; + case RT_PIXEL_FORMAT_B8G8R8A8_SRGB: + return VK_FORMAT_B8G8R8A8_SRGB; + case RT_PIXEL_FORMAT_R8G8B8_UNORM: + return VK_FORMAT_R8G8B8_UNORM; + case RT_PIXEL_FORMAT_B8G8R8_UNORM: + return VK_FORMAT_B8G8R8_UNORM; + case RT_PIXEL_FORMAT_R8G8B8_SRGB: + return VK_FORMAT_R8G8B8_SRGB; + case RT_PIXEL_FORMAT_B8G8R8_SRGB: + return VK_FORMAT_B8G8R8_SRGB; case RT_PIXEL_FORMAT_DEPTH24_STENCIL8: return VK_FORMAT_D24_UNORM_S8_UINT; + case RT_PIXEL_FORMAT_DEPTH32: + return VK_FORMAT_D32_SFLOAT; default: return VK_FORMAT_UNDEFINED; } @@ -20,6 +36,8 @@ VkSampleCountFlagBits rtSampleCountToFlags(unsigned int count) { while (count > 1) { if ((counts & count) == 0) count >>= 1; + else + break; } return (VkSampleCountFlagBits)count; } \ No newline at end of file diff --git a/src/renderer/vk/render_targets.c b/src/renderer/vk/render_targets.c index f198861..b111cdd 100644 --- a/src/renderer/vk/render_targets.c +++ b/src/renderer/vk/render_targets.c @@ -175,7 +175,7 @@ rt_render_target_handle RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_t slot->render_target.format = g_swapchain.format; slot->render_target.match_swapchain |= RT_RENDER_TARGET_MATCH_SWAPCHAIN_FORMAT; } - if (info->format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8) { + if (info->format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8 || info->format == RT_PIXEL_FORMAT_DEPTH32) { slot->render_target.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; diff --git a/src/runtime/asset_compiler.c b/src/runtime/asset_compiler.c index 40f152a..bd3d0c8 100644 --- a/src/runtime/asset_compiler.c +++ b/src/runtime/asset_compiler.c @@ -65,9 +65,11 @@ static rt_asset_db _asset_db; static rt_processing_queue _processing_queue; extern RT_ASSET_PROCESSOR_FN(PipelineProcessor); +extern RT_ASSET_PROCESSOR_FN(FramegraphProcessor); static rt_asset_processor _processors[] = { - {.file_ext = ".pipeline", .proc = PipelineProcessor} + {.file_ext = ".pipeline", .proc = PipelineProcessor}, + {.file_ext = ".framegraph", .proc = FramegraphProcessor}, }; static void ProcessorThreadEntry(void *); diff --git a/src/runtime/gfx.h b/src/runtime/gfx.h index 639752e..84d8fa0 100644 --- a/src/runtime/gfx.h +++ b/src/runtime/gfx.h @@ -17,10 +17,34 @@ extern "C" { #endif +typedef union { + float v[4]; + struct { + float r; + float g; + float b; + float a; + }; +} rt_color; + +/* NOTE(kevin): When you add a value here, you need to handle them in + * framegraph_processor.c : ParseFramegraph + * and in the render target and texture functions of all renderers. */ + typedef enum { RT_PIXEL_FORMAT_INVALID, + + RT_PIXEL_FORMAT_R8G8B8A8_UNORM, + RT_PIXEL_FORMAT_B8G8R8A8_UNORM, RT_PIXEL_FORMAT_R8G8B8A8_SRGB, + RT_PIXEL_FORMAT_B8G8R8A8_SRGB, + RT_PIXEL_FORMAT_R8G8B8_UNORM, + RT_PIXEL_FORMAT_B8G8R8_UNORM, + RT_PIXEL_FORMAT_R8G8B8_SRGB, + RT_PIXEL_FORMAT_B8G8R8_SRGB, + RT_PIXEL_FORMAT_DEPTH24_STENCIL8, + RT_PIXEL_FORMAT_DEPTH32, /* Special value indicating whichever format the swapchain uses */ RT_PIXEL_FORMAT_SWAPCHAIN, @@ -28,6 +52,21 @@ typedef enum { RT_PIXEL_FORMAT_count, } rt_pixel_format; +/* In renderer_api.h -> Not necessary for almost all gfx usage */ +typedef struct rt_renderer_init_info_s rt_renderer_init_info; + +RT_DLLEXPORT void rtRegisterRendererCVars(void); + +RT_DLLEXPORT rt_result rtInitGFX(rt_renderer_init_info *renderer_info); + +RT_DLLEXPORT void rtShutdownGFX(void); + +/* ********************************************************************* + * Framegraph API + * + * The framegraph is used to organize and schedule the work for a frame. + * *********************************************************************/ + /* Special value for the .width and .height fields of rt_render_target_info * to indicate that these should be set to the width or height of the swapchain, respectively. */ #define RT_RENDER_TARGET_SIZE_SWAPCHAIN 0 @@ -44,8 +83,8 @@ typedef struct { } rt_render_target_info; typedef enum { - RT_RENDER_TARGET_READ_INPUT_ATTACHMENT, RT_RENDER_TARGET_READ_SAMPLED, + RT_RENDER_TARGET_READ_INPUT_ATTACHMENT, RT_RENDER_TARGET_READ_count, } rt_render_target_read_mode; @@ -66,7 +105,7 @@ typedef enum { typedef struct { rt_render_target_id render_target; union { - float color[4]; + rt_color color; struct { float depth; int32_t stencil; @@ -102,20 +141,6 @@ typedef struct { rt_render_pass_finalize_fn *Finalize; } rt_render_pass_bind_fns; -/* In renderer_api.h -> Not necessary for almost all gfx usage */ -typedef struct rt_renderer_init_info_s rt_renderer_init_info; - -RT_DLLEXPORT void rtRegisterRendererCVars(void); - -RT_DLLEXPORT rt_result rtInitGFX(rt_renderer_init_info *renderer_info); - -RT_DLLEXPORT void rtShutdownGFX(void); - - -/* Framegraph API - * - * The framegraph is used to organize and schedule the work for a frame. - */ typedef struct rt_framegraph_s rt_framegraph; diff --git a/src/runtime/gfx_framegraph.c b/src/runtime/gfx_framegraph.c index cabb552..dec4afb 100644 --- a/src/runtime/gfx_framegraph.c +++ b/src/runtime/gfx_framegraph.c @@ -310,6 +310,7 @@ static bool ValidateInfo(const rt_framegraph_info *info) { rtReportError( "GFX", "Framegraph pass %u reads too many rendertargets: %u (maximum allowed is %u)", + i, passes[i].read_render_target_count, RT_RENDERPASS_MAX_READS); return false; @@ -317,6 +318,7 @@ static bool ValidateInfo(const rt_framegraph_info *info) { rtReportError( "GFX", "Framegraph pass %u writes too many rendertargets: %u (maximum allowed is %u)", + i, passes[i].write_render_target_count, RT_RENDERPASS_MAX_WRITES); return false; diff --git a/src/runtime/resource_manager.c b/src/runtime/resource_manager.c index 15652b5..3301e07 100644 --- a/src/runtime/resource_manager.c +++ b/src/runtime/resource_manager.c @@ -98,6 +98,16 @@ static size_t GetResourceDataSize(const rt_resource *resource) { const rt_shader_info *info = resource->data; return sizeof(rt_shader_info) + (info) ? info->bytecode_length : 0; } break; + case RT_RESOURCE_FRAMEGRAPH: { + const rt_framegraph_info *info = resource->data; + size_t size = sizeof(*info) + sizeof(rt_render_target_info) * info->render_target_count + + sizeof(rt_render_pass_info) * info->render_pass_count; + const rt_render_pass_info *passes = rtResolveConstRelptr(&info->render_passes); + for (uint32_t i = 0; i < info->render_pass_count; ++i) { + size += passes[i].read_render_target_count * sizeof(rt_render_target_read) + + passes[i].write_render_target_count * sizeof(rt_render_target_write); + } + } break; default: rtLog("RESMGR", "Tried to get size of an invalid resource type %u", resource->type); } @@ -117,8 +127,38 @@ static void CopyResourceData(const rt_resource *resource, void *dest) { memcpy(dest_info + 1, rtResolveConstRelptr(&info->bytecode), info->bytecode_length); rtSetRelptr(&dest_info->bytecode, (void *)(dest_info + 1)); } break; + case RT_RESOURCE_FRAMEGRAPH: { + const rt_framegraph_info *info = resource->data; + rt_framegraph_info *dest_info = dest; + memcpy(dest_info, info, sizeof(*info)); + memcpy(dest_info + 1, + rtResolveConstRelptr(&info->render_targets), + info->render_target_count * sizeof(rt_render_target_info)); + rtSetRelptr(&dest_info->render_targets, (void *)(dest_info + 1)); + char *passes_begin = + (char *)(dest_info + 1) + info->render_target_count * sizeof(rt_render_target_info); + memcpy(passes_begin, + rtResolveConstRelptr(&info->render_passes), + info->render_pass_count * sizeof(rt_render_pass_info)); + char *read_write_dest = passes_begin + info->render_pass_count * sizeof(rt_render_pass_info); + rt_render_pass_info *passes_dest = (rt_render_pass_info *)passes_begin; + const rt_render_pass_info *passes = + (const rt_render_pass_info *)rtResolveConstRelptr(&info->render_passes); + for (uint32_t i = 0; i < info->render_pass_count; ++i) { + rtSetRelptr(&passes_dest[i].read_render_targets, read_write_dest); + memcpy(read_write_dest, + rtResolveConstRelptr(&passes[i].read_render_targets), + sizeof(rt_render_target_read) * passes[i].read_render_target_count); + read_write_dest += sizeof(rt_render_target_read) * passes[i].read_render_target_count; + rtSetRelptr(&passes_dest[i].write_render_targets, read_write_dest); + memcpy(read_write_dest, + rtResolveConstRelptr(&passes[i].write_render_targets), + sizeof(rt_render_target_write) * passes[i].write_render_target_count); + read_write_dest += sizeof(rt_render_target_write) * passes[i].write_render_target_count; + } + } break; default: - rtLog("RESMGR", "Tried to get copy a resource of invalid type %u", resource->type); + rtLog("RESMGR", "Tried to copy a resource of invalid type %u", resource->type); } } @@ -749,6 +789,15 @@ RT_DLLEXPORT void rtPrefetchResources(const rt_resource_id *ids, uint32_t count) } } +RT_DLLEXPORT rt_resource_id rtGetResourceID(const char *name) { + size_t name_len = strlen(name); + rt_resource_id id = (rt_resource_id)rtHashBytes(name, name_len); + if (id == RT_INVALID_RESOURCE_ID || id == RT_TOMBSTONE_ID) + id = ~id; + return id; +} + + RT_DLLEXPORT rt_result rtCreateResources(uint32_t count, const char **names, const rt_resource *resources, @@ -764,11 +813,7 @@ RT_DLLEXPORT rt_result rtCreateResources(uint32_t count, rtLockWrite(&_namespace.lock); for (uint32_t i = 0; i < count; ++i) { - size_t name_len = strlen(names[i]); - rt_resource_id id = (rt_resource_id)rtHashBytes(names[i], name_len); - if (id == RT_INVALID_RESOURCE_ID || id == RT_TOMBSTONE_ID) - id = ~id; - + rt_resource_id id = rtGetResourceID(names[i]); bool inserted = false; for (size_t j = 0; j < ns_size; ++j) { size_t at = (id + j) % ns_size; diff --git a/src/runtime/resources.h b/src/runtime/resources.h index 76f90f1..1332c64 100644 --- a/src/runtime/resources.h +++ b/src/runtime/resources.h @@ -35,6 +35,8 @@ typedef enum { /* A pipeline state object */ RT_RESOURCE_PIPELINE, + RT_RESOURCE_FRAMEGRAPH, + RT_RESOURCE_TYPE_count, } rt_resource_type; @@ -74,6 +76,10 @@ RT_DLLEXPORT void rtPrefetchResources(const rt_resource_id *ids, uint32_t count) /* Returns the size of a resource in bytes, or 0 if the resource id is invalid. */ RT_DLLEXPORT size_t rtGetResourceSize(rt_resource_id id); +/* Returns the resource id that maps to a given name. + * Does not check if a resource with that id does exist. */ +RT_DLLEXPORT rt_resource_id rtGetResourceID(const char *name); + /* Logs information about a resource. Useful for debugging */ RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resource); diff --git a/src/runtime/threading.h b/src/runtime/threading.h index 42dd052..722a0d9 100644 --- a/src/runtime/threading.h +++ b/src/runtime/threading.h @@ -61,6 +61,26 @@ RT_DLLEXPORT void rtLockWrite(rt_rwlock *lock); RT_DLLEXPORT void rtUnlockWrite(rt_rwlock *lock); +/* Semaphore */ +typedef struct { + volatile int counter; + int max; + rt_condition_var *cond; +} rt_semaphore; + +typedef struct { + bool ok; + rt_semaphore semaphore; +} rt_create_semaphore_result; + +RT_DLLEXPORT rt_create_semaphore_result rtCreateSemaphore(int initial_value, int max_value); + +RT_DLLEXPORT void rtDestroySemaphore(rt_semaphore *sem); + +RT_DLLEXPORT void rtSignalSemaphore(rt_semaphore *sem); + +RT_DLLEXPORT void rtWaitOnSemaphore(rt_semaphore *sem); + /* Threads */ typedef struct rt_thread_s rt_thread; diff --git a/src/runtime/threading_rwlock.c b/src/runtime/threading_rwlock.c index f02f07c..68e74d0 100644 --- a/src/runtime/threading_rwlock.c +++ b/src/runtime/threading_rwlock.c @@ -1,7 +1,5 @@ #include "threading.h" -#include - /* Based on: https://eli.thegreenplace.net/2019/implementing-reader-writer-locks/ */ RT_DLLEXPORT rt_create_rwlock_result rtCreateRWLock(void) { @@ -26,7 +24,7 @@ RT_DLLEXPORT void rtLockRead(rt_rwlock *lock) { RT_DLLEXPORT void rtUnlockRead(rt_rwlock *lock) { rtLockConditionVar(lock->cond); - assert(lock->reader_count > 0); + RT_ASSERT(lock->reader_count > 0, "Tried to unlock a read-write lock that is not held."); --lock->reader_count; bool signal = lock->reader_count == 0; rtUnlockConditionVar(lock->cond, signal);