Accuracy of repeat_timeout on Windows vs Linux

67 views
Skip to first unread message

Lars Ruoff

unread,
Mar 25, 2018, 8:57:27 AM3/25/18
to fltkg...@googlegroups.com
Hello,

in my project, i'm using add_timeout/repeat_timeout to power my main loop in a game application that does some simple OpenGL rendering.
My objective is to have the loop running at 50 fps, meaning timeout values between frames of about 20 milisecs.
My problem is that while on a Linux host (Ubuntu 17.10 desktop, 64bit), this intervall is reasonably well respected, but i have significant delay on Windows 7 64bit.
On Windows, it seems as if the timer is called only at about half of the frequency specified.

FYI, my callback currently looks like this:

static void cb_update_tick(void* appWnd)
{
((MyAppWindow*) appWnd)->RunUpdate();
#ifdef WIN32
// Windows takes significantly more time here than specified.
Fl::repeat_timeout(0.01, cb_update_tick, appWnd);
#else
// The objective is to run this with 50 fps = one frame every 20 ms.
Fl::repeat_timeout(0.02, cb_update_tick, appWnd);
#endif
}

to obtain similar speeds on both systems.


Strangely, i am able to get about the desired result of 50 fps, when i set a timeout of 10ms (half of the needed value) on Windows like i do above.
Lowering the timeout further will not speed things up any more, so i seem to have hit a threshold.

FYI, at timeout values of about 100 milisecs and above, both Windows and Linux respect the given intervall.

I have read previous posts and comments on the Net that 20ms is probably touching the limit of accuracy of the used system clocks.

But note that i am not much concerned about accuray in the sense that the intervall between calls need not exactly be 20 ms between each frame. I'd be happy if the callback would be called every 20ms *on average* that is about 50 times per second.
Which it seems *is* possbile on Windows, but at the given lower (wrong) 10 ms timeout value.

Did anyone have had similar issues and got a solution?

Also, could there be a solution outside of FLTK?
I know i could use other high accuracy system clocks.
But how would i integrate them to a main loop where i render stuff in an FLTK OpenGL window? Is threading an issue here?

best regards,
Lars R.

imm

unread,
Mar 25, 2018, 10:59:10 AM3/25/18
to general fltk
The default process timer resolution under Windows is typically 1/60th
second, say 15.5ms or so roughly. Any timers you ask for therefore
tend to get quantized up to that, which is what you are seeing.

If you need better resolution, you need to set the tick for your apps
process to some finer grain - a lot of folks go for 1ms instead...

Not that due to the way the NT kernel handles its timers and coalesces
them across processes and so forth, changing the timer resolution for
your app may have knock on effects in other apps and processes (though
this is generally harmless...)

Off hand, I can't actually remember the API call that you use to query
the timer resolution, or to change it, but a bit of googling around
MSDN will find the suitable code!

Note that there is no fltk API for doing this, at it is very Windows
specific... The *nix based OS tend to have more "expected" timer
behaviours...

Lars Ruoff

unread,
Mar 25, 2018, 11:38:02 AM3/25/18
to fltkg...@googlegroups.com
Ok, thanks, that's a bit what i expected.
You're probably talking about the Win32 timeGetDevCaps/timeBeginPeriod functions.
I'm currently trying to use these, but with no effect. :(
(Setting it once to 1 ms before entering fltk main loop.)



--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Albrecht Schlosser

unread,
Mar 25, 2018, 1:48:04 PM3/25/18
to fltkg...@googlegroups.com
On 25.03.2018 17:38 Lars Ruoff wrote:

> You're probably talking about the Win32 timeGetDevCaps/timeBeginPeriod
> functions.
> https://msdn.microsoft.com/en-us/library/windows/desktop/dd743626(v=vs.85).aspx
> I'm currently trying to use these, but with no effect. :(
> (Setting it once to 1 ms before entering fltk main loop.)

I remember that I tried this as well at some time when I was debugging
the timer behavior in test/blocks.exe. I left some test code in the file
so you can take a look at it, but I don't think it's of much worth.

The MS docs are unclear at least: "You must match each call to
timeBeginPeriod with a call to timeEndPeriod, specifying the same
minimum resolution in both calls. An application can make multiple
timeBeginPeriod calls as long as each call is matched with a call to
timeEndPeriod.

This function affects a global Windows setting. Windows uses the lowest
value (that is, highest resolution) requested by any process."

As usual they don't say what's happening if you don't match each call of
timeBeginPeriod with a corresponding call to timeEndPeriod, or what
happens if/when the program exits w/o doing so...

I gave up.

K Dmitrij

unread,
Mar 25, 2018, 3:15:27 PM3/25/18
to fltkg...@googlegroups.com
Lars Ruoff wrote:

> Hello,

> in my project, i'm using add_timeout/repeat_timeout to power my main loop in a game application that does some simple OpenGL rendering.
> My objective is to have the loop running at 50 fps, meaning timeout values between frames of about 20 milisecs.
> My problem is that while on a Linux host (Ubuntu 17.10 desktop, 64bit), this intervall is reasonably well respected, but i have significant delay on Windows 7 64bit.
> On Windows, it seems as if the timer is called only at about half of the frequency specified.


You can use code below if you have c++11 support in your compiler
(and don't forget about the delta - time spent for calling functions
inside your repeat_callback, if you need exactly FPS)

Here is example only (and you need detached? thread for repeat_callback):

[CODE]
#include <chrono>
using system_clock_t = std::chrono::time_point<std::chrono::system_clock>;

using nanoseconds = std::chrono::nanoseconds;
using microseconds = std::chrono::microseconds;
using milliseconds = std::chrono::milliseconds;
using seconds = std::chrono::seconds;


template<typename T = std::chrono::milliseconds>
inline void sleep(uint32_t value) {
std::this_thread::sleep_for(T(value));
}


system_clock_t start = std::chrono::system_clock::now();
// here your code ... you can to calculate the delta this way too
system_clock_t end = std::chrono::system_clock::now();

int elapsed = std::chrono::duration_cast<milliseconds>(end-start).count();

if (elapsed >= 20) do_repeat_callback(); // to call the repeat
else sleep<milliseconds>(elapsed/4); // waiting
[/CODE]


PS: anyway, you will decide the design of that ))
but my opinion is: use FLTK functions with your the delta calculating
and taken into account its (value for sleep will be floating, not constant)


--
the best regards

Matthias Melcher

unread,
Mar 26, 2018, 10:27:19 AM3/26/18
to fltk.general

I would use some system timer that returns the elapsed time accurately. You can use that, just before calling Fl::add_timeout(), to calculate the exact delay that is required to get the callback when needed. That way, you become independent of the time spent within the call and of inaccuracies in the MSWindows timer.

Alternatively, if you need to sync an animation with audio, using the audio buffer callbacks to trigger an Fl::awake() works really well!

Lars Ruoff

unread,
Mar 26, 2018, 10:45:29 AM3/26/18
to fltkg...@googlegroups.com
Just for my understanding:
What would happen if from within a repeat_timeout callback, repeat_timeout is called to set a timer again, but processing further actions takes longer than the scheduled next timer interruption.
Like

void timer_callback(...)
{
// Set next timeout to 1ms from now
Fl::repeat_timeout(0.001, timer_callback, ...);
// Doing other stuff here takes longer than 1ms!
}

Will we get multiple occurrences (threads) of the function executing or will the next callback be called only once the function returns?
(Or not called at all because it has missed its timeslot?)


On Mon, Mar 26, 2018 at 4:27 PM, 'Matthias Melcher' via fltk.general <fltkg...@googlegroups.com> wrote:

I would use some system timer that returns the elapsed time accurately. You can use that, just before calling Fl::add_timeout(), to calculate the exact delay that is required to get the callback when needed. That way, you become independent of the time spent within the call and of inaccuracies in the MSWindows timer.

Alternatively, if you need to sync an animation with audio, using the audio buffer callbacks to trigger an Fl::awake() works really well!

--

Greg Ercolano

unread,
Mar 26, 2018, 12:23:56 PM3/26/18
to fltkg...@googlegroups.com
On 03/26/18 07:45, Lars Ruoff wrote:
> Just for my understanding:
> What would happen if from within a repeat_timeout callback, repeat_timeout is called to set a timer again, but processing further actions takes longer than the scheduled next timer interruption.

