ocornut / imgui

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

Lag when dragging child windows #1805

Closed usernameiwantedwasalreadytaken closed 6 years ago

usernameiwantedwasalreadytaken commented 6 years ago

Hi, first thanks for this amazing library!

Version/Branch of Dear ImGui:

1.60 (master) x 1.51

My Issue/Question:

It seems to me that sometime between version 1.51 and 1.60 some commit added a "lag" when dragging windows. I don't know if you can see it from the videos bellow, but when I drag the child window in version 1.51 the window "sticks to the mouse pointer", but when I do the same in version 1.60 first the mouse pointer goes, then the window follows (it is still pretty fast, but noticeable).

I'd like to know if this was on purpose and if there is any option/way to disable this lag in version 1.60 (so that it behaves like before)?

P.S.: Of course I could change imgui's code, but generally I like to avoid doing that so that I can update it easily.

Screenshots/Video

Before (1.51):

before

After (1.60):

after

Thanks anyways!

ocornut commented 6 years ago

@usernameiwantedwasalreadytaken AFAIK that hasn't changed from imgui's point of view.

However at the time of 1.51 the demo were configured to run at unthrolled framerate (notice the framerate is ~700 FPS in your first gif) and I enabled vsync in most of the example since then (notice the framerate says ~60 FPS in the second gif). Can you compare how both behave at identical framerate?

From imgui point of view, the moving of window using mouse input from Frame N should be reflected in the vertices emitted at the end of Frame N. The additional latency can be caused by a mixture of: A) The mouse movement between reading the input submitted for Frame N and the time Frame N is swap buffer. The system renders mouse cursor in a special manner to reduce that latency. B) The time between swapping buffers and the thing actually appearing on your screen. Additional buffering on the driver/rendering back-end side frequently affect this.

If you enable the software mouse cursor io.MouseDrawCursor = true the cursor should match the position, but then if your framerate is 60 FPS you feel like your mouse software feels slower than unusual.

It's a tricky problem to solve if you want vsync (which is generally the case for a game), and without burning CPU. You can tackle the problem by removing imgui from the equation: make a simple DX11/OpenGL app that draws a square around the mouse cursor and see how it behave, you will probably notice the same issue. It is my understanding that some app switch from a system-supported mouse cursor to a software mouse cursor when dragging elements to hide this issue. Another way would be to stop waiting for vsync when dragging elements, but on a typical game app (rather than a simplified demo app) that could be problematic.

usernameiwantedwasalreadytaken commented 6 years ago

Thanks for the prompt answer.

Can you compare how both behave at identical framerate?

Yeah! The framerate. I missed that... Now it runs exactly like before.

I don't have much experience with games and rendering like that (more like an MFC/Java/CSharp guy...). So this made me realize how much change can be caused by the framerate, both in terms of user experience and CPU/GPU usage:

unthrottled

I personally prefer the unthrottled user experience (not making a game). But the high CPU/GPU usage might make me stick with the (now) default vsync behavior.

Again, thanks very much. Closing this.

ocornut commented 6 years ago

60 FPS isn't a bad framerate but the way mouse cursor are specifically handled by modern OS makes the dragging at 60 FPS feels very laggy. As mentioned, the solution of temporarily switching to a software mouse cursor while dragging is something I would like to experiment with more later.

The unthrottled version will always burn 1 CPU core. You could perfectly also aim at a framerate in-between, say 120 FPS. It's not a perfect strategy client-wise but it's easy to setup. (By the way, the unthrottled 700 FPS you get is an unusually low number on any semi modern machine.. Curious if the GIF capture affected it?).

usernameiwantedwasalreadytaken commented 6 years ago

I guess for now I will just use vsync. Maybe later I can find a compromise.

By the way, the unthrottled 700 FPS you get is an unusually low number on any semi modern machine.. Curious if the GIF capture affected it?

Yes, it was definitely affecting it. Without the recorder, I can get to 1200-1300 FPS:

unthrottled_app

itsfarseen commented 5 years ago

As a workaround, you can sleep the main thread for 17ms (1/60fps = 16.67ms) before Polling Events in each frame. These stackoverflow questions explain this problem in the context of OpenGL: https://stackoverflow.com/questions/43821109/minimize-mouse-input-lag-on-gl-desktop-app https://stackoverflow.com/questions/19102189/noticable-lag-in-a-simple-opengl-program-with-mouse-input-through-glfw

melroy89 commented 3 years ago

As a workaround, you can sleep the main thread for 17ms (1/60fps = 16.67ms) before Polling Events in each frame.

Adding sleep/waits in production code is a NO go. Never do this people. I'm using Imgui with GLFW + Vulkan. I tried already using glfwWaitEvents() iso glfwPollEvents(). But I still suffer from this mouse lagging issue.

When I look to other applications like the Firefox browser, which is also hardware accelerated via the GPU with vsync, I do not experience this kind of mouse lag when dragging stuff around. This is really a bug in Imgui. Please re-open.

Ps. I also tried to disable dubble buffering (glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE )) without luck. Ps. Ps. I'm using Imgui for an application and not a game.

Ristovski commented 2 years ago

To anyone stumbling upon this in the future: If on Linux, one can use https://registry.khronos.org/OpenGL/extensions/SGI/GLX_SGI_video_sync.txt to significantly improve the latency (not sure what the Window equivalent is)

Example usage with GLFW is: glfwSwapInterval(0) since glXWaitVideoSyncSGI will do the blocking (to vsync), then:

glfwSwapBuffers(window);

uint remainder;
glXWaitVideoSyncSGI(1, 0, &remainder);

nVidia drivers recommend calling glFinish() before glXWaitVideoSyncSGI(), so beware.

Here is a simple demo showcasing the improvements (toggles between standard loop with vsync and no vsync + glXWaitVideoSyncSGI). As you can see, using glXWaitVideoSyncSGI improves the latency by quite a lot.

White square is a hardware accelerated cursor, green square is a quad updated with mouse coords, same effect can be seen in ImGui when dragging windows etc:

https://user-images.githubusercontent.com/994445/181042739-22e3d429-6e4e-4c91-9bc3-b6f5e2a88891.mp4

pieter3d commented 10 months ago

A crude but effective way of doing this (with GLFW anyway) is to put this in your loop:

  const bool dragging = ImGui::IsMouseDragging(ImGuiMouseButton_Left);
  glfwSwapInterval(dragging ? 0 : 1);