Yaron 53 Posted April 22, 2021 I have a function I'd like to call 120 times per seconds using TThread.Synchronize (as the function updates the screen). I don't want to completely bog down a CPU core by constantly calling "QueryPerformanceFrequency" in a loop until the time is right to trigger the function. I originally envisioned using Sleep(1), but in reality, Sleep(1) usually means 15.6ms unless I change the entire system's Tick accuracy using "TimeBeginPeriod/TimeEndPeriod". However, I don't want to affect the entire system as I've read there's a harsh 25% battery impact when switching the accuracy and I don't want to drain the client's battery. So what is the best, most precise way to time/trigger synchronized calls from a thread? Share this post Link to post
Anders Melander 1782 Posted April 22, 2021 5 minutes ago, Yaron said: 120 times per seconds You want to update the screen 120 times per second? Why on earth would you want to do that? Share this post Link to post
Yaron 53 Posted April 22, 2021 I want smooth scrolling, using Sleep(1), I measure 15-30ms delay per frame, resulting in unstable scrolling because the delay between screen-updates is inconsistent/juddery and ~33fps which looks really bad. Share this post Link to post
FPiette 382 Posted April 22, 2021 Use a multimedia timer via the Win32 API timeSetEvent() or CreateTimerQueueTimer() function. 1 Share this post Link to post
Anders Melander 1782 Posted April 22, 2021 If a regular WM_PAINT handler will not update fast enough I can't see how a timer controlled paint will. What am I missing? Share this post Link to post
Yaron 53 Posted April 22, 2021 Just now, Anders Melander said: If a regular WM_PAINT handler will not update fast enough I can't see how a timer controlled paint will. What am I missing? The problem is not the frame-update speed (which on my PC is about 3ms), it's about how to time the call to update the screen within a specific time-frame and not in 16/32ms intervals. Share this post Link to post
Anders Melander 1782 Posted April 22, 2021 Okay, that makes better sense. Instead of updating at a fixed 120 Hz I think you'll want to sync your refresh to the monitors vertical retrace. I don't know how to do that but I found this: http://masm32.com/board/index.php?topic=4410.0 Share this post Link to post
Yaron 53 Posted April 22, 2021 1 minute ago, Anders Melander said: Okay, that makes better sense. Instead of updating at a fixed 120 Hz I think you'll want to sync your refresh to the monitors vertical retrace. I don't know how to do that but I found this: http://masm32.com/board/index.php?topic=4410.0 I don't want to block for v-sync, I don't care if there's tearing. I just want to get 60+fps screen updates without bogging down a CPU core. I'm looking at queue timers as FPiette mentioned (looks like multimedia timers were depreciated in favor of queue timers), if anyone has a sample using "CreateTimerQueueTimer" in Delphi (compatible with Delphi7), that would be cool. Share this post Link to post
Yaron 53 Posted April 22, 2021 I found a good resource at https://github.com/grijjy/DelphiTimerQueue Hopefully it will resolve my issue, the workaround I'm currently using is having multiple thread that keep trying to update the screen if at least 1ms passed since another thread triggered an update. Not really the best approach. Share this post Link to post
Anders Melander 1782 Posted April 22, 2021 3 minutes ago, Yaron said: I don't want to block for v-sync, I don't care if there's tearing. I just want to get 60+fps screen updates without bogging down a CPU core. Yes, I understood that but if you're concerned about not wasting resources, as you state, then there's no reason to refresh more often than what the monitor can handle. 1 Share this post Link to post
Yaron 53 Posted April 22, 2021 Just now, Anders Melander said: Yes, I understood that but if you're concerned about not wasting resources, as you state, then there's no reason to refresh more often than what the monitor can handle. I don't care about wasting resources while there's screen updates to generate, I'm concerned about wasting resources when doing nothing but waiting for the next update. Trying to work with v-sync is a pain, if you miss an update window there's serious judder. Share this post Link to post
FPiette 382 Posted April 22, 2021 I don't know what you draw on screen, but have you considered off-loading everything to the GPU? Much like a game. 1 Share this post Link to post
Yaron 53 Posted April 22, 2021 (edited) 7 minutes ago, FPiette said: I don't know what you draw on screen, but have you considered off-loading everything to the GPU? Much like a game. I can't use the GPU, it would mean rewriting A LOT of code and in my use-case I actually prefer the GPU to be free to do other things. Does anyone know if the callback function in CreateTimerQueueTimer runs within my app's main process thread or in a completely different thread? If it's in a different thread, how do I synchronize the call to make sure it's running in the main thread? Using critical sections or is there another mechanism I'm not thinking of? Edited April 22, 2021 by Yaron Share this post Link to post
FPiette 382 Posted April 22, 2021 33 minutes ago, Yaron said: Does anyone know if the callback function in CreateTimerQueueTimer runs within my app's main process thread or in a completely different thread? I don't know but it is easy to know: call GetCurrentThreadId (Windows api) from hte main thread and from the callback. 1 Share this post Link to post
Yaron 53 Posted April 22, 2021 According to the GitHub sample I linked above, "The stdcall [callback] procedure will be called for each interval for each and every timer in the queue. In other words, you can expect this event to be called by multiple threads and it needs to be completely thread safe". So this brings up my previous question, how can I synchronize the actions I want to take with the main thread?, otherwise my code will not be thread safe. Share this post Link to post
luebbe 26 Posted April 22, 2021 The grijjy timer queue works fine. I'm using it to draw on screen in regular intervals. You shouldn't allow VCL style changes while drawing, It got me into all kinds of trouble. Share this post Link to post
Yaron 53 Posted April 22, 2021 1 minute ago, luebbe said: The grijjy timer queue works fine. I'm using it to draw on screen in regular intervals. You shouldn't allow VCL style changes while drawing, It got me into all kinds of trouble. The code I'm calling is very thread-unsafe, I would have to completely rewrite several sub-systems to make it thread safe, I think it would be more beneficial if I could somehow instantly synchronize the code. Thing is, how does synchronize work, is it only triggered on a system tick? If so, I'm stuck at again at the 16ms tick timing and this whole approach is pointless unless I rewrite a lot of code. I discovered a few things regarding timeBeginPeriod : 1. From MSDN: "Prior to Windows 10, version 2004, this function affects a global Windows setting. For all processes Windows uses the lowest value (that is, highest resolution) requested by any process. Starting with Windows 10, version 2004, this function no longer affects global timer resolution. For processes which call this function, Windows uses the lowest value (that is, highest resolution) requested by any process. For processes which have not called this function, Windows does not guarantee a higher resolution than the default system resolution." 2. timeBeginPeriod doesn't work as advertised. If i use timeBeginPeriod(16), you'll get the usual 16ms timer accuracy, however, at least on my system (fully patched win10), calling timeBeginPeriod(15) is enough to set an actual timer accuracy of 1-2ms. So if I can't find a way to synchronize callbacks without changing periodic timer accuracy, I believe using timeBeginPeriod with tthread/Sleep/synchronize is the safest approach I can use. Share this post Link to post
Remy Lebeau 1393 Posted April 22, 2021 (edited) 2 hours ago, Yaron said: Thing is, how does synchronize work, is it only triggered on a system tick? No, it is worse than that. TThread.Synchronize() puts the specified method pointer into a global thread-safe queue, along with a waitable event, then posts a signal to the main UI thread letting it know the queue is pending, and then waits on the queued event to be signaled. The main UI thread periodically iterates the queue (whenever it receives the posted signal, as well as whenever the main message loop enters an idle state, or user code calls Classes.CheckSynchronize() directly). All of the queued methods are called in order, and their events are signaled to unblock their waiting threads. Edited April 22, 2021 by Remy Lebeau Share this post Link to post