How does Dear ImGui handle Windows event processing concurrently with internal update/rendering? #8394
Replies: 3 comments 4 replies
-
This is definitely off-topic for this forum, but I can try and
The unsatisfying answer here is that for secondary viewports, Dear ImGui is not processing any windows events for moving/resizing because Windows isn't resizing secondary viewports. Dear ImGui is handling resizing and moving internally. From a Windows perspective, secondary viewports are just bare rectangles with no window decorations. To put it another way: Since Dear ImGui internally handles the movement and resizing, You can actually see the behavior difference for yourself by disabling This approach is not without downsides. On Windows it prevents using Aero Snap with secondary viewports (IE: dragging the window to screen edges or using Win+Left/Right to snap them to the sides.) Linux treats borderless windows fundamentally differently in ways that break multi-viewports (random example), and Wayland on Linux in general just hates applications wanting to control windows for themselves (IIRC it's literally impossible to query the location or size of your window.) It also means we loose out on certain affordances expected by OS users (for example, double-clicking the title bar on Windows windows to maximize them, hovering the maximize button on macOS or Windows 11.) You can get some of these things back by explicitly handling them, but it relies on cooperation with Windows that Dear ImGui doesn't currently do. (And in fact, most applications which draw their own non-client area do not get this correct. For example, did you know that if you double click the app's icon in the title bar that it will close? That one is almost always overlooked, even VSCode misses it. That used to be the main way I closed windows :/) Nik highlighted some of the workarounds he uses in ImHex here: #8268 (comment)
This isn't caused by failing to handle the events promptly, it's an unfortunate design decision of how Windows message loops work that dates back to the oldest versions of Windows. There's no great documentation on this AFAIK, but moving/sizing windows put the message loop into a modal loop state where the So really the question is "How do you keep redrawing the window when the window is in a modal loop state?" The way Windows "expects" you to do this is to handle the (In GLFW apps, this is what It is worth noting that you should be aware that doing anything complicated in a
Not sure if you're asking this because you guessed this might be what Dear ImGui is doing or because you want to use it as a solution. As you hopefully understand from above, this is not how Dear ImGui avoids the issue. So I'll assume the latter. It is possible to have your render loop and your message pump be on separate threads, and it is a possible solution to this problem without using I've done it, and it's not trivial. You can't use the Dear ImGui-provided backends so you must make your own, and you need to carefully marshal things between the two threads. This approach is also not without downsides. In particular, secondary viewports don't appear to resize as fluidly anymore because the message pump and rendering are on different timelines. (It still updates as you're resizing, but the window and rendered size will not always be perfectly in sync.) You can kind-of hide the effect on D3D by configuring the swap chain to not stretch when the window size and swap chain size are mismatched, but I don't think similar functionality exists in OpenGL. I would never consider this approach with OpenGL simply because it's an uncommon situation that I'd expect graphics drivers to handle poorly. (It requires swapping buffers from the rendering thread rather than the message pump thread.) If you were wanting your main viewport to live on the main thread and have secondary viewports live on another, you cannot do this with Dear ImGui without major shenanigans.
OpenGL does not play nice with multiple threads in general. I'd avoid having it interact with multiple threads entirely. Judging by your profile it looks like you're just starting out with things, which is great! I'd strongly recommend avoiding multithreading for a while though and focus on developing rigor and discipline in a single-threaded context first. Concurrency is extremely unforgiving.
Even though I don't personally love the structural changes it imposes on you, However, since you're (seemingly) still starting out I'd also strongly recommend you try to avoid the allure of chasing this level of polish and focus on learning more productive things. If your goal is to tinker on game engines professionally some day, there's about a thousand things more valuable than learning the finer details of Win32 junk and it's probably better to just use SDL. (I say this as someone who has wasted way too much time learning the finer details of Win32 junk and has historically avoided using SDL. Save learning Win32 junk for when someone is paying you to do it.) If you go searching, you'll find that many (maybe even most) games/engines just don't handle this at all. (SDL actually didn't even officially support handling it until November 2023.) Sorry this turned into a huge wall of text, hopefully it was useful! 😅 |
Beta Was this translation helpful? Give feedback.
-
SendMessage is blocking, while PostThreadMessage is not. The reason you would not want to use SendMessage in this case is to ensure that both threads run independently: your rendering will not block your system message processing, and your system message processing will not block your rendering. If you do not care about your message processing thread, and are OK with it blocking on your rendering, you should be able to use SendMessage if you so desire. I have never tested that method myself, though, so there may be some hidden gotchas that I am unaware of. - Casey |
Beta Was this translation helpful? Give feedback.
-
It's worth remembering that DTC was just a small demonstration I did for Jonathan Blow, since he was asking about this. In real code, I use neither PostThreadMessage nor SendMessage, since the better thing to do is to directly handle the messages where they arrive and deal with any resulting thread communication yourself via your own code. So I would recommend against thinking too hard about Post vs. Send - you probably shouldn't be shipping code with either. I am just explaining why DTC specifically was using PostThreadMessage, and why it is preferable if for some reason you didn't want to have your own interthread communication. There really isn't much reason to use SendMessage unless you need information that can only be accessed at the time of the message (which is not just the pointers you mentioned, but also things like GetKeyState, etc., that are tied to the message queue state). In my opinion, once you get to that point, the correct next step is to use your own thread communication that better supports more flexible data transfer, because SendMessage is a) blocking and b) won't let you actually call things like GetKeyState and have it work properly on the other thread anyway. - Casey |
Beta Was this translation helpful? Give feedback.
-
Hello, I was unsure of any other medium to ask this, so I am asking this here:
I'm currently working on a Win32 application that utilizes Dear ImGui's multi-viewport support. One aspect that really puzzles me is how Dear ImGui manages to process Windows events (such as resizing, moving, and input events) while still rendering frames without noticeable delays or stutters.
When I resize my Win32 OpenGL window, it doesn't paint until resized. Not the case with ImGui viewports.
Also, I have ran into several multi threading issues in my projects, so unclear what to do exactly, as I can call wglMakeCurrent for imgui_opengl3_impl related render calls on separate thread, but then viewports were causing issues by not rendering any viewport outside the main window.
I’m trying to gain a deeper understanding of how I can achieve fluid multi threaded integration between event handling on one thread, and all game related stuff for GUI and rendering on its own thread, so that I can apply similar principles to my own projects. Any insights, examples, or references you can provide would be extremely helpful.
Thanks for your time and for all the great work on ImGui.
Beta Was this translation helpful? Give feedback.
All reactions