diff --git a/src/renderer/vk/command_buffers.c b/src/renderer/vk/command_buffers.c index bc628b2..0513214 100644 --- a/src/renderer/vk/command_buffers.c +++ b/src/renderer/vk/command_buffers.c @@ -260,7 +260,7 @@ rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, .semaphore = rtGetSemaphore(info->wait_semaphores[i]), .value = info->wait_values[i], - .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, .deviceIndex = 0, }; wait_semaphores[i] = semaphore_info; @@ -270,7 +270,7 @@ rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, .semaphore = rtGetSemaphore(info->signal_semaphores[i]), .value = info->signal_values[i], - .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, .deviceIndex = 0, }; wait_semaphores[i] = semaphore_info; diff --git a/src/renderer/vk/frame.c b/src/renderer/vk/frame.c index 5a9af8d..46a055e 100644 --- a/src/renderer/vk/frame.c +++ b/src/renderer/vk/frame.c @@ -1,9 +1,59 @@ -#include "gpu.h" #include "command_buffers.h" +#include "gpu.h" +#include "swapchain.h" #include "runtime/renderer_api.h" +#define ONE_SECOND_NS 1000000000u + void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { g_gpu.current_frame_id = frame_id; + + rt_frame_data *frame = rtGetFrameData(frame_id); + + /* Wait until the previous frame is done */ + VkFence fence = g_swapchain.image_fences[frame_id % g_swapchain.image_count]; + RT_VK_CHECK(vkWaitForFences(g_gpu.device, 1, &fence, VK_TRUE, ONE_SECOND_NS)); + RT_VK_CHECK(vkResetFences(g_gpu.device, 1, &fence)); + rtResetCommandPools(frame_id); + + VkResult acquire_res = vkAcquireNextImageKHR(g_gpu.device, + g_swapchain.swapchain, + ONE_SECOND_NS, + frame->image_available, + fence, + &frame->swapchain_image_index); + if (acquire_res == VK_SUBOPTIMAL_KHR || acquire_res == VK_ERROR_OUT_OF_DATE_KHR) { + /* We need to recreate the swapchain and try again */ + rtLog("vk", "Swapchain has become suboptimal and needs to be re-created."); + vkDeviceWaitIdle(g_gpu.device); + if (rtRecreateSwapchain() != RT_SUCCESS) { + rtReportError("vk", "Failed to recreate the swapchain."); + return; + } + rtRenBeginFrame(frame_id); + } else if (acquire_res != VK_SUCCESS) { + rtReportError("vk", "vkAcquireNextImageKHR failed: %u", acquire_res); + } +} + +void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { + rt_frame_data *frame = rtGetFrameData(frame_id); + + uint32_t image_index = frame->swapchain_image_index; + + VkPresentInfoKHR present_info = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pImageIndices = &image_index, + .pSwapchains = &g_swapchain.swapchain, + .swapchainCount = 1, + .pWaitSemaphores = &frame->image_available, + .waitSemaphoreCount = 1, + }; + + VkResult res = vkQueuePresentKHR(g_gpu.present_queue, &present_info); + if (res != VK_SUCCESS) { + rtReportError("vk", "vkQueuePresentKHR failed: %u", res); + } } \ No newline at end of file diff --git a/src/renderer/vk/gpu.h b/src/renderer/vk/gpu.h index 7b54351..4566aa5 100644 --- a/src/renderer/vk/gpu.h +++ b/src/renderer/vk/gpu.h @@ -33,6 +33,12 @@ typedef struct { #endif } rt_native_window; +typedef struct { + uint32_t swapchain_image_index; + VkSemaphore image_available; + VkSemaphore render_finished; +} rt_frame_data; + typedef struct { VkInstance instance; VkDebugUtilsMessengerEXT messenger; @@ -60,14 +66,30 @@ typedef struct { unsigned int max_frames_in_flight; unsigned int current_frame_id; + + rt_frame_data frames[RT_VK_MAX_SUPPORTED_FRAMES_IN_FLIGHT]; } rt_vk_gpu; #ifndef RT_VK_DONT_DEFINE_GPU_GLOBAL extern rt_vk_gpu g_gpu; + + +RT_INLINE rt_frame_data *rtGetFrameData(unsigned int frame_id) { + return &g_gpu.frames[frame_id % g_gpu.max_frames_in_flight]; +} + #endif /* Helper functions */ +#define RT_VK_CHECK(expr) \ + do { \ + VkResult res = expr; \ + if (res != VK_SUCCESS) { \ + rtReportError("vk", "Vulkan command failed with error %u.\nCommand: %s", res, #expr); \ + } \ + } while (0) + VkFormat rtPixelFormatToVkFormat(rt_pixel_format format); VkSampleCountFlagBits rtSampleCountToFlags(unsigned int count); diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index c8afeae..cd3c114 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -543,6 +543,34 @@ static void DestroyAllocator(void) { vmaDestroyAllocator(g_gpu.allocator); } +static rt_result CreatePerFrameObjects(void) { + for (unsigned int i = 0; i < g_gpu.max_frames_in_flight; ++i) { + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + if (vkCreateSemaphore(g_gpu.device, + &semaphore_info, + g_gpu.alloc_cb, + &g_gpu.frames[i].render_finished) != VK_SUCCESS) { + return RT_UNKNOWN_ERROR; + } + if (vkCreateSemaphore(g_gpu.device, + &semaphore_info, + g_gpu.alloc_cb, + &g_gpu.frames[i].image_available) != VK_SUCCESS) { + return RT_UNKNOWN_ERROR; + } + } + return RT_SUCCESS; +} + +void DestroyPerFrameObjects(void) { + for (unsigned int i = 0; i < g_gpu.max_frames_in_flight; ++i) { + vkDestroySemaphore(g_gpu.device, g_gpu.frames[i].image_available, g_gpu.alloc_cb); + vkDestroySemaphore(g_gpu.device, g_gpu.frames[i].render_finished, g_gpu.alloc_cb); + } +} + extern rt_result InitPipelineManagement(void); extern void ShutdownPipelineManagement(void); extern rt_result InitRenderTargetManagement(void); @@ -582,6 +610,9 @@ rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) { if (res != RT_SUCCESS) return res; res = CreateAllocator(); + if (res != RT_SUCCESS) + return res; + res = CreatePerFrameObjects(); if (res != RT_SUCCESS) return res; res = InitPipelineManagement(); @@ -611,8 +642,12 @@ void RT_RENDERER_API_FN(Shutdown)(void) { ShutdownSemaphoreManagement(); ShutdownRenderTargetManagement(); ShutdownPipelineManagement(); + DestroyPerFrameObjects(); DestroyAllocator(); vkDestroyDevice(g_gpu.device, g_gpu.alloc_cb); vkDestroySurfaceKHR(g_gpu.instance, g_gpu.surface, g_gpu.alloc_cb); +#ifdef RT_DEBUG + vkDestroyDebugUtilsMessengerEXT(g_gpu.instance, g_gpu.messenger, g_gpu.alloc_cb); +#endif vkDestroyInstance(g_gpu.instance, g_gpu.alloc_cb); } diff --git a/src/renderer/vk/swapchain.c b/src/renderer/vk/swapchain.c index 509827d..6a36632 100644 --- a/src/renderer/vk/swapchain.c +++ b/src/renderer/vk/swapchain.c @@ -167,6 +167,23 @@ rt_result rtCreateSwapchain(void) { } } + /* Create fences */ + for (uint32_t i = 0; i < g_swapchain.image_count; ++i) { + VkFenceCreateInfo fence_info = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + /* Create as signalled so that we can wait on it the first time we render to that + swapchain image. */ + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }; + if (vkCreateFence(g_gpu.device, + &fence_info, + g_gpu.alloc_cb, + &g_swapchain.image_fences[i]) != VK_SUCCESS) { + rtReportError("vk", "Failed to create a fence for the swapchain"); + return 53; + } + } + return RT_SUCCESS; } @@ -178,6 +195,7 @@ rt_result rtRecreateSwapchain(void) { void rtDestroySwapchain(void) { for (uint32_t i = 0; i < g_swapchain.image_count; ++i) { + vkDestroyFence(g_gpu.device, g_swapchain.image_fences[i], g_gpu.alloc_cb); vkDestroyImageView(g_gpu.device, g_swapchain.image_views[i], g_gpu.alloc_cb); } vkDestroySwapchainKHR(g_gpu.device, g_swapchain.swapchain, g_gpu.alloc_cb); diff --git a/src/renderer/vk/swapchain.h b/src/renderer/vk/swapchain.h index 48b99c3..eb0de47 100644 --- a/src/renderer/vk/swapchain.h +++ b/src/renderer/vk/swapchain.h @@ -11,6 +11,7 @@ typedef struct { VkSwapchainKHR swapchain; VkImage images[RT_VK_MAX_SWAPCHAIN_IMAGES]; VkImageView image_views[RT_VK_MAX_SWAPCHAIN_IMAGES]; + VkFence image_fences[RT_VK_MAX_SWAPCHAIN_IMAGES]; uint32_t image_count; VkFormat format; VkExtent2D extent; diff --git a/src/runtime/gfx.h b/src/runtime/gfx.h index 75ee5f6..d4edd70 100644 --- a/src/runtime/gfx.h +++ b/src/runtime/gfx.h @@ -63,6 +63,8 @@ RT_DLLEXPORT void rtShutdownGFX(void); RT_DLLEXPORT void rtBeginGFXFrame(unsigned int frame_id); +RT_DLLEXPORT void rtEndGFXFrame(unsigned int frame_id); + /* ********************************************************************* * Framegraph API * diff --git a/src/runtime/gfx_main.c b/src/runtime/gfx_main.c index 1b31b0f..3e1fe07 100644 --- a/src/runtime/gfx_main.c +++ b/src/runtime/gfx_main.c @@ -26,6 +26,7 @@ extern void RT_RENDERER_API_FN(RegisterCVars)(void); extern rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *); extern void RT_RENDERER_API_FN(Shutdown)(void); extern void RT_RENDERER_API_FN(BeginFrame)(unsigned int); +extern void RT_RENDERER_API_FN(EndFrame)(unsigned int); extern rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *); extern void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle); extern rt_render_target_handle @@ -43,7 +44,7 @@ extern void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semapho extern uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle); #endif - extern rt_result InitFramegraphManager(void); +extern rt_result InitFramegraphManager(void); extern void ShutdownFramegraphManager(void); static bool LoadRenderer(void) { @@ -68,6 +69,7 @@ static bool LoadRenderer(void) { RETRIEVE_SYMBOL(Init, rt_init_renderer_fn); RETRIEVE_SYMBOL(Shutdown, rt_shutdown_renderer_fn); RETRIEVE_SYMBOL(BeginFrame, rt_begin_frame_fn); + RETRIEVE_SYMBOL(EndFrame, rt_end_frame_fn); RETRIEVE_SYMBOL(CompilePipeline, rt_compile_pipeline_fn); RETRIEVE_SYMBOL(DestroyPipeline, rt_destroy_pipeline_fn); RETRIEVE_SYMBOL(CreateRenderTarget, rt_create_render_target_fn); @@ -90,6 +92,7 @@ static bool LoadRenderer(void) { 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; @@ -137,4 +140,8 @@ RT_DLLEXPORT void rtShutdownGFX(void) { RT_DLLEXPORT void rtBeginGFXFrame(unsigned int frame_id) { g_renderer.BeginFrame(frame_id); +} + +RT_DLLEXPORT void rtEndGFXFrame(unsigned int frame_id) { + g_renderer.EndFrame(frame_id); } \ No newline at end of file diff --git a/src/runtime/main_loop.c b/src/runtime/main_loop.c index 0e576cd..c070890 100644 --- a/src/runtime/main_loop.c +++ b/src/runtime/main_loop.c @@ -40,6 +40,7 @@ void RenderThreadEntry(void *param) { 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); g_main_loop.r_frame_id += 1; diff --git a/src/runtime/renderer_api.h b/src/runtime/renderer_api.h index 6a0da71..db3d81c 100644 --- a/src/runtime/renderer_api.h +++ b/src/runtime/renderer_api.h @@ -140,6 +140,7 @@ typedef void rt_register_renderer_cvars_fn(void); typedef rt_result rt_init_renderer_fn(const rt_renderer_init_info *info); typedef void rt_shutdown_renderer_fn(void); typedef void rt_begin_frame_fn(unsigned int frame_id); +typedef void rt_end_frame_fn(unsigned int frame_id); typedef rt_pipeline_handle rt_compile_pipeline_fn(const rt_pipeline_info *info); typedef void rt_destroy_pipeline_fn(rt_pipeline_handle handle); typedef rt_render_target_handle rt_create_render_target_fn(const rt_render_target_info *info); @@ -160,6 +161,7 @@ typedef struct { rt_init_renderer_fn *Init; rt_shutdown_renderer_fn *Shutdown; rt_begin_frame_fn *BeginFrame; + rt_end_frame_fn *EndFrame; rt_compile_pipeline_fn *CompilePipeline; rt_destroy_pipeline_fn *DestroyPipeline; rt_create_render_target_fn *CreateRenderTarget;