SDL2: Replace time

--HG--
branch : sdl2
hg/feature/sdl2
kaetemi 11 years ago
parent bc449abb20
commit 220d58442c

@ -78,22 +78,13 @@ public:
/** Return the local time in milliseconds.
* Use it only to measure time difference, the absolute value does not mean anything.
* On Unix, getLocalTime() will try to use the Monotonic Clock if available, otherwise
* the value can jump backwards if the system time is changed by a user or a NTP time sync process.
* The value is different on 2 different computers; use the CUniTime class to get a universal
* time that is the same on all computers.
* \warning On Win32, the value is on 32 bits only, and uses the low-res timer unless probeTimerInfo was called and a high resolution timer can be used. It wraps around to 0 every about 49.71 days.
*/
static TTime getLocalTime();
/** Return the time in processor ticks. Use it for profile purpose.
* If the performance time is not supported on this hardware, it returns 0.
* \warning On a multiprocessor system, the value returned by each processor may
* be different. The only way to workaround this is to set a processor affinity
* to the measured thread.
* \warning The speed of tick increase can vary (especially on laptops or CPUs with
* power management), so profiling several times and computing the average could be
* a wise choice.
* If the performance time is not supported on this hardware, it returns getLocalTime().
*/
static TTicks getPerformanceTime ();

