0

I'm developing for Windows Phone XNA and would like to load textures with a smaller size to decrease memory impact where the full image isn't required.

My current solution is to use a rendertarget to draw and return that rendertarget as a smaller texture to use:

public static Texture2D LoadResized(string texturePath, float scale)
{
    Texture2D texLoaded = Content.Load<Texture2D>(texturePath);
    Vector2 resizedSize = new Vector2(texLoaded.Width * scale, texLoaded.Height * scale);
    Texture2D resized = ResizeTexture(texLoaded, resizedSize);
    //texLoaded.Dispose();
    return resized;
}

public static Texture2D ResizeTexture(Texture2D toResize, Vector2 targetSize)
{
    RenderTarget2D renderTarget = new RenderTarget2D(
        GraphicsDevice, (int)targetSize.X, (int)targetSize.Y);

    Rectangle destinationRectangle = new Rectangle(
        0, 0, (int)targetSize.X, (int)targetSize.Y);

    GraphicsDevice.SetRenderTarget(renderTarget);
    GraphicsDevice.Clear(Color.Transparent);

    SpriteBatch.Begin();
    SpriteBatch.Draw(toResize, destinationRectangle, Color.White);
    SpriteBatch.End();
    GraphicsDevice.SetRenderTarget(null);

    return renderTarget;
}

This works in that the texture gets resized but from memory usage it looks like the Texture "texLoaded" doesn't get freed. When using the uncommented Dispose method the SpriteBatch.End() will throw a disposed exception.

Any other way to load the texture resized for less memory usage?

1 Answers1

0

Your code is almost ok. There's a minor bug in it.

You'll probably notice that it only throws that exception the second time that you call LoadResized for any given texture. This is because ContentManager keeps an internal cache of content that it loads - it "owns" everything that it loads. That way, if you load something twice, it just gives you back the cached object. By calling Dispose you are disposing the object in its cache!

The solution, then, is to not use ContentManager to load your content - at least not the default implementation. You can inherit your own class from ContentManager that does not cache items, like so (code is based on this blog post):

class FreshLoadContentManager : ContentManager
{
    public FreshLoadContentManager(IServiceProvider s) : base(s) { }

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

Pass in Game.Services to create one. Don't forget to set the RootDirectory property.

Then use this derived content manager to load your content. You now can safely (and now should!) Dispose of all content that you load from it yourself.

You may also wish to attach an event handler to the RenderTarget2D.ContentLost event, so that, in the event the graphics device is "lost", the resized texture gets recreated.

Andrew Russell
  • 26,924
  • 7
  • 58
  • 104