How to Integrate Dear ImGui with Vulkan, DirectX 12, and Metal Graphics APIs
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 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 and 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. Required fields include Instance, PhysicalDevice, Device, QueueFamily, Queue, and DescriptorPool. You must also specify MinImageCount and ImageCount to match your swap-chain configuration.
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 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) 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):
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), providing a VkImageView and VkImageLayout to obtain an ImTextureID:
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).
DirectX 12 Backend Implementation
The DirectX 12 backend resides in backends/imgui_impl_dx12.h and 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) 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.
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) 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), then ImGui::NewFrame(). After ImGui::Render(), submit the draw data via ImGui_ImplDX12_RenderDrawData() (imgui_impl_dx12.h#L60-L61) with your ID3D12GraphicsCommandList:
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).
Metal Backend Implementation
The Metal backend is implemented in 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), providing a MTLDevice and MTLCommandQueue via the ImGui_ImplMetal_InitInfo structure:
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), then render with ImGui_ImplMetal_RenderDrawData() (imgui_impl_metal.h#L48-L50):
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) 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) 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:
// 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.*, orimgui_impl_metal.*from thebackends/directory along with your chosen platform backend. - Populate init structures: Fill
ImGui_ImplVulkan_InitInfo,ImGui_ImplDX12_InitInfo, orImGui_ImplMetal_InitInfowith your existing device, queues, and swap-chain parameters. - Follow the lifecycle: Call
NewFramebeforeImGui::NewFrame, construct your UI, then callRenderDrawDataafterImGui::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
AddTexturefunctions to display them viaImGui::Image().
Frequently Asked Questions
Do I need to use GLFW or SDL with these graphics APIs?
No. The renderer backends (imgui_impl_vulkan.cpp, 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.
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 →