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!