Game Development Community

Torque X huge memory leak - Fixed

by Giuseppe De Francesco · in Torque X 2D · 03/18/2010 (11:54 am) · 298 replies

Well,

I'm not sure this is the right place to post this, maybe the blog? At any rate I'm posting here now.

Torque X has a huge memory leak, but seriously! I was working to a RPG game for a start so I didn't really notice, but 2 weeks ago I started YASS, a multiplayer side scrolling space shooter: a lot of particles, really a lot, and a lot of projectiles as well.

To let you understand here follows a short video of the game:



This is the memory situation after 3 minutes of gameplay (single player!!!) the game slowed down a lot:

forum.xnaitalia.com/download/leak1.png

So, I searched the forum and I realized that this issue is an old one and GG never got a grip on it (go figure why...) so I decided to fix the problems myself because I really need to publish YASS ;)

Long story short: I revised the whole engine and made a lot of fixes, some of them really of great importance, so here it is the situation playing my game (as you can see after 9 minutes the game still is 20% less than the original 3 minutes!):

forum.xnaitalia.com/download/leak2.png

There are still many stuff to fix, but at any rate in the current state I'm able to publish the game ;) I will make the SVN diff files available upon request (free of charge: pino AT dftgames DOT com).

Cheers,
Pino

About the author

In the software eng. field since 1981, in charge of R&D during last 10 years. IEEE Senior Member (and volunteer).

#21
03/20/2010 (1:45 pm)
Heya,

A couple things.

My GUI/Util.cs is a bit diffrent from yours

I don't have this method:
private static void _CreateDynamicVertexBuffer()

Also this method is diffrent:
Mine looks like this:
private static void _CreateVertexBuffer()
        {
            int sizeInBytes = 1024 * GFXVertexFormat.VertexSize;
            _vertexBuffer = ResourceManager.Instance.CreateVertexBuffer(ResourceProfiles.ManualStaticVBProfile, sizeInBytes);
            Assert.Fatal(!_vertexBuffer.IsNull, "DrawUtil::_CreateVertexBuffer: failed to create a vertex buffer!");
        }


Also everything builds and compiles ok but the GUI is completely fried. The graphics dont display properly ect...

I'll look into it on my end and see what I can discover.

#22
03/20/2010 (2:46 pm)
This line in GUI Bitmap was causing the problem
res.Instance.Dispose();

Also disposing the SimpleMaterial Causes a problem in the GUI and a null reference.

This line:
_texture.Instance.Dispose();

causes a null reference.

I tried changing it to this:
if (_texture.Instance != null)
            {
                //_texture.Instance.Dispose();
            }

This caused a new problem though. If you swap out Bitmaps in the GUI the GUI gets confused and will swap in the wrong bitmaps.

Taking the line out fixes this problem. Not sure what problems this will cause though.
#23
03/20/2010 (2:51 pm)
Another issue:

When unloading a level and then reloading it A null reference is found here:

T2DStaticSprite.cs
public RenderMaterial Material
        {
            // forward access to quad object
            get { return _quad.Material; }
            set { _quad.Material = value; _quad.RefillVB = true; }
        }

Looks like the problem is caused because a material isn't loaded onto a template before the T2DSpawnObject tries to clone the T2DStaticSprite. :(
#24
03/20/2010 (3:04 pm)
I just reverted GUIBitmap back to the original file, so it's no longer an IDisposable and it works like a charm. Somehow the bitmap is being disposed between creating it and displaying it. Since its only used in the GUI and there are not many instances (less than 10) I think I can live with that. I use a few GUIBitmaps in game to display health bars, little icons for lives.

If you want to test it out, make a new project TorqueX Gui 2D Pro and run it with your changes. I put a break in the first line of the GuiStart, and you will see that once it tries to render there is nothing to render. I tried tracing it back but have not found out where this happens yet.

Overall though I am pretty pleased with the results, I can watch in memory and the T2DParticleEffects and T2DParticleEmitters actually decrease at times. My GCLatency has dropped to 25-27ms every 7-10 seconds. Before this the GCLatency would start out like that, and slowly build until the level was finished, easily growing to over 60ms. As you guys can figure out, at 60fps a frame is 16ms. Those garbage collections would cause my game to drop frames in a very noticeable manner.

