How to Use ImGui with BGFX and SDL in Equilibrium Engine

The Equilibrium Engine integrates Dear ImGui with BGFX and SDL2 through the ImguiBgfxSdlSystem ECS system, which handles context initialization, frame rendering, and input event forwarding automatically.

The Equilibrium Engine uses flecs as its Entity Component System (ECS) backbone and binds Dear ImGui to the BGFX graphics abstraction layer and SDL2 window system via the cimgui C bindings. This architecture allows developers to build immediate-mode GUI overlays without manually managing graphics API specifics or platform window events.

Architecture of the ImGui Integration

The integration is encapsulated in the ImguiBgfxSdlSystem, which registers three lifecycle callbacks as ECS observers. This system abstracts the complexity of wiring ImGui to BGFX and SDL2 while exposing a simple component-based API for user-defined UI modules.

The ImguiBgfxSdlSystem Lifecycle

The system provides three core callbacks that manage the ImGui context from startup to shutdown:

  • ImguiInitialize (imgui_bgfx_sdl_system.c:L70-L102): Creates an ImGuiContext, configures ImGui flags for keyboard navigation, docking, and multi-viewport support, loads the default font, and initializes both the BGFX and SDL backends.
  • ImguiUpdate (imgui_bgfx_sdl_system.c:L127-L162): Starts a new ImGui frame, executes every registered GuiSystem (user-defined UI modules), renders the ImGui draw data through BGFX, and updates platform windows.
  • ImguiShutdown (imgui_bgfx_sdl_system.c:L166-L171): Shuts down both ImGui backends and logs the cleanup.

Component Definitions

The engine defines GUI-related components in equilibrium/components/gui.h (L8-L14):

typedef struct GuiSystem {
    UpdateCallback Update;   // User-defined UI logic
} GuiSystem;

typedef struct GuiContext {
    void *value;             // Stores the ImGuiContext pointer
} GuiContext;

The GuiSystem component acts as the extension point for custom UI code, while GuiContext stores the underlying ImGui context pointer for internal use.

Backend Wiring and Initialization

The system initializes platform-specific backends during the ImguiInitialize phase. In imgui_bgfx_sdl_system.c:L98-L108, the engine calls:

// BGFX backend initialization (view-id 255 reserved for ImGui)
ImGui_Implbgfx_Init(255);

// SDL2 backend initialization (platform-specific variant)
ImGui_ImplSDL2_InitForMetal(window);  // Or InitForD3D, InitForOpenGL

The BGFX renderer implementation resides in 3rdparty/cimgui/imgui_impl_bgfx.h/.cpp, while the SDL2 input backend is located in 3rdparty/cimgui/imgui_impl_sdl.h. The initialization automatically configures multi-viewport support and applies a dark theme via ApplyImGuiDarkStyle.

Automatic Event Forwarding

The engine wires SDL2 input events directly to ImGui without manual intervention. In imgui_bgfx_sdl_system.c:L111-L113, the system sets the input callback:

input->callback = ImGui_ImplSDL2_ProcessEvent;

This ensures that every SDL event (mouse, keyboard, window) is automatically processed by ImGui before application systems handle them.

Step-by-Step Implementation

To add custom ImGui interfaces to your Equilibrium Engine project, you register the system once and then create entities with GuiSystem components.

1. Register the ImGui System

Call ImguiBgfxSdlSystemImport during engine startup, typically in your launcher or main.c after initializing the flecs world:

// launcher/launcher.cc or your bootstrap file
world_t *world = ecs_init();
ImguiBgfxSdlSystemImport(world);   // Registers ImGui + BGFX + SDL integration

This function (imgui_bgfx_sdl_system.c:L173-L188) registers the three lifecycle callbacks and imports the necessary component modules.

2. Define a Custom UI System

Create a component that implements the GuiSystem.Update callback. The engine handles igNewFrame and igRender automatically—you only write the widget logic:

// my_ui_system.c
#include "cimgui.h"
#include "equilibrium/components/gui.h"

static void MyUIUpdate(ecs_iter_t *it) {
    // ImGui frame already begun by ImguiUpdate
    igBegin("Demo Window", NULL, 0);
    
    if (igButton("Press me!", (ImVec2){0,0})) {
        ecs_trace("Button pressed!");
    }
    
    igEnd();
}

void RegisterMyUISystem(world_t *world) {
    ecs_entity_t ui = ecs_new_id(world);
    ecs_set(world, ui, GuiSystem, { .Update = MyUIUpdate });
}

Place this registration after importing the ImGui system:

// After ImguiBgfxSdlSystemImport(world)
RegisterMyUISystem(world);

3. Customizing ImGui Style (Optional)

The engine applies a dark style automatically during initialization. To override this, access the style struct after system import:

void SetCustomStyle(void) {
    ImGuiStyle *style = igGetStyle();
    style->WindowRounding = 6.0f;
    style->Colors[ImGuiCol_WindowBg] = (ImVec4){0.1f, 0.1f, 0.1f, 1.0f};
}

Call your custom style function immediately after ImguiBgfxSdlSystemImport(world).

Key Source Files

Understanding the file structure helps when debugging or extending the integration:

Summary

  • Register once: Call ImguiBgfxSdlSystemImport(world) during engine startup to initialize the BGFX and SDL2 backends automatically.
  • Extend via components: Implement GuiSystem.Update callbacks to draw UI; the engine manages frame lifecycle and rendering.
  • Event handling is automatic: SDL2 events are forwarded to ImGui via ImGui_ImplSDL2_ProcessEvent without manual wiring.
  • Multi-viewport support: The system initializes ImGui with docking and multi-viewport flags enabled by default.
  • View ID 255: BGFX reserves view ID 255 for ImGui rendering, ensuring it draws on top of the main scene.

Frequently Asked Questions

How do I initialize ImGui in Equilibrium Engine?

Import the ImguiBgfxSdlSystem module by calling ImguiBgfxSdlSystemImport(world) after ecs_init(). This single call registers the initialization, update, and shutdown callbacks that wire ImGui to BGFX and SDL2, as implemented in imgui_bgfx_sdl_system.c:L173-L188.

Do I need to call igNewFrame or igRender manually?

No. The ImguiUpdate system callback (imgui_bgfx_sdl_system.c:L127-L162) calls igNewFrame before executing your GuiSystem.Update callbacks and calls igRender followed by ImGui_Implbgfx_RenderDrawLists afterward. Your UI code only needs to contain ImGui drawing commands between igBegin and igEnd.

Which graphics APIs are supported?

The integration supports any renderer BGFX supports (Direct3D, Metal, OpenGL, Vulkan) because it uses BGFX's view system for rendering. The SDL2 backend initialization selects the appropriate platform-specific function (e.g., ImGui_ImplSDL2_InitForMetal, InitForD3D, or InitForOpenGL) based on the target platform during ImguiInitialize.

How do I disable docking or multi-viewport features?

Modify the ImGui configuration flags in imgui_bgfx_sdl_system.c:L70-L102 within the ImguiInitialize function. Look for the lines setting ImGuiConfigFlags_DockingEnable and ImGuiConfigFlags_ViewportsEnable, and remove or comment out the flags you do not need before the context is bound.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →