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).

#81
03/27/2010 (7:06 am)
Update to my previous post. I tried all 4 stock effect modes. Kill works fine (that's the one my game uses). Stop also seems to work fine, but I chose kill combined with pooling because it seemed more memory efficient.

Infinite works fine with the exception of the fact that my game now looks like a scene from Kill Bill.

Cycle is the one I'm not sure of. I did not see a situation where my effect stopped working. The effect continued to work, but it didn't look the same way as the effect playback in TXB. TXB shows an eruption, followed by a particle clearing, then another eruption. In my game, it seemed more like the original problem with line 1081. It just seems like the particles are doubling up on themselves repeatedly until the game kind of grinds to a halt. It gets so bad that nothing renders except the particles. I did not see the particle bloom followed by the clearing and then the next bloom the way it appears in TXB.

Can anyone with more experience using the Cycle effect mode comment here please?

Thanks
--RB
#82
03/27/2010 (7:26 am)
Hi,

sometimes I think I need to go to bed at 9pm and stop for good coding after that time... :( Matthew pointed out that as well and I couldn't see the issue...

This issue has been caused by the code I had in my GameManager component class. TX has an issue with Lights attached to Particles: the lights are not properly removed from the scene when the effect stops, thus I had the GameManager in charge of checking and removing the lights in the scene. This code collides with the changes I've been making and I just forgot to clean up my code of all the work-arounds I had in place.

I'm revising this again now having my work-arounds all commented out. I'll send a new diff file as soon all is propoerly cleaned up.

Sorry for that guys.

Pino
#83
03/27/2010 (7:42 am)
@Pino...I'm sure everyone here agrees, you need not apologize.

:)

Your generosity in sharing your work has been far more helpful than any oversights and small fringe issues that might arise as a result.

And besides...most coders do their best work after 2AM. ;)
#84
03/27/2010 (8:55 am)
I'm with Ron, Thanks so much Pino.
#85
03/27/2010 (10:09 am)
I hooked up a memory profiler to get rid of any remaining problems caused by my game code and found a few.

I'm not properly disposing my events and delegates so some of my objects/components are being held in memory by Torque Events and Delegates.

What is the proper way to dispose of Torque Events and Delegates? I tried setting them to null without luck. (I'm sure its simple but don't know exactly what I should be doing)

Thanks Guys!
#86
03/27/2010 (11:24 am)
Delegates can be nulled out or do:

TheDelegateRef -= MyDelegate;


An example of removing a Torque Event:

TorqueEventManager.SilenceEvents<bool>(GFXDevice.Instance.DeviceReset, OnDeviceReset);
#87
03/27/2010 (11:56 am)
Thanks again Duncan. :)

Btw. I've been slowly making my way through the Code Complete book you suggested. Tons of good stuff in there :)
#88
03/27/2010 (12:08 pm)
Glad you're finding it a good read :)

Also I forgot to thank Guisseppe for his sterling efforts. I haven't actually gotten around to integrating the diffs yet, but it sounds like it's making a real difference to the performance of the engine and I will be integrating it soon.
#89
03/27/2010 (12:15 pm)
OK there is still a problem, it's been there all along not new, nothing to do with Pino's code. Scenes do not unload. When the scene unloader is called it just leaves the SceneObject and scenecontainer and worst of all an array of SceneContainerBinReference[] thats about 256k in memory.

I am trying to trace through the code, it seems that they are never disposed. I thought I looked at this before and they were but I guess the weren't.

#90
03/27/2010 (12:28 pm)
@Henry: yep, that issue is hard to crack. I have SciTech .Net Memory Profiler and I integrate its results with Xte Profiler (having a better graph presentation) but I can't find yet where those unloaded scene are hooked.

BTW: I'm reprofiling the whole thing and I've found a couple of Dispose calls that should be replaced by the "= null" alone to avoid disposing of the template object.
#91
03/27/2010 (1:08 pm)
Whew! I thought it was me, that I screwed up something with my dif files and svn and such.

