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

#41
03/21/2010 (12:04 pm)
That property was missing in my code base too.

I figured out what is causing my GUI to crash.

I built a custom GUI Button class that swaps out Bitmaps based on button select state.

It seams that changing the child GUIBitmaps texture when loaded causes a problem.

I was planing on recoding that part of my gui anyway so I'm not so worried about it. I can change the way my buttons work.

Now if I can just fix my load re-problems ;)

Syncing up our code sounds like a good idea though. Just encase something is messed up.
#42
03/21/2010 (12:20 pm)
I hate to say this but I think that property Henry mentions is something that he added cause I can't find any reference to it in my code base either or in any documentation that I can find.
#43
03/21/2010 (1:00 pm)
I got it fixed!

I coded my gui so long ago I forgot all the weirdness I did.

I was forcing the unload of textures to clear the memory inside my gui. It was competing with your code. Everything seams to work now I just stopped manually disposing my GUI resources and let the engine do it like it should :).

The only thing that the code does change is you can no longer change out a bitmap on a gui dynamicly once it is being rendered (far as I can tell). This is acceptable though as it probably isnt the best way to do things anyway. Instead I'm now just hiding my highlighted buttons.

Thanks! I don't know what I'd do without this community.
#44
03/21/2010 (1:58 pm)
OK you guys are right I added that! Sorry, it was the only way I could get the mount point name of an object from the object. still it's handy to have .. ;)

I think I should put some comments in there when I change the engine..

thanks
#45
03/21/2010 (2:38 pm)
@Henry: that's why I use SVN, so I can at any moment revert to any previous commit or even to the code base. At any rate I've added your property in the current change so you are able to use it without surprises.

Guys, I'm sending you the whole thing so we can be sure to be fully sync.
#46
03/21/2010 (2:45 pm)
BTW: I don't put the graph here because is too big, but you can see it here: 19 minutes of very intense gameplay... I'm pleased with the achieved result :)
#47
03/21/2010 (3:25 pm)
I copied over your code:

Had to make 2 minor changes for things to work with my game,

in T2DStaticSprite
had to remove this line in Dispose()
_quad = null;
otherwise my spawn objects through a null reference when cloning a template on reload.

2nd
in T2DParticleEffect I had to remove this line
_effectData = null;

in the OnUnregister() method.

Otherwise particles would start creating multiple copies.


My only remaining issue is that for some reason my GUISplash Screens are still are not fading in. I've traced the code back through the project and everything looks to be working as it should. The alphas are getting set and all that. The GUI Starter project does not have this problem. I've tried reducing my code to bare bones at startup but still no luck.

As everything else works perfectly and as my performance has increased dramatically (I didn't even realize my game was dropping frames on the xbox thought it was my crap tv, but it runs soooo much smoother) I can live with the GUISplash problem. I can work around it to get the same effect.

Very happy you did this thanks so much. I would have been very sad in a month or so when I tested my game on a good tv and realized it was actually dropping frames. Also particle effects no longer take 17% of my processor power! ;)
#48
03/21/2010 (4:17 pm)
Hi Matthew,

I forgot the _effectData = null in that method... I'm getting old :) I still cannot get a grip on the other issue though: can you give some more details about the levels you are loading and the flow? I really wish to understand that. I don't think that getting rid of quad=null is a solution here because at any rate the object is disposed so the issue is somewhere else.

Glad you find my small contribution useful ;)
#49
03/21/2010 (4:34 pm)
OPS! Matthew, I just removed that line (_effectData=null) and... my explosions don't work anymore :)

I guess that your particle code contains something similar to the code that was in the GUI... competing with my code :) I'm quite sure because my explosions are plain particle objects (cloned templates) without any "special" code, they are just cloned and killed. Please double check the code and let me know ;)
#50
03/21/2010 (7:20 pm)
just out of curiosity does anyone know how this effects Torque X 3D at all?
#51
03/21/2010 (7:22 pm)
I'll show you what I'm doing

The only custom code I have with fx is a fx manager that holds data (such as where to play the fx and the fx name as a string) and passes it through a hit matrix to determine what fx to play.

In the end I just call this code to create particle fx though:

//This method spawns an fx that will kill it's self 
        public void SpawnKillFX(float posX, float posY, string fxName, bool reverse)
        {
            T2DParticleEffect _killFX = null;
            if(reverse)
                //create a particle that plays in the reverse direction
                _killFX = TorqueObjectDatabase.Instance.FindObject<T2DParticleEffect>(fxName + "rev");
            if(_killFX == null)
                //If kill fx is null we assume that we looked for a reverse fx and dont have one.
                _killFX = TorqueObjectDatabase.Instance.FindObject<T2DParticleEffect>(fxName);
            if (_killFX != null)
            {
                //clone the template
                _killFX = (T2DParticleEffect)_killFX.Clone();
                _killFX.Position = new Vector2(posX, posY);
                _killFX.Visible = true;
                TorqueObjectDatabase.Instance.Register(_killFX);
            }
        }

My T2DParticleEffect class is exactly the same as yours now :(. I don't know what could cause the difference.

Do you create Particles by cloning templates?




As far as the quad = null thing I have no idea whats causing that. I don't know what I'm doing diffrent. It only seems to happen to objects that are spawned from a template.

Here is the code that I load and unload levels with:
public void LoadLevelData()
        {

            switch (_currentMenu)
            {
                case MenuSelect.levelTest:
                    //create content manager
                    //Game.Instance.CreateContentManager();
                    //We set the scene loader to null first because we dont want the first level to trigger this delegate.
                    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");

                    //popContent manager.  Removes the content manager.
                    //Game.Instance.PopContentManager();
                    break;
            }
        }

public static void SceneLoaded(string sceneFile, TorqueSceneData scene)
        {
            GuiLoadingControl.Instance.LoadingFinished = true;
            //this loads the camera sets the GUIStyle and GUISceneview
            Game.Instance.LoadSharedLevelData();
        }

//My Unload code
//stopLevelSound
                    sound.loading.SoundLoading.Instance.EndLevelSound();
                    //unload
                    //unload level data
                    Game.Instance.SceneLoader.UnloadLastScene();
                    //unload character data
                    Game.Instance.SceneLoader.Unload(@"datalevelscharacterData.txscene");                   
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    _currentMenu = MenuSelect.winning;
                    guiWinning = new GuiWinScreen();
                    GUICanvas.Instance.SetContentControl(guiWinning);
// drop the content manager
                    //Game.Instance.UnloadContent();
                    //_contentManager.Unload();
                    //_contentManager.Dispose();
                    //_contentManager = null;




#52
03/21/2010 (7:30 pm)
Hi Scott a Ton of the fixes are in torque core and I believe that is shared so I'd guess that TX3D will need these and other fixes.
#53
03/21/2010 (8:23 pm)
I know that Core is shared between both 2D and 3D I was just wondering if anyone had 3D and applied these patches to see if anything broke or if there were crashes at all.

I don't own 3D yet but so far I haven't really had any problems with pino's changes with the exception of some of the hacks I had to do with the platformer starter kit to switch from one level to the next.
#54
03/21/2010 (9:18 pm)
Some day, however that's not today. I have 3D but wanted to post this game last weekend, I have to say that Pino has solved the thing that was slowing me down the most.

I do want to take a look at what this does to TX3D, however I also don't know what changes John has made.

#55
03/22/2010 (2:04 am)
@Scott: I've tested the Core with the FPS and there are a few issues because only the Core here is fixed, there is still the Torque3D library to be revised just like the 2D library. I have also 2 3D games under construction so extending the revision to the 3D engine is my next step ;)
#56
03/22/2010 (3:15 am)
Matthew,

yes, I only clone template objects, same for spawns.

reading your code something puzzles me. In your code you:
1) load datalevelscharacterData
2) load datalevelstrible
3) unload datalevelstrible
4) unload datalevelscharacterData

at which stage you got the exception? It would help if you could email me the stack trace ;)
#57
03/22/2010 (7:08 am)
I'll email the stack trace when I get home. :) I get the exception when at stage 6:

1) load datalevelscharacterData
2) load datalevelstrible
3) unload datalevelstrible
4) unload datalevelscharacterData
5) load datalevelscharacterData
6) load datalevelstrible
#58
03/22/2010 (8:51 am)
Greate Job Pino,

please include me in the share list of your bug fixes.

Regards,
João
#59
03/22/2010 (8:12 pm)
Here is something interesting. So remember how my GUI would not fade in and out.

I found this post:
http://www.torquepowered.com/community/forums/viewthread/112265

Funny thing is. The 3.14 engine code used with the new guistartup project fades the splash screen in fine. When I use the same code on my project with the same GUISplash screen it doesn't. Changing the code as the above thread suggests fixes the problem.

Perhaps its due to my project using fixed time stamp.

Just thought I'd post that I have fixed that issue. :)
#60
03/23/2010 (1:44 am)
Hi Matthew,

thanks for posting that. I've made the change in my code and it's not affecting the the Gui startup template so as that fixes your issue (fixed timed game) I leave it in for the next Diff files.

I've seen your call stack and I think I know what's happening. As you are unloading/loading very closely in time you are actually cloning a disposed object not GC yet (maybe because you're using a fixed timestep?) I'm going to wrap that up so to avoid the exception in that case (the object is being disposed so it won't even be used anymore). I'll do a couple of more tests anyway.