@ -20,19 +20,14 @@
#include "nel/misc/sstring.h"
#include "nel/misc/thread.h"
#include <SDL_timer.h>
#include <SDL_atomic.h>
#ifdef NL_OS_WINDOWS
# ifndef NL_COMP_MINGW
# define NOMINMAX
# endif
# include <windows.h>
#elif defined (NL_OS_UNIX)
# include <sys/time.h>
# include <unistd.h>
#endif
#ifdef NL_OS_MAC
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#ifdef DEBUG_NEW
@ -42,131 +37,22 @@
namespace NLMISC
{
namespace {
#ifdef NL_OS_WINDOWS
bool a_HaveQueryPerformance = false;
LARGE_INTEGER a_QueryPerformanceFrequency;
#endif
#ifdef NL_OS_UNIX
# if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
# if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0)
# define NL_MONOTONIC_CLOCK
# endif
# endif
# ifdef NL_MONOTONIC_CLOCK
bool a_CheckedMonotonicClock = false;
bool a_HasMonotonicClock = false;
uint64 a_MonotonicClockFrequency = 0;
uint64 a_MonotonicClockResolutionNs = 0;
bool hasMonotonicClock()
void CTime::probeTimerInfo(CTime::CTimerInfo &result)
{
if (!a_CheckedMonotonicClock)
{
/* Initialize the local time engine.
* On Unix, this method will find out if the Monotonic Clock is supported
* (seems supported by kernel 2.6, not by kernel 2.4). See getLocalTime().
*/
struct timespec tv;
if ((clock_gettime( CLOCK_MONOTONIC, &tv ) == 0) &&
(clock_getres( CLOCK_MONOTONIC, &tv ) == 0))
{
// nldebug( "Monotonic local time supported (resolution %.6f ms)", ((float)tv.tv_sec)*1000.0f + ((float)tv.tv_nsec)/1000000.0f );
result.HighPrecisionResolution = SDL_GetPerformanceFrequency();
result.IsHighPrecisionAvailable = (result.HighPrecisionResolution > 1000);
if (tv.tv_sec > 0)
{
nlwarning("Monotonic clock not ok, resolution > 1s");
a_HasMonotonicClock = false;
}
else
{
uint64 nsPerTick = tv.tv_nsec;
uint64 nsPerSec = 1000000000L;
uint64 tickPerSec = nsPerSec / nsPerTick;
a_MonotonicClockFrequency = tickPerSec;
a_MonotonicClockResolutionNs = nsPerTick;
a_HasMonotonicClock = true;
}
}
else
{
a_HasMonotonicClock = false;
}
a_CheckedMonotonicClock = true;
if (result.HighPrecisionResolution == 14318180)
{
nldebug("Detected known HPET era timer frequency");
}
return a_HasMonotonicClock;
}
# endif
#endif
}
void CTime::probeTimerInfo(CTime::CTimerInfo &result)
{
breakable
if (result.HighPrecisionResolution == 3579545)
{
#ifdef NL_OS_WINDOWS
LARGE_INTEGER winPerfFreq;
LARGE_INTEGER winPerfCount;
DWORD lowResTime;
if (!QueryPerformanceFrequency(&winPerfFreq))
{
nldebug("Cannot query performance frequency");
result.IsHighPrecisionAvailable = false;
}
else
{
result.HighPrecisionResolution = winPerfFreq.QuadPart;
}
if (winPerfFreq.QuadPart == 1000)
{
nldebug("Higher precision timer not available, OS defaulted to GetTickCount");
result.IsHighPrecisionAvailable = false;
}
if (!QueryPerformanceCounter(&winPerfCount))
{
nldebug("Cannot query performance counter");
result.IsHighPrecisionAvailable = false;
result.HighPrecisionResolution = 1000;
}
a_HaveQueryPerformance = result.IsHighPrecisionAvailable;
a_QueryPerformanceFrequency.QuadPart = winPerfFreq.QuadPart;
if (!result.IsHighPrecisionAvailable)
{
lowResTime = timeGetTime();
}
#else
// Other platforms are awesome. Generic implementation for now.
TTime localTime = getLocalTime();
result.IsHighPrecisionAvailable = true;
result.HighPrecisionResolution = 0;
# ifdef NL_MONOTONIC_CLOCK
timespec monoClock;
if (hasMonotonicClock())
{
clock_gettime(CLOCK_MONOTONIC, &monoClock);
result.HighPrecisionResolution = a_MonotonicClockFrequency;
}
else
{
nldebug("Monotonic clock not available");
}
# endif
#endif
if (result.HighPrecisionResolution == 14318180)
{
nldebug("Detected known HPET era timer frequency");
}
if (result.HighPrecisionResolution == 3579545)
{
nldebug("Detected known AHCI era timer frequency");
}
if (result.HighPrecisionResolution == 1193182)
{
nldebug("Detected known i8253/i8254 era timer frequency");
}
nldebug("Detected known AHCI era timer frequency");
}
if (result.HighPrecisionResolution == 1193182)
{
nldebug("Detected known i8253/i8254 era timer frequency");
}
}
@ -174,7 +60,7 @@ void CTime::probeTimerInfo(CTime::CTimerInfo &result)
* coordinated universal time, according to the system clock.
* This values is the same on all computer if computers are synchronized (with NTP for example).
*/
uint32 CTime::getSecondsSince1970 ()
uint32 CTime::getSecondsSince1970()
{
return uint32(time(NULL));
}
@ -195,192 +81,60 @@ uint32 CTime::getSecondsSince1970 ()
// return nl_mktime(timeinfo);
//}
static uint32 s_LastTicks = 0;
static sint64 s_LocalTime = 0;
SDL_SpinLock s_TimeLock = 0;
/* Return the local time in milliseconds.
* Use it only to measure time difference, the absolute value does not mean anything.
* On Unix, getLocalTime() will try to use the Monotonic Clock if available, otherwise
* the value can jump backwards if the system time is changed by a user or a NTP time sync process.
* The value is different on 2 different computers; use the CUniTime class to get a universal
* time that is the same on all computers.
* \warning On Win32, the value is on 32 bits only. It wraps around to 0 every about 49.71 days.
*/
TTime CTime::getLocalTime ()
TTime CTime::getLocalTime()
{
#ifdef NL_OS_WINDOWS
//static bool initdone = false;
//static bool byperfcounter;
// Initialization
//if ( ! initdone )
//{
//byperfcounter = (getPerformanceTime() != 0);
//initdone = true;
//}
/* Retrieve time is ms
* Why do we prefer getPerformanceTime() to timeGetTime() ? Because on one dual-processor Win2k
* PC, we have noticed that timeGetTime() slows down when the client is running !!!
*/
/* Now we have noticed that on all WinNT4 PC the getPerformanceTime can give us value that
* are less than previous
*/
//if ( byperfcounter )
//{
// return (TTime)(ticksToSecond(getPerformanceTime()) * 1000.0f);
//}
//else
//{
// This is not affected by system time changes. But it cycles every 49 days.
// return timeGetTime(); // Only this was left active before it was commented.
//}
/*
* The above is no longer relevant.
*/
if (a_HaveQueryPerformance)
// NOTE: This function is managed to wrap at the TTime boundary
sint64 localTime;
if (SDL_AtomicTryLock(&s_TimeLock))
{
// On a (fast) 15MHz timer this rolls over after 7000 days.
// If my calculations are right.
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
counter.QuadPart *= (LONGLONG)1000L;
counter.QuadPart /= a_QueryPerformanceFrequency.QuadPart;
return counter.QuadPart;
// Only one thread can check the clock at once. Please cache results of getLocalTime where possible
uint32 ticks = SDL_GetTicks();
uint32 delta = ticks - s_LastTicks;
localTime = s_LocalTime;
if (delta)
{
s_LastTicks = ticks;
if (delta < (15 * 60 * 1000)) // Time difference since last call must be less than 15 minutes
{
localTime += (sint64)delta;
s_LocalTime = localTime;
}
}
SDL_AtomicUnlock(&s_TimeLock);
}
else
{
// Use default reliable low resolution timer.
return timeGetTime();
}
#elif defined (NL_OS_UNIX)
#ifdef NL_MONOTONIC_CLOCK
if (hasMonotonicClock())
{
timespec tv;
// This is not affected by system time changes.
if ( clock_gettime( CLOCK_MONOTONIC, &tv ) != 0 )
nlerror ("Can't get clock time again");
return (TTime)tv.tv_sec * (TTime)1000 + (TTime)((tv.tv_nsec/*+500*/) / 1000000);
// If another thread is checking the clock simultaneously, return it's result
SDL_AtomicLock(&s_TimeLock);
localTime = s_LocalTime;
SDL_AtomicUnlock(&s_TimeLock);
}
#endif
// This is affected by system time changes.
struct timeval tv;
if ( gettimeofday( &tv, NULL) != 0 )
nlerror ("Can't get time of day");
return (TTime)tv.tv_sec * (TTime)1000 + (TTime)tv.tv_usec / (TTime)1000;
#endif
return localTime;
}
/* Return the time in processor ticks. Use it for profile purpose.
* If the performance time is not supported on this hardware, it returns 0.
* \warning On a multiprocessor system, the value returned by each processor may
* be different. The only way to workaround this is to set a processor affinity
* to the measured thread.
* \warning The speed of tick increase can vary (especially on laptops or CPUs with
* power management), so profiling several times and computing the average could be
* a wise choice.
* If the performance time is not supported on this hardware, it returns getLocalTime().
*/
TTicks CTime::getPerformanceTime ()
TTicks CTime::getPerformanceTime()
{
#ifdef NL_OS_WINDOWS
LARGE_INTEGER ret;
if (QueryPerformanceCounter (&ret))
return ret.QuadPart;
else
return 0;
#elif defined(NL_OS_MAC)
return mach_absolute_time();
#else
#if defined(HAVE_X86_64)
uint64 hi, lo;
__asm__ volatile (".byte 0x0f, 0x31" : "=a" (lo), "=d" (hi));
return (hi << 32) | (lo & 0xffffffff);
#elif defined(HAVE_X86) and !defined(NL_OS_MAC)
uint64 x;
// RDTSC - Read time-stamp counter into EDX:EAX.
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
#else // HAVE_X86
static bool firstWarn = true;
if (firstWarn)
{
nlwarning ("TTicks CTime::getPerformanceTime () is not implemented for your processor, returning 0");
firstWarn = false;
}
return 0;
#endif // HAVE_X86
#endif // NL_OS_WINDOWS
return SDL_GetPerformanceCounter();
}
/*
#define GETTICKS(t) asm volatile ("push %%esi\n\t" "mov %0, %%esi" : : "r" (t)); \
asm volatile ("push %eax\n\t" "push %edx"); \
asm volatile ("rdtsc"); \
asm volatile ("movl %eax, (%esi)\n\t" "movl %edx, 4(%esi)"); \
asm volatile ("pop %edx\n\t" "pop %eax\n\t" "pop %esi");
*/
/* Convert a ticks count into second. If the performance time is not supported on this
* hardware, it returns 0.0.
*/
double CTime::ticksToSecond (TTicks ticks)
double CTime::ticksToSecond(TTicks ticks)
{
#ifdef NL_OS_WINDOWS
LARGE_INTEGER ret;
if (QueryPerformanceFrequency(&ret))
{
return (double)(sint64)ticks/(double)ret.QuadPart;
}
else
#elif defined(NL_OS_MAC)
{
static double factor = 0.0;
if (factor == 0.0)
{
mach_timebase_info_data_t tbInfo;
mach_timebase_info(&tbInfo);
factor = 1000000000.0 * (double)tbInfo.numer / (double)tbInfo.denom;
}
return double(ticks / factor);
}
#endif // NL_OS_WINDOWS
{
static bool benchFrequency = true;
static sint64 freq = 0;
if (benchFrequency)
{
// try to have an estimation of the cpu frequency
TTicks tickBefore = getPerformanceTime ();
TTicks tickAfter = tickBefore;
TTime timeBefore = getLocalTime ();
TTime timeAfter = timeBefore;
for(;;)
{
if (timeAfter - timeBefore > 1000)
break;
timeAfter = getLocalTime ();
tickAfter = getPerformanceTime ();
}
TTime timeDelta = timeAfter - timeBefore;
TTicks tickDelta = tickAfter - tickBefore;
freq = 1000 * tickDelta / timeDelta;
benchFrequency = false;
}
return (double)(sint64)ticks/(double)freq;
}
return (double)(sint64)ticks / (double)(sint64)SDL_GetPerformanceFrequency();
}

Loading…
Cancel
Save