Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docking drop rectangle shaking / multiple windows don't have perfectly synchronized display #2361

Open
r-lyeh opened this issue Feb 18, 2019 · 18 comments

Comments

@r-lyeh
Copy link

r-lyeh commented Feb 18, 2019

The issue is much less noticeable in the gif due to low framerate. Sorry.
Repro-steps (I think). Maximize client window. Pick any tab which is large enough. Scroll it so it overlaps right and bottom screen borders. Notice the issue. Note: I am using a single monitor setup right now.

gif

Originally posted by @r-lyeh in #2353 (comment)

@ocornut
Copy link
Owner

ocornut commented Feb 18, 2019

Could you check in the Metrics window is the number of active viewports is changing?
Can you reproduce with the example application?
My intuition is this is due to multiple render targets not rendering synchronized.

@ocornut ocornut changed the title Bug: blue regions flickering Docking drop rectangle shaking/flickering Feb 18, 2019
@r-lyeh
Copy link
Author

r-lyeh commented Feb 19, 2019

Sure, will provide some more info asap

@r-lyeh
Copy link
Author

r-lyeh commented Feb 19, 2019

  1. The number of viewports is fixed during dragging. It only changes when dropped.
  2. No, I can't reproduce with example application. However, I have the root dockspace enabled whereas the example application does not.

Yes. It looks like out of sync, and I just found out that it degrades over time (first minutes are ok!).

@r-lyeh
Copy link
Author

r-lyeh commented Feb 19, 2019

Nvm the time degradation, I just happened on a fresh launch.

I just found out that viewport creation is always deterministic (it always happens when dropping outside the main client window); but... the viewport destruction does not always happens when dropping a small floating tab inside the main windows client; would that explain the out of sync?

@ocornut
Copy link
Owner

ocornut commented Feb 19, 2019

  1. The number of viewports is fixed during dragging. It only changes when dropped.

If it changes when dropped it means this is creating a viewport, and the swap/vsync of your multiple render contexts are not in sync. It may be tricky to solve depending on your setup/driver. See how the example application are using for render/swap/vsync.

Using ConfigDockingTransparentPayload=true attenuate the issue by using a different technique (make viewport transparent and using show the drop overlay on 1 of them), but it tends to need ConfigViewportsNoAutoMerge = true as well.

Apparently Visual Studio is using a fullscreen transparent hwnd to bypass this problem (not sure where I got this info, it's not verified).

2. No, I can't reproduce with example application. However, I have the root dockspace enabled whereas the example application does not.

In Demo>Examples>Docking you can have a fullscreen dockspace. I don't think this is any related tho.

@ocornut
Copy link
Owner

ocornut commented Feb 19, 2019

To test how your multiple render targets are synchronized:
#1542 (comment)

While you are at it, you may also experiment with swap intervals and multi-viewports and see how to get the render perfectly synchronized.
To do this you can render 2 rectangles in absolute position from 2 different imgui windows, then drag one window outside so it becomes a different platform window:

ImGui::Begin("Window A");
ImGui::GetOverlayDrawList()->AddRectFilled(ImVec2(300, 300), ImVec2(600, 600), IM_COL32(255, 0, 0, 255));
ImGui::End();
ImGui::Begin("Window B");
ImGui::GetOverlayDrawList()->AddRectFilled(ImVec2(300, 300), ImVec2(600, 600), IM_COL32(0, 255, 0, 255));
ImGui::End();

It'll look like this:
image
Now you can drag Window B quickly and see if the rectangles stays perfectly aligned with each others.

@r-lyeh
Copy link
Author

r-lyeh commented Feb 19, 2019

So much info. Thx. Testing all this in a few hours :D

@r-lyeh
Copy link
Author

r-lyeh commented Feb 19, 2019

More feedback.
It happens on example application if I enable Examples -> dockspace.
However, it gets fixed if I enable PassthruDockspace flag :)

@ocornut
Copy link
Owner

ocornut commented Feb 19, 2019

However, it gets fixed if I enable PassthruDockspace flag :)

Note that it doesn't get fixed per se, the framebuffers are still not synchronized but the way we render the drop boxed on a single layer doesn't rely on that synchronization.

Are you nvidia or ATI? We ought to figure out if it is even possible to have synchronized framebuffers.

EDIT Wait did you actually mean PassthruDockspace and not ConfigDockingTransparentPayload as discussed above??

@r-lyeh
Copy link
Author

r-lyeh commented Feb 20, 2019

New feedback.
Example application seems to work ok on Nvidia card no matter what dockspace/option I set up.
However, example application always fails on Intel card (laptop) when dockspace is set.

This is all still {1.68 wip + docking + viewports + example application + glfw3 + gl3} combo.

@r-lyeh
Copy link
Author

r-lyeh commented Feb 20, 2019

Errmm.. It happens on nvidia too, it's only that app needs to be on low framerate, I guess.
Maybe you can reproduce it by inserting random delays() in main loop?

@ocornut ocornut changed the title Docking drop rectangle shaking/flickering Docking drop rectangle shaking / multiple windows don't have perfectly synchronized display Feb 20, 2019
@PathogenDavid
Copy link
Contributor

I'm also experiencing this issue. However, I've never observed it in the examples. I can reproduce it in both the GLFW GL3 and DirectX 11 examples by inserting a long sleep (IE: 100 ms) between UpdatePlatformWindows and RenderPlatformWindowsDefault.

