Dear ImGui & DirectX 11

What is it?

​Dear ImGui is an immediate GUI, this mean it is updated every frame and maintains no state information. Meaning it is very easy to drop into a rendering pipeline. Which makes it ideal for development tools, especially when you take into account its expansive collection of functions.

imgui demo

You have access to all the code used to create everything in the ImGui Demo, this is incredibly useful for figuring out how functions are used. Ctrl +F is very handy here for finding stuff.


Rough Setup for DirectX 11

Setting up ImGui largely depends on your framework, the example implementation project is perfect for seeing what can be done but it is one big main function, normally your project won’t be setup like that once it matures. Therefore, I’m going to go over where the 5 main functions from the DirectX implementation need to go. What each of these functions is doing and why you need them and where best to put them.

Before that we need to add all the required files to the project, I added this to my DXFramework instead of my individual projects. Don’t forget the last few as they are needed for certain ImGui functions.

imconfig.h
imgui.cpp
imgui.h
imgui_demo.cpp
imgui_draw.cpp
imgui_impl_dx11.cpp
imgui_impl_dx11.h
imgui_internal.h
stb_rect_pack.h
stb_textedit.h
stb_truetype.h

The 5 Functions you are going to use are shown in the ​imgui_impl_dx11.h file. You should look at these functions, and try to decide what they are doing behind the scenes as it will help you understand how ImGui works.

IMGUI_API bool        ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context);
IMGUI_API void        ImGui_ImplDX11_Shutdown();
IMGUI_API void        ImGui_ImplDX11_NewFrame();

IMGUI_API LRESULT   ImGui_ImplDX11_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

The last function is part of the standard ImGui library found in imgui.h

IMGUI_API void          Render();

ImGui_ImplDX11_Init

​Init, sets up ImGui and binds it to your D3D Context giving ImGui something to draw onto, this means you must have a context to bind it to. The easiest place to do this is just after you have created your context, in my case this is in the BaseApplication don’t forget to include your imgui_impl_dx11.h.

// Create the Direct3D object.
m_Direct3D = new D3D(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
// Initialize the Direct3D object.
if (!m_Direct3D) { .... }    
 
// Setup ImGui binding, after d3d built, Must be initialise with d3d context etc, so must be done in application
ImGui_ImplDX11_Init(hwnd, m_Direct3D->GetDevice(), m_Direct3D->GetDeviceContext());

ImGui_ImplDX11_ShutDown()

Shutdown, as the names suggests is for when you shut down the program, this free’s up everything ImGui was using. This ensures your program closes cleanly.

void System::ShutdownWindows()
{
	ImGui_ImplDX11_Shutdown();

​​​Once this is called, it will invalidate everything in ImGui, unlink all the pointers to your context, and release all the ImGui assets, fonts, etc.. Therefore, if you try to call any ImGui functions it will cause your program to crash, unless you have re-initialised it.

For these reasons I only call this when my system shuts down, as the initialise function will be called when an application is created relinking the pointers for ImGui.


ImGui_ImplDX11_WndProcHandler

This function allows input to be passed to ImGui. It is commented out in the ImGui Implementation to avoid dragging dependencies on <windows.h> types into everything that needs the implementation header. This means we need an extern declaration in our code, as the function itself is already declared we just need to tell the compiler that it will show up. So declare the extern function with your windows message handler.

extern LRESULT ImGui_ImplDX11_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK System::WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{

Once declared, we just call the function passing along the message. The function currently processes all input from windows and can tell you if it has successfully processed a message. I usually want my game to process the messages as well so I just ignore this information.

ImGui_ImplDX11_WndProcHandler(hwnd, umessage, wparam, lparam);

Rendering ImGui

Now that ImGui has access to your D3D context you just need to Render ImGui stuff on the screen. To allow this you need to tell ImGui when a new frame begins and when it ends, that is what the last two functions are for. All of your ImGui code should be written between these two functions.

ImGui_ImplDX11_NewFrame();
{ //using brackets to control scope makes formatting and checking where the ImGui::Render(); is easier.
 
	ImGui::Begin("Framerate", 0, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar);
	ImGui::SetWindowSize(ImVec2(200, 30), ImGuiSetCond_FirstUseEver);
	ImGui::SetWindowPos(ImVec2(2, 2), ImGuiSetCond_FirstUseEver);
	ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);        
	ImGui::End();
	ImGui::ShowTestWindow();
}
ImGui::Render();

​There is a lot of built-in functions for ImGui which is outside the scope of this post. Have a look at the ImGui Demo code to see how everything is done. The ShowTestWindow function above displays the demo window with tonnes of examples within itself.

PS: make sure you are using your include’s when calling ImGui functions. If these are outside your solutions folder use ../ to move up one level in your folder structure for example:

#include "../DXFramework/imgui.h"
#include "../DXFramework/imgui_impl_dx11.h"
ocornut/imgui
comments powered by Disqus