# How to Set Up Dear ImGui with Metal: Complete Integration Guide for macOS and iOS

> Integrate Dear ImGui with Metal on macOS and iOS. Follow our guide to initialize ImGui_ImplMetal_Init, create new frames with ImGui_ImplMetal_NewFrame, and render draw data efficiently.

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

---

**To set up Dear ImGui with Metal, include the backend headers from the `backends/` folder, initialize with `ImGui_ImplMetal_Init(device)`, and call `ImGui_ImplMetal_NewFrame(renderPassDescriptor)` and `ImGui_ImplMetal_RenderDrawData(drawData, commandBuffer, commandEncoder)` each frame.**

Dear ImGui (ocornut/imgui) ships a first-party Metal renderer that enables high-performance immediate-mode GUI rendering on Apple platforms. The backend resides in [`backends/imgui_impl_metal.h`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_metal.h) and `backends/imgui_impl_metal.mm`, implementing a standard interface for device initialization, frame setup, and draw command submission. This guide walks through the complete integration process using the actual source implementation from the official repository.

## Metal Backend Architecture

The Metal backend implements the standard ImGui renderer interface through four primary functions: `ImGui_ImplMetal_Init`, `ImGui_ImplMetal_NewFrame`, `ImGui_ImplMetal_RenderDrawData`, and `ImGui_ImplMetal_Shutdown`. The architecture centers around the **`ImGui_ImplMetal_Data`** structure, which maintains a per-context **`MetalContext`** containing device references, pipeline caches, and resource pools.

### Context and Device Management

The **`ImGui_ImplMetal_Init()`** function allocates the backend data structure and stores the Metal device in the `MetalContext`. This context persists across frames, holding static objects like depth-stencil states and samplers created via **`ImGui_ImplMetal_CreateDeviceObjects()`** (lines 84-100 in `imgui_impl_metal.mm`).

### Pipeline State Caching

To avoid costly shader recompilation, the backend caches `MTLRenderPipelineState` objects keyed by framebuffer descriptors. The **`MetalContext::renderPipelineStateForFramebufferDescriptor`** method (lines 87-100) maintains a dictionary mapping pixel formats and sample counts to compiled pipelines, reusing them across frames.

### Buffer Reuse Strategy

The implementation minimizes allocation churn through **`MetalContext::dequeueReusableBufferOfLength:`** (lines 51-80). This method retrieves `MTLBuffer` objects from a per-context cache, purging entries older than one second to balance memory usage with performance.

### Texture Handling

For font atlases and user textures, **`ImGui_ImplMetal_UpdateTexture()`** creates `MTLTexture` instances and stores them in `ImTextureData::BackendUserData` as `MetalTexture` wrappers. The backend handles binding via `setFragmentTexture` during the render pass (line 89).

## Integration Workflow

Follow this sequence to integrate Dear ImGui into your Metal application:

1. **Create Metal Device**: Obtain your `MTLDevice` using `MTLCreateSystemDefaultDevice()` or equivalent.
2. **Initialize ImGui Context**: Call `ImGui::CreateContext()` to establish the global state.
3. **Initialize Metal Backend**: Invoke **`ImGui_ImplMetal_Init(device)`** to allocate backend data and create device objects.
4. **Per-Frame Execution**:
   - Build a `MTLRenderPassDescriptor` for your drawable.
   - Call **`ImGui_ImplMetal_NewFrame(renderPassDescriptor)`** to cache framebuffer settings.
   - Build UI with `ImGui::NewFrame()`, widget calls, and `ImGui::Render()`.
   - Retrieve draw data with `ImGui::GetDrawData()`.
   - Create command buffer and encoder from your `MTLCommandQueue`.
   - Submit with **`ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, commandEncoder)`**.
   - End encoding and present the drawable.
5. **Shutdown**: Call **`ImGui_ImplMetal_Shutdown()`** before releasing the Metal device.

## Implementation Examples

### Objective-C Integration (macOS)

This minimal example demonstrates the standard setup using Objective-C APIs:

```objective-c
#import <Metal/Metal.h>
#import "imgui.h"
#import "imgui_impl_metal.h"

int main(int argc, char* argv[])
{
    // Create Metal infrastructure
    id<MTLDevice> device = MTLCreateSystemDefaultDevice();
    id<MTLCommandQueue> commandQueue = [device newCommandQueue];
    CAMetalLayer* metalLayer = ...;  // Configure your layer
    
    // Initialize Dear ImGui
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    io.DisplaySize = ImVec2(1024, 768);
    ImGui_ImplMetal_Init(device);
    
    while (!shouldQuit) {
        // Configure render pass
        MTLRenderPassDescriptor* rpDesc = [MTLRenderPassDescriptor renderPassDescriptor];
        rpDesc.colorAttachments[0].texture = metalLayer.nextDrawable.texture;
        rpDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
        rpDesc.colorAttachments[0].clearColor = MTLClearColorMake(0.1, 0.1, 0.1, 1.0);
        
        // New frame
        ImGui_ImplMetal_NewFrame(rpDesc);
        ImGui::NewFrame();
        
        // Build UI
        ImGui::Begin("Hello Metal");
        ImGui::Text("Hello world!");
        ImGui::End();
        ImGui::Render();
        
        // Encode rendering
        id<MTLCommandBuffer> cmdBuf = [commandQueue commandBuffer];
        id<MTLRenderCommandEncoder> encoder = [cmdBuf renderCommandEncoderWithDescriptor:rpDesc];
        ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), cmdBuf, encoder);
        [encoder endEncoding];
        [cmdBuf presentDrawable:metalLayer.nextDrawable];
        [cmdBuf commit];
    }
    
    ImGui_ImplMetal_Shutdown();
    return 0;
}

```

### Metal-C++ Integration

For modern C++ projects, enable the Metal-C++ bindings by defining `IMGUI_IMPL_METAL_CPP` before including the header:

```cpp
#define IMGUI_IMPL_METAL_CPP
#include "imgui.h"
#include "imgui_impl_metal.h"
#include <Metal/Metal.hpp>

int main()
{
    MTL::Device* device = MTL::CreateSystemDefaultDevice();
    MTL::CommandQueue* cmdQueue = device->newCommandQueue();
    
    ImGui::CreateContext();
    ImGui_ImplMetal_Init(device);  // C++ overload
    
    while (!quit) {
        MTL::RenderPassDescriptor* rpDesc = MTL::RenderPassDescriptor::alloc()->init();
        rpDesc->colorAttachments()->object(0)->setTexture(currentDrawableTexture);
        rpDesc->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionClear);
        rpDesc->colorAttachments()->object(0)->setClearColor(MTL::ClearColor::Make(0.1f, 0.1f, 0.1f, 1.0f));
        
        ImGui_ImplMetal_NewFrame(rpDesc);
        ImGui::NewFrame();
        ImGui::Begin("Metal-C++");
        ImGui::Text("Running on Metal-C++");
        ImGui::End();
        ImGui::Render();
        
        MTL::CommandBuffer* cmdBuf = cmdQueue->commandBuffer();
        MTL::RenderCommandEncoder* encoder = cmdBuf->renderCommandEncoder(rpDesc);
        ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), cmdBuf, encoder);
        encoder->endEncoding();
        cmdBuf->presentDrawable(currentDrawable);
        cmdBuf->commit();
    }
    
    ImGui_ImplMetal_Shutdown();
    return 0;
}

```

### Loading Custom Textures

To display custom images, register `MTLTexture` objects with ImGui's texture system:

