How to Set Up Dear ImGui with DirectX 12: Complete Win32 Integration Guide
To set up Dear ImGui with DirectX 12, you must initialize both the Win32 platform backend (imgui_impl_win32.cpp) and the DirectX 12 renderer backend (imgui_impl_dx12.cpp), then call their respective NewFrame and RenderDrawData functions inside your main game loop.
Dear ImGui is a pure-C++ immediate-mode UI library that separates core functionality from platform-specific backends. When integrating with DirectX 12 in a Win32 application, you combine the core ImGui library with two official backends provided in the ocornut/imgui repository. This guide walks through the exact initialization sequence, frame loop implementation, and cleanup procedures using the official source files.
Understanding the Backend Architecture
Dear ImGui uses a three-layer architecture that keeps the core UI engine independent of graphics APIs and windowing systems.
Core ImGui (imgui.cpp, imgui_draw.cpp) handles widget logic, layout calculations, and generates ImDrawData structures. It knows nothing about Win32 or DirectX 12.
Platform Backend (backends/imgui_impl_win32.cpp) bridges the operating system and ImGui's input system. It translates Win32 window messages, mouse coordinates, keyboard states, and clipboard operations into the ImGuiIO structure.
Renderer Backend (backends/imgui_impl_dx12.cpp) converts ImDrawData into DirectX 12 command list operations. It manages descriptor heaps for fonts, constant buffers for transformation matrices, and texture uploads to the GPU.
This separation allows you to swap the renderer (for example, to Vulkan) while keeping the same Win32 platform code, or vice versa.
Required Backend Files
You need exactly two backend source files from the ocornut/imgui repository alongside the core library:
backends/imgui_impl_win32.cpp– HandlesHWNDregistration, mouse cursors, and input message processing.backends/imgui_impl_dx12.cpp– ManagesID3D12Deviceintegration, descriptor heaps, and command list recording.
Include their corresponding headers in your project:
#include "imgui.h"
#include "backends/imgui_impl_win32.h"
#include "backends/imgui_impl_dx12.h"
Initialization Steps
The setup sequence follows a strict order: create your window and DirectX 12 device first, then initialize ImGui core, followed by the platform and renderer backends.
Initialize ImGui Core
Create the ImGui context and configure IO flags before any backend initialization:
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard navigation
ImGui::StyleColorsDark(); // or ImGui::StyleColorsLight();
Initialize the Win32 Platform Backend
Call ImGui_ImplWin32_Init after creating your HWND but before showing the window. This installs a message hook for input processing:
// hWnd must be valid before this call
ImGui_ImplWin32_Init(hWnd);
This function is implemented in imgui_impl_win32.cpp and registers the window procedure hook required for mouse and keyboard capture.
Initialize the DirectX 12 Renderer Backend
Call ImGui_ImplDX12_Init only after your ID3D12Device, command queue, and render target descriptor heap are fully initialized. This function signature requires several DirectX 12 objects:
ImGui_ImplDX12_Init(
g_pd3dDevice, // ID3D12Device*
2, // int numFramesInFlight (swap chain buffer count)
DXGI_FORMAT_R8G8B8A8_UNORM, // DXGI_FORMAT rtvFormat
g_pd3dSrvDescHeap, // ID3D12DescriptorHeap* for CBV/SRV
g_mainRenderTargetResource, // ID3D12Resource** array of render targets
g_mainRenderTargetDescriptor // D3D12_CPU_DESCRIPTOR_HANDLE* array of RTV handles
);
The numFramesInFlight parameter must match your swap chain's back buffer count. The cbvSrvHeap must contain at least one descriptor for ImGui's font texture. According to imgui_impl_dx12.cpp, this function creates internal GPU resources including a dedicated descriptor heap for fonts and initializes the constant buffer pool.
Rendering Loop Implementation
Each frame requires a specific call sequence to synchronize CPU-side UI construction with GPU-side command list recording.
Starting a New Frame
Begin each frame by calling the backend NewFrame functions in this exact order, followed by ImGui::NewFrame():
// Prepare DirectX 12 backend (resets command allocator, sets descriptor heaps)
ImGui_ImplDX12_NewFrame();
// Prepare Win32 backend (updates mouse position, keyboard state)
// Note: ImGui_ImplWin32_NewFrame internally calls ImGui::NewFrame()
ImGui_ImplWin32_NewFrame();
// Start the ImGui frame
ImGui::NewFrame();
// Build your UI
ImGui::Begin("My Window");
ImGui::Text("Hello, DirectX 12!");
ImGui::End();
ImGui_ImplDX12_NewFrame() ensures the command allocator is reset and the font descriptor heap is bound to the command list for the upcoming draw calls.
Recording Draw Commands
After calling ImGui::Render(), record the draw data into your DirectX 12 command list before executing it:
ImGui::Render();
// Your existing DirectX 12 setup code here:
// - Set render target
// - Clear screen
// - Set root signature
// Record ImGui draw commands into your command list
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_pd3dCommandList);
// Execute command list and present
ImGui_ImplDX12_RenderDrawData translates the ImDrawData structure into ID3D12GraphicsCommandList calls, handling vertex buffer uploads, index buffers, shader resource views, and draw instanced commands.
Shutdown and Cleanup
When your application closes, destroy backends in the reverse order of creation to prevent dangling GPU resources:
ImGui_ImplDX12_Shutdown(); // Releases font texture, descriptor heaps, constant buffers
ImGui_ImplWin32_Shutdown(); // Unhooks window procedure, destroys cursors
ImGui::DestroyContext(); // Frees core ImGui memory
Failing to call ImGui_ImplDX12_Shutdown() will leak ID3D12Resource objects for the font texture and descriptor heaps allocated internally by the backend.
Complete Working Example
Here is a condensed version of the official examples/example_win32_directx12/main.cpp showing the complete integration pattern:
#include "imgui.h"
#include "backends/imgui_impl_win32.h"
#include "backends/imgui_impl_dx12.h"
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>
// Forward declarations
bool CreateDeviceD3D(HWND hWnd);
void CleanupDeviceD3D();
void CreateRenderTarget();
void CleanupRenderTarget();
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// DirectX 12 globals (simplified for brevity)
static ID3D12Device* g_pd3dDevice = nullptr;
static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = nullptr;
static ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr;
static ID3D12Resource* g_mainRenderTargetResource[2] = {};
static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[2] = {};
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
// Create application window
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L,
GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr,
_T("ImGui Example"), nullptr };
RegisterClassEx(&wc);
HWND hwnd = CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX12 Example"),
WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800,
nullptr, nullptr, wc.hInstance, nullptr);
// Initialize DirectX 12
if (!CreateDeviceD3D(hwnd))
{
CleanupDeviceD3D();
UnregisterClass(wc.lpszClassName, wc.hInstance);
return 1;
}
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui::StyleColorsDark();
// Setup Platform/Renderer backends
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplDX12_Init(g_pd3dDevice, 2, DXGI_FORMAT_R8G8B8A8_UNORM,
g_pd3dSrvDescHeap,
g_mainRenderTargetResource,
g_mainRenderTargetDescriptor);
// Main loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
// Start the Dear ImGui frame
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// Simple UI
ImGui::Begin("DirectX 12 Window");
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
1000.0f / io.Framerate, io.Framerate);
ImGui::End();
// Rendering
ImGui::Render();
// Record commands (simplified)
// ... clear render target, set viewport, etc.
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_pd3dCommandList);
// ... execute and present
}
// Cleanup
ImGui_ImplDX12_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
CleanupDeviceD3D();
UnregisterClass(wc.lpszClassName, wc.hInstance);
return 0;
}
Summary
Setting up Dear ImGui with DirectX 12 requires combining three components: the core ImGui library, the Win32 platform backend, and the DirectX 12 renderer backend.
- Include
imgui_impl_win32.cppandimgui_impl_dx12.cppfrom thebackends/folder in your build. - Call
ImGui_ImplWin32_Initwith yourHWNDandImGui_ImplDX12_Initwith yourID3D12Deviceand descriptor heaps. - Each frame, invoke
ImGui_ImplDX12_NewFrame,ImGui_ImplWin32_NewFrame, andImGui::NewFramein sequence. - Render by calling
ImGui_ImplDX12_RenderDrawDatawith your active command list. - Shut down in reverse order: DirectX 12 backend first, then Win32 backend, then destroy the ImGui context.
Frequently Asked Questions
Do I need both the Win32 and DirectX 12 backends?
Yes. Dear ImGui requires separate backends for platform input and rendering. The Win32 backend (imgui_impl_win32.cpp) handles window messages and input state, while the DirectX 12 backend (imgui_impl_dx12.cpp) handles GPU resource management and command list generation. You cannot render without both.
When should I call ImGui_ImplDX12_NewFrame?
Call it immediately before ImGui_ImplWin32_NewFrame at the start of every frame. This function resets the command allocator and binds the font descriptor heap to prepare for rendering. Calling it out of order may result in missing textures or GPU validation errors.
Can I use Dear ImGui with DirectX 12 without Win32?
Yes, but you must provide a custom platform backend. The core ImGui library is platform-agnostic. If you are using a different windowing system (such as SDL2 or GLFW), replace imgui_impl_win32.cpp with the corresponding platform backend (e.g., imgui_impl_sdl2.cpp) while keeping imgui_impl_dx12.cpp for rendering.
Where can I find the official DirectX 12 example?
The complete reference implementation is located at examples/example_win32_directx12/main.cpp in the ocornut/imgui repository. This file demonstrates proper swap chain handling, descriptor heap allocation, and frame synchronization patterns required for a production-ready DirectX 12 integration.
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 →