Thursday, May 1, 2008

Tutorial 3 : Timing and FPS

A timing class would help in creating a constant experience over multiple systems. We can also use our timing class to calculate the framerate of our demos.

The timer class will keep track of the total running time, time elapsed between Update calls and the application's framerate.

//  Timer.cpp

cTimer::cTimer()
: m_iCurrentTime(0)
, m_iLastTime(0)
, m_iLastFPSUpdate(0)
, m_iNumFrames(0)
, m_fFPS(0.0f)
, m_fRunningTime(0.0f)
, m_fTimeElapsed(0.0f)
, m_bTimerStopped(true)
{
QueryPerformanceFrequency( (LARGE_INTEGER *)&m_iTicksPerSecond );

m_iFPSUpdateInterval = m_iTicksPerSecond >> 1;
}

void cTimer::Start()
{
if ( !m_bTimerStopped )
{
// Already started
return;
}
QueryPerformanceCounter( (LARGE_INTEGER *)&m_iLastTime );
m_bTimerStopped = false;
}

void cTimer::Stop()
{
if ( m_bTimerStopped )
{
// Already stopped
return;
}
INT64 iStopTime = 0;
QueryPerformanceCounter( (LARGE_INTEGER *)&iStopTime );
m_fRunningTime += (float)(iStopTime - m_iLastTime) / (float)m_iTicksPerSecond;
m_bTimerStopped = true;
}

void cTimer::Update()
{
if ( m_bTimerStopped )
{
return;
}

// Get the current time
QueryPerformanceCounter( (LARGE_INTEGER *)&m_iCurrentTime );

m_fTimeElapsed = (float)(m_iCurrentTime - m_iLastTime) / (float)m_iTicksPerSecond;
m_fRunningTime += m_fTimeElapsed;

// Update FPS
m_iNumFrames++;
if ( m_iCurrentTime - m_iLastFPSUpdate >= m_iFPSUpdateInterval )
{
float fCurrentTime = (float)m_iCurrentTime / (float)m_iTicksPerSecond;
float fLastTime = (float)m_iLastFPSUpdate / (float)m_iTicksPerSecond;
m_fFPS = (float)m_iNumFrames / (fCurrentTime - fLastTime);

m_iLastFPSUpdate = m_iCurrentTime;
m_iNumFrames = 0;
}
m_iLastTime = m_iCurrentTime;
}

To create the timer we use the functions QueryPerformanceFrequency and QueryPerformanceCounter. The first function determines how many times per second the system counter fires. The second counter determines what the counter value is currently set to.
Most of the code is self-explanatory.

To calculate the elapsed time since the last update, we need to get the difference between the current counter value and the counter value from the last update.To convert the result into seconds, we divide it by the number of ticks the counter fires per second. The total running time is simply updated each frame with the current elapsed time value.

Calculating the frames per second involves a few steps. First, to prevent the FPS from updating every frame, we need to store an FPS update interval. If we updated the FPS every frame, it would change too fast for us to see any single value. The update interval is set to half the counter frequency. This means the timer will perform the FPS calculation every half second. Second, we need a variable that counts the number of times the Update method is called. If we call Update every frame, it will represent the number of frames rendered. To calculate the FPS, we divide this frame counter by the time passed since the last time the FPS was calculated. Once the FPS is calculated, we reset the frame counter to 0 and repeat.

// MainWindow.cpp

void cMainWindow::OnRender()
{
HRESULT hr;

// update the game timer
m_pGameTimer->Update();

hr = cDXBase::GetInstance().BeginRender();
if (SUCCEEDED(hr))
{
m_pGameApp->Render(m_pGameTimer->GetElapsedTime());
cDXBase::GetInstance().EndRender(hr);
}
}

With cTimer implemented, we can update our CMainWindow class to integrate our new timer class. We’ll need to access the timer when we render frames. As a result, we need to update the Render method definition in the CBaseApp class to include a float that will hold the elapsed time from the timer.

Code

Binaries

No comments :