From b88a3b271123f08fbfc53f5a746a4583b086224b Mon Sep 17 00:00:00 2001 From: MindSpunk Date: Sun, 9 Sep 2018 16:04:44 +1000 Subject: [PATCH] Examples: Vulkan: Added calls to supports runtime changing back buffer count. (#2071) --- examples/example_glfw_vulkan/main.cpp | 41 ++++++++++--- examples/example_sdl_vulkan/main.cpp | 48 ++++++++++++++- examples/imgui_impl_vulkan.cpp | 87 ++++++++++++++++++++------- examples/imgui_impl_vulkan.h | 10 ++- 4 files changed, 150 insertions(+), 36 deletions(-) diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 0a9b8ccc0..6459a7aa9 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -23,6 +23,7 @@ #define IMGUI_VULKAN_DEBUG_REPORT #endif +static uint32_t g_MinImageCount = 2; static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; @@ -211,8 +212,8 @@ static void SetupVulkanWindowData(ImGui_ImplVulkanH_WindowData* wd, VkSurfaceKHR //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. - ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); - ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, width, height); + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); } static void CleanupVulkan() @@ -373,6 +374,7 @@ int main(int, char**) init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; + init_info.QueuedFrames = wd->BackBufferCount; ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); // Load Fonts @@ -433,11 +435,14 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); - if (g_ResizeWanted) - { - ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, g_ResizeWidth, g_ResizeHeight); - g_ResizeWanted = false; - } + if (g_ResizeWanted) + { + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, g_ResizeWidth, g_ResizeHeight, g_MinImageCount); + ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, &g_WindowData, g_Allocator); + ImGui_ImplVulkan_SetQueuedFramesCount(g_WindowData.BackBufferCount); + g_WindowData.FrameIndex = 0; + g_ResizeWanted = false; + } // Start the Dear ImGui frame ImGui_ImplVulkan_NewFrame(); @@ -478,6 +483,28 @@ int main(int, char**) ImGui::Text("Hello from another window!"); if (ImGui::Button("Close Me")) show_another_window = false; + + if (ImGui::Button("Increase")) + { + g_MinImageCount++; + g_ResizeWanted = true; + } + + ImGui::SameLine(); + if (ImGui::Button("Decrease")) + { + if (g_MinImageCount != 2) + { + g_MinImageCount--; + g_ResizeWanted = true; + } + } + + ImGui::SameLine(); + ImGui::Text("Back Buffers: %i", g_MinImageCount); + + ImGui::Text("Frame Index %i", wd->FrameIndex); + ImGui::End(); } diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index e17a8ab74..b9bcffe9b 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -15,6 +15,8 @@ #define IMGUI_VULKAN_DEBUG_REPORT #endif +static uint32_t g_MinImageCount = 2; +static bool g_PendingSwapchainRebuild = false; static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; @@ -201,8 +203,8 @@ static void SetupVulkanWindowData(ImGui_ImplVulkanH_WindowData* wd, VkSurfaceKHR //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. - ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); - ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, width, height); + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); } static void CleanupVulkan() @@ -296,6 +298,20 @@ static void FramePresent(ImGui_ImplVulkanH_WindowData* wd) check_vk_result(err); } +static void RebuildSwapChain(int width, int height) +{ + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, width, height, g_MinImageCount); + ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, &g_WindowData, g_Allocator); + ImGui_ImplVulkan_SetQueuedFramesCount(g_WindowData.BackBufferCount); + g_WindowData.FrameIndex = 0; + g_PendingSwapchainRebuild = false; +} + +static void RebuildSwapChain() +{ + RebuildSwapChain(g_WindowData.Width, g_WindowData.Height); +} + int main(int, char**) { // Setup SDL @@ -354,6 +370,7 @@ int main(int, char**) init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; + init_info.QueuedFrames = wd->BackBufferCount; init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); @@ -422,9 +439,12 @@ int main(int, char**) if (event.type == SDL_QUIT) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) - ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, (int)event.window.data1, (int)event.window.data2); + RebuildSwapChain((int)event.window.data1, (int)event.window.data2); } + if (g_PendingSwapchainRebuild) + RebuildSwapChain(); + // Start the Dear ImGui frame ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(window); @@ -464,6 +484,28 @@ int main(int, char**) ImGui::Text("Hello from another window!"); if (ImGui::Button("Close Me")) show_another_window = false; + + if (ImGui::Button("Increase")) + { + g_MinImageCount++; + g_PendingSwapchainRebuild = true; + } + + ImGui::SameLine(); + if (ImGui::Button("Decrease")) + { + if (g_MinImageCount != 2) + { + g_MinImageCount--; + g_PendingSwapchainRebuild = true; + } + } + + ImGui::SameLine(); + ImGui::Text("Back Buffers: %i", g_MinImageCount); + + ImGui::Text("Frame Index %i", wd->FrameIndex); + ImGui::End(); } diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 2d4979319..833e4d3e4 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -64,8 +64,8 @@ struct FrameDataForRender VkBuffer VertexBuffer; VkBuffer IndexBuffer; }; -static int g_FrameIndex = 0; -static FrameDataForRender g_FramesDataBuffers[IMGUI_VK_QUEUED_FRAMES] = {}; +static int g_FrameIndex = 0; +static ImVector g_FramesDataBuffers = {}; // Font data static VkSampler g_FontSampler = VK_NULL_HANDLE; @@ -240,7 +240,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm VkResult err; FrameDataForRender* fd = &g_FramesDataBuffers[g_FrameIndex]; - g_FrameIndex = (g_FrameIndex + 1) % IM_ARRAYSIZE(g_FramesDataBuffers); + g_FrameIndex = (g_FrameIndex + 1) % g_FramesDataBuffers.size(); // Create the Vertex and Index buffers: size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); @@ -728,15 +728,6 @@ void ImGui_ImplVulkan_InvalidateDeviceObjects() { ImGui_ImplVulkan_InvalidateFontUploadObjects(); - for (int i = 0; i < IM_ARRAYSIZE(g_FramesDataBuffers); i++) - { - FrameDataForRender* fd = &g_FramesDataBuffers[i]; - if (fd->VertexBuffer) { vkDestroyBuffer (g_Device, fd->VertexBuffer, g_Allocator); fd->VertexBuffer = VK_NULL_HANDLE; } - if (fd->VertexBufferMemory) { vkFreeMemory (g_Device, fd->VertexBufferMemory, g_Allocator); fd->VertexBufferMemory = VK_NULL_HANDLE; } - if (fd->IndexBuffer) { vkDestroyBuffer (g_Device, fd->IndexBuffer, g_Allocator); fd->IndexBuffer = VK_NULL_HANDLE; } - if (fd->IndexBufferMemory) { vkFreeMemory (g_Device, fd->IndexBufferMemory, g_Allocator); fd->IndexBufferMemory = VK_NULL_HANDLE; } - } - if (g_FontView) { vkDestroyImageView(g_Device, g_FontView, g_Allocator); g_FontView = VK_NULL_HANDLE; } if (g_FontImage) { vkDestroyImage(g_Device, g_FontImage, g_Allocator); g_FontImage = VK_NULL_HANDLE; } if (g_FontMemory) { vkFreeMemory(g_Device, g_FontMemory, g_Allocator); g_FontMemory = VK_NULL_HANDLE; } @@ -746,6 +737,18 @@ void ImGui_ImplVulkan_InvalidateDeviceObjects() if (g_Pipeline) { vkDestroyPipeline(g_Device, g_Pipeline, g_Allocator); g_Pipeline = VK_NULL_HANDLE; } } +void ImGui_ImplVulkan_InvalidateFrameDeviceObjects() +{ + for (int i = 0; i < g_FramesDataBuffers.size(); i++) + { + FrameDataForRender* fd = &g_FramesDataBuffers[i]; + if (fd->VertexBuffer) { vkDestroyBuffer(g_Device, fd->VertexBuffer, g_Allocator); fd->VertexBuffer = VK_NULL_HANDLE; } + if (fd->VertexBufferMemory) { vkFreeMemory (g_Device, fd->VertexBufferMemory, g_Allocator); fd->VertexBufferMemory = VK_NULL_HANDLE; } + if (fd->IndexBuffer) { vkDestroyBuffer(g_Device, fd->IndexBuffer, g_Allocator); fd->IndexBuffer = VK_NULL_HANDLE; } + if (fd->IndexBufferMemory) { vkFreeMemory (g_Device, fd->IndexBufferMemory, g_Allocator); fd->IndexBufferMemory = VK_NULL_HANDLE; } + } +} + bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) { ImGuiIO& io = ImGui::GetIO(); @@ -768,6 +771,11 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend g_DescriptorPool = info->DescriptorPool; g_Allocator = info->Allocator; g_CheckVkResultFn = info->CheckVkResultFn; + g_FramesDataBuffers.resize(info->QueuedFrames); + for (int i = 0; i < g_FramesDataBuffers.size(); i++) + { + g_FramesDataBuffers[i] = FrameDataForRender(); + } ImGui_ImplVulkan_CreateDeviceObjects(); @@ -776,6 +784,7 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend void ImGui_ImplVulkan_Shutdown() { + ImGui_ImplVulkan_InvalidateFrameDeviceObjects(); ImGui_ImplVulkan_InvalidateDeviceObjects(); } @@ -783,6 +792,24 @@ void ImGui_ImplVulkan_NewFrame() { } +void ImGui_ImplVulkan_SetQueuedFramesCount(uint32_t count) +{ + if (count == g_FramesDataBuffers.size()) + { + return; + } + ImGui_ImplVulkan_InvalidateFrameDeviceObjects(); + + uint32_t old_size = g_FramesDataBuffers.size(); + g_FramesDataBuffers.resize(count); + for (uint32_t i = old_size; i < count; i++) + { + + g_FramesDataBuffers[i] = FrameDataForRender(); + } + g_FrameIndex = 0; +} + //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers @@ -901,7 +928,7 @@ void ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(VkPhysicalDevice physical_ // Create Command Buffers VkResult err; - for (int i = 0; i < IM_ARRAYSIZE(wd->Frames); i++) + for (int i = 0; i < wd->Frames.size(); i++) { ImGui_ImplVulkanH_FrameData* fd = &wd->Frames[i]; { @@ -951,10 +978,8 @@ int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_m return 1; } -void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h) +void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) { - uint32_t min_image_count = 2; // FIXME: this should become a function parameter - VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; err = vkDeviceWaitIdle(device); @@ -1015,7 +1040,18 @@ void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->BackBufferCount, NULL); check_vk_result(err); err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->BackBufferCount, wd->BackBuffer); - check_vk_result(err); + check_vk_result(err); + + for (uint32_t i = 0; i < wd->Frames.size(); i++) + { + ImGui_ImplVulkanH_DestroyFrameData(g_Instance, device, &wd->Frames[i], allocator); + } + uint32_t old_size = wd->Frames.size(); + wd->Frames.resize(wd->BackBufferCount); + for (uint32_t i = 0; i < wd->Frames.size(); i++) + { + wd->Frames[i] = ImGui_ImplVulkanH_FrameData(); + } } if (old_swapchain) vkDestroySwapchainKHR(device, old_swapchain, allocator); @@ -1102,14 +1138,10 @@ void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, I vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) //vkQueueWaitIdle(g_Queue); - for (int i = 0; i < IM_ARRAYSIZE(wd->Frames); i++) + for (int i = 0; i < wd->Frames.size(); i++) { ImGui_ImplVulkanH_FrameData* fd = &wd->Frames[i]; - vkDestroyFence(device, fd->Fence, allocator); - vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer); - vkDestroyCommandPool(device, fd->CommandPool, allocator); - vkDestroySemaphore(device, fd->ImageAcquiredSemaphore, allocator); - vkDestroySemaphore(device, fd->RenderCompleteSemaphore, allocator); + ImGui_ImplVulkanH_DestroyFrameData(instance, device, fd, allocator); } for (uint32_t i = 0; i < wd->BackBufferCount; i++) { @@ -1121,3 +1153,12 @@ void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, I vkDestroySurfaceKHR(instance, wd->Surface, allocator); *wd = ImGui_ImplVulkanH_WindowData(); } + +void ImGui_ImplVulkanH_DestroyFrameData(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_FrameData* fd, const VkAllocationCallbacks* allocator) +{ + vkDestroyFence(device, fd->Fence, allocator); + vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer); + vkDestroyCommandPool(device, fd->CommandPool, allocator); + vkDestroySemaphore(device, fd->ImageAcquiredSemaphore, allocator); + vkDestroySemaphore(device, fd->RenderCompleteSemaphore, allocator); +} diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 8bb35c865..96cf52bef 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -15,7 +15,7 @@ #include -#define IMGUI_VK_QUEUED_FRAMES 2 +//#define IMGUI_VK_QUEUED_FRAMES 2 // Please zero-clear before use. struct ImGui_ImplVulkan_InitInfo @@ -27,6 +27,7 @@ struct ImGui_ImplVulkan_InitInfo VkQueue Queue; VkPipelineCache PipelineCache; VkDescriptorPool DescriptorPool; + int QueuedFrames; const VkAllocationCallbacks* Allocator; void (*CheckVkResultFn)(VkResult err); }; @@ -35,6 +36,7 @@ struct ImGui_ImplVulkan_InitInfo IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplVulkan_SetQueuedFramesCount(uint32_t count); IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer); IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); @@ -42,6 +44,7 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); // Called by ImGui_ImplVulkan_Init() might be useful elsewhere. IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplVulkan_InvalidateFrameDeviceObjects(); //------------------------------------------------------------------------- @@ -63,8 +66,9 @@ struct ImGui_ImplVulkanH_FrameData; struct ImGui_ImplVulkanH_WindowData; IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, uint32_t queue_family, ImGui_ImplVulkanH_WindowData* wd, const VkAllocationCallbacks* allocator); -IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h); +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_WindowData* wd, const VkAllocationCallbacks* allocator); +IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyFrameData(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_FrameData* fd, const VkAllocationCallbacks* allocator); IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); @@ -101,7 +105,7 @@ struct ImGui_ImplVulkanH_WindowData VkImageView BackBufferView[16]; VkFramebuffer Framebuffer[16]; uint32_t FrameIndex; - ImGui_ImplVulkanH_FrameData Frames[IMGUI_VK_QUEUED_FRAMES]; + ImVector Frames; IMGUI_IMPL_API ImGui_ImplVulkanH_WindowData(); };