Skip to main content
gamedev.net
🔒 Locked

Windows timer

Started by simotix 11 replies 2.3k views
Original Post
simotix
simotix
I am creating a timer for my game (windows only) and I was previously using GetTickCount() but I am switching to QueryPerformanceCounter() to use a more accurate way.

I have four variables
LARGE_INTEGER m_liPerfStart;LARGE_INTEGER m_liPerfLast;LARGE_INTEGER m_liPerfFreq; float m_fFractionOfSecondPassed; 



I reset the timer at the start of my program by doing the following
m_fFractionOfSecondPassed = 0.0f;QueryPerformanceFrequency(&m_liPerfFreq);QueryPerformanceCounter(&m_liPerfStart);m_liPerfLast = m_liPerfStart;


Whenever I update my timer (such as at the end of the update loop)
m_liPerfLast = m_liPerfStart;QueryPerformanceCounter(&m_liPerfStart);m_fFractionOfSecondPassed = (m_liPerfStart.QuadPart - m_liPerfLast.QuadPart) / (float)m_liPerfFreq.QuadPart;


My question is, is this the best way to get the fraction of a second passed since the last reset (or update)?
Evil Steve
Evil Steve
This might be worth a read. Beware that QPC() has a few issues with it.

Personally, I use timeGetTime() and measure how many frames get rendered in Xms rather than how long 1 frame takes.
Krohm
Krohm
It sounds like a definetly better idea. However, I must say that so far I've been rather happy with QPC, when coupled with an affinity mask? Maybe I have been just lucky? That would be terrible.
Previously "Krohm"
simotix
simotix
Quote:
Original post by Evil Steve
This might be worth a read. Beware that QPC() has a few issues with it.

Personally, I use timeGetTime() and measure how many frames get rendered in Xms rather than how long 1 frame takes.


I took a look at that article and it was very interesting (it seems that nothing is really a good idea). I am looking to create a timer that can do more then just see how many frames got rendered in a certain time, I also would like to see the amount of time passed (in seconds, milliseconds, or anything) for profiling.

Quote:
Original post by Krohm
It sounds like a definetly better idea. However, I must say that so far I've been rather happy with QPC, when coupled with an affinity mask? Maybe I have been just lucky? That would be terrible.


Could you post an implementation of how you use it, or where you learned how to use it? I do not see what I am doing wrong
Krohm
Krohm
Quote:
Original post by simotix
Could you post an implementation of how you use it, or where you learned how to use it? I do not see what I am doing wrong
Sorry simotix, I wasn't referring to what you're doing but rather to what I am doing.
The only thing that's different to me is that I work on delta times and avoid FP math when possible.

Previously "Krohm"
taz0010
taz0010
Quote:

m_fFractionOfSecondPassed = (m_liPerfStart.QuadPart - m_liPerfLast.QuadPart) / (float)m_liPerfFreq.QuadPart;


Cast to double, not float. Floats have the potential to incur considerable precision loss, as the integer values you're dividing can be very large. Plus their size is hardware dependant so the accuracy problems are likely to pop up on other systems even if it works fine on yours.
21st Century Moose
21st Century Moose
If you cast to double and you're using D3D remember that you'll need D3DCREATE_FPU_PRESERVE in your CreateDevice otherwise you'll just make things worse.
Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
simotix
simotix
Quote:
Original post by taz0010
Quote:

m_fFractionOfSecondPassed = (m_liPerfStart.QuadPart - m_liPerfLast.QuadPart) / (float)m_liPerfFreq.QuadPart;


Cast to double, not float. Floats have the potential to incur considerable precision loss, as the integer values you're dividing can be very large. Plus their size is hardware dependant so the accuracy problems are likely to pop up on other systems even if it works fine on yours.


Either way, I am looking of seeing if that way of determining the fraction of a second passed is correct
taz0010
taz0010
Quote:
If you cast to double and you're using D3D remember that you'll need D3DCREATE_FPU_PRESERVE in your CreateDevice otherwise you'll just make things worse.


There are some posts on various forums that claim the DirectX API changes the precision mode to single, affecting *all* calculations throughout your program. There is not mentioned in the documentation however. Under D3DCREATE_FPU_PRESERVE, it says:

Quote:
Set the precision for Direct3D floating-point calculations to the precision used by the calling thread. If you do not specify this flag, Direct3D defaults to single-precision round-to-nearest mode for two reasons:
Double-precision mode will reduce Direct3D performance.
Portions of Direct3D assume floating-point unit exceptions are masked; unmasking these exceptions may result in undefined behavior.


