Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have been having some minor frame drop issues in a 2D XNA game.

To start with, I am a vsync'ed, mixed timestep. That is:

IsFixedTimestep = false;
graphicsDeviceManager.SynchronizeWithVerticalRetrace = true;

This is combined with a fixed physics timestep of 120hz, implemented with the pattern used in Fix Your Timestep! to avoid jittery physics movement. So, Update runs with a variable timestep, and another Tick runs with a fixed timestep (the idea is from Unity's Update/FixedUpdate).

When a sprite moves across the screen at a steady pace, every couple of seconds a jitter/hiccup is noticable. Some googling around led me to this forum post several years ago talking about the same issue.

To test this, I added this code to an Update method:

if(gameTime.ElapsedGameTime.TotalMilliseconds > 22)
{
    Logger.Log(gameTime.ElapsedGameTime.TotalMilliseconds.ToString());
}

Sure enough, every couple of seconds the log showed a time above 30 milliseconds, usually around 32. Seeing as one frame at 60 fps is 16.6 milliseconds, it seems that a frame is simply being lost. I added some additional code to flash the screen red whenever this happens. Again, sure enough, I notice the bullet hiccuping when the screen flashes red. If it is simply dropping a frame, that means that the bullet will hold still, then when the frame finally runs, the physics will run 4 times (since it runs twice as fast as the monitor, over two frames) and move the bullet a lot.

Now, I'm not sure if I can fix this. If this is indeed a quirk of XNA (and gamedev in general?) the frame drop may be unavoidable. So, I would either know how to either get rid of the frame drop, or mitigate how visible it is. A few more points:

1. It's probably not the GC.

In that forum post, they seemed to hone the problem down to a point where the GC would never/hardly run, and still saw the problem. To check myself, I added some code that checked GC.GetTotalMemory(false) every frame, and if it dropped from the last frame, log "GC". I think a drop in that would show a GC collect, but I could be wrong. "GC" was logged independent of the other log when a frame was dropped.

2. I can't use a variable timestep

I need the physics to be reproducible across a network. This means I need it to be as deterministic as possible (it actually doesn't matter 100%, but it will cause noticable artifacts the less deterministic it is). So, I can't just multiple the velocity of objects by gameTime.ElapsedGameTime.

3. I don't want to use IsFixedTimeStep

I like to keep the framerate under my control a bit. It gives more control over drawing, and allows several somewhat independent framerates. The forum post also stated that setting IsFixedTimeStep to false helped the problem. Granted though, I basically reimplented IsFixedTimeStep myself!

Additionally, setting IsFixedTimeStep = true; doesn't help the stuttering.

4. Some ideas?

So if I can't fix the problem, perhaps I can mitigate it somehow. Some ideas I've had are:

  • Motion blurring
  • Give trails to bullets
  • Increase the amount of "noise" on the screen so that a frame drop is a small issue
  • Separate update and draw in to separate threads?
  • Deal with tearing and unsync from the vertical retrace? This almost completely solves the problem, but GPU usage goes to 100% as it's updating and drawing as fast as it can.

Maybe something has also happened with XNA since the forum post in 2008, that I can use. Otherwise, I'm not sure what to do about the issue. Is it a really small issue? Yeah! I'd love to have a silky-smooth game, though. I'd love any ideas!

share|improve this question
    
I am not an expert, but I believe that since you're running with vsync, and monitors aren't exactly spot on 60Hz, you'll have a leftover frame or be a frame behind at some point, since the monitor Hz doesn't match the game FPS. EDIT: Note that disabling vsync and keeping IsFixedTimeStep will keep GPU from going crazy. –  William 'MindWorX' Mariager Aug 4 '12 at 16:19
    
I figured something like that would be the culprit. Unfortunately it does make a visible artifact, and I'm looking for a way to mitigate how noticeable that is. When dealing with physics I can actually use that little leftover time to blend the positions of the objects cleanly. That's a lot harder to do for monitor refreshes though. As for your edit, I would assume that would still cause a hiccup, because the 60hz fixed timestep would be out of phase with the ~60hz monitor refresh. –  Snea Aug 4 '12 at 16:22
1  
+1 for a nice and organized question :P –  Cyral Aug 4 '12 at 17:00
    
Are you able to reproduce the issue in an empty project? –  Andrew Russell Aug 5 '12 at 10:53
add comment

1 Answer

To start, there's a good post by Shawn Hargreaves on GameTime (more specifically, Fixed and Unfixed timestep) over here. Perhaps it will give you an idea or some things to try.

However, I'm not entirely sure that you can rule out the garbage collector.

For Garbage:

Run your game through CLR Profiler and see what it tells you. If you have access to an Xbox 360, you can use the XNA Remote Performance Monitor to tell you more quickly if there's an issue (although, unlike CLR Profiler, it won't tell you where the issue is). There's a series of blog posts by Shawn about using XNA RPM located here, and here.

Also, your method of checking garbage should work. However, my personal favorite way is something like this:

 WeakReference GarbageTracker = new WeakReference(new object());
 int GarbageCollections = 0;

Then, in your update:

if(!GarbageTracker.IsAlive)// if it's not alive.  If it's been collected, it will be set to false
{
      GarbageTracker = new WeakReference(new object());
      GarbageCollections++;
}

This will tell you when a collection happens, as that WeakReference will be collected with every collection that's run. Then, if it's been collected, we increment our counter and set it reset it.

Generally:

Some frame skips are unavoidable. Sometimes an update or (less often) a draw takes longer than it should, and there's no great way to adapt for that (well, more accurately, no great easy way to adapt for that). Off the top of my head, you might want to check gameTime.IsRunningSlowly for any lag. If XNA doesn't think it's running slowly, it's more than likely a refresh-rate problem.

Hopefully this helps!

share|improve this answer
    
I like that way of checking for a garbage collection. Unfortunately it didn't tell me anything more, as it seemed to happen at the same time as my other garbage collection tester, which was still independent of a frame skip. I've also had very little luck getting the CLR profiler to work. It just shows 0 for all the memory information. I'll have to give it another try. IsRunningSlowly is also never set to true, with a fixed or unfixed timestep, so it really probably is a refresh rate problem. Is there really no great way to adapt or mask that when it happens? –  Snea Aug 5 '12 at 7:35
    
If you're on Windows 7 (and I think maybe Windows Vista, I'm not sure) getting CLRProfiler working can be a bit of a pain. Specifically, you'll want the 32-bit version, and select "V4 Desktop CLR" once the profiler is running. Also, I ran across this blog post where someone was having the same problem you were (although he didn't solve it either): (codeforcake.com/blog/?p=83) I'll update my question if I can think of anything else. –  electroflame Aug 6 '12 at 22:00
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.