Forcing screen updates
-
Not sure what title to give to this question :)
Briefly! I am running a repeating simulation from a QT UI application, with periodic screen refreshes. It is vital this runs as fast as possible. I run simulation steps off a running
QTimer::timeout()
. The user can adjust the timeout value.The problem comes when the timeout is (deliberately) set to
0
. (With >= 1 screen refresh is fine, but that is way too much of a delay.) So timeouts come as quickly as possible, no (intentional) delay. The code behind has occasional, periodic calls to update widgets to latest values. Say this happens at just once per second (I use aQElapsedTimer
to determine this since last update). This will effectively hardly interfere with the fastest speed of millions of simulation steps.I find that my widget updates are not shown when
QTimer::interval() == 0
. I believe this is because Qt screen updates are only actually processed when the event loop has no other events to process, that's certainly how WindowsWM_PAINT
messages work so I would guess the same principle. And I suspect that the event queue is never empty, because of theQTimer
interval being 0.Does anyone have a recommendation as to how to deal with this best?
A second issue --- which is not so vital, so I didn't want to clutter the base question with it --- is that similarly with interval 0 if I have, say, tried to click a button that does not seem to get processed either, nor does clicking the window's close button, perhaps because it's too busy/non-responsive. [Update: it does actually respond: if I compile for Debug it's fairly responsive, but if I compile for Release I can wait for a minute before it notices the click --- this shows just how intensive my computations are!] An answer for that too would be great, but should not detract from the main question above about updating/redrawing.
Now, please, it would be really good if people did not suggest an alternative way of approaching of this. For example, I am perfectly well aware of how this might be done by having a separate thread for the simulation outside of the main UI thread. I really don't want to have to justify my choices, partly based on the criticality of obtaining the very best performance for the simulation, which I have carefully instrumented to get the fastest results. The question is about how to achieve updating of the UI based on single-thread where the timer has interval 0.
Thank you and I'd appreciate any answers!
-
Not sure what title to give to this question :)
Briefly! I am running a repeating simulation from a QT UI application, with periodic screen refreshes. It is vital this runs as fast as possible. I run simulation steps off a running
QTimer::timeout()
. The user can adjust the timeout value.The problem comes when the timeout is (deliberately) set to
0
. (With >= 1 screen refresh is fine, but that is way too much of a delay.) So timeouts come as quickly as possible, no (intentional) delay. The code behind has occasional, periodic calls to update widgets to latest values. Say this happens at just once per second (I use aQElapsedTimer
to determine this since last update). This will effectively hardly interfere with the fastest speed of millions of simulation steps.I find that my widget updates are not shown when
QTimer::interval() == 0
. I believe this is because Qt screen updates are only actually processed when the event loop has no other events to process, that's certainly how WindowsWM_PAINT
messages work so I would guess the same principle. And I suspect that the event queue is never empty, because of theQTimer
interval being 0.Does anyone have a recommendation as to how to deal with this best?
A second issue --- which is not so vital, so I didn't want to clutter the base question with it --- is that similarly with interval 0 if I have, say, tried to click a button that does not seem to get processed either, nor does clicking the window's close button, perhaps because it's too busy/non-responsive. [Update: it does actually respond: if I compile for Debug it's fairly responsive, but if I compile for Release I can wait for a minute before it notices the click --- this shows just how intensive my computations are!] An answer for that too would be great, but should not detract from the main question above about updating/redrawing.
Now, please, it would be really good if people did not suggest an alternative way of approaching of this. For example, I am perfectly well aware of how this might be done by having a separate thread for the simulation outside of the main UI thread. I really don't want to have to justify my choices, partly based on the criticality of obtaining the very best performance for the simulation, which I have carefully instrumented to get the fastest results. The question is about how to achieve updating of the UI based on single-thread where the timer has interval 0.
Thank you and I'd appreciate any answers!
@JonB
Movie update speed: 24 frames per second.
We have 30 frames per second in RTSP streaming
Is it better to save the display data(index them) to the disk first and the GUI loads the data for display if your simulation is too fast? In this way you may be able to select a proper interval for the timer. -
@hskoglund said in Forcing screen updates:
Hi, have you tried forcing updates using QWidget::repaint()?
That is what I was going to have to do next. There are a few widgets, and I preferred to do it via
update()
if possible, e.g. that knows whether my widget really needs repainting where I will probably be unconditional. But I agree it will (presumably) "work".It won't, however, do anything for the UI-input-not-immediately-responding situation, which I had hoped to address at the same time.
I have not thought this through, but what I think I was looking for was along the lines of: once every so often during all these 0 timeouts I could afford to discard any pending backlogs (if that happens, not sure it does), give The Qt event loop a moment without so it can update screen and see if any input, and then get back to hammering away at full speed. But I'm not clear in my own mind how I might achieve that :)
BTW, a sample run now shows that number of iterations, and hence timeout 0s, I am achieving is of the order of 220,000 per second (Release build). So we are talking in microseconds, not milliseconds. That gives some idea of the speed/intensity, and why I need timeout of 0; a timeout of 1, say, would be disastrous!
-
Not sure what title to give to this question :)
Briefly! I am running a repeating simulation from a QT UI application, with periodic screen refreshes. It is vital this runs as fast as possible. I run simulation steps off a running
QTimer::timeout()
. The user can adjust the timeout value.The problem comes when the timeout is (deliberately) set to
0
. (With >= 1 screen refresh is fine, but that is way too much of a delay.) So timeouts come as quickly as possible, no (intentional) delay. The code behind has occasional, periodic calls to update widgets to latest values. Say this happens at just once per second (I use aQElapsedTimer
to determine this since last update). This will effectively hardly interfere with the fastest speed of millions of simulation steps.I find that my widget updates are not shown when
QTimer::interval() == 0
. I believe this is because Qt screen updates are only actually processed when the event loop has no other events to process, that's certainly how WindowsWM_PAINT
messages work so I would guess the same principle. And I suspect that the event queue is never empty, because of theQTimer
interval being 0.Does anyone have a recommendation as to how to deal with this best?
A second issue --- which is not so vital, so I didn't want to clutter the base question with it --- is that similarly with interval 0 if I have, say, tried to click a button that does not seem to get processed either, nor does clicking the window's close button, perhaps because it's too busy/non-responsive. [Update: it does actually respond: if I compile for Debug it's fairly responsive, but if I compile for Release I can wait for a minute before it notices the click --- this shows just how intensive my computations are!] An answer for that too would be great, but should not detract from the main question above about updating/redrawing.
Now, please, it would be really good if people did not suggest an alternative way of approaching of this. For example, I am perfectly well aware of how this might be done by having a separate thread for the simulation outside of the main UI thread. I really don't want to have to justify my choices, partly based on the criticality of obtaining the very best performance for the simulation, which I have carefully instrumented to get the fastest results. The question is about how to achieve updating of the UI based on single-thread where the timer has interval 0.
Thank you and I'd appreciate any answers!
@JonB If the simulation is spinning for several seconds, you need to use some sort of occasional yield to pump the event loop to process the click events and do the redraw.
It definitely is possible to use a 0ms timer to drive UI updates. A lot of people do something conceptually similar, so I am not sure exactly what's going wrong with yours, so it will be down to the exact details of what you are doing. If Qt would never redraw until there were zero pending things to do, it would be impossible for 0ms timers to work in-general, because they would always be adding something to be done before the event loop could be finished entirely.
You may need to make some tradeoffs for simulation throughput vs. application responsiveness to get the behavior you want. At any given nanosecond, the main thread will be used for either Qt drawing the UI xor your code running simulation steps, but not both. And nothing is going to interrupt your simulation step code unless you specifically write something that returns from it or interrupts it.
-
@JonB If the simulation is spinning for several seconds, you need to use some sort of occasional yield to pump the event loop to process the click events and do the redraw.
It definitely is possible to use a 0ms timer to drive UI updates. A lot of people do something conceptually similar, so I am not sure exactly what's going wrong with yours, so it will be down to the exact details of what you are doing. If Qt would never redraw until there were zero pending things to do, it would be impossible for 0ms timers to work in-general, because they would always be adding something to be done before the event loop could be finished entirely.
You may need to make some tradeoffs for simulation throughput vs. application responsiveness to get the behavior you want. At any given nanosecond, the main thread will be used for either Qt drawing the UI xor your code running simulation steps, but not both. And nothing is going to interrupt your simulation step code unless you specifically write something that returns from it or interrupts it.
@wrosecrans said in Forcing screen updates:
If Qt would never redraw until there were zero pending things to do, it would be impossible for 0ms timers to work in-general, because they would always be adding something to be done before the event loop could be finished entirely.
That is interesting that you say Qt works (doesn't work) that way. Thanks. My recollection of straight Windows stuff was that the event loop only processed
WM_PAINT
messages if & when the message queue was empty. i.e. do no screen updating unless there are no other events waiting to process.I'm not asking anything about interrupting my computations. Each one is very fast (a small number of microseconds). What I am saying/seeing is really quite simple:
- I have a repeating
QTimer
of interval 0. - On timeout I do one computation.
- Once every so often I make an update to the UI, relatively infrequently (let's say once per second).
- I am seeing (a) the updates don't appear on the screen, or maybe appear once every 10 seconds, and (b) if I click anything it can take a minute and then it responds.
And behaviour seems pretty proportional to computation time. With the code nice and release-fast, so my work can be as fast as possible, I experience above non-responsiveness of UI. If you think about it, I am getting as many timeout(0) events as possible, busy Qt event loop. If I slow it down by, say, compiling for debug (but not run under debugger) then my computation runs slower (less frequent timeouts, less messages in the Qt event queue) and I get better responsiveness. Both for drawing and for inputs.
I will do more investigating in due course... :)
- I have a repeating
-
@wrosecrans said in Forcing screen updates:
If Qt would never redraw until there were zero pending things to do, it would be impossible for 0ms timers to work in-general, because they would always be adding something to be done before the event loop could be finished entirely.
That is interesting that you say Qt works (doesn't work) that way. Thanks. My recollection of straight Windows stuff was that the event loop only processed
WM_PAINT
messages if & when the message queue was empty. i.e. do no screen updating unless there are no other events waiting to process.I'm not asking anything about interrupting my computations. Each one is very fast (a small number of microseconds). What I am saying/seeing is really quite simple:
- I have a repeating
QTimer
of interval 0. - On timeout I do one computation.
- Once every so often I make an update to the UI, relatively infrequently (let's say once per second).
- I am seeing (a) the updates don't appear on the screen, or maybe appear once every 10 seconds, and (b) if I click anything it can take a minute and then it responds.
And behaviour seems pretty proportional to computation time. With the code nice and release-fast, so my work can be as fast as possible, I experience above non-responsiveness of UI. If you think about it, I am getting as many timeout(0) events as possible, busy Qt event loop. If I slow it down by, say, compiling for debug (but not run under debugger) then my computation runs slower (less frequent timeouts, less messages in the Qt event queue) and I get better responsiveness. Both for drawing and for inputs.
I will do more investigating in due course... :)
Hi,
What exactly are you repainting ? Widgets ? A scene ?
Depending on what you want to show, did you consider using OpenGL ?
- I have a repeating
-
Hi,
What exactly are you repainting ? Widgets ? A scene ?
Depending on what you want to show, did you consider using OpenGL ?
@SGaist
My friend, I am repainting 3 widgets with a number in them! That's the whole UI. "OpenGL" couldn't be further from the issue, I have no graphics issue at all.The question is about what is going when you have a
QTimer
ticking at 0 interval and each timeout does one very fast computation. Then immediately returns to the event loop, for the next timeout(0). So this is going to keep happening VERY fast. The issue is at, say, 300,000 timeout(0)s per second, that sort of figure! -
Thinking about it, I can "simulate" the worst of what is happening with just:
QTimer timer; connect(&timer, &QTimer::timeout, this, []() { /* do nothing, so it's quick! */ }); timer.start(0);
That's a 0-interval keep happening and doing nothing. So lots per second! At this point, if I try to close the window I claim I have to wait ages till it (Qt event loop) notices it. That can happen, say, a minute after I clicked the close button.
Then just add into the timeout slot a
QElapsedTimer
to update a label once per second. I claim I rarely get to see these updates ever shown on screen. Might happen a lot later, like the response to the window close button input does.So I have something to try in due course, unless anyone else is intrigued enough to see how Qt behaves under this load :)
-
Well, mystery behaviour solved. The culprit is calling
QWidget::setWindowTitle()
too frequently! Here is an absolutely minimal standalone repro:#include <QApplication> #include <QTimer> #include <QWidget> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; w.setGeometry(100, 100, 300, 100); w.show(); QTimer timer; timer.setInterval(0); int count = 0; // change next line down to `count % 100 == 0` and for me everything freezes completely! :( QObject::connect(&timer, &QTimer::timeout, [&]() { count++; if (count % 1000 == 0) w.setWindowTitle(QString("%1").arg(count)); } ); QTimer::singleShot(1000, [&timer]() { timer.start(); } ); // ensure widget actually gets time to show initially, even on fastest timer interval above return a.exec(); }
I am on Qt 15.5.3 under Ubuntu 20.04, using GNOME/X windows. This is in a VirtualBox VM under Windows. I periodically update the window title to show the simulation generation number as above.
In the above code, if updating the window title every
if (count % 1000 == 0)
all works fine. But if I reduce that toif (count % 100 == 0)
my whole system "freezes". Not only do I not see the title updates, not only does it not respond if I click the window's close box, but even the VM's Linux "shutdown" command fails to shut down Linux. I have to use the VM's "powerdown" command to force-terminate the whole OS!So the gist here must be: if sending lots of "title update"s frequently the whole windowing system must get so "busy" servicing the title updates that it becomes totally unresponsive.
If anybody would care to reproduce above code on their platform I should be most interested to hear behaviour on yours?! If your system is faster/slower than mine you might have to adjust the
count % 100
, e.g. down tocount % 10
or evencount % 1
.Obviously I now have a solution: I will change the condition for showing the title over to a
QElapsedTimer
so the user only sees the updates, say, 10 times per second. -
Well, mystery behaviour solved. The culprit is calling
QWidget::setWindowTitle()
too frequently! Here is an absolutely minimal standalone repro:#include <QApplication> #include <QTimer> #include <QWidget> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; w.setGeometry(100, 100, 300, 100); w.show(); QTimer timer; timer.setInterval(0); int count = 0; // change next line down to `count % 100 == 0` and for me everything freezes completely! :( QObject::connect(&timer, &QTimer::timeout, [&]() { count++; if (count % 1000 == 0) w.setWindowTitle(QString("%1").arg(count)); } ); QTimer::singleShot(1000, [&timer]() { timer.start(); } ); // ensure widget actually gets time to show initially, even on fastest timer interval above return a.exec(); }
I am on Qt 15.5.3 under Ubuntu 20.04, using GNOME/X windows. This is in a VirtualBox VM under Windows. I periodically update the window title to show the simulation generation number as above.
In the above code, if updating the window title every
if (count % 1000 == 0)
all works fine. But if I reduce that toif (count % 100 == 0)
my whole system "freezes". Not only do I not see the title updates, not only does it not respond if I click the window's close box, but even the VM's Linux "shutdown" command fails to shut down Linux. I have to use the VM's "powerdown" command to force-terminate the whole OS!So the gist here must be: if sending lots of "title update"s frequently the whole windowing system must get so "busy" servicing the title updates that it becomes totally unresponsive.
If anybody would care to reproduce above code on their platform I should be most interested to hear behaviour on yours?! If your system is faster/slower than mine you might have to adjust the
count % 100
, e.g. down tocount % 10
or evencount % 1
.Obviously I now have a solution: I will change the condition for showing the title over to a
QElapsedTimer
so the user only sees the updates, say, 10 times per second.I have tested your sample on macOS 12.6.5 with 1000, 100, 10, 1 and Qt 5.15.2 and 6.4.3.
It worked without any issue in all cases. Meaning I could resize the window and see the window title update.
-
I have tested your sample on macOS 12.6.5 with 1000, 100, 10, 1 and Qt 5.15.2 and 6.4.3.
It worked without any issue in all cases. Meaning I could resize the window and see the window title update.
@SGaist
This implies one of two things:-
My machine is very, very much faster than yours, so I can pound the event queue more. This seems highly unlikely :)
-
Any combination of: you have a Mac and (so I hear) they just work (but are for rich people/artists), I have a rubbish machine, X11/GNOME/Ubuntu can't cope, VirtualBox is a problem, I am jinxed ;-)
Thanks for testing. I am OK now that I have identified the problem here, and changed over to a
QElapsedTimer
to throttle the title updates. -
-
J JonB has marked this topic as solved on
-
@SGaist
This implies one of two things:-
My machine is very, very much faster than yours, so I can pound the event queue more. This seems highly unlikely :)
-
Any combination of: you have a Mac and (so I hear) they just work (but are for rich people/artists), I have a rubbish machine, X11/GNOME/Ubuntu can't cope, VirtualBox is a problem, I am jinxed ;-)
Thanks for testing. I am OK now that I have identified the problem here, and changed over to a
QElapsedTimer
to throttle the title updates.- I am rather inclined on the "OS API is different"
- You forgot the corporate machines, refurbished, etc variants ;-)
Additional test done with KDE Neon using Wayland on an x86_64 machine with an old NVIDIA card: 1000 and 100 works fine, at 10 I get "The Wayland connection experienced a fatal error: Resource temporarily unavailable" and the app crashes after, literally, a couple of seconds and seems to trigger some restart of other elements but without crashing the whole session (i.e. I can see the desktop background go black and back to the wallpaper and the menu bar disappear and reappear).
-
-
- I am rather inclined on the "OS API is different"
- You forgot the corporate machines, refurbished, etc variants ;-)
Additional test done with KDE Neon using Wayland on an x86_64 machine with an old NVIDIA card: 1000 and 100 works fine, at 10 I get "The Wayland connection experienced a fatal error: Resource temporarily unavailable" and the app crashes after, literally, a couple of seconds and seems to trigger some restart of other elements but without crashing the whole session (i.e. I can see the desktop background go black and back to the wallpaper and the menu bar disappear and reappear).
@SGaist
Thank you for your further test. Now I trust you do not think I am going mad/reporting nonsense ;-) As I said, on my platform it is so bad that you cannot interact with anything on the desktop.Worse, the desktop does not respond to the Ctrl+Alt+F3 key combination to switch to a console login where I could have killed the process and then returned to the desktop. And the VM's standard Linux "shutdown" order is ignored. Which leaves me with only the VM's "powerdown", which immediately terminates the OS rudely. Do you have any idea how many times I have had to do this and reboot the machine while developing...? :(
-
@SGaist
Thank you for your further test. Now I trust you do not think I am going mad/reporting nonsense ;-) As I said, on my platform it is so bad that you cannot interact with anything on the desktop.Worse, the desktop does not respond to the Ctrl+Alt+F3 key combination to switch to a console login where I could have killed the process and then returned to the desktop. And the VM's standard Linux "shutdown" order is ignored. Which leaves me with only the VM's "powerdown", which immediately terminates the OS rudely. Do you have any idea how many times I have had to do this and reboot the machine while developing...? :(
Never thought madness was involved. The first thing that came to mind when you wrote that it was due to setWindowTitle was that it was likely the kind of API that was not necessarily meant to be updated that frequently. And beside that, it's one of the parts that is handled outside of Qt so there might some more things coming into play.
-
Never thought madness was involved. The first thing that came to mind when you wrote that it was due to setWindowTitle was that it was likely the kind of API that was not necessarily meant to be updated that frequently. And beside that, it's one of the parts that is handled outside of Qt so there might some more things coming into play.
@SGaist said in Forcing screen updates:
not necessarily meant to be updated that frequently. And beside that, it's one of the parts that is handled outside of Qt so there might some more things coming into play.
Absolutely! Just something which didn't really occur to me, didn't realise I was hitting it quite so often or more to the point that it would cause complete non-operation of the desktop!
-
@SGaist said in Forcing screen updates:
not necessarily meant to be updated that frequently. And beside that, it's one of the parts that is handled outside of Qt so there might some more things coming into play.
Absolutely! Just something which didn't really occur to me, didn't realise I was hitting it quite so often or more to the point that it would cause complete non-operation of the desktop!
@JonB Just to be sure: Have you optimized your Vbox VM? I mean, installed guest drivers, set hyper-v paravirtualization in settings, 3d accel on,etc? In another thread here I complained about the lack of graphics performance of Vbox and why I switched to VMware - in the end switching to native linux as performance gap was still too much.
If you are on Win 11 you also might want to try Windows Subsystem for Linux. So far everybody seems to be surprised by the performance of graphics apps.
Just for fun‘s sake you could try to use xpra to forward all X11 calls to your native windows machine. This might improve performance as well :D
We had a similar problem in the early 2000s on Solaris SPARC systems using Motif/X11. Flooding the system just froze the machine as the graphics card was not fast enough to process all the X11 protocol calls (the majority are synchronous). -
@JonB Just to be sure: Have you optimized your Vbox VM? I mean, installed guest drivers, set hyper-v paravirtualization in settings, 3d accel on,etc? In another thread here I complained about the lack of graphics performance of Vbox and why I switched to VMware - in the end switching to native linux as performance gap was still too much.
If you are on Win 11 you also might want to try Windows Subsystem for Linux. So far everybody seems to be surprised by the performance of graphics apps.
Just for fun‘s sake you could try to use xpra to forward all X11 calls to your native windows machine. This might improve performance as well :D
We had a similar problem in the early 2000s on Solaris SPARC systems using Motif/X11. Flooding the system just froze the machine as the graphics card was not fast enough to process all the X11 protocol calls (the majority are synchronous).@DerReisende
Goodness knows :) I'm wedded to VirtualBox coz I just about know how to use it. I did "ESXi", that's VMware isn't it, and found it a nightmare to manage! I will have a look tomorrow at graphics settings, normally I don't think about them, didn't occur to me as I'm only writing to a window title, but who knows.About the only UI that was 100.00% reliable was a VT220, ever since windowing systems there have always been problems....