#25
03/20/2010 (3:11 pm)
@Matt

In my vanilla 3.1.4 Util.cs i have this:

private static void _CreateVertexBuffer()
{
   _vertexBuffer = ResourceManager.Instance.CreateVertexBuffer(ResourceProfiles.AutomaticStaticVBProfile, vertexSizeInBytes);

   Assert.Fatal(!_vertexBuffer.IsNull, "DrawUtil::_CreateVertexBuffer: failed to create a vertex buffer!");
}


private static void _CreateDynamicVertexBuffer()
{
   _dynamicvertexBuffer = ResourceManager.Instance.CreateDynamicVertexBuffer(ResourceProfiles.AutomaticStaticVBProfile, vertexSizeInBytes);

   Assert.Fatal(!_vertexBuffer.IsNull, "DrawUtil::_CreateVertexBuffer: failed to create a vertex buffer!");
}

This is pretty weird, tiny little code diffs as if they changed the DL at some point?
#26
03/20/2010 (3:42 pm)
Strange :(

Not a good sign that our code all differs slightly. I bet you they did put up new versions without changing the version number.

Are you having an issue with unloading and reloading a level like I am?

It seams that textures don't get re-loaded properly

Haven't been able to figure out why
#27
03/20/2010 (5:56 pm)
Hi guys,

I really don't have any issue with unloading and reloading, but the fact that our base code differs is not really promising... that's quite bad news.

I can tell that my 3.1.4 msi file has this revision number: {1A493F2E-8EAF-4632-9FA4-E8F857869406} (but the "content created" field looks ridiculous: 1st of Feb 2006!!!). Its size is 35.2MB. I downloaded it on the 18th of November 2009.

At this point some word from GG guys would be really appreciated because if we cannot be sure of sharing the same 3.1.4 code it's quite a problem.

If we cannot be sure about that the only thing I can do is to send you guys the full source code (as an SVN backup, so we can share the very same code base). This won't be a license infringement because I can see here that you actually own the Pro version, but it's quite annoying to know that we are all talking about a 3.1.4 release but the code we have is not the same... it's depressing.
#28
03/20/2010 (6:51 pm)
My 3.1.4 is the same as Henry's.
#29
03/20/2010 (7:06 pm)
So I re-downloaded the Torque X install file. Not the same 3.14 as I had before and looks to be the same as yours.

I'm going to copy over the classes and then re-implement you changes.

I'll let you know how it goes.
#30
03/20/2010 (8:57 pm)
I have no problem loading and and changing levels, however at the end of a level I pop an overlay dialog, showing scores etc, press A, GUI Screen with some story, press A, load level, then drop in.

#31
03/20/2010 (9:31 pm)
In T2DSceneObject.cs Line 388 this tiny method is missing, and of course I happen to use it ;)

public string MountedName
{
   get { return _mountedTo.Val.Name; }
}


#32
03/21/2010 (12:03 am)
So copied in the updated code. My code should now match.

I'm having a few issues still :(

1) My Gui Bitmaps no longer fade in and out.

2) My Gui will crash unless I remove this line from the Dispose() method in SimpleMaterial.cs (this is after reverting GUI Bitmap)

_texture.Instance.Dispose();

3) Lastly I'm still getting a crash due to a null reference error when I unload and then reload a level.

This happens when I'm loading the 2nd .txscene file. (I load two when I start a board) The first loads fine the 2nd crashes in the T2DStaticSprite Class with a null RenderMaterial Reference.

The stack goes back to TorqueSceneData.cs where the _LoadObjects method is registering the object with this line:
TorqueObjectDatabase.Instance.Register(tObj);

If it helps this only seams to happen with static sprites and not animated ones.

I'm not sure what is causing this. If you guys have any idea I'd love a hint.

I'm going to dig back in tomorrow but need rest. :)

Thanks for the help!
#33
03/21/2010 (5:34 am)
I found the issue with the GUI system: somebody forgot that textures are treated by reference... go figure... I'll double check a few scenarios and issue ta third set of patch files.

Still can't repro the loading error :( I'll start a new game project to see into that.

EDIT: to avoid misunderstanding... I'm that somebody who put a Dispose in the wrong place ;) I guess I'm doing this too quickly.
#34
03/21/2010 (7:06 am)
Guis, if you want to give it a try there are 6 simple changes till now and all works fine so maybe you can make those 6 yourself and let me know how it works for you:

In GuiBitmap.cs:

Line 112: get rid of th line
res.Instance.Dispose();

In TextureDivider.cs:

Line 456: get rid of th line
res.Instance.Dispose();

In SimpleMaterial.cs:

at the line 128 change the Dispose content like this:

public override void Dispose()
{
if (!_texture.IsNull)
{
_texture.Instance.Dispose();
_texture.Invalidate();
}
base.Dispose();
}

same for LightingMaterial.cs, Line 271 shall be like this:

public override void Dispose()
{
if (!_baseTexture.IsNull)
{
_baseTexture.Instance.Dispose();
_baseTexture.Invalidate();
}
base.Dispose();
}

Again same for GenericMaterial.cs, Line 306 shall be like this:

public override void Dispose()
{
for (int i = 0; i < _textureBinds.Count; i++)
{
if (!_textureBinds[i]._texture.IsNull)
{
_textureBinds[i]._texture.Instance.Dispose();
_textureBinds[i]._texture.Invalidate();
}
}
base.Dispose();
}

and in VideoMaterial.cs, the Dispose at Line 149 should be like this:

public override void Dispose()
{
if (!_texture.IsNull)
{
_texture.Instance.Dispose();
_texture.Invalidate();
}
_video = null;
_videoPlayer = null;
base.Dispose();
}

There are a few others places but the above are the most sensitive. I'll send a third patch after the testing. LEt me know how this worked for you.
#35
03/21/2010 (9:21 am)
Thanks again for all your help.

My GUI Still crashes with an invalid texture reference at this line:
_texLeft = srcRect.X / ((Texture2D)((ITextureMaterial)material).Texture.Instance).Width;
in the BitmapStretchSR method

It crashes with both a reverted and non reverted GUIBitmap.cs class

taking this line out of simplematerial again fixes the problem
_texture.Instance.Dispose();

I'm searching for the cause of the null reference on reload

If it helps I load two .txscene files when I start a board. One for shared data and one for level data. It alweays crashes on the 2nd.

#36
03/21/2010 (9:31 am)
I found the problem with the reload. It was the render quad that was null not the texture. I was too tired last night to think or I would have noticed this.

removing this line in T2DStaticSprite fixes the problem
_quad = null;


I now am having a texture error on reload problem when the level starts with my in game GUI (even with that line removed from simple material) I'll investigate and post my findings. Its caused by the GUIBitmap I use for my life bars.
#37
03/21/2010 (9:50 am)
That's weird... the error you're describing is caused by the excessive Dispose, when removed as per above posts all loads fine.

I'm testing launching a TorqueX GUI 2D Pro project. I've simply created a new project and run it: it works smoothly. Can you try the same project and tell if that runs? If not I guess we still have some code base difference, if it works then I'd lovo to know what your GUI does to replicate it here.
#38
03/21/2010 (10:25 am)
Havn't completly tracked this down yet but in SimpleMaterial.cs

After unloading a level when I reload. when _SetupGlobalParameters is called the lifebar that causes the crash accesses the following block of code.

if (_texture.Instance != null && _texture.Instance.IsDisposed)
            {
              
                _texture.Invalidate();
            }


This code is not accessed until the 2nd time I load the board and only by GUI textures that cause the crash. Commenting this code out causes a new crash. It seams that the Texture.Instance is getting disposed before the GUIBitmap can use it.
#39
03/21/2010 (11:23 am)
Well,

We still have code base differences: Henry's 3.1.4 has that public string MountedName which is missing in my 3.1.4 and Matthew just downloaded it and has issues that I can't repro.

I think that I'll add Henry's property and send to you guys my full 3.1.4 SVN repository. It's really depressing...
#40
03/21/2010 (11:28 am)
Heya The GUI Starter game does work. Without taking that line out of simplematerial.

I'm going to compair the code and see what I can find.