# How to Implement a Custom Rendering Backend for Dear ImGui

> Learn how to implement a custom rendering backend for Dear ImGui. Expose core functions, manage GPU resources, and translate draw data for your graphics API.

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

---

**To implement a custom rendering backend for ImGui, you must expose four core functions (`Init`, `Shutdown`, `NewFrame`, `RenderDrawData`), store GPU resources in a data structure attached to `io.BackendRendererUserData`, and translate `ImDrawData` into your graphics API's draw calls while preserving and restoring the previous graphics state.**

Dear ImGui (ocornut/imgui) separates platform and renderer responsibilities into distinct backend modules, allowing integration with any graphics API. When you implement a custom rendering backend for ImGui, you provide the bridge between high-level draw lists and GPU command submission. This guide follows the exact patterns found in [`backends/imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_opengl3.cpp) and the contract defined in [`docs/BACKENDS.md`](https://github.com/ocornut/imgui/blob/main/docs/BACKENDS.md).

## The Renderer Backend Contract

According to the ocornut/imgui source code, a renderer backend must expose a specific ABI that ImGui calls during the frame lifecycle. The reference OpenGL 3 implementation in [`backends/imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_opengl3.cpp) demonstrates the required function signatures and behavior.

| Function | Purpose | Reference in [`imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_opengl3.cpp) |
|----------|---------|--------------------------------------|
| `ImGui_ImplOpenGL3_Init` | Initialize the backend, load GL loader, compile shaders | Lines 31-33 |
| `ImGui_ImplOpenGL3_NewFrame` | Per-frame setup, ensure device objects exist | Lines 24-33 |
| `ImGui_ImplOpenGL3_RenderDrawData` | Core rendering routine, uploads buffers, issues draw calls | Lines 55-56 |
| `ImGui_ImplOpenGL3_Shutdown` | Release all GPU resources | Lines 16-24 |

The backend API is declared in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) through `ImGuiIO::BackendRendererUserData` and documented in [`docs/BACKENDS.md`](https://github.com/ocornut/imgui/blob/main/docs/BACKENDS.md). Your implementation must create and manage GPU resources (shaders, buffers, textures) and report capabilities through `ImGuiIO::BackendRendererFlags` (e.g., `ImGuiBackendFlags_RendererHasVtxOffset`).

## Step-by-Step Implementation Guide

### 1. Define the Backend Data Structure

Create a structure to hold shader handles, buffer IDs, and capability flags. Store a pointer to this structure in `io.BackendRendererUserData` so ImGui can retrieve it during callbacks.

```cpp
// imgui_impl_myapi.h
struct ImGui_ImplMyAPI_Data {
    MyShaderHandle  Shader;
    MyBufferHandle  Vbo, Ibo;
    bool            HasVtxOffset;   // Advertise via ImGuiBackendFlags_RendererHasVtxOffset
    
    ImGui_ImplMyAPI_Data() { memset(this, 0, sizeof(*this)); }
};

IMGUI_API bool ImGui_ImplMyAPI_Init();
IMGUI_API void ImGui_ImplMyAPI_Shutdown();
IMGUI_API void ImGui_ImplMyAPI_NewFrame();
IMGUI_API void ImGui_ImplMyAPI_RenderDrawData(ImDrawData* draw_data);

```

### 2. Initialize GPU Resources

The `Init` function creates shaders, generates buffers, and advertises renderer capabilities. Attach your data structure to `io.BackendRendererUserData` so ImGui can access it throughout the application lifetime.

```cpp
bool ImGui_ImplMyAPI_Init()
{
    ImGuiIO& io = ImGui::GetIO();
    ImGui_ImplMyAPI_Data* bd = IM_NEW(ImGui_ImplMyAPI_Data)();
    io.BackendRendererUserData = (void*)bd;
    io.BackendRendererName = "myapi";

    // Compile shaders (implement MyCreateShader for your API)
    bd->Shader = MyCreateShader(vertex_src, fragment_src);
    bd->Vbo = MyCreateBuffer();
    bd->Ibo = MyCreateBuffer();

    // Advertise support for base vertex offsets if available
    io.BackendRendererFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
    return bd->Shader != nullptr;
}

```

### 3. Handle New Frames

`NewFrame` is called each frame before any ImGui API usage. Ensure device objects are valid and recreate them if they were lost (e.g., due to device reset).

```cpp
void ImGui_ImplMyAPI_NewFrame()
{
    ImGui_ImplMyAPI_Data* bd = (ImGui_ImplMyAPI_Data*)ImGui::GetIO().BackendRendererUserData;
    if (!bd->Shader)   // Lost device scenario
        ImGui_ImplMyAPI_Init();
}

```

### 4. Render Draw Data

The `RenderDrawData` function translates `ImDrawData` into your graphics API commands. As implemented in [`backends/imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_opengl3.cpp), you must backup the current graphics state, upload vertex and index buffers, iterate over command lists, then restore the previous state.

```cpp
void ImGui_ImplMyAPI_RenderDrawData(ImDrawData* draw_data)
{
    if (!draw_data) return;
    ImGui_ImplMyAPI_Data* bd = (ImGui_ImplMyAPI_Data*)ImGui::GetIO().BackendRendererUserData;

    // Backup current graphics state (viewport, blend mode, active texture, etc.)
    MyStateBackup backup = MyCaptureState();

    // Upload vertex and index buffers
    size_t vtx_size = draw_data->TotalVtxCount * sizeof(ImDrawVert);
    size_t idx_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
    MyUpdateBuffer(bd->Vbo, draw_data->VtxBuffer.Data, vtx_size);
    MyUpdateBuffer(bd->Ibo, draw_data->IdxBuffer.Data, idx_size);

    // Process each command list
    for (int n = 0; n < draw_data->CmdListsCount; n++)
    {
        const ImDrawList* cmd_list = draw_data->CmdLists[n];
        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
        {
            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
            
            // Handle user callbacks
            if (pcmd->UserCallback)
            {
                pcmd->UserCallback(cmd_list, pcmd);
                continue;
            }
            
            // Set scissor rectangle and bind texture
            MySetScissor(pcmd->ClipRect);
            MyBindTexture((MyTexture)pcmd->GetTexID());
            
            // Issue draw call with base vertex offset if supported
            if (bd->HasVtxOffset && pcmd->VtxOffset)
                MyDrawElementsBaseVertex(pcmd->ElemCount,
                                        (pcmd->IdxOffset * sizeof(ImDrawIdx)),
                                        pcmd->VtxOffset);
            else
                MyDrawElements(pcmd->ElemCount,
                              (pcmd->IdxOffset * sizeof(ImDrawIdx)));
        }
    }

    // Restore previous graphics state to avoid side effects
    MyRestoreState(backup);
}

```

### 5. Release Resources on Shutdown

`Shutdown` releases all GPU resources and frees the backend data structure. Set `io.BackendRendererUserData` to `nullptr` to indicate the backend is inactive.

```cpp
void ImGui_ImplMyAPI_Shutdown()
{
    ImGuiIO& io = ImGui::GetIO();
    ImGui_ImplMyAPI_Data* bd = (ImGui_ImplMyAPI_Data*)io.BackendRendererUserData;
    if (!bd) return;
    
    MyDeleteShader(bd->Shader);
    MyDeleteBuffer(bd->Vbo);
    MyDeleteBuffer(bd->Ibo);
    IM_DELETE(bd);
    io.BackendRendererUserData = nullptr;
}

```

## Integrating with Your Application

Include your backend header and call the lifecycle functions in your main loop. This pattern mirrors the examples in the `examples/` folder of ocornut/imgui.

```cpp
#include "imgui.h"
#include "backends/imgui_impl_myapi.h"

int main()
{
    MyGraphicsDeviceInit();               // Your platform-specific initialization
    ImGui::CreateContext();
    ImGui_ImplMyAPI_Init();               // Initialize the renderer backend
    
    while (!MyWindowShouldClose())
    {
        ImGui_ImplMyAPI_NewFrame();       // Prepare backend for new frame
        ImGui::NewFrame();
        
        // Your ImGui UI code here
        
        ImGui::Render();
        ImGui_ImplMyAPI_RenderDrawData(ImGui::GetDrawData());
        MySwapBuffers();
    }
    
    ImGui_ImplMyAPI_Shutdown();
    MyGraphicsDeviceShutdown();
    return 0;
}

```

## Summary

- **Expose four required functions**: Implement `Init`, `Shutdown`, `NewFrame`, and `RenderDrawData` with signatures matching the backend contract in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h).
- **Store resources in `BackendRendererUserData`**: Create a custom structure (like `ImGui_ImplOpenGL3_Data`) holding shaders, buffers, and flags, and attach it to `ImGuiIO`.
- **Translate `ImDrawData` correctly**: Upload `VtxBuffer` and `IdxBuffer`, iterate over `ImDrawCmd` entries, handle user callbacks, set scissor rectangles, and issue textured draw calls.
- **Preserve graphics state**: Backup the entire relevant GPU state at the start of `RenderDrawData` and restore it before returning to avoid corrupting the host application's rendering.
- **Advertise capabilities**: Set `ImGuiBackendFlags_RendererHasVtxOffset` if your API supports base-vertex offsets, enabling ImGui to render large meshes efficiently.

## Frequently Asked Questions

### What is the difference between a platform backend and a renderer backend?

The **platform backend** handles window creation, input polling, and time-keeping for the host operating system (e.g., SDL2, GLFW), while the **renderer backend** translates ImGui's draw lists into GPU commands for a specific graphics API (OpenGL, Vulkan, DirectX). These are separate concerns in ocornut/imgui's architecture, allowing you to mix any platform backend with any renderer backend according to [`docs/BACKENDS.md`](https://github.com/ocornut/imgui/blob/main/docs/BACKENDS.md).

### How do I handle textures in a custom ImGui rendering backend?

ImGui uses `ImTextureID` as an opaque handle. Cast your native texture type to `ImTextureID` (typically via `(ImTextureID)(intptr_t)my_texture`) when binding in `RenderDrawData`. In the draw loop, retrieve the texture using `pcmd->GetTexID()` and cast it back to your API's texture handle before binding. This pattern is used throughout [`imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_opengl3.cpp) for font atlases and user images.

### Do I need to support vertex offsets in my renderer?

You should support vertex offsets if your graphics API provides base-vertex draw functions (e.g., `glDrawElementsBaseVertex` or Vulkan's `firstVertex`). Advertise this capability by setting `ImGuiBackendFlags_RendererHasVtxOffset` in `io.BackendRendererFlags`. This allows Dear ImGui to render large meshes exceeding 16-bit index limits by splitting them into separate draw lists with byte offsets.

### Why must I backup and restore graphics state when rendering ImGui?

The renderer backend must preserve the host application's graphics state—active textures, blend modes, viewport, scissor rectangles, and shader programs—to avoid side effects. As demonstrated in [`backends/imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_opengl3.cpp), capture the entire relevant state at the start of `RenderDrawData` and restore it before returning, ensuring your UI rendering does not interfere with the application's main rendering pipeline.