Version/Branch of Dear ImGui:
1.93.0 WIP, docking branch (regression from 98a66d8, 2026-08-04)
Back-ends:
any backend implementing Platform_GetWindowFramebufferScale (observed with imgui_impl_sdl2 + imgui_impl_metal)
Compiler, OS:
macOS, mixed-DPI monitors (1x main + 2x secondary)
Full config/build information:
// (Copy from the next line. Keep the ``` markers for formatting.)
Dear ImGui 1.93.0 WIP (19292)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 4, sizeof(ImDrawVert): 20
define: __cplusplus=202002
define: __APPLE__
define: __GNUC__=4
define: __clang_version__=21.0.0 (clang-2100.1.1.101)
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
define: NDEBUG
IM_ASSERT: runs expression: KO. expand size: KO (MAYBE DISABLED?!) (?)
--------------------------------
io.BackendPlatformName: imgui_impl_sdl2 (2.32.70, 2.32.70) (cocoa)
io.BackendRendererName: imgui_impl_metal
io.ConfigFlags: 0x00000481
NavEnableKeyboard
DockingEnable
ViewportsEnable
io.ConfigDpiScaleFonts
io.ConfigDpiScaleViewports
io.ConfigViewportsNoAutoMerge
io.ConfigViewportsNoDefaultParent
io.ConfigDockingWithShift
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00000C1E
HasMouseCursors
HasSetMousePos
PlatformHasViewports
RendererHasVtxOffset
RendererHasTextures
RendererHasViewports
--------------------------------
io.Fonts: 5 fonts, Flags: 0x00000004, TexSize: 512,512
io.Fonts->FontLoaderName: stb_truetype
io.DisplaySize: 1.00,1.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 0.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 1.00
style.ItemSpacing: 6.00,3.00
style.ItemInnerSpacing: 4.00,2.00
Details:
Description:
98a66d8 added this line to the tail of AddUpdateViewport():
viewport->FramebufferScale = g.IO.DisplayFramebufferScale; // Later overidden by Platform_GetWindowFramebufferScale() for platform having it
AddUpdateViewport() runs every frame for every window's viewport, and the tail runs for
existing viewports too, so this overwrites the per-window value each frame. The
"later overridden" part happens in UpdatePlatformWindows(), i.e. after Render() - so by
the time SetCurrentWindow() picks the pixel density and InitViewportDrawData() stamps
draw_data->FramebufferScale, every viewport carries the main viewport's scale instead of
its own. Before 98a66d8 the handler's value persisted across frames (nothing reset it), and
InitViewportDrawData() fell back to io.DisplayFramebufferScale only when the value was 0.
Symptom: with a 1x main monitor and 2x secondaries, dragging a viewport window onto a 2x
monitor renders all its content at half size (draw data built with scale 1.0 while the
renderer's drawable is 2x). The inverse setup renders at double size.
Steps to reproduce:
SDL2 (or any backend with Platform_GetWindowFramebufferScale) + viewports on macOS with
monitors of different scales; drag a window from the main monitor to one with a different
scale.
Fix: initialize only on viewport creation, keeping the intent of the original comment —
plus a fallback for the viewports-disabled case:
else
{
// New viewport
...
viewport->DpiScale = GetViewportPlatformMonitor(viewport)->DpiScale;
viewport->FramebufferScale = g.IO.DisplayFramebufferScale; // Later overidden by Platform_GetWindowFramebufferScale() for platform having it
}
viewport->Window = window;
if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
viewport->FramebufferScale = g.IO.DisplayFramebufferScale;
viewport->LastFrameActive = g.FrameCount;
The fallback is required: the main viewport is created in Initialize() with
FramebufferScale = (0,0) (memset) and never passes through the new-viewport branch of
AddUpdateViewport(), and Platform_GetWindowFramebufferScale() is only polled when
viewports are enabled. Without it, any app running without ImGuiConfigFlags_ViewportsEnable
stamps draw_data->FramebufferScale = (0,0) (InitViewportDrawData() no longer falls back to
io.DisplayFramebufferScale since 1.93), so every renderer backend hits its
fb_width <= 0 early-out and the window renders blank.
(An alternative would be applying the Platform_GetWindowFramebufferScale() value during
UpdateViewportsNewFrame() rather than in UpdatePlatformWindows(), so it is fresh at the time
draw data and pixel density consume it.)
Screenshots/Video:
No response
Minimal, Complete and Verifiable Example code:
Version/Branch of Dear ImGui:
1.93.0 WIP, docking branch (regression from 98a66d8, 2026-08-04)
Back-ends:
any backend implementing Platform_GetWindowFramebufferScale (observed with imgui_impl_sdl2 + imgui_impl_metal)
Compiler, OS:
macOS, mixed-DPI monitors (1x main + 2x secondary)
Full config/build information:
// (Copy from the next line. Keep the ``` markers for formatting.)
Details:
Description:
98a66d8 added this line to the tail of
AddUpdateViewport():AddUpdateViewport()runs every frame for every window's viewport, and the tail runs forexisting viewports too, so this overwrites the per-window value each frame. The
"later overridden" part happens in
UpdatePlatformWindows(), i.e. afterRender()- so bythe time
SetCurrentWindow()picks the pixel density andInitViewportDrawData()stampsdraw_data->FramebufferScale, every viewport carries the main viewport's scale instead ofits own. Before 98a66d8 the handler's value persisted across frames (nothing reset it), and
InitViewportDrawData()fell back toio.DisplayFramebufferScaleonly when the value was 0.Symptom: with a 1x main monitor and 2x secondaries, dragging a viewport window onto a 2x
monitor renders all its content at half size (draw data built with scale 1.0 while the
renderer's drawable is 2x). The inverse setup renders at double size.
Steps to reproduce:
SDL2 (or any backend with Platform_GetWindowFramebufferScale) + viewports on macOS with
monitors of different scales; drag a window from the main monitor to one with a different
scale.
Fix: initialize only on viewport creation, keeping the intent of the original comment —
plus a fallback for the viewports-disabled case:
The fallback is required: the main viewport is created in
Initialize()withFramebufferScale = (0,0)(memset) and never passes through the new-viewport branch ofAddUpdateViewport(), andPlatform_GetWindowFramebufferScale()is only polled whenviewports are enabled. Without it, any app running without
ImGuiConfigFlags_ViewportsEnablestamps
draw_data->FramebufferScale = (0,0)(InitViewportDrawData()no longer falls back toio.DisplayFramebufferScalesince 1.93), so every renderer backend hits itsfb_width <= 0early-out and the window renders blank.(An alternative would be applying the Platform_GetWindowFramebufferScale() value during
UpdateViewportsNewFrame() rather than in UpdatePlatformWindows(), so it is fresh at the time
draw data and pixel density consume it.)
Screenshots/Video:
No response
Minimal, Complete and Verifiable Example code: