# How to Set Up Dear ImGui with Vulkan: Complete Implementation Guide

> Learn how to set up Dear ImGui with Vulkan using the official backend. This guide covers initialization and rendering steps for seamless integration.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: how-to-guide
- Published: 2026-07-28

---

**Dear ImGui integrates with Vulkan through the official [`imgui_impl_vulkan.cpp`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/examples/example_win32_vulkan/main.cpp) (lines 180-196), you must create a pool with sufficient capacity:

```cpp
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`](https://github.com/ocornut/imgui/blob/main/examples/example_win32_vulkan/main.cpp) (lines 98-115) demonstrates this configuration:

```cpp
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`](https://github.com/ocornut/imgui/blob/main/examples/example_win32_vulkan/main.cpp) (lines 40-56) follows this sequence:

```cpp
// 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()`:

```cpp
// 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`](https://github.com/ocornut/imgui/blob/main/imgui_impl_vulkan.cpp) (lines 735-795).

## Shutdown and Resource Cleanup

On application exit, call the shutdown function before destroying Vulkan objects:

```cpp
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`**: Implements `ImGui_ImplVulkan_RenderDrawData()` and buffer management. All state lives in `BackendRendererUserData` rather 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 and `ImGui_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`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp)), the Vulkan backend ([`imgui_impl_vulkan.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_vulkan.cpp)) is platform-agnostic. Replace the platform backend with [`imgui_impl_glfw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_glfw.cpp), [`imgui_impl_sdl.cpp`](https://github.com/ocornut/imgui/blob/main/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.