XNA Creators Club Online
Page 1 of 1 (14 items)
Sort Posts: Previous Next

LoadContent, UnloadContent, and design patterns

Last post 11/8/2009 8:15 PM by Timotei. 13 replies.
  • 3/11/2008 9:13 PM

    LoadContent, UnloadContent, and design patterns

    Hello all,

    I'm quite new to XNA Framework 2.0 (at least new to trying to do anything serious with it). It's taken me a while to grasp the ins and outs of content loading in the framework, and what impact that has on designing a non-trivial game engine. It's something of a liberty, but I was hoping that if I posted my understanding here, then wiser minds than mine might be able to point out where I'm on the right track, and flame appropriately where I'm not. Also, if by some freak of fate it turns out I'm not talking absolute nonsense (ha!), then this post might be a potential reference for others searching for the same information.


    Apologies for the length of this post. I'm really only scratching the surface of a complex topic, and perhaps with an inappropriate lack of brevity...

    XNA 1.0


    In XNA 1.0, it was necessary to unload and reload content when the graphics device is reset (for example, if the user switches from fullscreen to windowed mode). By “content” we mean anything which internally referenced graphics device resources such as textures, vertex buffers, etc. This includes Models, Effects, VertexBuffers, etc. Game.LoadGraphicsContent() and Game.UnloadGraphicsContent() (and the GameComponent equivalents) were the requisite methods which all classes owning such content were required to implement.. Moreover any class which referenced such content, even if it didn’t own it, used to have to be aware that its previous references to a Model etc. could become invalid afer a graphics device reset; the objects would still exist, but be rendered useless and likely to throw exceptions. This lead to fun and excitement for the game developer.


    XNA 2.0


    In XNA 2.0, the GraphicsDevice is “virtualized”. This means that we no longer have to worry about graphics device resources, or the GraphicsDevice itself, disappearing on us at any point during the running of the game, including during a graphics device reset. Model instances, VertexBuffer instances, etc. will now always be valid and useful. In most cases we can simply ignore the idea of losing our content at any point during the running of the game. In some cases, such as DynamicVertexBuffer, the object (and thus references to it) will remain entirely valid, but the contents of the object may disappear during a device reset. (Why? The long answer is, well, long, but the short answer is “that’s just the way it is”. In Windows, XNA is built on top of Direct3D; Direct3D developers have to deal with it, and thus so do we. On the bright side, so many other things are easier for us by an order of magnitude.) In such cases this is clearly stated in the documentation, as is the way to handle it. (For example, DynamicVertexBuffer.ContentLost and .IsContentLost).

    LoadGraphicsContent and UnloadGraphicsContent, on Game and GameComponent, are obsolete as a result. They still work, but new and simpler methods on Game and GameComponent called LoadContent() and UnloadContent() have superceded them. Where I refer to LoadContent() and UnloadContent() below, read “…and LoadGameContent()/UnloadGameContent() for backward compatibility”. 

    The upshot of the above is that Game.LoadContent() is now only called by the XNA Framework once during the running of the game: on startup. Game.UnloadContent() is now only called once by the XNA Framework as well: on shutdown. For GameComponents, LoadContent() is called when that component is added to the Game’s Component collection (often on startup, in Game.BeginRun()), and UnloadContent() is called when that component is removed from the Game’s Component collection, or when the Game object is Dispose()’d on shutdown.

     

    Handling Content the Simple Way


    What does this mean? This means that the use of LoadContent() and UnloadContent() for managing the loading and unloading of resources is purely optional. I’ll explain this below.

    In simple games, LoadContent() is a convenient place to load everything. Your game is starting up and the game loop is not running yet. UnloadContent() is a good place to unload your content; though in practical terms, not a necessary one unless you make it so yourself. More on this later.


    Handling Content the Complex Way


    In complex games, bear in mind that you’re perfectly able to load and unload content whenever you like. Doing it on Draw() is a recipe for pain and suffering, but anywhere else is absolutely fine. Just be aware that this is most likely going to cause disk access and temporarily hurt your framerate. So if you can, load things at well defined points, such as on startup (in LoadContent()) or during a transition between game states or levels. 

    Of course, if you’re loading content all the time and never unloading anything, something has to give eventually. Ideally you’ll set things up so before you load a bunch of new content, you unload corresponding existing content. You can have multiple ContentManagers, if you wish, in order to “partition” your content into sections which you load and unload at different points during the game. However many ContentManagers you have, you can unload a ContentManager’s managed content, such as Models or Effects, by calling ContentManager.Unload() manually.

    Two things to remember here: this will not cause your UnloadContent() methods to be called by the XNA framework (there wouldn’t be much point, since in XNA 2.0 the point of UnloadContent() is that it doesn’t have to deal with content managed by a ContentManager); and if parts of your game code are hanging onto references to objects you obtained from that ContentManager, those references are now going to be pretty useless.

    If you try to use an Effect which has been unloaded in this way you’ll just end up with an ObjectDisposedException. If you try to use a Model, that model likely contains Effects, so again you’ll get an exception. Therefore, if you’re going to call ContentManager.Unload() you need to make sure all references to unloaded objects in your code have been tidied up, and that your code expects this to happen. The easiest way is to set such references to null, and check them before use, taking other appropriate action as required.

     

    UnloadContent() as Optional


    As previously stated, the only time the XNA Framework calls Game.UnloadContent() is when your game is closing down. The only time it calls GameComponent.UnloadContent() is if you manually remove that GameComponent from the Game yourself, or when the game is closing down. You do not need to unload content managed by a ContentManager (such as Models and Effects) here: that’s all handled for you.

    In fact, you don’t actually need to do anything here. Note that there is a difference between the need to do something, and best practice for doing something.


    The documentation, and the internet, states that in UnloadContent() one should release all content not managed by a ContentManager. An example might be calling Dispose() on a DynamicVertexBuffer.

    This is in fact optional because, unless you’re manually removing GameComponents from the Game, XNA is not going to call UnloadContent() unless your game is shutting down; and all XNA objects which hold onto external resources - such as content tied up with the graphics device - implement finalizers. When your game exits and the .NET framework shuts down, all finalizers will be called, and all such resources will be tidily released.


    I did say though that there is a difference between need and best practice. In .NET in general, and thus in XNA, it is best practice to Dispose() of IDisposable objects as soon as possible. Even in the simplest case, UnloadContent() is going to be called prior to finalization. In a simple game this is completely academic, but is tidy and good practice. In a complex game, management of content may well be an issue, and with a lot of content, you’re probably going to need to make sure that content gets unloaded as soon as it isn’t needed, so you can replace it with other exciting content. In this case, LoadContent() and UnloadContent() may form part of a useful pattern which you can follow to help you manage content and other resources: see below.

     

    LoadContent() and UnloadContent() as a Design Pattern


    One thing you will see with relation to content management is that some sample code (and I’m talking about good quality sample code and tutorials here; as always, take random tutorials and samples you find on the internet with a pinch of salt, as they may not be doing the “right thing”) will explicitly call LoadContent() and UnloadContent(). Some samples even declare these methods on classes other than Game and GameComponent.

    An example would be the “game state management” sample. (I’m going to assume, for the purposes of this example, that you’re at least slightly familiar with this sample.) Here, a ScreenManager (which derives from (Drawable)GameComponent) owns a collection of GameScreen objects which each implement methods called LoadContent() and UnloadContent(). The ScreenManager calls each GameScreen’s implementations of LoadContent() and UnloadContent() from its own methods of the same name. This is because: it is convenient for each GameScreen to load its own resources; the method may as well be called LoadContent() as anything else; and for completeness it makes sense to ask any existing GameScreen to load its content when ScreenManager.LoadContent().


    Now this sample is rather more clever, and is using LoadContent() and UnloadContent() as a pattern. When a screen is displayed via ScreenManager.AddScreen(), the ScreenManager calls GameScreen.LoadContent(). When a screen transitions off via ExitScreen(), or is removed the hard way via RemoveScreen(), the ScreenManager calls GameScreen.UnloadContent(). This is clearly outside the normal way in which LoadContent() and UnloadContent() are called by XNA, but follows the same pattern. It ensures that resources specific to that GameScreen can be handled by the screen in a consistent way regardless of the exact lifetime of the screen, and that they are tied to the lifetime of that screen.

    So unless your content lifetimes are very simple (load all at startup, destroy on shutdown), it may be worth your while to follow the same pattern. You can design your game so that resources are owned either by a content manager, or by a class which implements LoadContent() and UnloadContent(). You can ensure that whatever parent class owns that class also implements LoadContent() and UnloadContent() and calls its children in turn; and so on. You can also ensure that ultimately one of these ancestral classes is a GameComponent (and is part of the Game’s Components collection), or is the Game itself. If you have multiple content managers, you can consider who owns them, and ensure that their lifetime is properly controlled.


    Building on these basics, judicious use of LoadContent() and UnloadContent() will allow you to load resources when they are required, and dispose of them as soon as they are not.

    A further useful step is to also follow the Dispose pattern. This is a familiar one to seasoned .NET developers. The game state management sample does not do this, probably because it would additionally complicate the sample for developers new to .NET, and arguably the sample suffices well without it. But it may be worth your while to read up on this pattern; the XNA framework itself follows it closely.


    A simple way to follow it is to ensure that objects which manage content implement IDisposable, and have Finalizers, as and where appropriate according to the Dispose pattern. A detailed discussion of the pattern is beyond the scope of this article, but for those familiar with the pattern, a trivial suggestion is to implement all your content unloading in UnloadContent() as above, and when Dispose(bool disposing) is called with a disposing value of true, call UnloadContent(). Then you ensure as usual that all IDisposable objects are disposed of as soon as possible, which will cause their content to be unloaded as soon as possible. You then only need call UnloadContent() on a class explicitly from within its parent class’ UnloadContent() implementation, and can otherwise stick with Dispose(). If a content-owning object is never disposed, finalizers on the underlying XNA objects will cause them to be cleaned up nicely anyway.

     

    Conclusion


    Above all else, bear in mind that there is no magic to LoadContent() and UnloadContent(). These methods do nothing special in the XNA Framework, are there for your convenience and are entirely optional. You can load and unload content pretty much whenever you feel like it, according to the needs of your game. But these methods provide a useful pattern, which alongside the Dispose() pattern may make your life easier as your game grows in complexity.

    --formerly Genstein
  • 3/12/2008 11:23 AM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Wow, great writeup!

    I don't have anything useful to add here, just wanted to say that all looks accurate to me.
    XNA Framework Developer - blog - homepage
  • 3/12/2008 2:16 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Yes, I also think the write-up is a good service to the community!

    However, there is currently no way to unload only a single asset. Looking at the ContentManager in Reflector.NET, it does not "hook" into the content objects, so disposing the content objects will mean that anyone trying to re-load that object will get a copy of the disposed object, not a fresh copy. Similarly, it does not use weak references, so even if your application code "forgets" about the object, it's still kept alive by the content manager.

    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 3/12/2008 3:16 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Good catch Jon.

    You can customize the dispose and referencing behavior of the ContentManager if you want to make it use something more fancy like weak references, but by default, all content will be kept alive until the manager is unloaded, and all the content within a given manager can only be unloaded as a single block.
    XNA Framework Developer - blog - homepage
  • 3/12/2008 4:07 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    I think a very simple improvement would be for the content manager to add an event handler for the disposed event of each content it creates. It can then remove that content from the cache (and the list of disposables) which means you could re-load it later without getting rid of the content manager entirely.

    I'm posting this on Connect.

    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 3/12/2008 4:27 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Wow! Lots of good information there. I noticed one thing, though:

    "For GameComponents, LoadContent() is called when that component is added to the Game’s Component collection"

    That's true some of the time, but not 100% correct. What actually happens is this: As your game is starting it keeps track of all the Components you add, and stores them in a list of "things to initialize." Then, once the GraphicsDevice has been created, all of the GameComponents that have already been added are initialized in one go. (Initialize calls LoadContent.)

    After the GraphicsDevice has been created, if you add a new GameComponent to the collection, Initialize and LoadContent are called right away.
    Eli Tayrien - XNA Framework Developer
  • 3/12/2008 4:50 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    jwatte:
    I think a very simple improvement would be for the content manager to add an event handler for the disposed event of each content it creates.


    The problem with that is that there is no standard interface describing objects that have a Disposing event (unlike the Dispose method, which is indicated by the standard IDisposable interface). The Disposing event is just a convention that we happened to implement on most of the XNA Framework graphics types. So there's no generic way to examine which objects provide that functionality, or to subscribe to their event. We could use reflection to examine each instance we created, but in the absence of a more formal marker interface I'm not sure how good an idea that would be.
    XNA Framework Developer - blog - homepage
  • 3/12/2008 5:12 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Perhaps an IDisposableContent interface is in order.  Just have it inherit from IDisposable.  The content manager could simply do a "contentObject is IDisposableContent" check, and add an event handler if true.  This wouldn't work if people didn't use the IDisposableContent interface for their custom content pipeline types, but an addition to the documentation would at least explain that disposable content pipeline types should implement IDisposableContent instead of IDisposable directly.
    Microsoft DirectX/XNA MVP
  • 3/12/2008 7:43 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    ShawMishrak:
    Perhaps an IDisposableContent interface is in order.


    I had the same thought. You could even qualify Load<> such that the type argument to Load<> needs to be an IDisposableContent.


    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 3/13/2008 4:18 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Thanks so much for your eloquent replies. Nice to know I'm not barking up entirely the wrong perennial woody plant.

    I certainly have no issue at the moment with unloading content in large (ContentManager-sized) blocks. I can see that in some game types, that could be inconvenient - such as in an MMO, for example, when regularly loading and unloading a variety of character and armour models as a player moves around city. In those cases perhaps fine grained resource control is more vital.

    On the other hand, the ContentManager seems to be a very lightweight class - essentially a hashtable which knows about ContentReader<T> - and so could presumably be easily substituted for a hand-rolled equivalent with different load/unload semantics.

    --formerly Genstein
  • 9/22/2008 3:51 AM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    So I read your nice explanation of the Content Pipeline functionality and learned a few things...nice write up.

    I'm having issues in trying to be tidy (loading and unloading content manually as needed).  I try to load/unload content per level of my game.  At the end of the level I do a Content.Unload(); and then load all the new content (some into new reference variables, and some into old reference variables).

    I'm running into a wierd exception.  It says, "Cannot access disposed object."  It is referring to a Texture2D I had recently unloaded and reloaded with content.  When I break at that point and look at the texture object I'm trying to access, the IsDisposed property says false.  It is not disposed, but the exception is still thrown.  Why would this be? 

  • 10/14/2009 5:54 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    What a marvelous writeup!
    Thank you - let me know if you write anything else.

    Finally I understand what the deal is with that devil Load/Unload stuff. ScreenManager example makes it confusing, because I thought Load/Unload-Content is exclusive to GameComponenets - but now I understand that it is optional.

    It would be great if someone could write up about general way of using GameComponenets. Initially I thought of using GameComponents for all the "components" of my game, but later realized that GameComponents should be used more like Parents in a tree; with each GameComponent managing it's children (non-game-components) and taking care of their loading and other things for them.
  • 11/6/2009 4:29 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Hey this is a great article. It clears many things up as I start to performance tune my nearly completed game. Thanks!!!
  • 11/8/2009 8:15 PM In reply to

    Re: LoadContent, UnloadContent, and design patterns

    Nice article. 5 stars from me. Also, I like the ideea of almost every big part from our games to implement a Load/UnloadContent method. Is just very good from a perspective of OOP and also very flexibile and usable.
Page 1 of 1 (14 items) Previous Next