How to Set Up Dear ImGui with Vulkan: Complete Implementation Guide
Dear ImGui integrates with Vulkan through the official imgui_impl_vulkan.cpp backend by initializing an ImGui_ImplVulkan_InitInfo structure with your Vulkan instance, physical device, logical device, queue, and descriptor pool, then calling ImGui_ImplVulkan_Init() before rendering frames with ImGui_ImplVulkan_RenderDrawData().
Dear ImGui (ocornut/imgui) provides first-class Vulkan support via renderer backends located in the backends/ directory. The implementation requires connecting your existing Vulkan context to ImGui's rendering layer, setting up descriptor pools for texture management, and routing draw commands through your command buffers each frame.
Creating the Vulkan Instance and Device
Before initializing ImGui, you must create a standard Vulkan instance and logical device with the necessary extensions enabled. According to examples/example_win32_vulkan/main.cpp (lines 77-99), you need VK_KHR_surface, VK_KHR_win32_surface (or your platform's equivalent), and VK_KHR_swapchain.
The example demonstrates this in the SetupVulkan function, where it selects a physical device with present support and creates the logical device with graphics and present queues. Ensure your device creation includes at least one graphics queue family that supports surface presentation.
Setting Up the Descriptor Pool
ImGui requires a descriptor pool to manage texture samplers and sampled images internally. As implemented in examples/example_win32_vulkan/main.cpp (lines 180-196), you must create a pool with sufficient capacity:
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE },
{ VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE },
};
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = pool_sizes[0].descriptorCount + pool_sizes[1].descriptorCount;
pool_info.poolSizeCount = 2;
pool_info.pPoolSizes = pool_sizes;
VkDescriptorPool imgui_desc_pool;
vkCreateDescriptorPool(device, &pool_info, nullptr, &imgui_desc_pool);
The constants IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE and IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE define the minimum required descriptors, ensuring sufficient space for fonts and user textures.
Initializing the Backend
With your Vulkan context ready, populate the ImGui_ImplVulkan_InitInfo structure and initialize the renderer. The example in examples/example_win32_vulkan/main.cpp (lines 98-115) demonstrates this configuration:
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = vk_instance;
init_info.PhysicalDevice = vk_physical;
init_info.Device = device;
init_info.QueueFamily = graphics_queue_family;
init_info.Queue = graphics_queue;
init_info.PipelineCache = pipeline_cache; // optional
init_info.DescriptorPool = imgui_desc_pool;
init_info.MinImageCount = 2; // typically 2-3 for double/triple buffering
init_info.ImageCount = swapchain_image_count;
init_info.Allocator = nullptr; // optional custom allocator
init_info.CheckVkResultFn = [](VkResult err) {
if (err != VK_SUCCESS) { fprintf(stderr, "[Vulkan] Error %d\n", err); abort(); }
};
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui::StyleColorsDark();
// Initialize platform backend (Win32 shown here)
ImGui_ImplWin32_Init(hwnd);
// Initialize renderer backend
ImGui_ImplVulkan_Init(&init_info);
This configuration binds your Vulkan context to ImGui's internal renderer state, which is stored in ImGui::GetIO().BackendRendererUserData to maintain a stateless design.
Creating Swap Chain and Framebuffers
ImGui provides helper utilities (ImGui_ImplVulkanH_*) to simplify swap chain management. The function ImGui_ImplVulkanH_CreateOrResizeWindow, used in SetupVulkanWindow (lines 30-33 of the example), handles surface format selection, present mode configuration, and framebuffer creation.
You must create a render pass compatible with your swap chain images. The example creates a simple color attachment render pass that clears the screen before rendering ImGui geometry.
Implementing the Per-Frame Render Loop
Each frame requires specific backend calls to synchronize ImGui with Vulkan command buffers. The main loop in examples/example_win32_vulkan/main.cpp (lines 40-56) follows this sequence:
// 1. Start ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplWin32_NewFrame(); // or your platform equivalent
ImGui::NewFrame();
// 2. Build UI
ImGui::Begin("Hello Vulkan");
ImGui::Text("Application average %.3f ms/frame", 1000.0f / ImGui::GetIO().Framerate);
ImGui::End();
// 3. Generate draw data
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
// 4. Record Vulkan commands
VkCommandBuffer cmd = frame.command_buffer;
vkBeginCommandBuffer(cmd, &begin_info);
vkCmdBeginRenderPass(cmd, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
// 5. Render ImGui draw lists
ImGui_ImplVulkan_RenderDrawData(draw_data, cmd);
// 6. Submit and present
vkCmdEndRenderPass(cmd);
vkEndCommandBuffer(cmd);
vkQueueSubmit(queue, 1, &submit_info, frame.fence);
vkQueuePresentKHR(queue, &present_info);
The ImGui_ImplVulkan_RenderDrawData() function records all necessary vertex buffer bindings, scissor rectangles, and draw commands into your command buffer.
Handling Custom Textures
To display images in your UI, register Vulkan image views with ImGui using ImGui_ImplVulkan_AddTexture():
// Assuming you have created image_view and sampler
VkDescriptorSet tex_desc = ImGui_ImplVulkan_AddTexture(
sampler,
image_view,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
);
// Use in UI
ImGui::Image((ImTextureID)tex_desc, ImVec2(256, 256));
The backend manages the descriptor set allocation from the pool you provided during initialization. For implementation details, see imgui_impl_vulkan.cpp (lines 735-795).
Shutdown and Resource Cleanup
On application exit, call the shutdown function before destroying Vulkan objects:
ImGui_ImplVulkan_Shutdown();
ImGui_ImplWin32_Shutdown(); // platform backend
ImGui::DestroyContext();
// Then destroy your descriptor pool, device, and instance
vkDestroyDescriptorPool(device, imgui_desc_pool, nullptr);
This sequence ensures all internal ImGui resources are released before the underlying Vulkan device is destroyed.
Backend Architecture
The Vulkan backend is designed to be stateless and embeddable. Key components include:
imgui_impl_vulkan.cpp/h: ImplementsImGui_ImplVulkan_RenderDrawData()and buffer management. All state lives inBackendRendererUserDatarather than global variables.ImGui_ImplVulkanH_*helpers: Utility functions for swap chain creation, surface format selection, and per-frame resource management (ImGui_ImplVulkanH_Window,ImGui_ImplVulkanH_Frame).- Platform separation: The renderer backend (
imgui_impl_vulkan) is distinct from platform backends (e.g.,imgui_impl_win32), allowing you to mix Vulkan with any windowing system.
Common Pitfalls
Swap chain invalidation on resize: If vkQueueSubmit returns VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR, the swap chain no longer matches the surface properties. Set a rebuild flag and call ImGui_ImplVulkanH_CreateOrResizeWindow() on the next frame to recreate the swap chain and framebuffers.
Missing font textures: If text appears as solid rectangles, ensure your descriptor pool contains at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors. Modern versions create the font texture automatically on the first frame, but older versions required explicit calls to ImGui_ImplVulkan_CreateFontsTexture().
Dynamic state leakage: ImGui modifies scissor and viewport state dynamically but does not restore previous values. After calling ImGui_ImplVulkan_RenderDrawData(), reset your pipeline's dynamic state if your application relies on specific viewport or scissor configurations.
Summary
- Initialize the backend with
ImGui_ImplVulkan_Init()after creating your Vulkan device and descriptor pool. - Use
ImGui_ImplVulkanH_CreateOrResizeWindow()to handle swap chain and framebuffer setup. - Call
ImGui_ImplVulkan_NewFrame()at the start of each frame andImGui_ImplVulkan_RenderDrawData()during command buffer recording. - Manage custom textures via
ImGui_ImplVulkan_AddTexture()using descriptors from your initialization pool. - Handle swap chain rebuilds explicitly when the window resizes to avoid presentation errors.
Frequently Asked Questions
How do I handle window resizing with Dear ImGui and Vulkan?
When the window resizes, Vulkan signals VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR during presentation. Detect this error in your render loop, set a rebuild flag, and on the next frame call ImGui_ImplVulkanH_CreateOrResizeWindow() with the new dimensions to recreate the swap chain, image views, and framebuffers while preserving your existing Vulkan instance and device.
What size should the descriptor pool be for ImGui Vulkan?
Allocate at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE and IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE of type VK_DESCRIPTOR_TYPE_SAMPLER. If you load many custom textures, increase these counts proportionally to your texture count to avoid descriptor allocation failures.
Can I use Dear ImGui with Vulkan on Linux or macOS?
Yes. While the example uses Win32 (imgui_impl_win32.cpp), the Vulkan backend (imgui_impl_vulkan.cpp) is platform-agnostic. Replace the platform backend with imgui_impl_glfw.cpp, imgui_impl_sdl.cpp, or your custom implementation, and create the surface using vkCreateXcbSurfaceKHR, vkCreateWaylandSurfaceKHR, or vkCreateMacOSSurfaceMVK as appropriate for your target platform.
Why does my application crash when calling ImGui_ImplVulkan_RenderDrawData?
This typically occurs when the ImGui_ImplVulkan_InitInfo structure contains invalid handles (null device, queue, or descriptor pool) or when the command buffer is in an invalid state. Verify that your descriptor pool was created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT and that MinImageCount and ImageCount match your swap chain configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →