# How to Integrate Dear ImGui with Vulkan, DirectX 12, and Metal Graphics APIs

> Learn to integrate Dear ImGui with Vulkan, DirectX 12, and Metal graphics APIs. Follow simple steps to add backend files and update your render loop for seamless UI integration.

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

---

**To integrate Dear ImGui with modern graphics APIs, include the appropriate renderer backend files from the `backends/` directory and call the `Init`, `NewFrame`, and `RenderDrawData` functions at specific points in your render loop.**

Dear ImGui is an immediate-mode graphical user interface library designed for integration with game engines and real-time visualization tools. According to the [ocornut/imgui](https://github.com/ocornut/imgui) source code, the library employs a dual-backend architecture that separates platform handling (window and input processing) from renderer implementation (GPU command generation). This design allows you to pair any platform backend—such as GLFW, SDL, or Win32—with Vulkan, DirectX 12, or Metal by simply copying the relevant backend files into your project and initializing them with your existing graphics objects.

## Vulkan Backend Implementation

The Vulkan backend is implemented in [`backends/imgui_impl_vulkan.h`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_vulkan.h) and [`backends/imgui_impl_vulkan.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_vulkan.cpp). This backend requires you to provide all Vulkan objects (instance, device, queues), as ImGui never assumes ownership of your graphics context or creates internal Vulkan objects.

### Initialization and Configuration

Before rendering, populate the `ImGui_ImplVulkan_InitInfo` structure defined at [imgui_impl_vulkan.h#L96-L140](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.h#L96-L140). Required fields include `Instance`, `PhysicalDevice`, `Device`, `QueueFamily`, `Queue`, and `DescriptorPool`. You must also specify `MinImageCount` and `ImageCount` to match your swap-chain configuration.

```cpp
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = vkInstance;
init_info.PhysicalDevice = vkPhysicalDevice;
init_info.Device = vkDevice;
init_info.QueueFamily = graphicsQueueFamily;
init_info.Queue = graphicsQueue;
init_info.DescriptorPool = vkDescriptorPool;
init_info.MinImageCount = 2;
init_info.ImageCount = swapChainImageCount;
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.PipelineInfoMain.RenderPass = mainRenderPass;
ImGui_ImplVulkan_Init(&init_info);

```

Call `ImGui_ImplVulkan_Init()` defined at [imgui_impl_vulkan.h#L42-L45](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.h#L42-L45) after creating the ImGui context with `ImGui::CreateContext()`.

### Per-Frame Rendering Workflow

Each frame requires three specific calls synchronized with your command buffer recording. First, invoke `ImGui_ImplVulkan_NewFrame()` ([imgui_impl_vulkan.h#L44-L45](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.h#L44-L45)) before `ImGui::NewFrame()`. After building your UI and calling `ImGui::Render()`, submit the draw data via `ImGui_ImplVulkan_RenderDrawData()` ([imgui_impl_vulkan.h#L45-L46](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.h#L45-L46)):

```cpp
ImGui_ImplVulkan_NewFrame();
ImGui::NewFrame();
// ... UI construction code ...
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer);

```

### Dynamic Rendering and Custom Textures

For Vulkan 1.3 or when using `VK_KHR_dynamic_rendering`, set `UseDynamicRendering = true` in the init info and populate `PipelineRenderingCreateInfo` to render without a traditional `VkRenderPass`.

To use custom textures, call `ImGui_ImplVulkan_AddTexture()` ([imgui_impl_vulkan.h#L56-L58](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.h#L56-L58)), providing a `VkImageView` and `VkImageLayout` to obtain an `ImTextureID`:

```cpp
ImTextureID textureId = ImGui_ImplVulkan_AddTexture(
    imageView, 
    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
);
ImGui::Image(textureId, ImVec2(256, 256));

```

The backend stores renderer state accessible via `ImGui::GetPlatformIO().Renderer_RenderState` (see `ImGui_ImplVulkan_RenderState`) for callbacks that need access to the current Vulkan context. Cleanup is handled by `ImGui_ImplVulkan_Shutdown()` ([imgui_impl_vulkan.h#L43-L44](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_vulkan.h#L43-L44)).

## DirectX 12 Backend Implementation

The DirectX 12 backend resides in [`backends/imgui_impl_dx12.h`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_dx12.h) and [`backends/imgui_impl_dx12.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_dx12.cpp). Like Vulkan, this backend requires you to manage the swap-chain, render-targets, and descriptor heaps independently.

### Initialization with Descriptor Management

The `ImGui_ImplDX12_InitInfo` structure ([imgui_impl_dx12.h#L33-L53](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx12.h#L33-L53)) requires an `ID3D12Device`, command queue, and callbacks for SRV (Shader Resource View) descriptor allocation. You can provide `SrvDescriptorAllocFn` and `SrvDescriptorFreeFn` to let ImGui manage descriptors dynamically, or use the legacy single-descriptor fields for simple applications.

```cpp
ImGui_ImplDX12_InitInfo init_info = {};
init_info.Device = d3dDevice;
init_info.CommandQueue = commandQueue;
init_info.NumFramesInFlight = 2;
init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
// Configure descriptor callbacks for dynamic allocation
init_info.SrvDescriptorAllocFn = myAllocFn;
init_info.SrvDescriptorFreeFn = myFreeFn;
ImGui_ImplDX12_Init(&init_info);

```

Call `ImGui_ImplDX12_Init()` ([imgui_impl_dx12.h#L56-L58](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx12.h#L56-L58)) to initialize the pipeline state objects and root signature.

### Rendering and Resource Integration

Each frame follows the standard pattern: call `ImGui_ImplDX12_NewFrame()` ([imgui_impl_dx12.h#L59-L60](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx12.h#L59-L60)), then `ImGui::NewFrame()`. After `ImGui::Render()`, submit the draw data via `ImGui_ImplDX12_RenderDrawData()` ([imgui_impl_dx12.h#L60-L61](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx12.h#L60-L61)) with your `ID3D12GraphicsCommandList`:

```cpp
ImGui_ImplDX12_NewFrame();
ImGui::NewFrame();
// ... UI construction ...
ImGui::Render();
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), commandList);

```

The backend creates one SRV descriptor per texture internally. Register external textures using `ImGui_ImplDX12_AddTexture()` following the same pattern as Vulkan. Render state for callbacks is accessible via `ImGui_ImplDX12_RenderState`. Terminate the backend with `ImGui_ImplDX12_Shutdown()` ([imgui_impl_dx12.h#L56-L59](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_dx12.h#L56-L59)).

## Metal Backend Implementation

The Metal backend is implemented in [`backends/imgui_impl_metal.h`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_metal.h) and `backends/imgui_impl_metal.mm`, requiring Objective-C++ compilation for the implementation file.

### Platform Initialization

Initialize with `ImGui_ImplMetal_Init()` ([imgui_impl_metal.h#L33-L45](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_metal.h#L33-L45)), providing a `MTLDevice` and `MTLCommandQueue` via the `ImGui_ImplMetal_InitInfo` structure:

```cpp
ImGui_ImplMetal_InitInfo init_info = {};
init_info.Device = device;
init_info.CommandQueue = commandQueue;
ImGui_ImplMetal_Init(&init_info);

```

### Render Pass Configuration

Unlike Vulkan or DirectX 12, the Metal backend requires you to provide an `MTLRenderPassDescriptor` each frame, as ImGui does not manage the `CAMetalLayer` or render targets. Call `ImGui_ImplMetal_NewFrame()` ([imgui_impl_metal.h#L46-L48](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_metal.h#L46-L48)), then render with `ImGui_ImplMetal_RenderDrawData()` ([imgui_impl_metal.h#L48-L50](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_metal.h#L48-L50)):

```cpp
ImGui_ImplMetal_NewFrame();
ImGui::NewFrame();
// ... UI construction ...
ImGui::Render();
MTLRenderPassDescriptor* descriptor = [view currentRenderPassDescriptor];
ImGui_ImplMetal_RenderDrawData(
    ImGui::GetDrawData(), 
    commandBuffer, 
    descriptor
);

```

Register textures using `ImGui_ImplMetal_AddTexture()` ([imgui_impl_metal.h#L51-L53](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_metal.h#L51-L53)) to obtain an `ImTextureID`. Render state is stored in `ImGui_ImplMetal_RenderState` for callback access. Shutdown with `ImGui_ImplMetal_Shutdown()` ([imgui_impl_metal.h#L54-L55](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_metal.h#L54-L55)) when your application terminates.

## Common Integration Pattern

All three backends follow an identical lifecycle within your application. After creating the ImGui context, initialize the backend, then in your main loop call the per-frame preparation, build your UI, and submit draw data. Here is a complete example using the Vulkan backend:

```cpp
// 1. Create ImGui context
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();

// 2. Backend-specific initialization
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = vkInstance;
init_info.PhysicalDevice = vkPhysicalDevice;
init_info.Device = vkDevice;
init_info.QueueFamily = graphicsQueueFamily;
init_info.Queue = graphicsQueue;
init_info.DescriptorPool = vkDescriptorPool;
init_info.MinImageCount = 2;
init_info.ImageCount = swapChainImageCount;
ImGui_ImplVulkan_Init(&init_info);

// 3. Main loop
while (running) {
    // Platform event processing (GLFW/SDL/etc.)
    
    // Backend new frame preparation
    ImGui_ImplVulkan_NewFrame();
    ImGui::NewFrame();
    
    // UI construction
    ImGui::Begin("Hello");
    ImGui::Text("Application average %.3f ms/frame", 1000.0f / io.Framerate);
    ImGui::End();
    
    // Rendering
    ImGui::Render();
    ImDrawData* draw_data = ImGui::GetDrawData();
    ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffer);
    
    // Present swap chain
}

```

## Summary

- **Copy backend files**: Include `imgui_impl_vulkan.*`, `imgui_impl_dx12.*`, or `imgui_impl_metal.*` from the `backends/` directory along with your chosen platform backend.
- **Populate init structures**: Fill `ImGui_ImplVulkan_InitInfo`, `ImGui_ImplDX12_InitInfo`, or `ImGui_ImplMetal_InitInfo` with your existing device, queues, and swap-chain parameters.
- **Follow the lifecycle**: Call `NewFrame` before `ImGui::NewFrame`, construct your UI, then call `RenderDrawData` after `ImGui::Render`.
- **Retain resource ownership**: ImGui never owns swap-chains, render targets, or descriptor pools—you maintain full control over your graphics pipeline.
- **Use AddTexture APIs**: Register GPU textures with the backend-specific `AddTexture` functions to display them via `ImGui::Image()`.

## Frequently Asked Questions

### Do I need to use GLFW or SDL with these graphics APIs?

No. The renderer backends ([`imgui_impl_vulkan.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_vulkan.cpp), [`imgui_impl_dx12.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_dx12.cpp), and `imgui_impl_metal.mm`) are entirely separate from windowing code. You can pair them with any platform backend (GLFW, SDL, Win32, macOS Cocoa, etc.) or implement your own platform layer by populating the `ImGuiIO` structure directly with window and input data.

### Can I use Vulkan dynamic rendering instead of render pass objects?

Yes. In `ImGui_ImplVulkan_InitInfo`, set `UseDynamicRendering = true` and provide your color attachment format in the `PipelineRenderingCreateInfo` structure. This allows the backend to render using `VK_KHR_dynamic_rendering` without requiring a `VkRenderPass` object during initialization.

### How do I load fonts or display textures with these backends?

Each backend provides a specific `AddTexture` function: `ImGui_ImplVulkan_AddTexture()` for Vulkan image views, `ImGui_ImplDX12_AddTexture()` for DirectX 12 resources, and `ImGui_ImplMetal_AddTexture()` for Metal textures. These return an `ImTextureID` handle that you can pass to `ImGui::Image()` or `ImGui::ImageButton()`. For fonts, use the standard `ImGui::GetIO().Fonts->AddFontFromFileTTF()` API; the backend handles the texture upload automatically during initialization.

### What graphics API version requirements do these backends have?

The Vulkan backend supports Vulkan 1.0 and higher, with optional features available for Vulkan 1.3 dynamic rendering. The DirectX 12 backend requires Windows 10 or later with a DirectX Feature Level 12_0 capable device. The Metal backend supports Metal 2.0 and higher, requiring macOS 10.13+ or iOS 11.0+. All backends are header-only compatible with the respective API SDKs.