I will note some small things that I wish I’d learned earlier, but hopefully one or two of you can appreciate this list of significant and not so significant things to consider.
Object Oriented Programming
So, yeah I love OOP. And so do the tools I use, like ReSharper for Visual Studio (this thing is godsend, heavily recommended.)
I did work with OOP in mind for many years now and so it was pretty obvious to me how to do stuff, which later on turned out to be great for clean programming, but maybe not optimal for applications which need to crank out maximum performance at thousands of frames per minute.
Now it should be worth to note that on modern hardware, especially when not CPU bound, these things don’t matter as much. Many times the beauty of the program and the ease of working with a proper OOP setup saves more time to the programmer to an extend that makes it worth using them anyways (your time vs. program runtime)
Just a quick list of what to avoid, especially if called hundreds of times per frame.
- foreach –> use “for” instead, foreach creates unnecessary garbage. This does not apply to all foreach loops, however, but you are safe with “for”.
- LINQ Expressions –> look awesome (and make the code look sophisticated to absolute beginners) but are even worse in terms of garbage generation.
- Events –> if your projectiles call an event on all the enemies to check if it has hit, you might as well rewrite all your code. Again unnecessary garbage creation.
- Interfaces are apparently very slow. Call directly. Something I had to learn at a point where basically my whole infrastructure implements interfaces and the calls are made via these :(
Indirect calls in general are not great.
Check these out:
- there is other stuff like inlining everything and using fields instead of properties but I cannot comment on that and I don’t think it’s worth thinking about these.
- Lists. Lists are awesome, I use lists a lot. But if you have big lists which change a lot use pools or arrays instead. Again, to avoid unnecessary garbage.
- SUPER IMPORTANT:
Do not manually tie together strings like this
string s = “number: “+ mynumber;
This will generate garbage every time. Use a stringbuilder instead. Good sources:
- can you think of others? Leave a comment!
Don’t use “SetRenderTarget”
EDIT: With the new releases of Monogame this is no longer a problem. You can use SetRenderTarget to your hearts content. SetRenderTargets() with bindings is still needed for multiple rendertargets though (as it should)
“Why not? Every tutorial ever uses it!” you may say, but hear me out.
One of the major problems as well as benefits with using .net as an underlying platform is garbage collection. It’s awesome because you don’t have to bother allocating memory manually and you can’t really forget to release said memory later, so memory leaks can basically never happen (well they can in some cases, I talk about that later).
But – the garbage collector will cause a small frame spike every time it decides enough useless stuff has piled up and needs to be disposed.
So we must avoid creating a lot of data (arrays of data!) every frame. However, if you want to switch your current render target you do just that.
will create a new array containing “myRenderTarget” every frame! And renderTargets are not small at all. In fact, render target changes accounted for more than 40% of my whole garbage alone! So we should avoid it.
Usually, somewhere in the initialize() function and the resize() function you create your renderTarget like this:
myRenderTarget = new RenderTarget2D(_graphicsDevice,
(int)(width * GameSettings.SuperSample),
Now, what you should do as well is the following:
Have a new field in your render class of the type RenderTargetBinding – like this
private readonly RenderTargetBinding _myRenderTargetBinding =
and assign it like this under your renderTarget creation:
_myRenderTargetBinding = new RenderTargetBinding(_myRenderTarget);
when assigning the current renderTarget to the GPU use:
the important thing here is to use SetRenderTargets instead of SetRenderTarget (note the plural!)
Obviously when using multiple render targets, the same thing applies, your binding array just contains more items.
The image below captured the “hot path” of memory allocations. Note how the difference.
In case you use geometry instancing to pass a lot of individual geometry with the same mesh in one call you have to use SetVertexBuffers.
Usually like this:
This will create crazy amounts of garbage, since, again, every frame we set up a new array and fill it with these properties.
Have a field called something like
private readonly VertexBufferBinding _vertexBuffers = new VertexBufferBinding;
and then fill it like this
_vertexBuffers = _myMeshVertexBufferBinding;
_vertexBuffers = _myInstancesVertexBufferBinding;
Obviously filling the _vertexBuffer should only happen once, since the mesh of the model does not change. So only use the second part every frame. Then call:
Well, you are most likely using Visual Studio. And it’s really great just by itself (minus the crashes). Especially stuff like the deep Git integration and a nice variety of profilers.
But you can improve your experience a lot. Stuff I would heavily recommend:
- ReSharper, simply an improvement for intelliSense. Helps you tons with refactoring and gives advice on optimization. Hard to work without it once you get hooked.
- HLSL Tools for Visual Studio. If you use Visual Studio to write your shaders this is a godsend. You can find it in VS studio itself in “Extensions and Plugins”
- Intel Graphics Performance Analyzers (GPA) – I have tried many profilers for the GPU side of things, including RenderDoc, AMD GPUPerf, the default VS ones and some more. All of these are good, but Intel GPA is the most competent and comprehensible for me.
I may or may not add some other stuff, but I hope what content is there will help you regardless. If you have more tips or improvements for this entry, feel free to leave a comment here (or if you don’t like WordPress, you can hit me up on twitter as well.
Obviously links to other beginner tips would be appreciated a lot!