If you’re like me and kinda hop freely among the 3 main desktop platforms one of the most annoying things you’ll notice is that HiDPI and fractional display scaling works pretty much without any special consideration on MacOS and Windows… yet on Linux it can be a minefield full of bear traps and edge cases for different apps.
Wayland definitely improved the situation, and the mainstream desktop environments on Linux really have come a long way. As some distros move slower than others it might be a while before all the advances are uniformly available though.
One thing I consistently experience though is that applications using ImGUI frequently ignore my display scale. And indeed, once I had Dear ImGUI hooked up in my MoonWorks app I found that I couldn’t see anything because it wasn’t taking my display scale into account by default. (How could it? It’s my job to tell it that in the first place right?)
So this post was intended to be just a note to my future self regarding how I resolved this and got “proper” (aka readable for my middle-age eyesight).
Setup Link to heading
When initializing your ImGUI integration at startup, you’ll want to handle a couple of little details. Here is my initialization function for example:
public static System.IntPtr Initialize(Game game, string fontPath)
{
var context = ImGui.CreateContext();
ImGui.SetCurrentContext(context);
var io = ImGui.GetIO();
io.BackendFlags = ImGuiBackendFlags.None | ImGuiBackendFlags.RendererHasVtxOffset;
io.ConfigFlags = ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.DpiEnableScaleFonts |
ImGuiConfigFlags.DpiEnableScaleViewports;
var display = SDL3.SDL.SDL_GetPrimaryDisplay();
var displayScale = SDL3.SDL.SDL_GetDisplayContentScale(display);
io.Fonts.AddFontFromFileTTF(fontPath, 32);
io.FontGlobalScale = 0.5f;
io.DisplaySize = new Vector2(game.MainWindow.Width, game.MainWindow.Height);
io.DisplayFramebufferScale = new Vector2(displayScale);
ImGui.StyleColorsLight();
return context;
}
Several things to notice:
First we get the “primary” display from SDL and query it’s display scale:
var display = SDL3.SDL.SDL_GetPrimaryDisplay();
var displayScale = SDL3.SDL.SDL_GetDisplayContentScale(display);
The second thing to note is that I’m doing something kind weird with fonts:
io.Fonts.AddFontFromFileTTF(fontPath, 32);
io.FontGlobalScale = 0.5f;
This is creating the font for the GUI “extra large”, and then scaling it down to half the size. The purpose is to ensure that fonts are still crisp despite the display scaling.
16
it looked about the same. So maybe the bigger issue is that your font selection matters more than this oversizing of the atlas.Finally, you’ll want to be sure that ImGUI has both the actual game display size the framebuffer scale:
io.DisplaySize = new Vector2(game.MainWindow.Width, game.MainWindow.Height);
io.DisplayFramebufferScale = new Vector2(displayScale);
Rendering Link to heading
At this point you’re basically done. The only thing you have to adjust is how you provide mouse coordinates to ImGUI at the start of a frame:
var mousePosX = Inputs.Mouse.X / io.DisplayFramebufferScale.X;
var mousePosY = Inputs.Mouse.Y / io.DisplayFramebufferScale.Y;
io.AddMousePosEvent(mousePosX, mousePosY);
io.DisplayFramebufferScale
to reflect the current display.Example Link to heading
First here is my game without display scaling:
And here it is with display scaling: