ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
59.73k stars 10.17k forks source link

Multiple viewports get confused by negative coordinates #2176

Open tom-seddon opened 5 years ago

tom-seddon commented 5 years ago

Version/Branch of Dear ImGui:

168af9b3777c0c3701b9222d95b085334adc52dc

Back-end file/Renderer/OS: (or specify if you are using a custom engine back-end)

Back-ends: imgui_impl_opengl3.cpp + imgui_implsdl.cpp OS: Windows 10 Compiler: VC++ (if the question is related to building)_

My Issue/Question: (please provide context)

If you have a display with negative coordinates, floating dear imgui windows - what's the right term for this? - the ones outside the main viewport, I mean - can't be moved on to it.

Standalone, minimal, complete and verifiable example: (see CONTRIBUTING.md)

  1. Configure displays so there is a display to the left of your primary monitor. Windows on this display will have negative X coordinates
  2. Build and run example_sdl_opengl3
  3. Move Windows window fully onto your primary display
  4. Drag dear imgui window out of Windows window and onto the display to its left

Expected result: window moves as you'd expect

Actual result: window isn't allowed to move onto the display with negative coordinates. Looks similar to when you try to drag a dear imgui window off the edge of the viewport in the non-multiple-viewports case

Thanks,

--Tom

ocornut commented 5 years ago

Hello @tom-seddon,

I looked at this today (SDL+GL3, Windows) and couldn't reproduce the issue.

See e.g. a screenshot of the Metrics window being moved in the second monitor which has negative coordinates:

image

Could you confirm that the issue is happening for you with the latest version?

One reason I can think that may cause this sort of bug is if the ImGuiPlatformMonitor info wasn't not provided by the back-end. However as far as I know the SDL back-end has been filling this information (see code in ImGui_ImplSDL2_UpdateMonitors()) before you made you made your post, and unless I am mistaken this information is not reliant on any specific recent version of SDL.

Could you:

Thank you.

tom-seddon commented 5 years ago

It turns out this problem seems to be associated with having a mixed-DPI setup. I have a Macbook Pro, with high-DPI laptop screen on the left (1), then two normal-DPI monitors (2, 3 - 2 is the primary display):

image

It behaves OK in a Parallels VM, where the laptop screen is treated as normal DPI, but I get the bug when running Windows 10 natively, where the laptop screen is high DPI.

Confirm your version of SDL?

SDL version is 2.0.9, from https://www.libsdl.org/release/SDL2-devel-2.0.9-VC.zip

See if it also happen with the Win32+DX11

Win32+DX11 version is usable, though when a window straddles a low DPI and a high DPI display it appears to lternates (at 60Hz...) between being drawn at low DPI scale and drawn at high DPI scale.

I can file a separate bug for this if you like.

and GLFW+GL3 back-end?

GLFW+GL3 is sort-of usable, though when a window is on a high DPI display the contents are drawn unscaled, so you get a window that's the right size visually (i.e., 4x as many pixels) with tiny contents:

image

I can file a separate bug for this if you like.

Also open Demo>Inputs,Navigation&Focus->Keyboard,Mouse&Navigation State and see the value of io.MousePos. Perhaps SDL is failing to return a correct mouse position

Mouse positions look OK, it's just the window position that apperas to be clamped.

What I did to build:

I'm using Visual Studio 2017, with compiler Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27023.1 for x86. Run x86 Native Tools Command Prompt for VS 2017, then enter these commands. Uses wget and unzip, but I think those come with Git for Windows...

md imgui-issue-2176
cd imgui-issue-2176
wget --no-check-certificate https://www.libsdl.org/release/SDL2-devel-2.0.9-VC.zip
unzip SDL2-devel-2.0.9-VC.zip
git clone https://github.com/ocornut/imgui
cd imgui
git checkout docking
cd examples\example_sdl_opengl3
set SDL2_DIR=..\..\..\SDL2-2.0.9
build_win32
copy %SDL2_DIR%\lib\x86\SDL2.dll Debug
debug\example_sdl_opengl3.exe.exe

Thanks,

--Tom

ocornut commented 5 years ago

Thanks for the details.

It turns out this problem seems to be associated with having a mixed-DPI setup. I have a Macbook Pro, with high-DPI laptop screen on the left (1), then two normal-DPI monitors (2, 3 - 2 is the primary display):

I couldn't reproduce with mixed-DPI. More over, the fact that it seems to work (even partially) with other back-ends suggest this might be an issue related to the back-end? I am not sure I understand the reason at this point.

Win32+DX11 version is usable, though when a window straddles a low DPI and a high DPI display it appears to lternates (at 60Hz...) between being drawn at low DPI scale and drawn at high DPI scale.

