CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Brian Peek [MVP C#]

A compendium of random uselessness. Enjoy.

XNA Timing Bug

While working on my game for Maker Faire (which you'll be able to download in about a month), I came across a timing bug on the Xbox 360 using XNA.  For an application that requires very accurate timing (millisecond precision), one can use the StopWatch object in .NET 2.0.  This was working quite well in my PC build, but on the Xbox 360 build, I would notice that time would drift with no explanation.  After digging around for a couple days and writing a simple sample to prove the point, I determined that the time value returned by the StopWatch object would drift by about 150ms/minute, which is considerable when precision within a few milliseconds is required (and considerable when it should be accurate to begin with).

I eventually found a workaround:  DateTime.UtcNow.Ticks is perfectly accurate on both platforms.  So a quick rewrite to use this property in all locations requiring accurate timing fixed everything quite nicely.

The item is bugged, sample code is sent to the XNA team and it will hopefully be fixed in a future version.  But, for now, if you're looking for accurate timing on the 360, DateTime.UtcNow.Ticks is your friend.

Update: After Chad's comment on CodeBetter.com, just wanted to clarify a few things...

  1. DateTime.UtcNow.Ticks may in fact not be precise on any given platform.  As stated below, sitting in a tight loop may not see the Ticks value increase for several milliseconds.
  2. StopWatch internally actually uses DateTime.UtcNow.Ticks if the QueryPerformanceCounter doesn't give back the results the StopWatch wants (precision information).  On the 360 side of things, QPC does return the valid information to the StopWatch so QPC is what gets used.
  3. The issue above is not one of precision, it's one of accuracy.  The length of a millisecond on the 360 according to StopWatch/QPC is just plain wrong.  Updates to the StopWatch value are certainly quite fast (millisecond precision) yet over time, comparing the time to "wall time" or DateTime.UtcNow.Ticks shows that the StopWatch runs slower than real-world time by about 150ms per minute.  It never catches up and in fact, just drifts further and further behind.  After about 10 minutes, StopWatch is one second in the past.  But again, only on the 360.  On the tested PCs, there was a drift of only 2 or 3 milliseconds in that same ten minute timeframe.

 Cross-posted from BrianPeek.com.

 

 


Published May 30 2007, 02:48 AM by Brian Peek
Filed under:

Comments

cmyers said:

Brian, You should Google on StopWatch and QueryPerformanceCounter, because you're not alone on this. Many PC processors have this problem also, especially AMD ones and dual-core (AMD or Intel) ones. There are ways of compensating for this. As far as DateTime.Now and UtcNow, while the docs state that the RESOLUTION (accuracy) is 10 milliseconds, my observations have shown its PRECISION (repeatability/reliability) is around 200 milliseconds. For example, I've seen that repeated calls to dump DateTime.Now/UtcNow to the console in a tight loop will show that the value doesn't change but once every 150-200ms. I've tried this on Intel and AMD, single and dual core and the DateTime.Now/UtcNow are reliably imprecise and StopWatch/QueryPerformanceCounter can be all over the board (inaccurate, but precise, accurate but imprecise, etc). Here's a good article from a guy working on a video editing app (which has the same kinds of timing requirements you have): http://www.virtualdub.org/blog/pivot/entry.php?id=106
# May 30, 2007 8:58 AM

Brian Peek said:

Thanks for the link.  I've been bitten on the PC side by these inaccuracies in the past, but this time it wasn't the PC I found inaccurate in all of my testing, but the Xbox.  Unfortunately since the CompactFramework is new to this platform and the fact that it is the CF, my options for timing are limited.

Also note that the issue I was having wasn't that I wouldn't get updates to the tick count fast enough, but the fact that the tick count was just plain wrong.

My sample app that proved the point showed that the Stopwatch (which uses QPC internally on the 360) actually redefined the length of a millisecond.  :)  The sample app would start a Stopwatch and grab the time value for DateTime.UtcNow.Ticks and compare the two over time.  You can watch the difference between the two values increase at a constant rate such that after 1 minute, the value returned from DateTime.UtcNow.Ticks was indeed real time, and the value returned by the Stopwatch was 150ms in the past.  After 2 minutes, it was 300ms off.  After 3, 450ms.

Quite bizarre, but according to the Stopwatch on the 360's CF, a millisecond isn't quite a milisecond, but DateTime.UtcNow.Ticks was perfectly accurate for my needs.

# May 30, 2007 1:49 PM

Dan said:

Hi I have just read your article with great interest. I am writing an Xbox 360 game for my final year project at university. The game is going to be loosely themed on Worms. I have a projectile class that takes an accuracy (how many times it updates itself for the time spent in the air). At the moment the accuracy is set at 0.001 so it uses these intervals to work out (x,y) at a given time. The results are correct however i now need to make it perform at close to real time. Can you suggest a decent starting place to look for answers please. Any advice would be appreciated. Thanks Dan
# June 26, 2007 11:10 AM

Brian Peek said:

Dan,

I assume you're using XNA since you're posting here.  ;)

I'm not 100% sure I understand the question at hand, but I would be maintaining a list of projectiles and enumerating the list once per call to the XNA Update method.  Here I'd take the elapsed ticks passed into the Update method to determine how far each projectile would have moved in that time frame.

So, you'd like have a Projectile class which contains its own update method, also taking in a TimeSpan of elapsed time since the last frame.  So, something like this in your main game screen (pseudo-code):

void Update(GameTime time)

{

 foreach(Projectile p in projectiles)

   p.Update(time);

}

void Draw(GameTime time)

{

 foreach(Projectile p in projectiles)

   p.Draw(time);

}

Then in your Projectile class (pseudo-code again):

void Update(GameTime time)

{

 this.X = VelocityX * time.ElapsedTime.TotalMilliseconds;

}

Hope that makes some amount of sense.  If not, feel free to post back here, post to my forum at www.brianpeek.com or fire off an email to me at "my first name@my first and last name.com" .  :)

# June 26, 2007 4:44 PM

Dan said:

Hi thanks for the quick reply. That has cleared a lot up for me. I had a poorly designed projectile class so have re done it to be more flexible and fit in better with my game. How do I get round a situation such as the update gets called again before the timer refreshes (do i just ignore this as it is unlikely that the human eye will be able to notice such a small hiccup?) Thanks for you help Dan
# June 27, 2007 9:01 AM

Brian Peek said:

Dan,

Not sure I understand...when/why would you be getting multiple updates in a single frame?

# June 27, 2007 4:36 PM

BrianPeek.com said:

Update: This issue has been fixed in XNA Game Studio 2.0. Woo hoo! While working on my game for Maker

# December 24, 2007 6:52 AM

Brian Peek said:

Update: This issue has been fixed in XNA Game Studio 2.0. Woo hoo! While working on my game for Maker

# December 24, 2007 6:52 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Brian Peek

For anyone who was following this blog at CodeBetter, please re-point your browsers and RSS readers to http://www.brianpeek.com/ . Thanks! Brian is a Microsoft C# MVP and a recognized .NET expert with over 6 years experience developing .NET solutions, and over 9 years of professional experience architecting and developing solutions using Microsoft technologies and platforms. Along with .NET and its associated languages, Brian is particularly skilled in the languages of C, C++ and assembly language for a variety of CPUs. He is also an expert in a variety of technologies including web development (ASP.NET, ASP, Javascript, HTML, XML, etc.), document imaging, GIS, graphics, game development, and hardware interfacing. Brian has a strong background in developing applications for the health-care industry, as well as developing solutions for portable devices, such as tablet PCs and PDAs. Along with Jonathan Goodyear, he co-authored the book "Debugging ASP.NET" published by New Riders. He is also a member of MSDN's Coding4Fun writing team, contributing articles on a monthly basis. Check out Devlicio.us!