Timing and Frames Per Second

  CU_Timing.zip (27.5 KiB, 3,609 hits)

If you tried running the Transformation Tutorial on different computers, or if you resized the window, the objects may have moved at different speeds. This is because faster computers can process more game loops than slower computers. We can prevent this by updating our game based on how much time has passed since we rendered the last frame. This will create a constant experience over multiple systems. We can also use our timing information to calculate the framerate of our demos, which gives us a good performance rating. To manage all the timing functionality, we’ll create a new class, CTimer. The calculations in CTimer use the high performance hardware counter found in most modern computers. Older computers may not support the high performance counter but I’m assuming that the people reading this tutorial have a fairly decent computer (heck, mines from 2001). If you want to support older computers, you would want to use the Windows timer function, timeGetTime(), if there was no hardware support. However, I don’t do that in this tutorial.

#ifndef CTIMER_H
#define CTIMER_H
#include "stdafx.h" 

class CTimer
{
public:
    CTimer();
    void Start();
    void Stop();
    void Update(); 

    BOOL IsStopped() { return m_timerStopped; }
    float GetFPS() { return m_fps; }
    float GetRunningTime() { return m_runningTime; }
    float GetElapsedTime() { return m_timerStopped ? 0.0f : m_timeElapsed; } 

private:
    INT64 m_ticksPerSecond;
    INT64 m_currentTime;
    INT64 m_lastTime;
    INT64 m_lastFPSUpdate;
    INT64 m_FPSUpdateInterval;
    UINT m_numFrames;
    float m_runningTime;
    float m_timeElapsed;
    float m_fps;
    BOOL m_timerStopped;
};
#endif

The CTimer class has methods to Start, Stop, and Update to the counter. CTimer will also keep track of the total running time, the time elapsed between Update calls, and the application’s framerate. Some of the variables are of type INT64 because we’ll be needing 64 bits for those values.

#include “..\include\stdafx.h”
#include “..\include\CTimer.h”

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Default constructor.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
CTimer::CTimer() Toggle

{

QueryPerformanceFrequency( (LARGE_INTEGER *)&m_ticksPerSecond );

m_currentTime = m_lastTime = m_lastFPSUpdate = 0;
m_numFrames = 0;
m_runningTime = m_timeElapsed = m_fps = 0.0f;
m_FPSUpdateInterval = m_ticksPerSecond >> 1;
m_timerStopped = TRUE;

}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Starts the timer.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CTimer::Start() Toggle

{

if ( !m_timerStopped )
{

// Already started
return;

}
QueryPerformanceCounter( (LARGE_INTEGER *)&m_lastTime );
m_timerStopped = FALSE;

}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Stops the timer.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CTimer::Stop() Toggle

{

if ( m_timerStopped )
{

// Already stopped
return;

}
INT64 stopTime = 0;
QueryPerformanceCounter( (LARGE_INTEGER *)&stopTime );
m_runningTime += (float)(stopTime – m_lastTime) / (float)m_ticksPerSecond;
m_timerStopped = TRUE;

}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Updates the timer. Calculates the time elapsed since the last Update call.
Updates the frames per second and updates the total running time.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CTimer::Update() Toggle

{

if ( m_timerStopped )
{

return;

}

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

m_timeElapsed = (float)(m_currentTime – m_lastTime) / (float)m_ticksPerSecond;
m_runningTime += m_timeElapsed;

// Update FPS
m_numFrames++;
if ( m_currentTime – m_lastFPSUpdate >= m_FPSUpdateInterval )
{

float currentTime = (float)m_currentTime / (float)m_ticksPerSecond;
float lastTime = (float)m_lastFPSUpdate / (float)m_ticksPerSecond;
m_fps = (float)m_numFrames / (currentTime – lastTime);

m_lastFPSUpdate = m_currentTime;
m_numFrames = 0;

}

m_lastTime = m_currentTime;

}

To create our timer, we will use a couple of Windows methods: QueryPerformanceFrequency and QueryPerformanceCounter. The first function determines how many times per second your system’s counter fires. We need this value to calculate all of our timing values such as the time passed between frames and the frames per second. The second function determines what the counter’s value is currently set to.

The CTimer class include methods to Start, Stop, and Update the counter. Most of the code is pretty 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.

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Updates the current frame.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CFramework::OnUpdateFrame()
{

if ( m_pTimer != NULL )
{

m_pTimer->Update();

}
if ( m_pGameApp != NULL && m_pGraphics != NULL && m_pTimer != NULL )
{

m_pGameApp->OnUpdateFrame( m_pGraphics->GetDevice(), m_pTimer->GetElapsedTime() );

}

}

With CTimer implemented, we can update our CFramework class to integrate our new timer class. We’ll need to access the timer when we update frames and when we render frames. As a result, we need to update the OnUpdateFrame and OnRenderFrame method definitions in the CBaseApp class to include a float that will hold the elapsed time from_ the timer. Shown above is the updated OnUpdateFrame method of the CFramework class. This method updates the timer and then calls the OnUpdateFrame method of its member CBaseApp instance. With the elapsed time passed through, our application-specific class, CGameApp, will be able to take advantage of this value. The OnRenderFrame is updated in much the same way.

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Updates the current frame.
Parameters:
[in] pDevice – Pointer to a DIRECT3DDEVICE9 instance
[in] elapsedTime – Time elapsed since last frame
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CGameApp::OnUpdateFrame( LPDIRECT3DDEVICE9 pDevice, float elapsedTime )
{

// Rotate
m_transform.RotateRel( 0.0f, 3.14f * elapsedTime, 0.0f );

}

In the OnUpdateFrame and OnRenderFrame methods of CGameApp, we now have access to the elapsed time between frames. We will use this value to control how much our geometry should animate. By multiplying the animation value by the elapsed time, the animation will be scaled depending on how much time has passed since the last frame update. With this in mind, we now specify our animation values in terms of units per second. In the above example, I rotate the geometry 3.14 radians (360 degrees) per second.

Having this elapsed time value will be crucial when working with keyframe and skeletal animations.

Laters, C

One thought on “Timing and Frames Per Second”

Matheus Faiotto July 8, 2013 at 1:23 pm

Hey chad,

I’ve been following your DirectX 9 Tutorials, and at the moment I cannot figure out what I did wrong. I even downloaded your copy of the source code to this tutorial, and checked line by line.

I’ve uploaded a copy of my source to a website. Let me know what I did wrong. DirectX 9 Tutorial 10.zip (http://puu.sh/3y5Er.zip)

Thanks,
Matheus Faiotto.

Comments are closed.

Leave A Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

<code> For inline code.
[sourcecode] For multiline code.