thanks!
#92
03/27/2010 (2:26 pm)
Just found more issues while double checking the one at hand. I'll send a new diff tonight or tomorrow morning (GMT) including the proper lights management when attached to particles (like explosions).
#93
03/27/2010 (4:37 pm)
Ok guys,

just shipped the new patches. Let me know how this works for you. Here the memory footprint is very low, seriously reduced ;)

Pino
#94
03/28/2010 (7:21 am)
Heya Pino, The latest patch didn't get to my inbox. You mind re-sending. :)
#95
03/28/2010 (7:41 am)
Hi Matthew,

how strange... just sent again ;)
#96
03/28/2010 (10:05 am)
@Pino ... any chance of obtaining the patch too?

My email address dave(at)vhp(dot)co(dot)uk

Thank you for sharing your considerable efforts.
#97
03/28/2010 (10:33 am)
Thanks for the patch Pino. :)

After applying the latest patch I get the following in my memory profiler for the T2DParticleEffect

This instance is disposed and still directly rooted by a delegate. This can indicate that a delegate has not been properly removed and is a common cause of memory leaks.
The allocation call stacks of the delegates below will provide information about why the delegate was created.


Uncommenting this code fixes the issue for me:
if (_effectData != null)
            {
                _effectData.ClearEmitterData();
                _effectData.OnEmitterListChanged -= _OnEmitterListChanged; 
            }
#98
03/28/2010 (11:05 am)
We'd like to receive the patch as well. All of your efforts are very much appreciated! joshery (at) msn (dot) com
#99
03/28/2010 (11:24 am)
Hi Matthew,

Your change has unwanted effects if one is cloning particles from templates ;) But you are correct, the delegate has to be remuved from the stack, so this is what it should do the magic:

if (_effectData != null)
{
    //_effectData.ClearEmitterData();
    _effectData.OnEmitterListChanged -= _OnEmitterListChanged;
}

leaving commented the call to ClearEmitterData will leave alone the data in the Template.

I'm committing this change not sending a diff for this small thing, assuming that all of us will do this in the code.

Thanks Matthew :)
#100
03/28/2010 (1:32 pm)
Thanks again for the fix!

Not to bring the bringer of bad news. I may have found a bug with the scenegraphs and loading multiple scenes. Then again it may be something I'm doing. In anycase I have found a work around.

In my ai component I use his code to find and generate my path nodes
T2DSceneGraph myPathGraph = TorqueObjectDatabase.Instance.FindObject<T2DSceneGraph>("LevelSceneGraph");

myPathGraph.FindObjects(rec, Game.Instance.ObjTypes.Ground, 0xFFFFFFFF, foundObjects);

            if (foundObjects.Count > 0)
            {
                foreach (T2DSceneObject pn in foundObjects)
                {
                    Pathnode p = new Pathnode();
                    p.Node = pn;
                    _pathnodeList.Add(p);
                }

                foundObjects.Clear();
                return false;
            }
            else
                return true;


So after I unload and reload my scenes the above code can no longer find any objects in the Scenegraph. It seams that the 2nd time the scenes load objects load into the wrong Scenegraph.

My Loading code
Game.Instance.SceneLoader.OnSceneLoaded = null;
                    //alweays load shared data first.
                    Game.Instance.SceneLoader.Load(@"datalevelscharacterData.txscene");

                    T2DSceneGraph _sceneGraph = (T2DSceneGraph)TorqueObjectDatabase.Instance.FindObject<T2DSceneGraph>("DefaultSceneGraph");
                    _sceneGraph.Name = "LevelSceneGraph";
                    Game.Instance.SceneLoader.OnSceneLoaded = SceneLoaded;

Game.Instance.SceneLoader.Load(@"datalevelstrible.txscene");

The first time this code runs everything works fine. The 2nd time no objects are placed in the "LevelSceneGraph".

I did checks just before this loading code re-executes and found that nether the default or level scenegraph exists (as expected).


I did found an easy work around. Just changing the SceneGraph Name in the Level.xml file and removing the renaming of the scenegraph in the above code fixes the problem. There may be some kind of race condition in the code though I have not verified that.

This problem might be caused by something in my code but I wanted to post it encase it turns out to be an actual issue.