timer: Added SDL_GetTicks64(), for a timer that doesn't wrap every ~49 days.

Note that this removes the timeGetTime() fallback on Windows; it is a
32-bit counter and SDL2 should never choose to use it, as it only is needed
if QueryPerformanceCounter() isn't available, and QPC is _always_ available
on Windows XP and later.

OS/2 has a similar situation, but since it isn't clear to me that similar
promises can be made about DosTmrQueryTime() even in modern times, I decided
to leave the fallback in, with some heroic measures added to try to provide a
true 64-bit tick counter despite the 49-day wraparound. That approach can
migrate to Windows too, if we discover some truly broken install that doesn't
have QPC and still depends on timeGetTime().

Fixes #4870.
This commit is contained in:
Ryan C. Gordon
2021-10-23 15:00:31 -04:00
parent 0d631c741f
commit 99c9727dc0
11 changed files with 128 additions and 97 deletions

View File

@@ -40,25 +40,31 @@
typedef unsigned long long ULLONG;
static ULONG ulTmrFreq = 0;
static ULLONG ullTmrStart;
static ULLONG ullTmrStart = 0;
/* 32-bit counter fallback...not used if DosTmrQuery* is functioning. */
static ULONG ulPrevTmr = 0;
static Uint64 ui64TmrStartOffset = 0; /* Used if 32-bit counter overflows. */
void
SDL_TicksInit(void)
{
ULONG ulRC;
ulRC = DosTmrQueryFreq(&ulTmrFreq);
ULONG ulTmrStart; /* for 32-bit fallback. */
ULONG ulRC = DosTmrQueryFreq(&ulTmrFreq);
if (ulRC != NO_ERROR) {
debug_os2("DosTmrQueryFreq() failed, rc = %u", ulRC);
} else {
ulRC = DosTmrQueryTime((PQWORD)&ullTmrStart);
if (ulRC == NO_ERROR)
if (ulRC == NO_ERROR) {
return;
}
debug_os2("DosTmrQueryTime() failed, rc = %u", ulRC);
}
ulTmrFreq = 0; /* Error - use DosQuerySysInfo() for timer. */
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, (PULONG)&ullTmrStart, sizeof(ULONG));
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &ulTmrStart, sizeof (ULONG));
ullTmrStart = (ULLONG) ulTmrStart;
ulPrevTmr = ulTmrStart;
}
void
@@ -66,24 +72,32 @@ SDL_TicksQuit(void)
{
}
Uint32
SDL_GetTicks(void)
Uint64
SDL_GetTicks64(void)
{
ULONG ulResult;
ULLONG ullTmrNow;
Uint64 ui64Result;
ULLONG ullTmrNow;
if (ulTmrFreq == 0) /* Was not initialized. */
if (ulTmrFreq == 0) { /* Was not initialized. */
SDL_TicksInit();
}
if (ulTmrFreq != 0) {
DosTmrQueryTime((PQWORD)&ullTmrNow);
ulResult = (ullTmrNow - ullTmrStart) * 1000 / ulTmrFreq;
ui64Result = (ullTmrNow - ullTmrStart) * 1000 / ulTmrFreq;
} else {
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, (PULONG)&ullTmrNow, sizeof(ULONG));
ulResult = (ULONG)ullTmrNow - (ULONG)ullTmrStart;
ULONG ulTmrNow;
DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &ulTmrNow, sizeof (ULONG));
if ( ((ULLONG) ulTmrNow) < ulPrevTmr ) { /* have we overflowed the 32-bit counter since last check? */
/* Note that this is incorrect if you went more than ~98 days between calls to SDL_GetTicks64(). */
/* One could query QSV_TIME_HIGH and QSV_TIME_LOW here to verify, but it's probably not worth it. */
ui64TmrStartOffset += 0xFFFFFFFF;
}
ui64Result = (((Uint64) ulTmrNow) - ullTmrStart) + ui64TmrStartOffset;
ulPrevTmr = ulTmrNow;
}
return ulResult;
return ui64Result;
}
Uint64
@@ -92,7 +106,7 @@ SDL_GetPerformanceCounter(void)
QWORD qwTmrNow;
if (ulTmrFreq == 0 || (DosTmrQueryTime(&qwTmrNow) != NO_ERROR))
return SDL_GetTicks();
return SDL_GetTicks64();
return *((Uint64 *)&qwTmrNow);
}