Skip to main content

Overview

Renderer backends handle the graphics API interface for Dear ImGui. They are responsible for:
  • Creating and updating textures (font atlas, user textures)
  • Compiling and managing shaders
  • Setting up render state (blending, scissor testing, etc.)
  • Rendering indexed triangle meshes with clipping
  • Managing graphics API resources
Renderer backends must be paired with a Platform Backend for windowing and input.

Available Renderer Backends

OpenGL3 Backend

File: imgui_impl_opengl3.cpp / imgui_impl_opengl3.h

Best for cross-platform desktop and web

Supports modern OpenGL (3.x/4.x), OpenGL ES (2.0/3.0), and WebGL. The most portable renderer backend.

Features

  • ✅ User texture binding (use GLuint as ImTextureID)
  • ✅ Large meshes support (64k+ vertices) on desktop OpenGL
  • ✅ Dynamic texture updates
  • ✅ Supports desktop GL, GLES, and WebGL

Supported APIs

  • Desktop OpenGL 2.x, 3.x, 4.x
  • OpenGL ES 2.0 (WebGL 1.0)
  • OpenGL ES 3.0 (WebGL 2.0)

Basic Usage

#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>

int main() {
    // ... GLFW initialization ...
    
    // Decide GL+GLSL versions
    const char* glsl_version = "#version 330";
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    
    GLFWwindow* window = glfwCreateWindow(1280, 720, "App", NULL, NULL);
    glfwMakeContextCurrent(window);
    
    // Setup Dear ImGui
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    
    // Initialize backends
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init(glsl_version);
    
    // Main loop
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        
        // Start frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
        
        // Your UI code
        ImGui::ShowDemoWindow();
        
        // Render
        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        glfwSwapBuffers(window);
    }
    
    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    
    return 0;
}

GLSL Version Selection

The glsl_version parameter should match your OpenGL context:
// Desktop GL 3.3+
const char* glsl_version = "#version 330";

// Desktop GL 4.1+ (macOS)
const char* glsl_version = "#version 410";

// OpenGL ES 2.0 (WebGL 1.0)
const char* glsl_version = "#version 100";

// OpenGL ES 3.0 (WebGL 2.0)
const char* glsl_version = "#version 300 es";

Configuration Defines

For OpenGL ES, define in your imconfig.h or build system:
// Enable OpenGL ES 2.0
#define IMGUI_IMPL_OPENGL_ES2

// Enable OpenGL ES 3.0
#define IMGUI_IMPL_OPENGL_ES3
These are auto-detected on iOS, Android, and Emscripten.

API Functions

// Initialize with optional GLSL version string
bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);

// Per-frame update
void ImGui_ImplOpenGL3_NewFrame();

// Render Dear ImGui draw data
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);

// Shutdown
void ImGui_ImplOpenGL3_Shutdown();

// Device objects management
bool ImGui_ImplOpenGL3_CreateDeviceObjects();
void ImGui_ImplOpenGL3_DestroyDeviceObjects();

// Manual texture update
void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex);

Vulkan Backend

File: imgui_impl_vulkan.cpp / imgui_impl_vulkan.h

Modern explicit graphics API

Vulkan backend with full control over rendering pipeline. Best for high-performance applications.

Features

  • ✅ User texture binding (use VkDescriptorSet as ImTextureID)
  • ✅ Large meshes support (64k+ vertices)
  • ✅ Dynamic texture updates
  • ✅ Dynamic rendering support (Vulkan 1.3)
  • ✅ Exposes render state for callbacks

Initialization

Vulkan backend requires more setup than other backends:
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h"
#include <vulkan/vulkan.h>

// Vulkan error check callback
static void check_vk_result(VkResult err) {
    if (err == 0) return;
    fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
    if (err < 0) abort();
}

int main() {
    // ... Create Vulkan instance, device, queues, etc. ...
    
    // Setup Dear ImGui
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    
    // Initialize platform backend
    ImGui_ImplGlfw_InitForVulkan(window, true);
    
    // Initialize Vulkan backend
    ImGui_ImplVulkan_InitInfo init_info = {};
    init_info.Instance = instance;
    init_info.PhysicalDevice = physical_device;
    init_info.Device = device;
    init_info.QueueFamily = queue_family;
    init_info.Queue = queue;
    init_info.DescriptorPool = descriptor_pool;
    init_info.MinImageCount = min_image_count;
    init_info.ImageCount = image_count;
    init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
    init_info.PipelineInfoMain.RenderPass = render_pass;
    init_info.PipelineInfoMain.Subpass = 0;
    init_info.CheckVkResultFn = check_vk_result;
    
    ImGui_ImplVulkan_Init(&init_info);
    
    // Main loop
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        
        // Start frame
        ImGui_ImplVulkan_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
        
        // Your UI code
        ImGui::ShowDemoWindow();
        
        // Render
        ImGui::Render();
        ImDrawData* draw_data = ImGui::GetDrawData();
        
        // Record command buffer
        VkCommandBufferBeginInfo begin_info = {};
        begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
        vkBeginCommandBuffer(command_buffer, &begin_info);
        
        // Begin render pass
        VkRenderPassBeginInfo render_pass_info = {};
        // ... setup render pass ...
        vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
        
        // Render Dear ImGui
        ImGui_ImplVulkan_RenderDrawData(draw_data, command_buffer);
        
        vkCmdEndRenderPass(command_buffer);
        vkEndCommandBuffer(command_buffer);
        
        // Submit and present
        // ...
    }
    
    // Cleanup
    vkDeviceWaitIdle(device);
    ImGui_ImplVulkan_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    
    return 0;
}

Texture Management

Register custom textures with Vulkan:
// Add a texture
VkDescriptorSet my_texture_id = ImGui_ImplVulkan_AddTexture(
    sampler,
    image_view,
    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
);

// Use in Dear ImGui
ImGui::Image((ImTextureID)my_texture_id, ImVec2(width, height));

// Remove texture when done
ImGui_ImplVulkan_RemoveTexture(my_texture_id);

Dynamic Rendering (Vulkan 1.3+)

ImGui_ImplVulkan_InitInfo init_info = {};
// ... other setup ...
init_info.UseDynamicRendering = true;

// Setup pipeline info for dynamic rendering
VkPipelineRenderingCreateInfoKHR rendering_info = {};
rendering_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
rendering_info.colorAttachmentCount = 1;
rendering_info.pColorAttachmentFormats = &color_format;
init_info.PipelineInfoMain.PipelineRenderingCreateInfo = rendering_info;

ImGui_ImplVulkan_Init(&init_info);

DirectX 11 Backend

File: imgui_impl_dx11.cpp / imgui_impl_dx11.h

Recommended for Windows desktop

DirectX 11 is well-supported, stable, and provides excellent performance on Windows.

Features

  • ✅ User texture binding (use ID3D11ShaderResourceView* as ImTextureID)
  • ✅ Large meshes support (64k+ vertices)
  • ✅ Dynamic texture updates
  • ✅ Exposes render state for callbacks

Basic Usage

#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"
#include <d3d11.h>

ID3D11Device* g_pd3dDevice = nullptr;
ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;

void CreateRenderTarget() {
    ID3D11Texture2D* pBackBuffer;
    g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
    g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_mainRenderTargetView);
    pBackBuffer->Release();
}

int main() {
    // Create D3D11 device
    DXGI_SWAP_CHAIN_DESC sd = {};
    sd.BufferCount = 2;
    sd.BufferDesc.Width = 1280;
    sd.BufferDesc.Height = 720;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hwnd;
    sd.SampleDesc.Count = 1;
    sd.Windowed = TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
    
    D3D_FEATURE_LEVEL featureLevel;
    D3D11CreateDeviceAndSwapChain(
        NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0,
        NULL, 0, D3D11_SDK_VERSION, &sd,
        &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext
    );
    
    CreateRenderTarget();
    
    // Setup Dear ImGui
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    
    // Initialize backends
    ImGui_ImplWin32_Init(hwnd);
    ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
    
    // Main loop
    MSG msg = {};
    while (msg.message != WM_QUIT) {
        if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            continue;
        }
        
        // Start frame
        ImGui_ImplDX11_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();
        
        // Your UI code
        ImGui::ShowDemoWindow();
        
        // Render
        ImGui::Render();
        const float clear_color[4] = { 0.45f, 0.55f, 0.60f, 1.00f };
        g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL);
        g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color);
        ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
        
        g_pSwapChain->Present(1, 0);
    }
    
    // Cleanup
    ImGui_ImplDX11_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();
    
    return 0;
}

API Functions

// Initialize with D3D11 device and context
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);

// Per-frame update
void ImGui_ImplDX11_NewFrame();

// Render Dear ImGui draw data
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);

// Shutdown
void ImGui_ImplDX11_Shutdown();

// Device objects (for device reset)
bool ImGui_ImplDX11_CreateDeviceObjects();
void ImGui_ImplDX11_InvalidateDeviceObjects();

// Manual texture update
void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex);

DirectX 12 Backend

File: imgui_impl_dx12.cpp / imgui_impl_dx12.h Modern explicit DirectX API, similar complexity to Vulkan. Best for applications that need fine-grained control over GPU resources.

Metal Backend

File: imgui_impl_metal.mm / imgui_impl_metal.h

Apple's modern graphics API

Metal backend for macOS and iOS. Supports both Objective-C and C++ APIs.

Features

  • ✅ User texture binding (use MTLTexture as ImTextureID)
  • ✅ Large meshes support (64k+ vertices)
  • ✅ Dynamic texture updates
  • ✅ ObjC and C++ API support

