ContentManager.ReadAsset

Originally posted to Shawn Hargreaves Blog on MSDN, Friday, March 9, 2007

ContentManager.ReadAsset is a new function coming in the next version of the XNA Framework. What is it for, and why should you care?

You almost certainly don't care. In fact you should probably stop reading this blog post right now.

ReadAsset is for crazy people who want to customize how the ContentManager resolves shared asset references, or how it manages object lifespans. The vast majority of users will be fine with the default behavior, so will never need to know about this.

ReadAsset is a protected method that loads data directly from an XNB file, bypassing the higher level policies implemented by the default Load implementation. When called from an overridden Load method, this gives you the flexibility to do pretty much anything you want.

For instance this simple customization bypasses the default shared reference tracking:

    class MyContentManager : ContentManager
{
public MyContentManager(IServiceProvider serviceProvider)
: base(serviceProvider)
{ }

public override T Load<T>(string assetName)
{
return ReadAsset<T>(assetName, null);
}
}

With this tweaked ContentManager, every time you call Load you will get back a fresh copy of the asset. If you load two models that both reference the same texture, they will each load a separate copy of that texture.

The second parameter to ReadAsset is a delegate used to keep track of disposable objects. If you pass null, the default implementation remembers everything that is disposable, and frees them all in one go when the ContentManager is unloaded. You can change this behavior by passing a custom delegate:

    class MyContentManager : ContentManager
{
public MyContentManager(IServiceProvider serviceProvider)
: base(serviceProvider)
{ }

public override T Load<T>(string assetName)
{
return ReadAsset<T>(assetName, IgnoreDisposableAsset);
}

void IgnoreDisposableAsset(IDisposable disposable)
{
}
}

Because the IgnoreDisposableAsset delegate does nothing, this ContentManager will not track asset lifespans in any way. I don't recommend that, but maybe it would be useful for someone who intends to manually dispose all their content, or who places an inordinate amount of trust in the garbage collector!

As a more realistic example, this version reproduces the behavior of the default ContentManager, merging shared assets and keeping track of disposable content:

    class MyContentManager : ContentManager
{
public MyContentManager(IServiceProvider serviceProvider)
: base(serviceProvider)
{ }


Dictionary<string, object> loadedAssets = new Dictionary<string, object>();
List<IDisposable> disposableAssets = new List<IDisposable>();


public override T Load<T>(string assetName)
{
if (loadedAssets.ContainsKey(assetName))
return (T)loadedAssets[assetName];

T asset = ReadAsset<T>(assetName, RecordDisposableAsset);

loadedAssets.Add(assetName, asset);

return asset;
}


public override void Unload()
{
foreach (IDisposable disposable in disposableAssets)
disposable.Dispose();

loadedAssets.Clear();
disposableAssets.Clear();
}


void RecordDisposableAsset(IDisposable disposable)
{
disposableAssets.Add(disposable);
}
}

To implement more sophisticated asset management policies, you can keep track of recursive calls to the Load method, building up trees recording which meshes loaded what textures, how many meshes are referencing each texture, etc. You can then use that information to provide arbitrarily complex loading and unloading functionality.

If you need to, that is. Most people won't want to bother.

Blog index   -   Back to my homepage