# How to Set Up Dear ImGui with DirectX 12: Complete Win32 Integration Guide

> Integrate Dear ImGui with DirectX 12 easily. Follow this guide to set up Win32 and DX12 backends for seamless rendering in your application.

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

---

**To set up Dear ImGui with DirectX 12, you must initialize both the Win32 platform backend ([`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp)) and the DirectX 12 renderer backend ([`imgui_impl_dx12.cpp`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_win32.cpp)** – Handles `HWND` registration, mouse cursors, and input message processing.
- **[`backends/imgui_impl_dx12.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_dx12.cpp)** – Manages `ID3D12Device` integration, descriptor heaps, and command list recording.

Include their corresponding headers in your project:

```cpp
#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:

```cpp
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:

```cpp
// hWnd must be valid before this call
ImGui_ImplWin32_Init(hWnd);

```

This function is implemented in [`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/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:

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

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

```cpp
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:

```cpp
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`](https://github.com/ocornut/imgui/blob/main/examples/example_win32_directx12/main.cpp) showing the complete integration pattern:

```cpp
#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.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp) and [`imgui_impl_dx12.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_dx12.cpp) from the `backends/` folder in your build.
- Call `ImGui_ImplWin32_Init` with your `HWND` and `ImGui_ImplDX12_Init` with your `ID3D12Device` and descriptor heaps.
- Each frame, invoke `ImGui_ImplDX12_NewFrame`, `ImGui_ImplWin32_NewFrame`, and `ImGui::NewFrame` in sequence.
- Render by calling `ImGui_ImplDX12_RenderDrawData` with 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`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp)) handles window messages and input state, while the DirectX 12 backend ([`imgui_impl_dx12.cpp`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp) with the corresponding platform backend (e.g., [`imgui_impl_sdl2.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_sdl2.cpp)) while keeping [`imgui_impl_dx12.cpp`](https://github.com/ocornut/imgui/blob/main/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`](https://github.com/ocornut/imgui/blob/main/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.