From 6740e876f6fcefadea1443ba13d15457af310684 Mon Sep 17 00:00:00 2001 From: ChrisTom-94 Date: Sat, 6 Sep 2025 21:36:04 +0200 Subject: [PATCH] Backend: Vulkan: refactor frame semaphores handling to use ImageIndex for synchronization (backend and examples) --- backends/imgui_impl_vulkan.cpp | 51 +++++++++++++++----------- backends/imgui_impl_vulkan.h | 3 +- examples/example_glfw_vulkan/main.cpp | 28 +++++++------- examples/example_sdl2_vulkan/main.cpp | 28 +++++++------- examples/example_sdl3_vulkan/main.cpp | 29 ++++++++------- examples/example_win32_vulkan/main.cpp | 28 +++++++------- 6 files changed, 92 insertions(+), 75 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index da0f6e305..4092441ae 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1619,10 +1619,7 @@ void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_devi err = vkCreateFence(device, &info, allocator, &fd->Fence); check_vk_result(err); } - } - for (uint32_t i = 0; i < wd->SemaphoreCount; i++) - { ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[i]; { VkSemaphoreCreateInfo info = {}; @@ -1659,12 +1656,15 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one. // Destroy old Framebuffer for (uint32_t i = 0; i < wd->ImageCount; i++) + { ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); - for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + } + wd->Frames.clear(); wd->FrameSemaphores.clear(); wd->ImageCount = 0; + wd->FrameIndex = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); @@ -1716,9 +1716,8 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers); check_vk_result(err); - wd->SemaphoreCount = wd->ImageCount + 1; wd->Frames.resize(wd->ImageCount); - wd->FrameSemaphores.resize(wd->SemaphoreCount); + wd->FrameSemaphores.resize(wd->ImageCount); memset(wd->Frames.Data, 0, wd->Frames.size_in_bytes()); memset(wd->FrameSemaphores.Data, 0, wd->FrameSemaphores.size_in_bytes()); for (uint32_t i = 0; i < wd->ImageCount; i++) @@ -1904,9 +1903,11 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui //vkQueueWaitIdle(bd->Queue); for (uint32_t i = 0; i < wd->ImageCount; i++) + { ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); - for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + } + wd->Frames.clear(); wd->FrameSemaphores.clear(); vkDestroyRenderPass(device, wd->RenderPass, allocator); @@ -2059,11 +2060,26 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) vd->SwapChainNeedRebuild = vd->SwapChainSuboptimal = false; } - ImGui_ImplVulkanH_Frame* fd = nullptr; - ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; + + for (;;) + { + err = vkWaitForFences(v->Device, 1, &fd->Fence, VK_TRUE, 100); + if (err == VK_SUCCESS) + { + err = vkResetFences(v->Device, 1,&fd->Fence); + check_vk_result(err); + break; + } + if (err == VK_TIMEOUT) continue; + check_vk_result(err); + } + + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->FrameIndex]; + { { - err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); + err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->ImageIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR) { vd->SwapChainNeedRebuild = true; // Since we are not going to swap this frame anyway, it's ok that recreation happens on next frame. @@ -2073,15 +2089,8 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) vd->SwapChainSuboptimal = true; else check_vk_result(err); - fd = &wd->Frames[wd->FrameIndex]; - } - for (;;) - { - err = vkWaitForFences(v->Device, 1, &fd->Fence, VK_TRUE, 100); - if (err == VK_SUCCESS) break; - if (err == VK_TIMEOUT) continue; - check_vk_result(err); } + { err = vkResetCommandPool(v->Device, fd->CommandPool, 0); check_vk_result(err); @@ -2203,9 +2212,9 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) return; VkResult err; - uint32_t present_index = wd->FrameIndex; + uint32_t present_index = wd->ImageIndex; - ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[present_index]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; @@ -2223,7 +2232,7 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) vd->SwapChainSuboptimal = true; else check_vk_result(err); - wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores + wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // Swap frame } void ImGui_ImplVulkan_InitMultiViewportSupport() diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 00c903060..eeda74d65 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -219,8 +219,7 @@ struct ImGui_ImplVulkanH_Window VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) - uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR - uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) + uint32_t ImageIndex; // Current available swapchain image index (returned by vkAcquireNextImageKHR) used to access RenderCompleteSemaphore ImVector Frames; ImVector FrameSemaphores; diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 2936b620d..1155d44e2 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -263,16 +263,7 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); - if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - g_SwapChainRebuild = true; - if (err == VK_ERROR_OUT_OF_DATE_KHR) - return; - if (err != VK_SUBOPTIMAL_KHR) - check_vk_result(err); - + VkResult err; ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking @@ -281,6 +272,16 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); } + + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->FrameIndex].ImageAcquiredSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->ImageIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + { err = vkResetCommandPool(g_Device, fd->CommandPool, 0); check_vk_result(err); @@ -308,6 +309,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -330,14 +332,14 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) { if (g_SwapChainRebuild) return; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &wd->FrameIndex; + info.pImageIndices = &wd->ImageIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) g_SwapChainRebuild = true; @@ -345,7 +347,7 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) return; if (err != VK_SUBOPTIMAL_KHR) check_vk_result(err); - wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores + wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // Swap frame } // Main code diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index b1b6cca96..ec6f43ae9 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -254,16 +254,7 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); - if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - g_SwapChainRebuild = true; - if (err == VK_ERROR_OUT_OF_DATE_KHR) - return; - if (err != VK_SUBOPTIMAL_KHR) - check_vk_result(err); - + VkResult err; ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking @@ -272,6 +263,16 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); } + + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->FrameIndex].ImageAcquiredSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->ImageIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + { err = vkResetCommandPool(g_Device, fd->CommandPool, 0); check_vk_result(err); @@ -299,6 +300,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -321,14 +323,14 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) { if (g_SwapChainRebuild) return; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &wd->FrameIndex; + info.pImageIndices = &wd->ImageIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) g_SwapChainRebuild = true; @@ -336,7 +338,7 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) return; if (err != VK_SUBOPTIMAL_KHR) check_vk_result(err); - wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores + wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // Swap frame } // Main code diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp index a6733f5da..11a8dc79c 100644 --- a/examples/example_sdl3_vulkan/main.cpp +++ b/examples/example_sdl3_vulkan/main.cpp @@ -256,16 +256,7 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); - if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - g_SwapChainRebuild = true; - if (err == VK_ERROR_OUT_OF_DATE_KHR) - return; - if (err != VK_SUBOPTIMAL_KHR) - check_vk_result(err); - + VkResult err; ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking @@ -274,6 +265,17 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); } + + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->FrameIndex].ImageAcquiredSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->ImageIndex); + + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + { err = vkResetCommandPool(g_Device, fd->CommandPool, 0); check_vk_result(err); @@ -301,6 +303,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -323,14 +326,14 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) { if (g_SwapChainRebuild) return; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &wd->FrameIndex; + info.pImageIndices = &wd->ImageIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) g_SwapChainRebuild = true; @@ -338,7 +341,7 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) return; if (err != VK_SUBOPTIMAL_KHR) check_vk_result(err); - wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores + wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // Swap frame } // Main code diff --git a/examples/example_win32_vulkan/main.cpp b/examples/example_win32_vulkan/main.cpp index 63f2d1449..3b16be613 100644 --- a/examples/example_win32_vulkan/main.cpp +++ b/examples/example_win32_vulkan/main.cpp @@ -252,16 +252,7 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); - if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - g_SwapChainRebuild = true; - if (err == VK_ERROR_OUT_OF_DATE_KHR) - return; - if (err != VK_SUBOPTIMAL_KHR) - check_vk_result(err); - + VkResult err; ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking @@ -270,6 +261,16 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); } + + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->FrameIndex].ImageAcquiredSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->ImageIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + { err = vkResetCommandPool(g_Device, fd->CommandPool, 0); check_vk_result(err); @@ -297,6 +298,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -319,14 +321,14 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) { if (g_SwapChainRebuild) return; - VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->ImageIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &wd->FrameIndex; + info.pImageIndices = &wd->ImageIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) g_SwapChainRebuild = true; @@ -334,7 +336,7 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) return; if (err != VK_SUBOPTIMAL_KHR) check_vk_result(err); - wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores + wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // Swap frame } LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);