Basic Usage (Objective-C)

#include "imgui.h"
#include "imgui_impl_osx.h"
#include "imgui_impl_metal.h"
#import <Metal/Metal.h>

id<MTLDevice> device;
id<MTLCommandQueue> commandQueue;

int main() {
    // Create Metal device
    device = MTLCreateSystemDefaultDevice();
    commandQueue = [device newCommandQueue];
    
    // Setup Dear ImGui
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    
    // Initialize backends
    ImGui_ImplOSX_Init(view);
    ImGui_ImplMetal_Init(device);
    
    // In your render loop:
    MTLRenderPassDescriptor* renderPassDescriptor = // ... your render pass
    id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
    id<MTLRenderCommandEncoder> renderEncoder =
        [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
    
    // Start frame
    ImGui_ImplMetal_NewFrame(renderPassDescriptor);
    ImGui_ImplOSX_NewFrame(view);
    ImGui::NewFrame();
    
    // Your UI code
    ImGui::ShowDemoWindow();
    
    // Render
    ImGui::Render();
    ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), commandBuffer, renderEncoder);
    
    [renderEncoder endEncoding];
    [commandBuffer presentDrawable:drawable];
    [commandBuffer commit];
    
    return 0;
}

WebGPU Backend

File: imgui_impl_wgpu.cpp / imgui_impl_wgpu.h

Next-generation web and native graphics

WebGPU is the future of web graphics and also works natively. Supports both desktop and web platforms.

Configuration

Define the WebGPU backend in your imconfig.h:
// For Dawn (Emscripten 4.0.10+)
#define IMGUI_IMPL_WEBGPU_BACKEND_DAWN

// For wgpu-native
#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU

Renderer Backend Lifecycle

All renderer backends follow this pattern:
1

Initialization

Initialize the renderer backend after creating the graphics device:
// Example: OpenGL3
ImGui_ImplOpenGL3_Init("#version 330");

// Example: DirectX 11
ImGui_ImplDX11_Init(device, device_context);
The backend sets up:
  • Shaders and pipeline state
  • Default texture (font atlas)
  • Vertex/index buffers
  • Backend flags
2

Per-Frame Update

Call the renderer’s NewFrame() function:
ImGui_ImplOpenGL3_NewFrame();
This prepares the backend for rendering.
3

Rendering

After ImGui::Render(), call the renderer’s RenderDrawData() function:
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
ImGui_ImplOpenGL3_RenderDrawData(draw_data);
The backend:
  • Sets up render state
  • Binds textures and buffers
  • Submits draw calls
  • Restores previous state
4

Shutdown

Clean up resources before destroying the graphics device:
ImGui_ImplOpenGL3_Shutdown();

Custom Texture Usage

All renderer backends support custom textures:

OpenGL3

GLuint my_texture;
glGenTextures(1, &my_texture);
glBindTexture(GL_TEXTURE_2D, my_texture);
// ... upload texture data ...

ImGui::Image((ImTextureID)(intptr_t)my_texture, ImVec2(width, height));

Vulkan

VkDescriptorSet my_texture = ImGui_ImplVulkan_AddTexture(
    sampler, image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

ImGui::Image((ImTextureID)my_texture, ImVec2(width, height));

DirectX 11

ID3D11ShaderResourceView* my_texture;
// ... create texture and SRV ...

ImGui::Image((ImTextureID)my_texture, ImVec2(width, height));

Render State Access

Some backends expose render state for custom draw callbacks:
// Get render state during rendering
auto* state = (ImGui_ImplDX11_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
ID3D11Device* device = state->Device;
ID3D11DeviceContext* context = state->DeviceContext;

// Use in ImDrawCallback
ImGui::GetWindowDrawList()->AddCallback([](const ImDrawList*, const ImDrawCmd*) {
    auto* state = (ImGui_ImplDX11_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
    // Custom rendering using state->Device, state->DeviceContext, etc.
}, nullptr);

Advanced: Manual Texture Updates

For precise control over texture update timing (e.g., for staged rendering):
// Disable automatic texture updates
ImDrawData* draw_data = ImGui::GetDrawData();
draw_data->Textures = nullptr;

// Manually update textures
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) {
    if (tex->Status != ImTextureStatus_OK)
        ImGui_ImplOpenGL3_UpdateTexture(tex);
}

Backend Comparison

FeatureOpenGL3VulkanDX11DX12MetalWebGPU
ComplexityLowHighLowHighMediumMedium
PortabilityExcellentGoodWindowsWindowsAppleExcellent
PerformanceGoodExcellentGoodExcellentExcellentGood
Setup CodeMinimalExtensiveModerateExtensiveModerateModerate
WebAssemblyYesNoNoNoNoYes

Next Steps

Platform Backends

Learn about platform backends for windowing

Custom Backend

Create your own renderer backend

Build docs developers (and LLMs) love