In our case, however, this happens even at high frame rates. I suspect this is because our Windows UI (message pump) thread is separate from our game thread (and thus the ImGui thread.) I suspect this causes some amount of latency between ImGui calling Platform_SetWindowPos and the window position actually updating.

imgui/imgui.cpp

Lines 10485 to 10493 in 87883ab

// Apply Position and Size (from ImGui to Platform/Renderer back-ends)
if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)
g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)
g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);
if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)
g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);
viewport->LastPlatformPos = viewport->Pos;
viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;

This can be observed if you modify your rectangle test to print ImGui::GetWindowViewport()->Pos to the window:

image

This screenshot was taken while moving Window A. (It's fine as long as it isn't moving.) The left-top corner of this screenshot represents the left-top corner of the primary monitor. If you measure, Window A is actually at 216, 198, but it thinks it's at 216, 194. If you measure the error in the rectangle it is 4 pixels off, which matches the discrepancy exactly.

I've tried some basic mucking with how ImGui deals with platform window positions (such as skipping PlatformRequestMove, using Platform_GetWindowPos to update LastWindowPos after calling Platform_SetWindowPos instead of setting LastPlatformPos to Pos, using the same to also update Pos), but nothing seems to fix the issue.

Unfortunately modifying the ImGui example to use a separate thread for the message pump is non-trivial, otherwise I'd make a more minimal repro with it to ensure it's not something else I'm doing.

@ocornut
Copy link
Owner

ocornut commented Apr 3, 2019

I haven't tried it yet but I've noticed that Vulkan allow vkQueuePresentKHR() to process multiple swap chains simultaneously via the swapchainCount value of VkPresentInfoKHR, so it might be possible to guarantee synchronized swapping, but I haven't tested that. Maybe a helper in imgui_impl_vulkan.cpp could attempt to do synchronized swapping, given the present info for viewports owned by the user (e.g. main) and creating a new VkPresentInfoKHR including those and the ones owned by imgui_impl_vulkan.
https://vulkan.lunarg.com/doc/view/1.0.33.0/linux/vkspec.chunked/ch29s06.html#VkPresentInfoKHR

@RT2Code
Copy link

RT2Code commented Nov 29, 2021

I recently stumbled into the same problem while trying to switch to Windows swap chain flip presentation model. Even with V-Sync enabled, the drop rectangles can appear heavily desynchronized.

ezgif-6-00846f2e251d

Apparently Visual Studio is using a fullscreen transparent hwnd to bypass this problem (not sure where I got this info, it's not verified).

Yes, I'm pretty sure that's how Visual Studio does it because its docking UI is always drawn on top, even if there is a window from another app in the way :

Untitled

Wouldn't that be a good solution to fix this issue?

@stormsc1
Copy link

I just ran into this issue, did anyone ever figure out a solution?

@naubry
Copy link

naubry commented Feb 15, 2022

Indeed, I notice this shaking when I'm on my pc with an Intel UHD Graphics 630 but I have no worries when I run on my pc with an RTX 3080 (except when I try to record the screen to show that it does not shake...)

@jeremyong
Copy link

jeremyong commented Jun 28, 2022

Having the docking indicators drawn on top as independent composited HWNDs would be pretty helpful here actually. It will be difficult to always ensure different viewports remain synchronized in a general architecture. In some engines, different windows present on disparate threads which latch at different rates to keep auxiliary UI responsive even if the main viewport suffers from scene-complexity related stutter. It's also quite difficult to manage swapchains identically in the general case, with the Vulkan WSI extension and DXGI interfaces having fairly different capabilities (waitable events/late-latching, hardware-accelerated scaling, different semantics for flip models, driver extensions for VRR, etc.). Coupled with the fact that most engines were developed with a "primary viewport" in mind, it wouldn't surprise me if the actual swapchain behavior was subtly different, even with the same graphics settings.

(Note: after finding this synchronization issue in my own implementation, I tried out the DX12 example for comparison and the same issue is present there on a 2080 RTX TI)

@Daedie-git
Copy link

Hi,

I've been using ImGui with docking and multi-viewport against the back-end of my own engine (linux, glfw, vulkan) and I have been experiencing this problem as well.

Some things I've noticed:

  • I've implemented the "multiple swapchain, single present" approach. This didn't change anything.
  • More interestingly. While dragging the separated viewport around, I'm setting this kind of patterns:
[imGuiSetWindowPos] (2735, 502)
[pollEvents] before glfwPollEvents()
[glfwWindowPosCallback] (2755, 513)
[pollEvents] after glfwPollEvents()
[imGuiSetWindowPos] (2733, 499)
[pollEvents] before glfwPollEvents()
[pollEvents] after glfwPollEvents()
[imGuiSetWindowPos] (2733, 498)
[pollEvents] before glfwPollEvents()
[pollEvents] after glfwPollEvents()
[pollEvents] before glfwPollEvents()
[pollEvents] after glfwPollEvents()
[pollEvents] before glfwPollEvents()
[glfwWindowPosCallback] (2735, 502)
[glfwWindowPosCallback] (2733, 499)
[glfwWindowPosCallback] (2733, 498)
[pollEvents] after glfwPollEvents()

Since this is all executed synchronously, it looks to me that the actual window positions are always lagging behind the ones set by imgui, but imgui is calculating it's state based on the window position it expects instead of what is actually there. To me this seems like it explains the desync behavior quite well.

Acquiring and working with the actual window position would yield considerably more accurate results I'd say. You'd probably have to separate out a target position (which you update and pass to SetWindowPos) and a current position (for rendering).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants