![]() |
| [[ Home | Forums | 3D Engines Database | Wiki | Articles/Tutorials | Game Dev Jobs | IRC Chat Network | Contact Us ]] |
|
|||||||
![]() |
|
|
Thread Tools | Search this Thread | Display Modes |
|
|
#1 |
|
Valued Member
Join Date: Sep 2004
Location: Ontario, Canada
Posts: 155
|
Ok, this may get a little long and rambling so I apologize in advance.
In my game I use QueryPerformanceCounter for timing. I've got "the timing issue": http://support.microsoft.com/kb/327809 STATUS Microsoft has confirmed that this is a problem in programs that make erroneous assumptions about the QueryPerformanceCounter and QueryPerformanceFrequency output. Damn. I hate when I make erroneous assumptions. MORE INFORMATION On certain computers, the result returned by QueryPerformanceFrequency will be the clock speed of the CPU. On a computer with a processor running faster than 2.1 GHz, this frequency value requires at least 32 bits of precision. Some programs work with the result of QueryPerformanceFrequency as a signed integer value, which has only 31 bits of precision and a sign flag. These programs behave incorrectly on these faster CPUs. To avoid this problem, programs must use all 64 bits returned from both QueryPerformanceFrequency and QueryPerformanceCounter. I learned to use QueryPerformanceCounter in a tutorial somewhere, but the heart of it is like this: Code:
So then how do I go about using all 64 bits returned? Aren't the LARGE_INTEGER and LONGLONG all a bit dodgey to begin with? Ok, if you're still with me, we're off to another URL! http://msdn.microsoft.com/library/de...Processors.asp Recommendations Games need accurate timing information, but also need to implement their timing code in a way that avoids the problems associated with RDTSC usage. The following steps should be taken when implementing use of high-resolution timing: Use the QueryPerformanceCounter and QueryPerformanceFrequency API instead of the RDTSC instruction. These APIs may make use of RDTSC, but might instead make use of a motherboard timing chip or some other system services that will provide quality high-resolution timing information. Right. Isn't this what got me into this problem in the first place? When computing deltas, the values should be clamped to ensure any bugs in the timing values do not cause crashes or unstable time-related computations. The clamp range should be from 0 (to prevent negative delta values) Clamp from zero to infinity and beyond? Finally while the QueryPerformanceCounter / QueryPerformanceFrequency API is intended to be multiprocessor aware, bugs in the BIOS or motherboard drivers may result in these routines returning different values as the thread moves from one processor to another. Ahh, the joys of PC game development... We recommend that all game timing be computed on a single thread, and that thread is set to stay running on a single processor through the SetThreadAffinityMask Windows API. Typically this would be the main game thread. Ok, well I only have the main thread, running in the game's process. I haven't been able to find how to get a handle to that thread. Can I use SetProcessAffinityMask instead using the "pseudo-handle" I get from GetCurrentProcess? Shouldn't that accomplish the same thing? Even if I can, I have read the msdn entries, but I still can't seem to figure out how to create the affinity mask itself to say "only use processor 0". dwProcessAffinityMask [in] The affinity mask for the threads of the process. Yeah, that's helpful. Is this like a bit mask? It's a DWORD. 0x00000000 for processor 0? 0x00000001 for processor 1? If I call GetProcessAffinityMask Code:
on my single processor laptop, both pam and sam are have a value of 1. So is this all I need? Code:
It hasn't made any difference. But then again, since I don't have the ability to debug on a dual processor machine, I'm not sure what value is being returned on a multi processor setup. Can I just do this? Code:
Thanks for any input! |
|
|
|
|
|
#2 |
|
DevMaster Staff
Join Date: Oct 2004
Location: Seattle, WA
Posts: 3,707
|
Regarding QueryPerformanceCounter (QPC):
I'm not convinced it's the best counter to use for game timing. As the article hints at, this counter isn't guaranteed to be monotonic, even on single-processor systems - it increases unsteadily and sometimes goes backwards! (Actually, what happens is the performance counter runs too fast, getting ahead of real time, and is then corrected by a lower-resolution timer somewhere else in the system). This isn't what you want to do when measuring frame times for your game - even if you clamp the delta values to zero, you won't get a smoothly increasing counter, which causes things like player movement and physics to go haywire. Moreover, on laptops the processor speed will actually vary depending on CPU usage - and the value returned by QPF may or may not change to represent this. So basically QPC/QPF (by themselves) are fubared for use as game timers. Personally, I've had the best results with timeGetTime(), even though the resolution is not guaranteed to be even as small as 1 ms. There are also some articles out there discussing more complex time systems that take into account the values of timeGetTime(), QPC, and rdtsc (if available) to generate a sort of "averaged" time. However, if you still want to try QPC, I would suggest converting LARGE_INTEGER to a floating-point value as follows: Code:
Regarding affinity masks: SetThreadAffinityMask "A thread affinity mask is a bit vector in which each bit represents the processors that a thread is allowed to run on. A thread affinity mask must be a subset of the process affinity mask for the containing process of a thread. A thread can only run on the processors its process can run on." You can get the thread handle with, of all things, GetCurrentThread And, as mentioned in the paragraph I just quoted, you can also set the process affinity mask and that will also act as an affinity mask for all threads.
___________________________________________
Currently working at Sucker Punch reedbeta.com - OpenGL demos and other projects Luabridge - a lightweight, dependency-free C++/Lua binding library. CD Lite - an unobtrusive, minimal CD player application for Windows. Last edited by Reedbeta : 04-26-2006 at 12:07 AM. |
|
|
|
|
|
#3 | |
|
DevMaster Staff
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
|
Quote:
But I read somewhere timing is becoming more and more of an issue due to the release of multicore CPU's to the main public so it won't be long before regular mainboards have high-resolution timing hardware available.
___________________________________________
C++ addict - Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3. |
|
|
|
|
|
|
#4 | |
|
Valued Member
Join Date: Sep 2004
Location: Ontario, Canada
Posts: 155
|
Thanks for the info, Reedbeta.
Seems the SetProcessAffinityMask worked for my friends home machine (dual processor), but not on his work machine (hyperthreaded). So in my latest attempt I've ditched QueryPerformanceCounter and got back to timeGettime for the cinematic sequences where I'm having the issue. Quote:
Just out of curiosity (and since I'm frequently mathematically inept), can you explain how this magic number works? ( 4294967296.0f ) Damn, I don't know how I missed GetCurrentThread, as that was just what I was looking for. Sure is easy to get lost in the msdn... Thanks for the help! Regards, |
|
|
|
|
|
|
#5 |
|
DevMaster Staff
Join Date: Oct 2004
Location: Seattle, WA
Posts: 3,707
|
It's simply 2^32. It's as if you're bit-shifting the high part left by 32 bits and then adding the low part, except done in floating point.
___________________________________________
Currently working at Sucker Punch reedbeta.com - OpenGL demos and other projects Luabridge - a lightweight, dependency-free C++/Lua binding library. CD Lite - an unobtrusive, minimal CD player application for Windows. |
|
|
|
|
|
#6 |
|
Valued Member
Join Date: Sep 2004
Location: Ontario, Canada
Posts: 155
|
Ah! Perfect, thanks!
![]() |
|
|
|
|
|
#7 |
|
Senior Member
Join Date: Sep 2005
Location: .nl
Posts: 504
|
it is quite interesting to look at timeGetTime's code in winmm.dll. I don't have much time (and correct tools) now, but it looks like it is simply using gettickcount.
|
|
|
|
|
|
#8 |
|
Valued Member
Join Date: Sep 2004
Location: Ontario, Canada
Posts: 155
|
Heh, at this point I don't care what it's doing, my problem has been resolved. Now I can finish and release my product!
Regards, |
|
|
|
|
|
#9 | |
|
Valued Member
Join Date: Oct 2005
Location: Switzerland
Posts: 117
|
Quote:
Err... Just a second... You're basically saying that the MSDN is giving wrong information here? Has anybody effectively observed that the frequency was changing or the timer jumping backwards in time (on a more or less recent system)? Since I'll need the timing for accurate real-time process automation tasks, I need it to stay at least ever incrementing. For absolute synchronisation, I'll have a GPS clock available. Does anybody know wheter SetSystemTimeAdjustment()/GetSystemTimeAdjustment() has anything to do with this? And what do these functions actually do? I'm not really getting what the MSDN says about these... |
|
|
|
|
|
|
#10 |
|
DevMaster Staff
Join Date: Oct 2004
Location: Seattle, WA
Posts: 3,707
|
I have definitely observed the QPC value jumping backwards on my laptop (Athlon 64 3200, not cutting edge but certainly recent). And the QPF returned value may well be constant, but the values returned by QPC may be based on the variable-clock-speed processor. So what I'm saying is that QPF doesn't necessarily give you the correct result with which to convert QPC values to real time.
___________________________________________
Currently working at Sucker Punch reedbeta.com - OpenGL demos and other projects Luabridge - a lightweight, dependency-free C++/Lua binding library. CD Lite - an unobtrusive, minimal CD player application for Windows. |
|
|
|
|
|
#11 |
|
Senior Member
Join Date: Sep 2005
Location: Hamburg / Germany
Posts: 597
|
I shipped numerous titles using QueryPerformanceCounter and never had problems with it.
Using the full 64 bit is important, but I can't see a reason why one wouldn't do so.. the QUADPART of LARGE_INTEGER directly maps to __int64, so you can do arithmetic on them them directly. Btw, doing differences on time and summing them into a float is a bad idea.. That will introduce rounding errors that add up over time. A showstopper for network games. |
|
|
|
|
|
#12 | |
|
Valued Member
Join Date: Oct 2005
Location: Switzerland
Posts: 117
|
Quote:
|
|
|
|
|
|
|
#13 |
|
Valued Member
Join Date: Aug 2005
Location: Seoul
Posts: 272
|
|
|
|
|
|
|
#14 | |
|
Valued Member
Join Date: Oct 2005
Location: Switzerland
Posts: 117
|
Quote:
![]() |
|
|
|
|
|
|
#15 |
|
DevMaster Staff
Join Date: Oct 2004
Location: Seattle, WA
Posts: 3,707
|
Yeah, that's about the size of it.
![]()
___________________________________________
Currently working at Sucker Punch reedbeta.com - OpenGL demos and other projects Luabridge - a lightweight, dependency-free C++/Lua binding library. CD Lite - an unobtrusive, minimal CD player application for Windows. |
|
|
|
![]() |
| Thread Tools | Search this Thread |
| Display Modes | |
|