I also cannot reproduce this either (testing on Windows 10 with mixed DPI). The DPI Scale is updated for every viewports in UpdateViewportsNewFrame(), the code does:

        float new_dpi_scale;
        if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)
            new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);
        else if (viewport->PlatformMonitor != -1)
            new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
        else
            new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
        if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
        {

It would be good to add some printing (e.g. using IMGUI_DEBUG_LOG()) or debugger to figure out which path it is taking and why the DPI scale would be alternating every frame.

Because multiple-DPI is currently such in flex in Windows 10, specifying the exact Platform Toolset version and Platform/Windows SDK version might be important (I am not sure) I tried the win32+dx11 example both with VS2010 and VS2015 + SDK 10.0.17763. It is also possible that Parallels is making things behave differently that on normal Windows.

Even though multiple-DPI is not trivially supported by default it appears to be possible by overriding Platform_OnChangedViewport and on DPI change swapping font and/or font atlas and swapping style with a scaled style. Those steps are not ideal and would best be handled by core imgui. However I haven't yet found situation where a window would scale back and forth every frame.

Sorry this is all incredibly tricky!

GLFW+GL3 is sort-of usable, though when a window is on a high DPI display the contents are drawn unscaled, so you get a window that's the right size visually (i.e., 4x as many pixels) with tiny contents:

Does the PR in #2306 fixes it?

tom-seddon commented 5 years ago

Just an update, as it's been nearly a month: I haven't forgotten about this! I just haven't been doing any dear imgui and/or Windows work lately. But I will look at this within the next week or two, I hope.

--Tom

tom-seddon commented 5 years ago

I had a look at this today, commit 7a5196601e96ccc880ea00e5aed0ef3a786edad2, so hopefully #2306 is in there. I tried this on my other laptop, which has never had Parallels on it. Windows 1809 (17763.379), Visual Studio 2017, Windows SDK 10.0.16299.0, build tools v141.

Working with example_sdl_opengl3.

The problem with the screwy window dragging appears to be that the monitors are disjoint. Monitor arrangement is roughly as per the image above. Monitor 1 (the high DPI one, on the left) runs from (-2880,0) to (-1440,900), as it's got a position measured in pixels, but a size that's measured in logical units, or whatever the Windows term is. And Monitor 0 (normal DPI, in the middle) is from (0,0) to (1440,2560), leaving a 1440 pixel gap. Monitor 2 (normal DPI, on the right) is where you'd expect.

  | Name | Value | Type
-- | -- | -- | --
◢ | [0] | {MainPos={x=0 y=0} MainSize={x=1440 y=2560} WorkPos={x=0 y=0} ...} | ImGuiPlatformMonitor
  | ▶ MainPos | {x=0 y=0} | ImVec2
  | ▶ MainSize | {x=1440 y=2560} | ImVec2
  | ▶ WorkPos | {x=0 y=0} | ImVec2
  | ▶ WorkSize | {x=1440 y=2560} | ImVec2
  | DpiScale | 1.00000000 | float
◢ | [1] | {MainPos={x=-2880 y=0} MainSize={x=1440 y=900} WorkPos={x=-2880 y=0} ...} | ImGuiPlatformMonitor
  | ▶ MainPos | {x=-2880 y=0} | ImVec2
  | ▶ MainSize | {x=1440 y=900} | ImVec2
  | ▶ WorkPos | {x=-2880 y=0} | ImVec2
  | ▶ WorkSize | {x=1313 y=900} | ImVec2
  | DpiScale | 1.00000000 | float
◢ | [2] | {MainPos={x=1440 y=0} MainSize={x=1440 y=2560} WorkPos={x=1440 y=0} ...} | ImGuiPlatformMonitor
  | ▶ MainPos | {x=1440 y=0} | ImVec2
  | ▶ MainSize | {x=1440 y=2560} | ImVec2
  | ▶ WorkPos | {x=1440 y=0} | ImVec2
  | ▶ WorkSize | {x=1440 y=2560} | ImVec2
  | DpiScale | 1.00000000 | float

The monitors are disjoint because the sdl_opengl3 example doesn't have some of the Windows stuff that the DirectX11 has. So I experimentally put some of it in, and that seemed to fix it.

Adding SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); to main, fixes the monitor positions. The dragging now works, though of course on the high DPI monitor the window looks tiny.

Adding io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports | ImGuiConfigFlags_DpiEnableScaleFonts; to main.cpp makes the Dear Imgui windows resize themselves when moving from normal DPI to high DPI monitor, and scale up the contents too. (I note the DirectX11 example recommends not using the font scaling flag, but this was just an experiment.) Dragging the SDL window isn't so good, though, as it now doesn't resize when moving from normal DPI to high DPI, and so the newly-enlarged Dear Imgui windows can escape from it. I assume this is an open issue.

So maybe the bug then is just that the sdl_opengl3 example doesn't have this Windows stuff in it, or maybe simply doesn't have this Windows stuff in it just yet? Or perhaps it shouldn't have any Windows stuff in it, being SDL/OpenGL3, and therefore theoretically portable? - in which case maybe a note in the readme would suffice.

Regarding the oscillating behaviour, with all this extra code in place I reproduced it with the sdl_opengl3 example. I think this is due to the resizing interacting with ImGui::FindPlatformMonitorForRect, which takes the area into account, and so it's possible for the window to get into an resizing loop when it straddles two monitors. Once it's mainly on the high DPI monitor, it enlarges itself - but it's possible it's then mainly on the normal DPI monitor! So it shrinks itself. And the process repeats.

Thanks,

--Tom

bel8z commented 2 years ago

GLFW+GL3 is sort-of usable, though when a window is on a high DPI display the contents are drawn unscaled, so you get a window that's the right size visually (i.e., 4x as many pixels) with tiny contents:

This issue still applies to the current docking branch, i tested it with the provided GLFW+GL3 example by moving a viewport to a secondary monitor. Were you able to solve/investigate it? I can file another report if you prefer, but it seems related.

tom-seddon commented 2 years ago

GLFW+GL3 is sort-of usable, though when a window is on a high DPI display the contents are drawn unscaled, so you get a window that's the right size visually (i.e., 4x as many pixels) with tiny contents:

This issue still applies to the current docking branch, i tested it with the provided GLFW+GL3 example by moving a viewport to a secondary monitor. Were you able to solve/investigate it? I can file another report if you prefer, but it seems related.

In the end, no. I decided to stick with single-window mode.

--Tom