How to Use ImTextureID with Custom Texture Types in DirectX
ImTextureID is an opaque graphics handle that you can redefine in imconfig.h to match your DirectX shader resource view type, eliminating manual pointer casts and enabling compile-time type safety.
Dear ImGui (ocornut/imgui) renders images by storing a lightweight ImTextureID inside draw commands. The meaning of this value is determined solely by your renderer backend; for DirectX 11, the backend expects an ID3D11ShaderResourceView* pointer. You can either cast this pointer to the default 64-bit integer type or override the type definition itself to embed custom metadata and avoid dangerous reinterpretation.
What Is ImTextureID and How DirectX Interprets It
ImTextureID is a typedef that defaults to ImU64 (64-bit unsigned integer) as of recent versions. In imgui.h, the declaration appears around line 339, accompanied by instructions on how to override it:
// imgui.h (lines 339-344)
#ifndef ImTextureID
typedef ImU64 ImTextureID; // Default: store any 64-bit value (pointer, handle, etc.)
#endif
When the DirectX 11 backend processes draw commands in backends/imgui_impl_dx11.cpp, it extracts the raw value and casts it directly back to ID3D11ShaderResourceView*:
// backends/imgui_impl_dx11.cpp (lines 73-158)
ID3D11ShaderResourceView* tex_srv = (ID3D11ShaderResourceView*)cmd->GetTexID();
This means any value you store in ImTextureID must be safe to cast to a shader resource view pointer (or whatever type your specific backend expects) when the render function executes.
Why Define a Custom Type for DirectX Textures
Redefining ImTextureID via imconfig.h provides three concrete advantages:
- Type Safety: The compiler catches attempts to pass incompatible pointers (e.g., raw
ID3D11Texture2D*instead ofID3D11ShaderResourceView*). - Zero-Cost Abstraction: You avoid repetitive
(ImTextureID)(intptr_t)casts throughout your UI code. - Extended Metadata: You can store a pointer to a custom struct containing the view plus dimensions, format flags, or engine-specific handles.
Three Implementation Strategies
Choose the approach that matches your team's safety requirements and DirectX workflow.
Method 1: Direct Casting with Default ImU64 (Legacy Compatible)
Keep the default definition and cast your DirectX resource view to ImTextureID using an intermediate intptr_t conversion. This works immediately without modifying library headers.
ID3D11ShaderResourceView* srv = CreateMyTexture(width, height);
ImTextureID tid = (ImTextureID)(intptr_t)srv; // Safe intptr_t bridge
ImGui::Image(tid, ImVec2((float)width, (float)height));
Downside: You lose compiler type checking and must ensure the pointer remains valid for the entire frame.
Method 2: Redefine ImTextureID as DirectX Pointer (Recommended)
Before including imgui.h, define ImTextureID to be exactly ID3D11ShaderResourceView*. The canonical location for this is imconfig.h in your project root or alongside your ImGui source:
// imconfig.h
#define ImTextureID ID3D11ShaderResourceView*
// Must include imconfig.h before imgui.h (naturally happens if you follow integration guides)
#include "imgui.h"
Now you can pass the view directly:
ID3D11ShaderResourceView* srv = CreateMyTexture(256, 256);
ImGui::Image(srv, ImVec2(256.0f, 256.0f)); // No cast needed
The DirectX 11 backend retrieves the pointer verbatim in imgui_impl_dx11.cpp (line 73 ff.) and binds it to the pixel shader without additional indirection.
Method 3: Custom Struct Wrapper (Advanced)
When you need to bundle extra data (size, mip levels, debug names), create a wrapper struct and redefine the type accordingly:
// imconfig.h
struct MyDXTexture {
ID3D11ShaderResourceView* srv;
int width;
int height;
const char* debug_name;
};
#define ImTextureID MyDXTexture*
Usage requires dereferencing to access the view, but the backend must be taught how to extract the raw pointer. Since the official backend expects ID3D11ShaderResourceView* directly, you have two options:
- Store the view pointer as the ID and keep your metadata in a parallel lookup table keyed by that pointer.
- Modify the backend (or wrap it) to perform
tex_id->srvwhen binding.
For most applications, storing the raw view pointer as ImTextureID and using a separate map for metadata keeps changes minimal and preserves compatibility with imgui_impl_dx11.cpp.
DirectX Backend Integration Details
The DirectX 11 renderer never inspects the content of ImTextureID until draw submission. In imgui_impl_dx11.cpp (around line 158), the engine iterates over ImDrawCmd structures and calls:
// Extract the texture pointer stored by ImGui::Image()
ID3D11ShaderResourceView* srv = (ID3D11ShaderResourceView*)pcmd->GetTexID();
ctx->PSSetShaderResources(0, 1, &srv);
Consequently, whatever you pass to ImGui::Image() must be exactly sizeof(void*) or sizeof(ImU64) and must be valid when RenderDrawData() executes. If you defined ImTextureID as a custom struct pointer, ensure your custom backend extraction matches this definition.
Conversion utilities exist for debugging purposes in imgui.cpp (function DebugTextureIDToU64 around line 16580), which handles the cast safely for internal logging.
Practical Code Examples
Basic DirectX 11 Texture Display
// Initialize ImGui with DX11 backend
ImGui_ImplDX11_Init(device, device_context);
// Load texture via D3DX or custom loader
ID3D11ShaderResourceView* my_srv = LoadTextureFromFile("image.png");
// In your render loop
ImGui::Begin("DirectX Texture");
ImTextureID tex_id = (ImTextureID)(intptr_t)my_srv; // Method 1
ImGui::Image(tex_id, ImVec2(256, 256));
ImGui::End();
Type-Safe Custom Definition
// imconfig.h (include before imgui.h)
#define ImTextureID ID3D11ShaderResourceView*
// Application code
void ShowTexture(ID3D11ShaderResourceView* srv, float w, float h) {
ImGui::Image(srv, ImVec2(w, h)); // Method 2: no cast, type-checked
}
Using ImTextureRef (v1.92+)
The newer ImTextureRef API wraps ImTextureID with additional safety:
// Convert raw view to texture reference
ImTextureRef tex_ref = ImTextureRefFromID((ImTextureID)(intptr_t)srv);
ImGui::Image(tex_ref, ImVec2(128, 128));
Refer to imgui_demo.cpp (around line 1888) for expanded examples demonstrating ImageButton with explicit string IDs to avoid widget ID collisions.
Summary
ImTextureIDdefaults toImU64inimgui.hbut can be overridden inimconfig.hto any pointer-sized type.- The DirectX 11 backend expects
ID3D11ShaderResourceView*and casts the ID blindly inimgui_impl_dx11.cpp. - Method 1: Cast via
(ImTextureID)(intptr_t)ptrfor quick integration. - Method 2:
#define ImTextureID ID3D11ShaderResourceView*inimconfig.hfor type safety. - Method 3: Wrap custom structs only if you modify the backend extraction logic or maintain a parallel lookup.
- Keep texture resources alive for the duration of the frame where they are referenced in draw commands.
Frequently Asked Questions
Can I use ImTextureID with DirectX 12?
Yes, but you must adapt the backend. DirectX 12 does not use ID3D11ShaderResourceView*; instead you typically pass a D3D12_GPU_DESCRIPTOR_HANDLE (which is 64-bit) or a custom index into your descriptor heap. Define ImTextureID as ImU64 (the default) or as your custom handle type, then update imgui_impl_dx12.cpp to interpret the value correctly when calling SetGraphicsRootDescriptorTable.
Why did ImTextureID change from void* to ImU64 in recent versions?
Prior to v1.91.4, ImTextureID was defined as void*. The change to ImU64 (as seen in imgui.h lines 339-344) prevents accidental pointer arithmetic and accommodates graphics APIs that use non-pointer handles (e.g., Vulkan uint64_t image views). When porting existing DirectX 11 code, replace (ImTextureID)ptr with (ImTextureID)(intptr_t)ptr to silence compiler warnings.
How do I handle multiple texture types in the same application?
If your engine uses different representations for textures (e.g., raw ID3D11Resource* vs. wrapped engine handles), do not redefine ImTextureID to a specific class pointer. Instead, keep the default ImU64 and store a unified handle (such as a pointer to a base texture struct) that the backend can dispatch on. Alternatively, use separate ImGuiContext instances with different backend implementations, though this is rarely necessary.
What is the difference between ImTextureID and ImTextureRef?
ImTextureID is the legacy low-level storage (64-bit integer). ImTextureRef (defined around line 350 in imgui.h) is a newer wrapper structure that can hold either an ImTextureID or a pointer to ImTextureData (used for the font atlas). It provides a type-safe way to distinguish between raw GPU handles and CPU-side image data. Use ImTextureRefFromID() to construct a reference when migrating code to the newer API introduced in v1.92.
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 →