From ee24cd49037003ee2a754634c41396f2c97eecd1 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Tue, 27 Feb 2024 10:00:00 +0100 Subject: [PATCH] Differentiate between graphics and compile passes --- assets/test.framegraph | 10 ++- src/app_framework/main_loop.c | 8 +- src/asset_compiler/asset_compiler.c | 16 ++++ src/asset_compiler/asset_compiler.h | 3 + src/asset_compiler/framegraph_processor.c | 40 ++++++--- src/game/main.c | 19 ++-- src/gfx/gfx.h | 11 +++ src/gfx/gfx_framegraph.c | 103 ++++++++++++++++++++-- src/gfx/gfx_main.c | 42 +++++---- src/gfx/renderer_api.h | 4 + src/renderer/vk/command_buffers.c | 5 +- src/renderer/vk/commands.c | 5 +- src/renderer/vk/frame.c | 14 ++- src/renderer/vk/gpu.h | 2 + src/renderer/vk/gpu_sync.c | 39 ++++++-- src/renderer/vk/helper.c | 27 ++++++ src/renderer/vk/render_targets.c | 21 +++-- 17 files changed, 292 insertions(+), 77 deletions(-) diff --git a/assets/test.framegraph b/assets/test.framegraph index 1b11095..4f981e8 100644 --- a/assets/test.framegraph +++ b/assets/test.framegraph @@ -23,6 +23,7 @@ render_targets { passes { pass0 { + type GRAPHICS; writes { color0 { clear_value { @@ -49,6 +50,7 @@ passes { } pass1 { + type GRAPHICS; reads { color0 { mode SAMPLED; @@ -56,7 +58,13 @@ passes { } writes { swapchain_out { - clear NO; + clear_value { + r 1.0; + g 0.0; + b 0.0; + a 1.0; + } + clear YES; discard NO; } } diff --git a/src/app_framework/main_loop.c b/src/app_framework/main_loop.c index 68e1c6f..ece8cd7 100644 --- a/src/app_framework/main_loop.c +++ b/src/app_framework/main_loop.c @@ -18,11 +18,11 @@ void UpdateThreadEntry(void *param) { while (!g_main_loop.shutdown) { /* Wait until the render thread has catched up */ rtWaitOnSemaphore(&g_main_loop.update_proceed); - rtLog("UT", "Processing %u", g_main_loop.u_frame_id); + //rtLog("UT", "Processing %u", g_main_loop.u_frame_id); (g_main_loop.GameUpdate)(); - rtLog("UT", "Finished %u", g_main_loop.u_frame_id); + //rtLog("UT", "Finished %u", 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); @@ -37,13 +37,13 @@ void RenderThreadEntry(void *param) { rtLog("RT", "RenderThread Entry"); while (!g_main_loop.shutdown) { rtWaitOnSemaphore(&g_main_loop.render_proceed); - rtLog("RT", "Processing %u", g_main_loop.r_frame_id); + //rtLog("RT", "Processing %u", g_main_loop.r_frame_id); rtBeginGFXFrame(g_main_loop.r_frame_id); (g_main_loop.GameRender)(); rtEndGFXFrame(g_main_loop.r_frame_id); - rtLog("RT", "Finished %u", g_main_loop.r_frame_id); + //rtLog("RT", "Finished %u", 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); diff --git a/src/asset_compiler/asset_compiler.c b/src/asset_compiler/asset_compiler.c index c1b4424..13381c6 100644 --- a/src/asset_compiler/asset_compiler.c +++ b/src/asset_compiler/asset_compiler.c @@ -393,6 +393,22 @@ static void ProcessorThreadEntry(void *param) { } } +RT_DLLEXPORT void rtWaitForAssetProcessing(void) { + int turns = 5; + while (turns > 0) { + rtSleep(100); + int in_processing_count = 0; + for (int i = 0; i < rt_AssetDBSize.i; ++i) { + rtLockRead(&_asset_db.lock); + if (_asset_db.files[i] != RT_INVALID_FILE_ID && _asset_db.data[i].in_processing) + ++in_processing_count; + rtUnlockRead(&_asset_db.lock); + } + if (!in_processing_count) + --turns; + } +} + /* Utilities for asset processors*/ rt_loaded_asset LoadAsset(rt_file_id file) { diff --git a/src/asset_compiler/asset_compiler.h b/src/asset_compiler/asset_compiler.h index 65572d6..59e7a19 100644 --- a/src/asset_compiler/asset_compiler.h +++ b/src/asset_compiler/asset_compiler.h @@ -15,6 +15,9 @@ RT_DLLEXPORT rt_result rtInitAssetCompiler(void); RT_DLLEXPORT void rtShutdownAssetCompiler(void); +/* Wait until all pending assets are processed. */ +RT_DLLEXPORT void rtWaitForAssetProcessing(void); + #ifdef __cplusplus } #endif diff --git a/src/asset_compiler/framegraph_processor.c b/src/asset_compiler/framegraph_processor.c index db3ccd8..f304cd7 100644 --- a/src/asset_compiler/framegraph_processor.c +++ b/src/asset_compiler/framegraph_processor.c @@ -7,19 +7,6 @@ #include #include -typedef struct { - rt_render_target_info *render_targets; - rt_render_pass_info *render_passes; - rt_render_target_read *reads; - rt_render_target_write *writes; - uint32_t render_target_count; - uint32_t render_pass_count; - uint32_t read_capacity; - uint32_t write_capacity; - uint32_t read_count; - uint32_t write_count; -} rt_parsed_framegraph; - static int RenderTargetExists(const rt_framegraph_info *framegraph, rt_render_target_id id) { const rt_render_target_info *render_targets = rtResolveConstRelptr(&framegraph->render_targets); for (uint32_t i = 0; i < framegraph->render_target_count; ++i) { @@ -236,6 +223,33 @@ static rt_result ParseFramegraph(const char *text, passes[i].id = rtCalculateRenderPassID(pass_stmt->attribute.start, pass_stmt->attribute.length); + rtSetRelptr(&passes[i].name, NULL); + passes[i].name_len = 0; + + passes[i].type = RT_RENDER_PASS_TYPE_GRAPHICS; + const rt_parsed_stmt *type_stmt = rtFindStatement(&state, pass_stmt->list_index, "type"); + if (type_stmt) { + if (type_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected GRAPHICS or COMPUTE as the value of \"passes.%.*s.type\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (rtCompareSpanToString(type_stmt->value, "GRAPHICS") == 0) { + passes[i].type = RT_RENDER_PASS_TYPE_GRAPHICS; + } else if (rtCompareSpanToString(type_stmt->value, "COMPUTE") == 0) { + passes[i].type = RT_RENDER_PASS_TYPE_COMPUTE; + } else { + rtLog("AC", + "Expected GRAPHICS or COMPUTE as the value of \"passes.%.*s.type\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + } const rt_parsed_stmt *write_list_stmt = rtFindStatement(&state, pass_stmt->list_index, "writes"); diff --git a/src/game/main.c b/src/game/main.c index d399900..7d883d9 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -18,7 +18,7 @@ static void PassPrepare(rt_render_pass_id pass, uint32_t write_count, const rt_render_target_read *reads, uint32_t read_count) { - rtLog("GAME", "Prepare pass %x", pass); + //rtLog("GAME", "Prepare pass %x", pass); } static void PassExecute(rt_render_pass_id pass, @@ -26,7 +26,7 @@ static void PassExecute(rt_render_pass_id pass, uint32_t write_count, const rt_render_target_read *reads, uint32_t read_count) { - rtLog("GAME", "Execute pass %x", pass); + //rtLog("GAME", "Execute pass %x", pass); } static void PassFinalize(rt_render_pass_id pass, @@ -34,7 +34,7 @@ static void PassFinalize(rt_render_pass_id pass, uint32_t write_count, const rt_render_target_read *reads, uint32_t read_count) { - rtLog("GAME", "Finalize pass %x", pass); + //rtLog("GAME", "Finalize pass %x", pass); } /* Called after the runtime has finished its initialization and before entering the main-loop*/ @@ -42,14 +42,13 @@ void Init(void) { rtLog("GAME", "Init"); rtInitAssetCompiler(); - rt_resource_id resid = rtGetResourceID("assets/test.framegraph"); - while (rtGetResourceSize(resid) == 0) - rtSleep(10); + rtWaitForAssetProcessing(); rt_temp_arena temp = rtGetTemporaryArena(NULL, 0); - size_t size = rtGetResourceSize(resid); - rt_resource *res = rtArenaPush(temp.arena, size); + rt_resource_id resid = rtGetResourceID("assets/test.framegraph"); + size_t size = rtGetResourceSize(resid); + rt_resource *res = rtArenaPush(temp.arena, size); rtGetResource(resid, res); _framegraph = rtCreateFramegraph(res->data); @@ -57,8 +56,8 @@ void Init(void) { 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); + 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 */ diff --git a/src/gfx/gfx.h b/src/gfx/gfx.h index a71f6ea..b8d09b3 100644 --- a/src/gfx/gfx.h +++ b/src/gfx/gfx.h @@ -118,8 +118,19 @@ typedef struct { rt_render_target_write_flags flags; } rt_render_target_write; +typedef enum { + RT_RENDER_PASS_TYPE_GRAPHICS, + RT_RENDER_PASS_TYPE_COMPUTE, +} rt_render_pass_type; + typedef struct { rt_render_pass_id id; + + /* For debug purposes, can be 0 */ + rt_relptr name; + uint32_t name_len; + + rt_render_pass_type type; /* list of rt_render_target_reads */ rt_relptr read_render_targets; /* list of rt_render_target_writes */ diff --git a/src/gfx/gfx_framegraph.c b/src/gfx/gfx_framegraph.c index 092598f..275e897 100644 --- a/src/gfx/gfx_framegraph.c +++ b/src/gfx/gfx_framegraph.c @@ -29,6 +29,8 @@ typedef struct { typedef struct { rt_render_pass_id id; + rt_render_pass_type type; + const char *name; int execution_level; unsigned int read_count; unsigned int write_count; @@ -52,6 +54,11 @@ static rt_framegraph *_framegraphs; static rt_framegraph *_first_free; static rt_mutex *_free_list_lock; +#define NAMES_CAPACITY 512 +static char _name_buffer[512]; +static char *_name_next; +static rt_mutex *_name_lock; + static void ReturnFrameGraph(rt_framegraph *framegraph) { rtLockMutex(_free_list_lock); framegraph->next_free = _first_free; @@ -63,18 +70,25 @@ rt_result InitFramegraphManager(void) { _free_list_lock = rtCreateMutex(); if (!_free_list_lock) return RT_UNKNOWN_ERROR; + _name_lock = rtCreateMutex(); + if (!_name_lock) { + rtDestroyMutex(_free_list_lock); + return RT_UNKNOWN_ERROR; + } _framegraphs = calloc((size_t)rt_MaxFramegraphs.i, sizeof(rt_framegraph)); if (!_framegraphs) return RT_OUT_OF_MEMORY; for (int i = 0; i < rt_MaxFramegraphs.i; ++i) _framegraphs[i].next_free = (i < rt_MaxFramegraphs.i - 1) ? &_framegraphs[i + 1] : NULL; _first_free = &_framegraphs[0]; + _name_next = &_name_buffer[0]; return RT_SUCCESS; } void ShutdownFramegraphManager(void) { free(_framegraphs); rtDestroyMutex(_free_list_lock); + rtDestroyMutex(_name_lock); } typedef struct { @@ -226,6 +240,28 @@ CreateRenderPasses(rt_framegraph *graph, const rt_framegraph_info *info, rt_aren 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].id = pass_info[i].id; + graph->passes[i].type = pass_info[i].type; + graph->passes[i].name = NULL; + + const char *name = rtResolveConstRelptr(&pass_info[i].name); + if (name) { + size_t name_strlen = strlen(name); + if (name_strlen + 1 == pass_info[i].name_len) { + rtLockMutex(_name_lock); + ptrdiff_t name_off = _name_next - _name_buffer; + if ((name_off + pass_info[i].name_len) < NAMES_CAPACITY) { + char *dst_name = _name_next; + memcpy(dst_name, name, pass_info[i].name_len); + _name_next += pass_info[i].name_len; + graph->passes[i].name = dst_name; + } else { + rtLog("GFX", "Ran out of storage for debug name %s", name); + } + rtUnlockMutex(_name_lock); + } else { + rtLog("GFX", "Declared name-length for pass %u does not match strlen()"); + } + } } /* Sort by execution level */ @@ -409,7 +445,7 @@ static rt_render_target *GetRenderTarget(rt_framegraph *framegraph, rt_render_ta } static void -BeginPass(rt_framegraph *framegraph, uint32_t pass_idx, rt_command_buffer_handle cmdbuf) { +BeginGraphicsPass(rt_framegraph *framegraph, uint32_t pass_idx, rt_command_buffer_handle cmdbuf) { 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; @@ -500,6 +536,45 @@ BeginPass(rt_framegraph *framegraph, uint32_t pass_idx, rt_command_buffer_handle g_renderer.CmdBeginPass(cmdbuf, &begin_info); } + +static void +BeginComputePass(rt_framegraph *framegraph, uint32_t pass_idx, rt_command_buffer_handle cmdbuf) { + 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; + + for (uint32_t i = 0; i < write_count; ++i) { + rt_render_target *rt = GetRenderTarget(framegraph, writes[i].render_target); + RT_ASSERT(rt != NULL, "Invalid render target in pass write."); + g_renderer.CmdTransitionRenderTarget(cmdbuf, + rt->api_render_target, + RT_RENDER_TARGET_STATE_STORAGE_IMAGE); + } + + for (uint32_t i = 0; i < read_count; ++i) { + rt_render_target *rt = GetRenderTarget(framegraph, reads[i].render_target); + RT_ASSERT(rt != NULL, "Invalid render target in pass read."); + /* We need to transition the render target */ + + switch (reads[i].mode) { + case RT_RENDER_TARGET_READ_SAMPLED: + g_renderer.CmdTransitionRenderTarget(cmdbuf, + rt->api_render_target, + RT_RENDER_TARGET_STATE_SAMPLED_IMAGE); + break; + case RT_RENDER_TARGET_READ_DIRECT: + g_renderer.CmdTransitionRenderTarget(cmdbuf, + rt->api_render_target, + RT_RENDER_TARGET_STATE_STORAGE_IMAGE); + break; + default: + RT_ASSERT(0, "Invalid render target read mode"); + } + } +} + + RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) { int execution_level = framegraph->passes[0].execution_level; uint32_t level_start = 0; @@ -519,18 +594,23 @@ RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) { 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*/ - rt_alloc_command_buffer_info cmdbuf_alloc = { - .target_queue = RT_GRAPHICS_QUEUE, - }; + bool is_graphics_pass = + framegraph->passes[pass_idx].type == RT_RENDER_PASS_TYPE_GRAPHICS; rt_command_buffer_handle cmdbuf; + rt_alloc_command_buffer_info cmdbuf_alloc = { + .target_queue = is_graphics_pass ? RT_GRAPHICS_QUEUE : RT_COMPUTE_QUEUE, + }; if (g_renderer.AllocCommandBuffers(1, &cmdbuf_alloc, &cmdbuf) != RT_SUCCESS) { rtLog("GFX", "Failed to allocate a command buffer for framegraph pass %u (%x)", @@ -539,8 +619,13 @@ RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) { continue; } - BeginPass(framegraph, pass_idx, cmdbuf); + if (is_graphics_pass) { + BeginGraphicsPass(framegraph, pass_idx, cmdbuf); + } else { + BeginComputePass(framegraph, pass_idx, cmdbuf); + } + /* framegraph->passes[pass_idx].bound_fns.Prepare(id, writes, write_count, @@ -556,12 +641,14 @@ RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) { write_count, reads, read_count); - - g_renderer.CmdEndPass(cmdbuf); + */ + if (is_graphics_pass) { + g_renderer.CmdEndPass(cmdbuf); + } rt_submit_command_buffers_info submit = {.command_buffer_count = 1, .command_buffers = &cmdbuf}; - g_renderer.SubmitCommandBuffers(RT_GRAPHICS_QUEUE, &submit); + g_renderer.SubmitCommandBuffers(is_graphics_pass ? RT_GRAPHICS_QUEUE : RT_COMPUTE_QUEUE, &submit); } /* Start next level */ diff --git a/src/gfx/gfx_main.c b/src/gfx/gfx_main.c index 09a62b1..f49fac1 100644 --- a/src/gfx/gfx_main.c +++ b/src/gfx/gfx_main.c @@ -43,6 +43,8 @@ extern rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t, rt_gpu_semaphore_handle *); extern void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semaphore_handle *); extern uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle); +extern rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void); +extern rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void); extern void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle, const rt_cmd_begin_pass_info *); extern void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle); @@ -87,6 +89,8 @@ static bool LoadRenderer(void) { RETRIEVE_SYMBOL(CreateSemaphores, rt_create_gpu_semaphores_fn); RETRIEVE_SYMBOL(DestroySemaphores, rt_destroy_gpu_semaphores_fn); RETRIEVE_SYMBOL(GetSemaphoreValue, rt_get_gpu_semaphore_value_fn); + RETRIEVE_SYMBOL(GetSwapchainAvailableSemaphore, rt_get_swapchain_available_semaphore_fn); + RETRIEVE_SYMBOL(GetRenderFinishedSemaphore, rt_get_render_finished_semaphore_fn); RETRIEVE_SYMBOL(CmdBeginPass, rt_cmd_begin_pass_fn); RETRIEVE_SYMBOL(CmdEndPass, rt_cmd_end_pass_fn); RETRIEVE_SYMBOL(CmdTransitionRenderTarget, rt_cmd_transition_render_target_fn); @@ -99,24 +103,26 @@ static bool LoadRenderer(void) { } #undef RETRIEVE_SYMBOL #else - g_renderer.RegisterCVars = &rtRenRegisterCVars; - g_renderer.Init = &rtRenInit; - g_renderer.Shutdown = &rtRenShutdown; - g_renderer.BeginFrame = &rtRenBeginFrame; - g_renderer.EndFrame = &rtRenEndFrame; - g_renderer.CompilePipeline = &rtRenCompilePipeline; - g_renderer.DestroyPipeline = &rtRenDestroyPipeline; - g_renderer.CreateRenderTarget = &rtRenCreateRenderTarget; - g_renderer.GetSwapchainRenderTarget = &rtRenGetSwapchainRenderTarget; - g_renderer.DestroyRenderTarget = &rtRenDestroyRenderTarget; - g_renderer.AllocCommandBuffers = &rtRenAllocCommandBuffers; - g_renderer.SubmitCommandBuffers = &rtRenSubmitCommandBuffers; - g_renderer.CreateSemaphores = &rtRenCreateSemaphores; - g_renderer.DestroySemaphores = &rtRenDestroySemaphores; - g_renderer.GetSemaphoreValue = &rtRenGetSemaphoreValue; - g_renderer.CmdBeginPass = &rtRenCmdBeginPass; - g_renderer.CmdEndPass = &rtRenCmdEndPass; - g_renderer.CmdTransitionRenderTarget = &rtRenCmdTransitionRenderTarget; + g_renderer.RegisterCVars = &rtRenRegisterCVars; + g_renderer.Init = &rtRenInit; + g_renderer.Shutdown = &rtRenShutdown; + g_renderer.BeginFrame = &rtRenBeginFrame; + g_renderer.EndFrame = &rtRenEndFrame; + g_renderer.CompilePipeline = &rtRenCompilePipeline; + g_renderer.DestroyPipeline = &rtRenDestroyPipeline; + g_renderer.CreateRenderTarget = &rtRenCreateRenderTarget; + g_renderer.GetSwapchainRenderTarget = &rtRenGetSwapchainRenderTarget; + g_renderer.DestroyRenderTarget = &rtRenDestroyRenderTarget; + g_renderer.AllocCommandBuffers = &rtRenAllocCommandBuffers; + g_renderer.SubmitCommandBuffers = &rtRenSubmitCommandBuffers; + g_renderer.CreateSemaphores = &rtRenCreateSemaphores; + g_renderer.DestroySemaphores = &rtRenDestroySemaphores; + g_renderer.GetSemaphoreValue = &rtRenGetSemaphoreValue; + g_renderer.GetSwapchainAvailableSemaphore = &rtRenGetSwapchainAvailableSemaphore; + g_renderer.GetRenderFinishedSemaphore = &rtRenGetRenderFinishedSemaphore; + g_renderer.CmdBeginPass = &rtRenCmdBeginPass; + g_renderer.CmdEndPass = &rtRenCmdEndPass; + g_renderer.CmdTransitionRenderTarget = &rtRenCmdTransitionRenderTarget; #endif return true; } diff --git a/src/gfx/renderer_api.h b/src/gfx/renderer_api.h index 42286d7..eadb4bc 100644 --- a/src/gfx/renderer_api.h +++ b/src/gfx/renderer_api.h @@ -203,6 +203,8 @@ typedef rt_result rt_create_gpu_semaphores_fn(uint32_t count, rt_gpu_semaphore_handle *p_semaphores); typedef void rt_destroy_gpu_semaphores_fn(uint32_t count, rt_gpu_semaphore_handle *semaphores); typedef uint64_t rt_get_gpu_semaphore_value_fn(rt_gpu_semaphore_handle semaphore); +typedef rt_gpu_semaphore_handle rt_get_swapchain_available_semaphore_fn(void); +typedef rt_gpu_semaphore_handle rt_get_render_finished_semaphore_fn(void); typedef void rt_cmd_begin_pass_fn(rt_command_buffer_handle cmdbuf, const rt_cmd_begin_pass_info *info); @@ -227,6 +229,8 @@ typedef struct { rt_create_gpu_semaphores_fn *CreateSemaphores; rt_destroy_gpu_semaphores_fn *DestroySemaphores; rt_get_gpu_semaphore_value_fn *GetSemaphoreValue; + rt_get_swapchain_available_semaphore_fn *GetSwapchainAvailableSemaphore; + rt_get_render_finished_semaphore_fn *GetRenderFinishedSemaphore; /* Command Buffer Functions */ rt_cmd_begin_pass_fn *CmdBeginPass; diff --git a/src/renderer/vk/command_buffers.c b/src/renderer/vk/command_buffers.c index 7cb69c2..3189d9c 100644 --- a/src/renderer/vk/command_buffers.c +++ b/src/renderer/vk/command_buffers.c @@ -334,9 +334,10 @@ out: } VkCommandBuffer rtGetCommandBuffer(rt_command_buffer_handle cmdbuf) { - if (!RT_IS_HANDLE_VALID(cmdbuf) || cmdbuf.index >= (uint32_t)rt_VkCommandBufferRingBufferSize.i) + uint32_t mod = (uint32_t)rt_VkCommandBufferRingBufferSize.i; + if (!RT_IS_HANDLE_VALID(cmdbuf)) return VK_NULL_HANDLE; - uint32_t slot = cmdbuf.index - 1; + uint32_t slot = (cmdbuf.index - 1) % mod; if (_command_buffers[slot].version != cmdbuf.version) { return VK_NULL_HANDLE; } diff --git a/src/renderer/vk/commands.c b/src/renderer/vk/commands.c index 637557e..09bdf64 100644 --- a/src/renderer/vk/commands.c +++ b/src/renderer/vk/commands.c @@ -164,6 +164,9 @@ void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmdb rt_render_target_state new_state) { GET_CMDBUF(cmdbuf, cmdbuf_handle) uint32_t image_index = g_gpu.current_frame_id % g_gpu.max_frames_in_flight; + if (render_target.index == g_renderer.GetSwapchainRenderTarget().index) { + image_index = rtGetFrameData(g_gpu.current_frame_id)->swapchain_image_index; + } rt_render_target *rt = rtGetRenderTarget(render_target); if (!rt) { @@ -213,7 +216,7 @@ void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmdb VkDebugUtilsLabelEXT debug_label = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, .pLabelName = "Transition Render Target", - .color = {0.f, 0.f, 0.f, 0.f}, + .color = {.13f, .54f, .13f, .75f}, }; vkCmdBeginDebugUtilsLabelEXT(cmdbuf, &debug_label); #endif diff --git a/src/renderer/vk/frame.c b/src/renderer/vk/frame.c index 1ccab33..adacaa9 100644 --- a/src/renderer/vk/frame.c +++ b/src/renderer/vk/frame.c @@ -41,6 +41,11 @@ void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { } else if (acquire_res != VK_SUCCESS) { rtReportError("vk", "vkAcquireNextImageKHR failed: %u", acquire_res); } + + /* Update the swapchain render target */ + rt_render_target_handle swap_rt_handle = g_renderer.GetSwapchainRenderTarget(); + rt_render_target *swap_rt = rtGetRenderTarget(swap_rt_handle); + swap_rt->states[frame->swapchain_image_index] = RT_RENDER_TARGET_STATE_INVALID; } void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { @@ -64,13 +69,12 @@ void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { #ifdef RT_DEBUG VkDebugUtilsLabelEXT debug_label = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .color = {0.f, 0.f, 0.f, 0.f}, + .color = {.13f, .54f, .13f, 1.f}, .pLabelName = "Transition Swapchain" }; vkCmdBeginDebugUtilsLabelEXT(cmd, &debug_label); #endif - VkImageMemoryBarrier2 image_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, @@ -115,12 +119,6 @@ void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { return; } - /* Update the swapchain render target */ - rt_render_target_handle swap_rt_handle = g_renderer.GetSwapchainRenderTarget(); - rt_render_target *swap_rt = rtGetRenderTarget(swap_rt_handle); - swap_rt->states[image_index] = RT_RENDER_TARGET_STATE_INVALID; - - VkPresentInfoKHR present_info = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pImageIndices = &image_index, diff --git a/src/renderer/vk/gpu.h b/src/renderer/vk/gpu.h index fa750f4..1d9caca 100644 --- a/src/renderer/vk/gpu.h +++ b/src/renderer/vk/gpu.h @@ -96,4 +96,6 @@ VkSampleCountFlagBits rtSampleCountToFlags(unsigned int count); VkQueue rtGetQueue(rt_gpu_queue queue); +const char *rtVkFormatToString(VkFormat format); + #endif diff --git a/src/renderer/vk/gpu_sync.c b/src/renderer/vk/gpu_sync.c index 032d5cc..63c309d 100644 --- a/src/renderer/vk/gpu_sync.c +++ b/src/renderer/vk/gpu_sync.c @@ -1,8 +1,8 @@ #include "gpu.h" #include "runtime/config.h" -#include "runtime/threading.h" #include "runtime/handles.h" +#include "runtime/threading.h" #include "gfx/renderer_api.h" @@ -10,11 +10,15 @@ RT_CVAR_I(rt_VkMaxSemaphores, "Maximum number of semaphores. Default: 1024", 1024); +#define SWAPCHAIN_AVAILABLE_SEMAPHORE_INDEX 0xffffff +#define RENDER_FINISHED_SEMAPHORE_INDEX 0xfffffe + typedef struct rt_gpu_semaphore_s { uint32_t version; VkSemaphore semaphore; uint64_t current_value; - /* TODO: State tracking. We want to ensure that we don't introduce gpu hangs by waiting on a not-signaled semaphore. */ + /* TODO: State tracking. We want to ensure that we don't introduce gpu hangs by waiting on a + * not-signaled semaphore. */ struct rt_gpu_semaphore_s *next_free; } rt_gpu_semaphore; @@ -58,7 +62,9 @@ void ShutdownSemaphoreManagement(void) { } } -rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, const rt_gpu_semaphore_info *info, rt_gpu_semaphore_handle *p_semaphores) { +rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, + const rt_gpu_semaphore_info *info, + rt_gpu_semaphore_handle *p_semaphores) { for (uint32_t i = 0; i < count; ++i) { rtLockMutex(_lock); rt_gpu_semaphore *sem = _first_free; @@ -75,7 +81,7 @@ rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, const rt_gpu_sema } sem->version = (sem->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION; - + VkSemaphoreTypeCreateInfo type_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE, @@ -85,7 +91,7 @@ rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, const rt_gpu_sema .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = &type_info, }; - + if (vkCreateSemaphore(g_gpu.device, &semaphore_info, g_gpu.alloc_cb, &sem->semaphore) != VK_SUCCESS) { for (uint32_t j = 0; j < i; ++j) { @@ -120,6 +126,15 @@ void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semaphore_hand VkSemaphore rtGetSemaphore(rt_gpu_semaphore_handle handle) { uint32_t index = handle.index; + + if (index == SWAPCHAIN_AVAILABLE_SEMAPHORE_INDEX) { + rt_frame_data *fd = rtGetFrameData(g_gpu.current_frame_id); + return fd->image_available; + } else if (index == RENDER_FINISHED_SEMAPHORE_INDEX) { + rt_frame_data *fd = rtGetFrameData(g_gpu.current_frame_id); + return fd->render_finished; + } + if (!RT_IS_HANDLE_VALID(handle) || index >= (uint32_t)rt_VkMaxSemaphores.i) return VK_NULL_HANDLE; if (_semaphores[index].version != handle.version) @@ -137,4 +152,18 @@ uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle semaphore _semaphores[index].semaphore, &_semaphores[index].current_value); return _semaphores[index].current_value; +} + +rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) { + return (rt_gpu_semaphore_handle){ + .version = 1, + .index = SWAPCHAIN_AVAILABLE_SEMAPHORE_INDEX, + }; +} + +rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void) { + return (rt_gpu_semaphore_handle){ + .version = 1, + .index = RENDER_FINISHED_SEMAPHORE_INDEX, + }; } \ No newline at end of file diff --git a/src/renderer/vk/helper.c b/src/renderer/vk/helper.c index 2de9a4f..28cb63f 100644 --- a/src/renderer/vk/helper.c +++ b/src/renderer/vk/helper.c @@ -53,4 +53,31 @@ VkQueue rtGetQueue(rt_gpu_queue queue) { default: return VK_NULL_HANDLE; } +} + +const char *rtVkFormatToString(VkFormat format) { + switch (format) { + case VK_FORMAT_R8G8B8A8_UNORM: + return "R8G8B8A8_UNORM"; + case VK_FORMAT_B8G8R8A8_UNORM: + return "B8G8R8A8_UNORM"; + case VK_FORMAT_R8G8B8A8_SRGB: + return "R8G8B8A8_SRGB"; + case VK_FORMAT_B8G8R8A8_SRGB: + return "B8G8R8A8_SRGB"; + case VK_FORMAT_R8G8B8_UNORM: + return "R8G8B8_UNORM"; + case VK_FORMAT_B8G8R8_UNORM: + return "B8G8R8_UNORM"; + case VK_FORMAT_R8G8B8_SRGB: + return "R8G8B8_SRGB"; + case VK_FORMAT_B8G8R8_SRGB: + return "B8G8R8_SRGB"; + case VK_FORMAT_D24_UNORM_S8_UINT: + return "D24_UNORM_S8_UINT"; + case VK_FORMAT_D32_SFLOAT: + return "D32_SFLOAT"; + default: + return "UNDEFINED"; + } } \ No newline at end of file diff --git a/src/renderer/vk/render_targets.c b/src/renderer/vk/render_targets.c index 3485e04..88d9bc5 100644 --- a/src/renderer/vk/render_targets.c +++ b/src/renderer/vk/render_targets.c @@ -58,26 +58,33 @@ static bool CreateImageAndView(VkExtent2D extent, }; vkGetPhysicalDeviceFormatProperties2(g_gpu.phys_device, format, &props); if ((props.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) == 0) { - rtLog("vk", "Requested render target format can not be sampled."); + rtLog("vk", + "Requested render target format %s can not be sampled.", + rtVkFormatToString(format)); usage &= ~VK_IMAGE_USAGE_SAMPLED_BIT; } if ((props.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0) { - rtLog("vk", "Requested render target format can not be used for storage."); + rtLog("vk", + "Requested render target format %s can not be used for storage.", + rtVkFormatToString(format)); usage &= ~VK_IMAGE_USAGE_STORAGE_BIT; } if (((usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) && ((props.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) == 0)) { - rtReportError("vk", - "Tried to create a render target color attachment, but the format does not " - "support the color attachment usage."); + rtReportError( + "vk", + "Tried to create a render target color attachment, but the format %s does not " + "support the color attachment usage.", + rtVkFormatToString(format)); return false; } else if (((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) && ((props.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0)) { rtReportError("vk", - "Tried to create a render target depth/stencil attachment, but the format " - "does not support the depth/stencil attachment usage."); + "Tried to create a render target depth/stencil attachment, but the format %s" + "does not support the depth/stencil attachment usage.", + rtVkFormatToString(format)); return false; }