mirror of
https://github.com/ocornut/imgui.git
synced 2026-10-29 07:54:40 +00:00
A template for multithreading? (GUI & Worker) #9539
Labels
No labels
accidentally closed
android
backends
bug
building
clipper
coloredit
color spaces
cplusplus
data types
demo
disabled
doc
docking
dpi
drag drop
drawing/drawlist
dropped
dx12
enhancement
focus
font/text
gallery
help wanted
idle/power saving
ime
in progress
inputs
inputtext
internals
label/id and id stack
language bindings
layout
linux/bsd
menus
multi-contexts
multi-threading
multi-viewports
nav
news/info
opengl
optimization
osx/ios
overlap
plot/graph
popups
question
scrolling
selection
settings
sliders/drags
style
tables/columns
tabs
test wanted
tools
tooltips
tree
tricks & tips
useful widgets
vulkan
web
window pos/depth
window sizing
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ocornut/imgui#9539
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Version/Branch of Dear ImGui:
Version (head):
39e7bf5a0, Branch: masterBack-ends:
imgui_impl_sdl2.cpp + imgui_impl_opengl2.cpp
Compiler, OS:
Linux + GCC
Full config/build information:
Details:
Dear Community,
First of all sorry for yet another question about multithreading. I have read several (maybe not all) similar (at least at first sight) issues but I didn't find anything which I could find helpful.
My task is simple. I have GUI to control and read some hardware (a microcontroller). Now everything is done in one thread, so one can see in the code:
all of those member-function calls of the object
tare actual communications to the microcontroller. Now everything is working fine (though FPS is 20-22) and it doesn't feel freezing or lagging. However, I would like to split somehow the part of reading / writing to the uC and actually displaying the GUI. I don't quite understand what is the design it should be ? More over, I have a long running tasks like caibration. And I want to display the progress, for example. So the only one way that comes to my mind is like having a proxy object (protected by a mutex) (or objects in a queue) which would represent a state of my uC (current positions, moving or idleing, etc) and also a queue of commands (also protected) (which means writing to the uC). Then one thread (the worker) is the only thread which has the actual access to the device, communicates with it according to the command queue, and fills the proxy object (locking the mutex before). Then in the GUI (main) thread we read the proxy object and fill the GUI dynamic widgets (like labels and plots) (also locking the mutex before).Is this approach natural or I must learn more about the imGui internals to do it ? Because I see people more asking about two different threads like rendering and the rest, but not about ubiquitous "Worker & GUI" case.
Thank you in advance !
Screenshots/Video:
Minimal, Complete and Verifiable Example code:
Don't think I have something for this type of issue.
That (UI/ImGui in main thread, device communication in a worker thread, synchronization through a proxy object) is a natural approach. From the perspective of the UI, there is no real difference to a single threaded approach, so you don't have to worry about ImGui internals or specifics for this.
To keep this smooth, you should minimize the time spend in a locked mutex in both threads. In the main thread, don't lock the mutex and construct your UI based on the state of the proxy object. Instead, lock the mutex once per frame, copy out the state, append the commands collected during the last frame to the command queue, then unlock the mutex right away and construct the UI based on your copy of the state. In the worker thread, don't lock the mutex and update the state by polling your device. Poll your device first, update or construct a state local to the thread, then once per cycle lock the mutex and copy into the proxy, collect new commands for the next cycle while at it and unlock again.
If you do this carefully, you can probably even avoid any allocations while in a locked mutex. If your command queue is f.e. an
std::list, you can splice its contents to the end of another list without allocating or freeing anything. If you construct a complete state per cycle in your worker thread, you can swap it with the proxy state instead of copying. That way you can avoid expensive memory operations inside of the mutex if you have dynamic containers as part of the state. You can also swap the state in your main thread instead of copying it once per frame, but then you need to secure it with a generation (number incremented on every update, only swap out if number changed) or a simple flag (set to valid when swapping in a new state, only swap out if valid, set to invalid after swapping out).You should always be aware of the possibility of multiple update cycles happening between two frames or multiple frames being displayed between two update cycles. Do not assume that the commands appended to the queue one frame earlier have been delivered to the worker thread yet. Also, if your state contains deltas of some kind, don't assume the UI thread has seen them yet when updating your state again.
Yes, this is exactly how it was in my mind :)
Yes, of course.
Yes, this is on of the most important points I was thinking about.
This is also important indeed: if, f.e. the uC is not in the
idlestate you cannot move (so the button must turn from "Go" into "Stop").Okay, I will start working on it on Saturday as I am quite busy now. Thank you for your help !