Which implies that the forced single precision is only in affect during DirectX API calls.

To test this, I had my program load in two double precision values from a text file, divide them and output the result. The program was displaying the correct double precision value, while using single precision datatypes resulted in the expected loss of precision.
I tried this both with and without SSE (which I'm told ignores the FP setting outright), and with the various floating point settings (strict/fast/precise).
My resulted showed no indication that the CPU precision flag had been affected.

So this may be an issue that doesn't affect the hardware I'm using, or maybe it's something Microsoft patched out? Not being able to rely on 64 bit precision is a pretty huge issue IMO.
taz0010
taz0010
Quote:
Either way, I am looking of seeing if that way of determining the fraction of a second passed is correct


Yes that's how it's done. There are more complicated implementations (running on own thread, setting CPU affinity) which are used to avoid bugs which are present on some hardware.
Jan Wassenberg
Jan Wassenberg
Quote:
I took a look at that article and it was very interesting (it seems that nothing is really a good idea).

hm, I wish that article contained a link to its newer version at http://wassenberg.dreamhosters.com/articles.html, because there are indeed safe solutions (either Windows 7, or a homemade HPET driver for XP, or requiring a CPU with invariant TSC, or affinitizing, but I don't like the latter).

Quote:
So this may be an issue that doesn't affect the hardware I'm using, or maybe it's something Microsoft patched out? Not being able to rely on 64 bit precision is a pretty huge issue IMO.

Yeah - they're probably not doing that many multiplies/divides in software anymore. Also, the P4 uarch added optimizations for FLDCW that might have made it feasible to switch precision upon entry/exit.

Quote:
I tried this both with and without SSE (which I'm told ignores the FP setting outright)

Correct - there is no precision control, but SSE's MXCSR does provide a rounding control.
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3
simotix
simotix
Quote:
Original post by taz0010
Quote:
If you cast to double and you're using D3D remember that you'll need D3DCREATE_FPU_PRESERVE in your CreateDevice otherwise you'll just make things worse.


There are some posts on various forums that claim the DirectX API changes the precision mode to single, affecting *all* calculations throughout your program. There is not mentioned in the documentation however. Under D3DCREATE_FPU_PRESERVE, it says:

Quote:
Set the precision for Direct3D floating-point calculations to the precision used by the calling thread. If you do not specify this flag, Direct3D defaults to single-precision round-to-nearest mode for two reasons:
Double-precision mode will reduce Direct3D performance.
Portions of Direct3D assume floating-point unit exceptions are masked; unmasking these exceptions may result in undefined behavior.


Which implies that the forced single precision is only in affect during DirectX API calls.

To test this, I had my program load in two double precision values from a text file, divide them and output the result. The program was displaying the correct double precision value, while using single precision datatypes resulted in the expected loss of precision.
I tried this both with and without SSE (which I'm told ignores the FP setting outright), and with the various floating point settings (strict/fast/precise).
My resulted showed no indication that the CPU precision flag had been affected.

So this may be an issue that doesn't affect the hardware I'm using, or maybe it's something Microsoft patched out? Not being able to rely on 64 bit precision is a pretty huge issue IMO.


So are you recommending that I do or do not use this?

Quote:
Original post by taz0010
Quote:
Either way, I am looking of seeing if that way of determining the fraction of a second passed is correct


Yes that's how it's done. There are more complicated implementations (running on own thread, setting CPU affinity) which are used to avoid bugs which are present on some hardware.


I am currently brushing up on my threading after taking a long break for it and I will run it in its own thread. I will probably respond to this thread in the future about this. I understand it running in its own thread, but why does CPU affinity matter?
taz0010
taz0010
Quote:
I understand it running in its own thread, but why does CPU affinity matter?


Each CPU core has it's own timer state whose values will inevitably drift apart. As part of the multitasking process, Windows will periodically switch the process over to a different core, and on some systems, this will result in the timer value of the new core being read instead of the old one. The timer fluctuation will result in choppy movement, and negative timestep values being generated. AMD released a software patch a while back, which (I think) periodically synchronises the timers on each core. I don't know if the problem affected Intel CPUs or not.

Anyway, setting the CPU affinity to one core only will ensure that the thread is only run on that core, which eliminates the issue entirely. But there can be a pretty big performance hit if the core you set affinity to is being used for something else. Having a thread just for the timer will reduce the impact.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.