QPC Timing – ShiVa Engine

QPC Timing

Sometimes, it can be necessary to time your code very accurately, be it for general performance measurement, benchmarking certain sections of the code, or predicting input delay from user action to rendering. Windows provides APIs that you can use to acquire high-resolution time stamps or measure time intervals. The primary API for native code (C++) is QueryPerformanceCounter (QPC). QPC is independent of and isn’t synchronized to any external time reference. For more detailed information on QPC, please visit MSDN.

Stopwatch

Taking an accurate time measurement with QPC is quite easy and requires very little code. First, you need to define your target variables:

// time
LARGE_INTEGER _timeStart, _timeEnd, _timeDelta, _timeFrequency = {0};
float _timeInputToRender;

Then, at any point in your code where you would like to start your stopwatch, put this line:

QueryPerformanceCounter(&_timeStart);

To stop your stopwatch, use:

QueryPerformanceCounter(&_timeEnd);

Now you need to compute the difference between these two timestamps, which is done by subtracting the quadParts:

_timeDelta.QuadPart = _timeEnd.QuadPart - _timeStart.QuadPart;

Next, you need to compare this number against the frequency of the timer. Since we are dealing with integers, division can be a very lossy operation. By multiplying with 10e6 first, we are offsetting the error by 6 decimal points. In the last step, we are dividing by 10e6 again, but his time, we are using floating point numbers.

QueryPerformanceFrequency(&_timeFrequency);
auto d = (_timeDelta.QuadPart * 1000000) / _timeFrequency.QuadPart;
_timeInputToRender = static_cast(d) / 1000000;

This float can then be sent in a standard ShiVa event to a handler in an AIModel.

Benchmarking

There are essentially two use cases for this code: You can either (1) benchmark sections of your code, or (2) benchmark how long the engine takes between the stages of its own event loop.
(1) If you want to know how long a certain function or a set of function takes in your plugin or native code application, simply frame the section in question with QueryPerformanceCounter() calls.
(2) Plugin.cpp exposes the engine event loop. By inserting QPC calls into event functions, you can measure a lot of interesting things, for instance how long just the rendering process takes, or how much time we need from the user pressing a button and the change being rendered in the engine:

void ShiVa2SteamVR::OnEngineEvent ( S3DX::uint32 _iEventCode, S3DX::uint32 _iArgumentCount, S3DX::AIVariable *_pArguments )
{
    switch ( _iEventCode )
    {
	case eEngineEventApplicationStart: break;
	case eEngineEventApplicationPause: break;
	case eEngineEventApplicationResume: break;
	case eEngineEventApplicationStop: break;
	case eEngineEventFrameUpdateBegin: startTimer(); break;
	case eEngineEventFrameUpdateEnd: break;
	case eEngineEventFrameRenderBegin: break;
	case eEngineEventFrameRenderEnd: stopTimer(); break;
    }
}

If you are working towards a specific FPS target, like 60 Hz on Desktops or 90 Hz on VR, this is a good way to determine the time how long creating a frame actually takes and then comparing it to the frametime budget (0.0167 on Desktop, 0.011 on VR).




Need more answers?

  • slackBanner