Game Timing Model

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
Nessie

Game Timing Model

Post by Nessie »

I had posted the other day about the ITimer class and some misc. thoughts. After looking at the class a bit more, I noticed that getTime() requeries the highPerformanceTimer for every call.

I wouldn't classify this exactly as a bug, but I'm not sure it's a good idea for a couple of reasons.

The most important issue is that I'd consider a game a simulation of sorts. Each render frame should ideally be a snapshot from a specific point in time. However, the current timing model in the game will cause animating models (etc) to be rendered at slightly different points in time.

Yes, kind of nit-picky, but this might get to be more of a problem if/when I being adding networking to my game. Also, if nothing else, calculating the time for the current render frame once...and then returning this value whenever it's requested during this current render frame...is just more efficient.

Here's some code I'm using in os.cpp, which also supports globally scaling the timing system:

Code: Select all

// A few local vars to handle the needs of the timing model
static BOOL HighPerformanceTimerSupport = FALSE;
static LARGE_INTEGER HighPerformanceFreq;
static union
{
	LARGE_INTEGER LastTime;		// for high performance timer
	DWORD		  dwLastTime;	// for lower resolution timer
};
static u32 CurrentTime;
static f32 TimeScale;
static f32 FrameTime;


void Timer::initTimer()
{
	HighPerformanceTimerSupport = QueryPerformanceFrequency(&HighPerformanceFreq);
	if (HighPerformanceTimerSupport)
	{
		QueryPerformanceCounter(&LastTime);
	}
	else
	{
		dwLastTime = GetTickCount();
	}

	CurrentTime = 0;
	TimeScale = 1.0f;
	FrameTime = 0.0f;
}

void Timer::advanceTime()
{
	u32 advance;

	if (HighPerformanceTimerSupport)
	{
		LARGE_INTEGER nTime;
		QueryPerformanceCounter(&nTime);

		advance = u32((nTime.QuadPart-LastTime.QuadPart) * 1000 / HighPerformanceFreq.QuadPart);
		LastTime.QuadPart = nTime.QuadPart;
	}
	else
	{
		advance = GetTickCount() - dwLastTime;
		dwLastTime = GetTickCount();
	}

	advance = u32(advance * TimeScale);

	// NOTE: this may or may not be a good thing to do, but on a fast system with a low timescale,
	//	truncation may cause situations where the amount to advance per frame is zero.
	//	In other words time would appear to be frozen even though it shouldn't be.
	// NOTE: the other hack was added because hitting breakpoints in the game would cause huge time
	//	jumps because time spent in the debugger still counts.
	if ( advance <= 0 )
	{
		if (TimeScale != 0.0f )
		{
			advance = 1;
		}
	}
	else if ( advance > 200 )
	{
		advance = 200;
	}

	CurrentTime += advance;
	FrameTime = advance / 1000.0f;
}

u32 Timer::getTime()
{
	return CurrentTime;
}

f32 Timer::getFrameTime()
{
	return FrameTime;
}

void Timer::setTimeScale( f32 timeScale )
{
	TimeScale = timeScale;
}

f32 Timer::getTimeScale()
{
	return TimeScale;
}
Because I wanted the timing to be auto-updated at the start of each render frame (the start of the current snapshot in time), I added this to CVideoNull.cpp:

Code: Select all

bool CVideoNull::beginScene(bool backBuffer, bool zBuffer, SColor color)
{
	// The rendering system (for animating meshes, etc) renders the scene in a
	//	snapshot type fashion.  For this reason, advancing the time just prior
	//	to starting rendering ensures that the current frame we see drawn is
	//	representative of the time the snapshot was taken (ie, now)
	os::Timer::advanceTime();
	PrimitivesDrawn = 0;
	return true;
}
Of course, a side effect of this is that the FramesPerSecond counter is busted, but that is fixable.

One other point is that there may still be reasonable uses for the old class, as in, simple performance monitoring of functions etc. I suppose that functionality could just be rolled up into the modified version of the ITimer class.
niko
Site Admin
Posts: 1759
Joined: Fri Aug 22, 2003 4:44 am
Location: Vienna, Austria
Contact:

Post by niko »

You're right, I've noticed this problem too. Thanks for this post which made me not to forget this. :)
Post Reply