Random stutter when using 2D spritebatch, any suggestions?

I’m honestly out of ideas. To put an example, now I’m porting an old game of mine where a normal level has 10k calls to spriteBatch.draw (I put a counter just to check it) and I get no performance drops at all.

If it’s not top secret and you can pack your executable+assets, I can check if the problem also happens in my computer, and if it does, check the code a bit with ILSpy, but other than that I have no idea what could be causing it.

Thanks, that would be great if you could take a look. How should I send it to you?

I’ve managed to recreate the issue in a completely empty project that does nothing except load a single texture and then draw it 300 times while logging to a console window. I think this conclusively rules out any GC issue with my code, as there is no garbage to collect, GC.CollectionCount never increments, and the memory usage stays pretty much constant.

I also tested my original project on another computer and had the same issue, so that rules out some other things I was thinking it could be, like a problem with my GPU or interference from something like the Nvidia app.

If you’ve isolated the problem (so it’s not including personal code in it) the best way would be probably posting the code in github, either that or uploading a zip somewhere. This way any other with a keener eye than mine may check it too when I fail to find the problem :rofl:

OK, here’s a test solution on github: https://github.com/Monocle8/MonoGameTest

It’s a basically empty project that loads a plain square texture and draws it 600 times. I’ve set it up to log to the console when a slow draw happens, and also when GC collects. It also outputs the memory usage every 5 seconds or so. It’s set up on the assumption that screen resolution will be 1920x1080, as that’s what I’m using.

Usually, when I run it, the stuttering begins within 30 seconds and happens at random intervals, but sometimes takes over a minute to start.

I’m also using VS 2022 and the project is set to target .Net 6.0, but I’ve tried changing it to .Net 5.0 or .Net core 3.1 and it made no difference.

weird, I’ve run it for quite some time (~650) but I got nothing except for a slight increase in memory usage (there’s a copy-paste induced bug with two of the GC.CollectionCount in the code but it shouldn’t affect the result).

It’s an i7 + intelHD, I’ll try to test the code in another computer later.

Hmmm. I’ve tried it on two PCs and also had a friend try it. The problem happened on both of mine, but only happened on his when windowed and the window was moved.

I’m not really sure where to go from here. I don’t want to give up on Monogame as Monogame/XNA is the only thing I’ve used for game dev in about 15 years, but this whole thing has been massively discouraging.

I would suggest to disable TieredCompilation in your csproj, always (as well as Ready2Run, but that doesn’t affect debugging). But I don’t think this would have any impact on your small example (the IL code would likely be entirely JIT’d).

In your test solution, all of your WriteLine() generate garbage because of the string constructions (this is what makes the memory usage to grow). In a small example like this one it’s not dramatic, but if you have that throughout all your code base, it will very much likely trigger garbage collections at some point.

One thing that might cause stutter is the GPU driver, especially when it enforces Vsync despite whatever you set from MonoGame (and intel drivers for OpenGL are notoriously meh). It might cause desyncs between the FixedTimeStep and the GPU driver, hence visible hiccups.

Also, there is a MonoGame behavior which might lead to misinterpretation: if the application isn’t focused, the game loop sleeps every frame to consume less power. It will sleep for InactiveSleepTime (which by default is much lower than 60fps). You can disable that by setting InactiveSleepTime to 0.

To make proper performance test, I would suggest disabling IsFixedTimeStep and enabling _graphics.SynchronizeWithVerticalRetrace. If you have stutters from here, it’s most definitely the code generating GC’s or .NET JIT’ing stuff.

On another topic, if you disable both Vsync and fixed timestep, an empty project will run so fast that the frame pacer accuracy will be outmatched, possibly generating misleading stutters. But in your example you are drawing some sprites, so I would assume that the frame pacer is able to do its job (unless you friend’s and your machine are overpowered).

In regard to not having issues with XNA back in the days, it turns out the .NET runtime has also evolved a lot and they changed much of it to be more performant, which also made it much more picky in regard to the code you write. I’m not saying that it’s necessarily your code, it could also be MG’s frame pacer having issues, so just some insights.

MonoGame itself is designed to generate 0 garbage past LoadContent(). So if the problem really is the GC, I would look into writing garbage free code with C# (I might do a write up about that at some point).

1 Like

Huh, the problem goes away if I disable both IsFixedTimeStep and SynchronizeWithVerticalRetrace.

I think @nihanworks suggested this earlier, but at the time I thought it sounded like a different problem for some reason. I think because for me it made no difference if I’m in windowed/fullscreen or GL or DX.

I didn’t expect improvments in techology would cause me to be less able to get away with writing sloppy code. I guess having always got away with it before, I’m not used to writing efficient code in C#. My assumption was that for a simple 2D game it wouldn’t matter all that much.

I noticed when I ran the profiler on my main project, one of the main collected types is the Linq WhereListIterator. Do I want to be avoiding Linq in a game project?

At least we can say that it’s not your code. (But it might still be a problem with the fixed timestep pacing of MG.)

.NET 5+ came with more parameters to control what the compiler and runtime are doing, but the misleading part is likely that the default value of those parameters aren’t quite what .NET 4.x was doing. And since .NET isn’t quite designed for games, I guess that working on games tend to expose more regularly the differences. That is why the 3.8.1 templates will have TieredCompilation and Ready2Run disabled explicitly.

Linq is very garbage-prone, but like everything that is generating garbage, it’s up to each and everyone to like it or not.

To be fair, it’s okay to have some garbage during your game loop. I would vouch for a “if it’s not a problem, don’t fix it” approach. The important thing is to have fun while coding.

1 Like

I’ve encountered this issue before, when I was on Intel integrated graphics. It’s not an uncommon issue, either. Do a search for “stutter” and you’ll find quite a few threads about the same or similar issues. I haven’t experienced it recently, however, possibly because my current machine has an NVIDIA GPU.

I recommend not using fixed time step. Fixed time step may be easier to work with and ensure consistent behavior, but it comes with the downside of the possibility of stuttering and does not allow variable (including arbitrarily high) frame rates.

Indeed. In my current project, I create new objects for animations each time an animation plays. Though performance wasn’t a problem, I was still concerned about the garbage, so I tried implementing an object pool. In doing so, I found that I had made my game perform worse than when I was just creating objects with abandon. Apparently the optimization of the runtime and the garbage collector is better than anything I could manage.

Edit: Actually, I can’t remember for sure, but it might have been memory usage I was profiling, not CPU usage, meaning trying to use an object pool made my memory usage higher. Either way, though, it’s good policy to not worry about it unless it becomes a problem.

But that comes down to design. Unless your using structs you shouldnt be creating a new animation object for each frame for every object. Thats a heap of unnecessary garbage.

Depending on how your projects structured have 1 animation object for each animation thing, or each type of animation.

That 1 object can hold the parameters of where in the animation it is, type of animation, drawing for the animation etc

The new animation objects aren’t necessarily created every frame, just each time a new animation is needed. The animations are like effects, that appear on screen for a bit, do their animation, and then disappear. Multiple instances of the same animation type can exist at once.

As I said, my attempt to implement some sort of object pool IIRC was less efficient than just creating the new animations without worrying about it, though I can’t remember whether it was less efficient in CPU or memory.

I should note that my project is a puzzle game that isn’t performance-critical and, besides the animations, doesn’t feature much movement. An occasional stutter triggered by the GC would thus hardly be noticeable. That said, I may give the object pooling thing another go, just to see if I can get it to work better, but I wouldn’t sweat shipping the game as is without the object pooling.