Strange memory behaviour

Hello!
While working on content management, I noticed that my loaded from stream textures are making the game use a lot of memory. It made me quite surprised because I made dynamical loading/unloading and meanwhile my old project with plain dictionary full of textures was using like 5 times less or so.
So I started testing around and got into situation where loading texture into my ContentManager class is better than executing same code in main game class.
Example of loading from main class:
_items = new Dictionary<Item, TextureAtlas>(); (...) _contentManager.LoadContent(_texturePackName); // this method executes same code as line below _items.Add(Item.Gem_blue, TextureAtlas.Create("BlueGem", loadTextureFromFile("Content/giant_white_nothing.png"), 7444, 3264)); (...) private Texture2D loadTextureFromFile(string path) { FileStream fileStream = new FileStream(path, FileMode.Open); Texture2D sprite = Texture2D.FromStream(GameMain.Instance.GraphicsDevice, fileStream); fileStream.Dispose(); return sprite; }
The _contentManager.LoadContent method is pretty much the same. There is nothing that would change anything in the way textures are stored or loaded, just lots of other stuff that gets loaded as well.
Differences are like this:
Not loaded at all: 120MB
Loaded from manager: 260MB
Loaded from main: 475MB
And I’m really out of ideas what could be the reason.

Every time loadTextureFromFile is called, it’s creating a new texture in memory. This texture needs to be disposed after it’s used. I don’t know the implemention of your ContentManager, but it should be caching the texture that’s created and reusing it instead of creating a new one each time.

Ah above code is called in LoadContent so it runs just once. Indeed whenever the texture is used on the game drawing side, it’s loaded (as in: reused same object) from “cache” of ContentManager.

So the ContentManager automatically caches Textures loaded? Dang, looks like I can get rid of my image caching system, then.

XNA ContentManager does that.
If you use method similar to one I posted, you have to cache them yourself.
Just making sure, as my usage of ContentManager name might been misleading :sweat_smile:

Maybe I am understanding something wrong - but you don’t need to cache a texture. Once loaded it resides in GPU memory and if you want to access it you just tell the GPU which you want. It’s already there. (Loading all that textures to the gfx mem is what happens when you sit in front of a loading screen of some AAA game, that’s why modern GPUs have a lot of RAM)

The only time you need to send the texture data again is, if you modified it, or when the device was lost (that is what the ContentManager may handle for your automatically, but I am not exactly sure, if it does. At least I never had to do that manually in XNA/Mono while it was sort of mandatory in plain DirectX)

Another way to put it.

From stream textures refernces aren’t tracked / cached by the content manager that is why you must explicitly dispose of any of them that have been instatiated using the from stream method before assigning a new texture instance to that reference. Or before exiting the application typically in unload content.

Like 99% without looking that the content manager keeps its own internal reference to the texture so this isn’t a problem for it when unload is called.

But if I load it from stream in first place, how can I retreive it later on? I think that’s why I need the reference to it.

Edit:
Actually GPU memory doesn’t seem to be bothered by whatever I am loading. Just tried starting the “load everything” project and no matter if I loaded my giant file, usage was the same.

When you load FromStream its the same thing as creating a new texture by using SetData.

Texture2D t = // in either case from stream or set data to a new texture.

t is the reference to the texture you can’t mix this in with the content manager.
You are fully responsible for t now which is a reference that the video card driver holds as well as the gpu.

You have to be very careful not to overwrite this by assigning another texture reference to it.

t = t2; // how will you ever dispose of t now ? // Answer you can't because you don't have a handle to it anymore

In the above case t is now a reference to t2, not a value by value copy of it, disposing t2 can’t dispose the original t and you have no way to get the reference back to dispose of it the memory has leaked.

You have to be very careful not to copy it to another texture then dispose of that texture then use this one

Texture2D t2 = t;
t2.Dispose();
string s = t.Name;  // very bad null reference exception best case. 
t.Name = "blah" // If this were c++ this could be seriously disastrous as this would constitute a attempt to write to a dangling reference.

.

t2 = t;
t2 = t3;
t.Dispose()

// you just disposed t3 and t is stuck in memory again with no way to get it out. All 3 of these references are now or shortly will be null.

Fine i wont do that but then you still can…

Consider this.

public class level{
   // ....
   Texture2D t;
   private void LoadFromFile(string filename){
     t = fromfile ...
   }
 //...
}

public Game { // ect..

level lvl1 = new level( ... );
level lvl2 = new level( ... );
level currentLevel;
public void LoadLevel(level n)
{
   currentLevel = n.Load();
   // ********************* poof you just lost your refernce to t  **************
   // this will keep on running along fine no exceptions thrown and everything seemlying is ok.
   // but its not.
   // worse it has extended the problem into the level of the os.
   // were it can persist as a vm or gpu memory leak os wide till the computer restarts or a out of memory exception is thrown.
   // in fact this sort of leak is so complicated i can't even directly tell you i fully understand everything that can be affect.
}

In short FromStream or SetData is extremely easy to misuse and is why we have a content manager.
There are times were you may require these methods, for example in a paint program or such were you must juggle user selected textures at runtime. However you shouldn’t be using them unless you require them because of all of the above mentioned ways and probably more i forgot about that you can screw yourself up.

This is nice and informative post, although I’m bit not sure about gpu/vm leaks as I see everything returns to old state soon as I kill the process. Nevertheless it’s matter of few lines to add the unload into my implementation, so I might do that just in case it’s up to OS/drivers/anything in the world.

Currently in my implementation I keep array of ContentItems, each of them having object reference (+ other stuff like id, type etc). On the first attempt of getting the stored object, it is getting loaded and then stays there until unload gets called. In unload I call Dispose and set reference back to null (so next it time it will load again).
If I want memory chart to look nice I also call GC.Collect at that point, otherwise it’s bit random. I know it should be limited somehow but for now I don’t have big enough project to bother with it (but keeping it in mind).

Just as I was writing this I started to think that entire “issue” with old project and different memory behaviour depending on place of loading might be not related to Texture2D itself, but to garbage collection.
So I put GC.Collect right after loading… And yeh, now it doesn’t matter which class loads it…