Handling High-DPI Displays and Viewport Scaling with Dear ImGui
Dear ImGui handles high-DPI displays through per-viewport Scale factors and global DisplayFramebufferScale, configured via ConfigDpiScaleFonts and ConfigDpiScaleViewports flags in the docking branch, combined with platform-specific backend helpers like ImGui_ImplWin32_EnableDpiAwareness().
Dear ImGui provides comprehensive support for high-DPI (HiDPI) and per-monitor DPI scaling through a combination of IO configuration flags and backend-specific helpers. Whether you are targeting Retina displays on macOS or mixed-DPI monitor setups on Windows, the library's viewport system in ocornut/imgui automatically propagates scaling factors to render crisp UI at any resolution. This guide examines the source-level implementation to show exactly how display scaling works and how to configure it correctly.
Understanding the Core DPI Architecture
Dear ImGui separates DPI handling into global display metrics and per-viewport scaling factors, allowing the interface to adapt when windows move between monitors with different densities.
DisplayFramebufferScale
The global density of the primary display is stored in ImGuiIO::DisplayFramebufferScale (defined in [imgui.h around line 2420](https://github.com/ocornut/imgui/blob/master/imgui.h#L2420)). This ImVec2 represents the ratio between device pixels and logical pixels (e.g., 2.0, 2.0 for Retina displays). The value feeds into each viewport's FramebufferScale and is used by the renderer to determine fringe sizes and line thicknesses.
Per-Viewport Scale Factors
Each ImGuiViewport structure maintains its own Scale value (see [imgui.h line 3486](https://github.com/ocornut/imgui/blob/master/imgui.h#L3486)). This factor is derived from the platform window's DPI and is applied to all UI geometry when the viewport is drawn. During NewFrame(), the backend updates the runtime DPI scale in CurrentDpiScale (handled in [imgui.cpp line 4688](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L4688)), which propagates to ImDrawData::FramebufferScale for the rendering pipeline.
Automatic Scaling Configuration (Docking Branch)
The docking branch introduces automatic DPI handling through two critical configuration flags defined near [imgui.h lines 2296-2298](https://github.com/ocornut/imgui/blob/master/imgui.h#L2296-L2298):
ConfigDpiScaleFonts: When enabled, Dear ImGui automatically rebuilds the font atlas with sizes multiplied byFontScaleDpi, ensuring text remains sharp when moving between monitors.ConfigDpiScaleViewports: Enables automatic scaling of the entire UI when the monitor DPI changes, adjusting layout calculations without manual intervention.
Configure these before your first NewFrame():
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Required for per-monitor scaling
io.ConfigDpiScaleFonts = true; // Auto-rebuild fonts for DPI
io.ConfigDpiScaleViewports = true; // Auto-scale UI elements
Platform-Specific DPI Awareness
Dear ImGui relies on the underlying windowing backend to provide accurate DPI information. Each platform requires specific initialization to declare DPI awareness.
Windows (Win32)
For Windows applications, call the backend helper before creating your window. In [imgui_impl_win32.cpp (lines 24-30)](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp#L24-L30), Dear ImGui provides ImGui_ImplWin32_EnableDpiAwareness(), which wraps SetProcessDpiAwarenessContext():
// Enable DPI awareness before any window creation
ImGui_ImplWin32_EnableDpiAwareness();
// Or manually for older Windows versions:
::SetProcessDPIAware();
SDL2
Pass the SDL_WINDOW_ALLOW_HIGHDPI flag when creating your window, and include the Windows-specific DPI awareness call when building for that platform:
SDL_Window* window = SDL_CreateWindow(
"Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
#ifdef _WIN32
::SetProcessDPIAware(); // Required for correct DPI reporting on Windows
#endif
GLFW
Enable per-monitor scaling in GLFW to ensure glfwGetMonitorContentScale returns correct values, which the backend uses to populate viewport scales:
// Before glfwCreateWindow()
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
// Alternative: query scale manually for custom handling
float xscale, yscale;
glfwGetMonitorContentScale(monitor, &xscale, &yscale);
Handling Custom Geometry and Style Scaling
When ConfigDpiScaleFonts is disabled, or when implementing custom draw calls, you must manually multiply coordinates by the current DPI scale to maintain visual consistency.
Manual style scaling requires multiplying ImGuiStyle fields (such as CellPadding and FrameRoundings) by your DPI factor, as the automatic viewport scaling only affects built-in layout calculations (see handling in [imgui.cpp around line 9194](https://github.com/ocornut/imgui/blob/master/imgui.cpp#L9194)).
For custom drawing, retrieve the scale from the current viewport:
ImGuiViewport* vp = ImGui::GetWindowViewport();
float dpiScale = vp->Scale; // Matches io.DisplayFramebufferScale.x
// Scale custom primitives
ImDrawList* dl = ImGui::GetWindowDrawList();
dl->AddCircle(
ImVec2(100, 100) * dpiScale,
10.0f * dpiScale,
ImGui::GetColorU32(ImGuiCol_Text)
);
Complete Implementation Example
Here is a complete workflow integrating platform setup, ImGui initialization, and runtime DPI handling:
// 1. Platform DPI setup (Win32 example)
ImGui_ImplWin32_EnableDpiAwareness();
// 2. Create SDL2 window with HiDPI support
SDL_Window* window = SDL_CreateWindow(
"High-DPI ImGui",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
// 3. Initialize ImGui with viewport and auto-scaling
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.ConfigDpiScaleFonts = true;
io.ConfigDpiScaleViewports = true;
// 4. Runtime DPI inspection
ImGui::NewFrame();
ImGui::Text("Display Scale: %.2f", io.DisplayFramebufferScale.x);
// Move window between monitors to see automatic scale updates
ImGuiViewport* vp = ImGui::GetMainViewport();
ImGui::Text("Viewport Scale: %.2f", vp->Scale);
Summary
DisplayFramebufferScaleinImGuiIOstores the global display density and feeds into rendering calculations for line thickness and anti-aliasing.Viewport::Scaleprovides per-monitor DPI factors that automatically adjust when windows move between displays of different densities.ConfigDpiScaleFontsandConfigDpiScaleViewports(docking branch only) enable automatic font atlas rebuilding and UI scaling without manual intervention.- Platform backends must declare DPI awareness via
ImGui_ImplWin32_EnableDpiAwareness(),SDL_WINDOW_ALLOW_HIGHDPI, orGLFW_SCALE_TO_MONITORto report correct scale values. - Custom draw calls require manual multiplication by
viewport->Scalewhen automatic viewport scaling is disabled or for non-standard primitives.
Frequently Asked Questions
Why does my text appear blurry on high-DPI displays?
Blurry text typically indicates that the font atlas was generated at a low resolution and is being stretched by the display scale. Enable io.ConfigDpiScaleFonts = true (docking branch) to automatically rebuild fonts at the correct pixel density, or manually load fonts multiplied by io.DisplayFramebufferScale.x before calling ImGui::NewFrame().
What is the difference between DisplayFramebufferScale and Viewport->Scale?
DisplayFramebufferScale in ImGuiIO represents the primary monitor's density and affects global rendering parameters like anti-aliasing fringe sizes. Viewport->Scale is specific to each window and updates when the viewport moves to a monitor with different DPI. When multi-viewport support is enabled, Dear ImGui uses the per-viewport scale for layout calculations while the framebuffer scale handles draw data output.
Do I need the docking branch to support high-DPI displays?
No, but the docking branch provides automatic handling through ConfigDpiScaleFonts and ConfigDpiScaleViewports. In the master branch, you must manually detect DPI changes using platform APIs (like glfwGetMonitorContentScale), reload fonts with scaled sizes, and adjust ImGui::GetStyle() metrics accordingly.
How do I scale my own ImDrawList geometry to match ImGui's DPI handling?
Query the current viewport's scale factor and multiply your coordinates and sizes by this value. Retrieve the scale via ImGui::GetWindowViewport()->Scale or ImGui::GetMainViewport()->Scale, then apply it to all AddCircle, AddRect, and vertex positions in your custom draw lists to ensure they align with automatically-scaled ImGui widgets.
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 →