I'm thinking what matt means is using the more accurate get elapsed time
function in your callback to determine the current frame to display, rather
than counting on the callback count to show the next consecutive frame.

You should really do something like that anyway, so if the cpu suddenly
does something high load for longer than a few frames, your playback
jumps to the right frame to stay in sync with realtime.

If the repeat timer callback is 1/60th sec as Ian says, that's a little
faster than you need (1/50th), then at worst you might get two callbacks
for the same frame, so you'd simply ignore that callback and wait for
the next to show the next frame.

Or avoid timeouts, and use threads to do more accurate timing.
One technique I can think of offhand: doing a Sleep()/wait
on the "more accurate elapsed timer" (whatever it is) to wait
for the precise time edges you want, then have the thread trigger
an event in the main FLTK loop to show the new frame.
Some pseudo code for that child thread:

float fps = 50.0;
float samptime = 1 / (fps * 3); // shorter sample time (e.g. 1/(50*3) == .0066
while ( 1 ) {
Sleep(secwait * 1000); // wait short 'sample time' .0066secs, or 6.6 millisecs
TimeVal now = SomeAccurateTimeFunction();
TimeVal nxtfrm = (some math to calculate nearest 1/50th sec);
while ( SomeAccurateTimeFunction() < nxtfrm ) { Sleep(1); } // short loop to find accurate time
TriggerFltkEvent();
}

..or something like that. This way you can more accurately find
the 'leading edge' for your display-frame callback. Perhaps there's
a more accurate way to sleep at a higher time resolution than Sleep().

Greg Ercolano

unread,
Mar 26, 2018, 12:38:08 PM3/26/18
to fltkg...@googlegroups.com
Also, I'd agree with Matt on syncing frames to audio, esp. if you're
doing sound sync. Audio playback involves accurate hardware to feed out
the audio accurately, so locking your visual to the current audio going
out the speaker is a good way to stay in sync.

Even in a no-sound situation, you can simply play all zeroes to the
sound hardware, just so you can time lock to it, instead of the system timer.

Back when I used to write image playback tools (early 90's on SGI's),
I time locked to audio.. worked great. And you could slew around the
audio, and the visuals would never loose sync. And you could cheat the
frame rate a little, to force sync to be dead accurate, in case your
graphics hardware was always a frame or two behind the audio.

Matthias Melcher

unread,
Mar 26, 2018, 4:21:58 PM3/26/18
to fltkg...@googlegroups.com


Am Montag, 26. März 2018 18:23:56 UTC+2 schrieb Greg Ercolano:
On 03/26/18 07:45, Lars Ruoff wrote:
> Just for my understanding:
> What would happen if from within a repeat_timeout callback, repeat_timeout is called to set a timer again, but processing further actions takes longer than the scheduled next timer interruption.

Timer events are queued. That means that they are put like balls in a pipe. The first one that you put in is the first one that comes out. Events can happen at any time (keyboard, mouse move, network data, expired timers), but they stay in that pipe until you return form the timeout callback, or any other callback for that matter.

Here is some pseudo code for what I was talking about.

start:
double baseTime = time_now();
set_timeout(timeout_cb, 0.1);
Fl::run()

timeout_cb()
{
  ... do some stuff every 0.1 seconds
  currentTime = time_now();
  timeSpent = currentTime - baseTime;
  timeToNextFrame = 0.1-timeSpent;
  if (timeToNextFrame<0.0) ... // your timeout took longer than allowed, fix baseTime and timeToNextFrame.
  set_timeout(timeout_cb, timeToNextFrame);
  baseTime += 0.1;
}

Also, Lars' last post may solve this, too.


Lars Ruoff

unread,
Mar 27, 2018, 3:05:00 AM3/27/18
to fltkg...@googlegroups.com
Thanks,
i understand what you mean.
Anyway, currently i don't get the requested resolution even when using timerBeginPeriod.
I also tried using this tool: [https://vvvv.org/contribution/windows-system-timer-tool] to set the accuracy from within another process.
The timer accuracy seems to be set correctly, but doesnt have any effect on the Fl::repeat_timeout accuracy. :(

Reply all
Reply to author
Forward
0 new messages