```cpp
// Create your Metal texture
id<MTLTexture> myMTLTex = ...;  // Loaded via MTKTextureLoader or manual creation

// Prepare ImGui texture data
ImTextureData texData;
texData.Width = myMTLTex.width;
texData.Height = myMTLTex.height;
texData.Format = ImTextureFormat_RGBA32;
texData.TexID = (ImTextureID)(intptr_t)myMTLTex;
texData.BackendUserData = (__bridge_retained void*)[[MetalTexture alloc] initWithTexture:myMTLTex];
texData.Status = ImTextureStatus_OK;

// Register
ImGui::GetPlatformIO().Textures.push_back(&texData);

// Use in UI
ImGui::Image((ImTextureID)(intptr_t)myMTLTex, ImVec2(256, 256));

```

The backend automatically binds these textures during `ImGui_ImplMetal_RenderDrawData()` via `setFragmentTexture`.

## Key Source Files

The Metal backend implementation resides in the `backends/` directory of the ocornut/imgui repository:

- **[`backends/imgui_impl_metal.h`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_metal.h)**: Declares the public API including `ImGui_ImplMetal_Init()`, `ImGui_ImplMetal_NewFrame()`, `ImGui_ImplMetal_RenderDrawData()`, and texture helper functions.
- **`backends/imgui_impl_metal.mm`**: Contains the complete Objective-C++ implementation, including `ImGui_ImplMetal_Data` structure, pipeline state caching, buffer reuse logic, and the full render loop.
- **[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)**: Defines core structures such as `ImDrawData`, `ImTextureData`, and `ImTextureFormat` used by the backend.

## Summary

- **Include the backend**: Add [`backends/imgui_impl_metal.h`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_metal.h) and `imgui_impl_metal.mm` to your project.
- **Initialize once**: Call `ImGui_ImplMetal_Init(device)` after creating your Metal device.
- **Frame preparation**: Pass your `MTLRenderPassDescriptor` to `ImGui_ImplMetal_NewFrame()` to enable pipeline caching.
- **Submit draws**: Use `ImGui_ImplMetal_RenderDrawData()` inside your command encoder to handle vertex buffers, index buffers, and scissor clipping.
- **Cleanup**: Call `ImGui_ImplMetal_Shutdown()` before device destruction.
- **C++ support**: Define `IMGUI_IMPL_METAL_CPP` for Metal-C++ API compatibility.

## Frequently Asked Questions

### Can I use Dear ImGui with Metal on iOS?

Yes, the Metal backend supports both macOS and iOS. The initialization and rendering code remains identical across platforms. Ensure your `CAMetalLayer` is properly configured for iOS view hierarchies, and the backend handles the rest through the same `ImGui_ImplMetal_*` interface.

### How do I enable Metal-C++ bindings instead of Objective-C?

Define `IMGUI_IMPL_METAL_CPP` in your [`imconfig.h`](https://github.com/ocornut/imgui/blob/main/imconfig.h) or as a compiler flag before including [`imgui_impl_metal.h`](https://github.com/ocornut/imgui/blob/main/imgui_impl_metal.h). This enables overloads accepting `MTL::Device*` and `MTL::RenderPassDescriptor*` instead of Objective-C `id<MTLDevice>` pointers. The implementation uses bridging casts internally to support both APIs from the same source file.

### Does the Metal backend support custom fonts and textures?

Yes. The backend implements `ImGui_ImplMetal_UpdateTexture()` to handle font atlas generation and user-provided textures. Store your `MTLTexture` references in `ImTextureData::BackendUserData` as `MetalTexture` objects. The renderer automatically binds these textures during draw submission using the texture IDs generated by ImGui.

### How does the backend optimize rendering performance?

The implementation employs several optimization strategies: **pipeline state caching** via `renderPipelineStateForFramebufferDescriptor` avoids shader recompilation, **buffer reuse** through `dequeueReusableBufferOfLength:` minimizes allocation overhead by caching `MTLBuffer` objects for one second, and **texture management** ensures GPU resources persist across frames unless explicitly updated.