Texture caching in 2D XNA game

Developing on the XNA framework for Xbox, I have different 2D image resources being used depending on the level. At the end of a level, I'd like to leave as much loaded as is reasonable, so that the next time a level needs that resource, it's already loaded. However, there will be a great many resources, so I'm assuming that it's a poor idea to leave everything loaded always, simply because memory is limited. Is there a good way to know how much to reasonably unload to keep things running smoothly?

My idea: If the .net garbage collector supported soft references (like the JVM), I could change references to any resources not being explicitly used to soft references, and change them back or reload when they're needed again. That way the garbage collector, who knows the state of memory much better than I, can intelligently drop references as needed when loading new content. In other words, a simple cache. This soft reference construct doesn't exist in C#, however; is there an equivalent or some better way to handle this?

• In c# you have WeakReference – Kikaimaru Jul 27 '12 at 8:13
• The concept you described is called an Object Pool – person27 Jun 26 at 2:28

First of all, probably the nicest way to implement this is by customising ContentManager as described in this blog post. Implementing your own content management solution over the top is silly when you can just customise the caching policy of the existing one.

The number one problem with the solution you are suggesting is that most XNA resources are "unmanaged". Check out this answer for a description.

This means that you cannot rely on the garbage collector to unload resources when memory runs low. Because the garbage collector cannot see the unmanaged memory - not to mention GPU resources - that many of these objects are using. That is why many of these objects are IDisposable.

Now, if you accept that the GC won't tidy up for you automatically, you may consider the idea of invoking the GC between levels with GC.Collect(). This would work - but now let's come back to customising ContentManager - because we can get the exact same effect without abusing weak references:

The external interface I am proposing works like this: Right before you start loading your next level, you call:

myCustomContentManager.MarkAllAsUnused();


You then start loading your next level, calling an overwritten version of Load as normal. Once you've finished loading, then call:

myCustomContentManager.UnloadAllUnused();


Internally you could implement this by maintaining two resource caching dictionaries in your custom content manager (the default ContentManager only has one). An "in use" dictionary and an "unused" dictionary. On a load request, check both dictionaries, and move the loaded resource to the "in use" dictionary if it isn't already there.

On MarkAllAsUnused, move everything to the "unused" dictionary, and on UnloadAllUnused, unload only that dictionary.

Of course, this is an optimisation. If you don't need to be optimising load time, but you still need to optimise memory, there are simpler ways.

• Following through all the links, I have a much deeper understanding now. Two dictionaries was exactly what my other attempt was, but now I understand the proper way to structure that, thank you!! – akroy Jul 29 '12 at 18:36

You know best what resources are going to be needed in the next level so instead of soft references or using a solution that involves manually loading and unloading all content why not just use a cache (you've talked about it a couple of times). KISS really applies here I think.

So just encapsulate the content manager. Create the new methods AddToCache(...), RemoveFromCache(...) and create a Load method that first checks the cache to see if something is available.

This approach isn't fancy, isn't automatic but I think it might be exactly what you need.

• XNA's ContentManager already acts as a cache. Generally it's better to modify its caching policy using inheritance, than creating a wrapper. Especially given that, unless you modify it anyway, your only option for unloading is to unload the entire content manager at once. – Andrew Russell Jul 27 '12 at 12:32
• In that case he won't even need to make his own. He'll just have to remember what not to unload ever frame (I believe that the Unload method even removes it from the cache, doesn't it? The documentation don't mention the cache here anyway msdn.microsoft.com/en-us/library/…). – Roy T. Jul 27 '12 at 19:01
• ContentManager keeps a database of everything it has loaded. When asked to load something, it will check its database first (ie: it is a cache). The default behaviour of ContentManager is to unload the entire database when you call Unload() - you must inherit from ContentManager if you want more fine-grained control over what is unloaded. – Andrew Russell Jul 28 '12 at 6:13
• Damn I misread, it's indeed all data :/... – Roy T. Jul 28 '12 at 7:10