Use nanosleep for I_SleepDuration on *nix
most unix systems use something called a "high-resolution timer" which is a timer that ticks at high frequency (as the name implies). this timer is the same timer that is used by the wall clock, and as such, is able to achieve the same accuracy as the wall time, which is often on microsecond accuracy. this makes it an ideal solution for frame capping, since the CPU is actually sleeping while we're capping. on my system, when capping the game to 35 fps, SRB2's CPU usage goes down to around 20% during gameplay, in contrast to the previous solution which normally used more than 85% CPU.
the only catch is that it's not supported on Windows, and i also haven't been able to test that Mac OS is using the same technique for nanosleep (if someone can verify, please do!). because of that, Mac OS (for now, at least) and Windows will remain using the old frame capping logic.
...and for reference, the old solution...
...it's pretty damn accurate. not perfect, but you aren't going to notice such a small difference at 300 fps anyway.
Merge request reports
Activity
Let look at Debian man page about clock_nanosleep(2)
Standard C library (
_libc
_,
_-lc
_), since glibc 2.17
Before glibc 2.17, Real-time library (
_librt
_,
_-lrt
_)
So, we need GLIBC library for this function.
We do not use GLIBC on all Linux/GNU, so you may need add a check for this function.
So, we may run into a issue with alpine, which use musl as their C library
So, I do not think MacOS will have this, they do not use GLIBC
Now for FreeBSD, it look like it been there since 11.1
So, we need GLIBC library for this function.
no, we don't.
clock_nanosleep
is part of the POSIX standard: https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_nanosleep.htmlalso, musl has
clock_nanosleep
too: https://git.musl-libc.org/cgit/musl/tree/src/time/clock_nanosleep.ci wouldn't use a function that's non-standard, and considering that it was standardized in 2000, i highly doubt that Mac OS wouldn't it, either. the only concern i have is the accuracy on Mac OS, which remains to be tested.
Ahh, I see.
I have questions on how to handle errors from
clock_nanosleep
but looping onEINTR
will do.But for MacOS, It looks like the function exists in 10.12 and higher, (Released on September 20, 2016)
You will get this funny runtime error when it is ran on 10.11:
dyld: lazy symbol binding failed: Symbol not found: _clock_nanosleep
and the bad news, libSDL2's XCode project had set
MACOSX_DEPLOYMENT_TARGET
to10.11
We will break support for OS X El Capitan, but that OS is old, right?
I have questions on how to handle errors from
clock_nanosleep
but looping onEINTR
will do.no need to handle any other errors. this is what can be returned from
clock_nanosleep
(taken directly from manpages):EFAULT request or remain specified an invalid address. EINTR The sleep was interrupted by a signal handler; see signal(7). EINVAL The value in the tv_nsec field was not in the range [0, 999999999] or tv_sec was negative. EINVAL clockid was invalid. (CLOCK_THREAD_CPUTIME_ID is not a permitted value for clockid.) ENOTSUP The kernel does not support sleeping against this clockid.
EFAULT
can't happen since we're using a valid address,EINTR
is already handled,EINVAL
can't happen either since we're clamping to 999999999 using modulo and passing a constant to clockid, andENOTSUP
is highly unlikely since both Linux and FreeBSD fully supportCLOCK_MONOTONIC
, which is our target OSes for this PR at the moment.We will break support for OS X El Capitan, but that OS is old, right?
7 years was a long time ago, yes, but the question is just how many this is going to affect. i don't know how many Mac OS players SRB2 has, let alone how many that runs 10.11 or earlier. however, this is all under the assumption that
clock_nanosleep
will even work as intended on Mac OS, since if it doesn't, there's no point in discussing it further. so, let's just check if it even works before we make the choice on dropping Mac OS 10.11 or not.
mentioned in commit 3ba48ba4
mentioned in merge request !2242 (merged)
2278 struct timespec ts = { 2279 .tv_sec = duration / precision, 2280 .tv_nsec = duration * 1000000000 / precision % 1000000000, 2281 }; 2282 int status; 2283 do status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts); 2284 while (status == EINTR); 2285 #else 2286 UINT64 precision = I_GetPrecisePrecision(); 2287 INT32 sleepvalue = cv_sleep.value; 2288 UINT64 delaygranularity; 2289 precise_t cur; 2290 precise_t dest; 2291 2292 { 2293 double gran = round(((double)(precision / 1000) * sleepvalue * MIN_SLEEP_DURATION_MS)); Yeah, we noticed that in our vcpkg branch, see LoganAir/SRB2@f6124f39
mentioned in merge request !2583 (merged)