Game Development Community

Crashes loading image datablocks.

by Bret Patterson · in iTorque 2D · 06/10/2009 (7:31 am) · 12 replies

I've organized all my datablocks into a groups that I load before a level is loaded and then expect to be unloaded when I call endLevel().

The problem I'm having is that sometimes during load it will crash while loading the datablock. When loading my second level it only happens sometimes and when coming back to the first level it always happens.

I use my own class named:
LevelManager to load levels which has this function:
function LevelManager::loadLevel(%level) {
	%file = "game/data/levels/" @ %level @ ".t2d";
	%datablock = "game/gamescripts/" @ %level @ "Datablocks.cs";
	%script = "./" @ %level @ ".cs";
	
	exec(expandFilename(%script));
	exec(expandFilename(%datablock));
	SceneWindow2D.loadLevel(expandFilename(%file));
	
}

I then say LevelManager::loadLevel("test"); and it executes a script named "test.cs" and then
exec's the custom datablock for that level named "testDatablocks.cs".
The testDataBlocks.cs file then loads the level specific datablocks and looks like this:

$CurrentLevelDatablockSet = new SimSet();

exec("game/gamescripts/datablocks/commonDatablocks.cs");
commonDatablocks::loadDatablocks();


An example of my datablock load is:
commonDatablocks.cs
function outdoorsDatablocks::loadDataBlocks() {
 	%tempDataBlocks = new SimSet() {
.. datablocks
}
   	%count = %tempDataBlocks.getCount();  
   	for( %i = 0; %i < %count; %i++ ) {  
    	  $CurrentLevelDatablockSet.add( %tempDataBlocks.getObject( %i ) );  
   	}  
}


I then modified my game/gamescripts/datablocks.cs to look like this so that it merges
the datablocks on level load:
function LoadProjectLastLevel()  
{  
   %projectFile = "project.t2dProj";  
   %xml = new ScriptObject() { class = "XML"; };  
   %lastlevel = "";  
   if( %xml.beginRead( %projectFile ) )  
   {  
      if( %xml.readClassBegin( "TorqueProject" ) )  
      {  
         %lastlevel = %xml.readField( "LastLevel" );              
         echo( "-Mat LastLevel:" SPC %lastlevel );  
         %xml.readClassEnd();  
      }      
      %xml.endRead();  
   }  
   // Delete the object  
   %xml.delete();  
   return %lastlevel;     
}  
  
function loadCurrentLevelDatablocks( %levelfile ) {  
   //load level datablocks and add them to the all datablocks list   
   %pos = strstr(%levelFile, "game/data/levels/" );  
   %start = strlen("game/data/levels/");
   %start = %start + %pos;
   %end = strlen( %levelfile ) - %start - 4;//remove ".cs"  
   %datablocksFile = getSubStr( %levelfile, %start, %end );  
   %datablocksFile = "game/gamescripts/" @ %datablocksFile @ "Datablocks.cs";  
    

   
   %count = $CurrentLevelDatablockSet.getCount();  
   for( %i = 0; %i < %count; %i++ ) {  
      %object = $CurrentLevelDatablockSet.getObject( %i );  
      $managedDatablockSet.remove( %object );  
   }  
   $CurrentLevelDatablockSet.deleteContents();
   
   echo( "loading:" SPC %datablocksFile );  
   exec( %datablocksFile );   
   %count = $CurrentLevelDatablockSet.getCount();  
   for( %i = 0; %i < %count; %i++ ) {  
      $managedDatablockSet.add( $CurrentLevelDatablockSet.getObject( %i ) );  
   }  
     
}  

if( isToolBuild() ) {  
   //-Mat in case we are loading a new scene from the GUI interface  
   exec("game/gameScripts/LevelManager.cs");  
   %level = LoadProjectLastLevel();  
   loadCurrentLevelDatablocks( %level );  
}


The crashes stack trace that I'm getting is:
#0	0x964b8bba in tiny_free_list_add_ptr
#1	0x964b767c in tiny_malloc_from_free_list
#2	0x964b01cd in szone_malloc
#3	0x964b00d8 in malloc_zone_malloc
#4	0x964b006c in malloc
#5	0x0018ca51 in dMalloc_r at platformMemory.cc:1465
#6	0x000af149 in VectorResize at tVector.cc:28
#7	0x0024517d in Vector<t2dVector>::resize at tVector.h:473
#8	0x002451b3 in Vector<t2dVector>::setSize at tVector.h:240
#9	0x0023f103 in t2dPhysics::generateCollisionPoly at t2dPhysics.cc:1157
#10	0x002419ab in t2dPhysics::setCollisionPolyPrimitive at t2dPhysics.cc:991
#11	0x00241b78 in t2dPhysics::initialise at t2dPhysics.cc:324
#12	0x002a8fe3 in t2dTileLayer::createTileObject at t2dTileMap.cc:1485
#13	0x002b46bf in t2dTileLayer::setStaticTile at t2dTileMap.cc:1708
#14	0x002b5437 in t2dTileLayer::loadStream_3 at t2dTileMap.cc:5146
#15	0x002a70f1 in t2dTileLayer::loadStream at t2dTileMap.cc:4768
#16	0x002b285a in t2dTileLayer::loadTileLayer at t2dTileMap.cc:3952
#17	0x002b3692 in t2dTileLayer::setLayerFile at t2dTileMap.cc:1193
#18	0x000873c0 in SimObject::setDataField at simBase.cc:1062
#19	0x0006fc9b in CodeBlock::exec at compiledEval.cc:4500
#20	0x0007be42 in cexec at consoleFunctions.cc:1372
#21	0x00070f0b in CodeBlock::exec at compiledEval.cc:4851
#22	0x00070995 in CodeBlock::exec at compiledEval.cc:4758
#23	0x00070995 in CodeBlock::exec at compiledEval.cc:4758
#24	0x00070995 in CodeBlock::exec at compiledEval.cc:4758
#25	0x00070995 in CodeBlock::exec at compiledEval.cc:4758
#26	0x003355c6 in Namespace::Entry::execute at Namespace.cc:918
#27	0x00076c72 in Con::execute at console.cc:2040
#28	0x00086bea in SimConsoleEvent::process at simBase.cc:1808
#29	0x0008c09f in Sim::advanceToTime at simManager.cc:229
#30	0x0008c116 in Sim::advanceTime at simManager.cc:238
#31	0x000d963b in DemoGame::processTimeEvent at main.cc:706
#32	0x00189889 in GameInterface::processEvent at gameInterface.cc:100
#33	0x00189ccf in GameInterface::processEvents at gameInterface.cc:213
#34	0x000d8a6a in DemoGame::mainLoop at main.cc:508
#35	0x002dc113 in _MacCarbGameInnerLoop at macCarbMain.cc:29
#36	0x95126ac5 in CFRunLoopRunSpecific
#37	0x95126c78 in CFRunLoopRunInMode
#38	0x92b9f28c in RunCurrentEventLoopInMode
#39	0x92b9f0a5 in ReceiveNextEventCommon
#40	0x92bfdb36 in _AcquireNextEvent
#41	0x92bfc293 in RunApplicationEventLoop

Update 10am This doesn't appear to crash on the iphone. I did notice that I run out of memory though if I switch back and forth a few times. It's likely that my datablocks are not being unloaded as I expect. The unload is likely a bug in my code which I'll look into asap.

#1
06/10/2009 (8:54 am)
Fortunately I am not having any crashing when loading image datablock issues, however my unload is also not working (no surprise since I think we both got loading/unloading from the same place). If you do figure out how to unload properly that would be awesome!
#2
06/10/2009 (9:29 am)
Wow, at least I'm not alone and going crazy.

I found and used the same code in my game and was crashing on the iPhone consistently. I had to abandon it and simply "reset" the level by deleting all the objects (which I added into a SimGroup) and repositioning everyone, but I'd rather just reload the level.

If anyone can figure this out, I'll revert to this method again.
#3
06/10/2009 (10:13 am)
The only crashes I'm seeing on the iphone are because of out of memory errors. That's because after looking at the code it doesn't have any logic to unload the datablocks when in game mode. So I'm going to need to delete all the datablocks in the $CurrentLevelDatablockSet before every level load to free up the memory. This should fix the crashes from out of memory on the iphone.

On the MAC however I'm getting crashes unrelated to this. This is likely due to the fact that I call endLevel() on a level that loaded commonDataBlocks.cs and then call loadLevel() on a new level which immediately reloads the commonDataBlocks.cs graphics. It's possible that the MAC code doesn't like this for some reason, but it's hard to tell. I'll likely factor out the common graphics that need to always be loaded and only load them once and not unload, however until I get that structure in place I unload everything from current level and reload everything for the new level.

I'm going to have to do the factoring before release because I plan on supported the "portal" like effect that diablo supported for going to/from the town to buy/sell. However that functionality is going to not be put in for while yet.
#4
06/10/2009 (11:17 am)
Ya, I'm getting out of memory issues, too (see my thread). I have yet to convert everything from PNG to PVR (I hear conflicting reports that this actually works), but I have to find a way to unload everything when loading a new scene. My game just chokes after 10 minutes of play, and I can't figure out why.
#5
06/11/2009 (7:09 am)
To fix the out of memory issue I had to do the following:
in the game/gamescripts/datablocks.cs I created the simset once, to prevent myself from overwriting the global variable before freeing it.
$CurrentLevelDatablockSet = new SimSet();

Next you need to do the following to free the current levels datablocks before loading the new one. I modified my LevelManager::loadLevel to be:
function LevelManager::loadLevel(%level) {
	%file = "game/data/levels/" @ %level @ ".t2d";
	%datablock = "game/gamescripts/" @ %level @ "Datablocks.cs";
	%script = "./" @ %level @ ".cs";
	%count = $CurrentLevelDatablockSet.getCount();  
   for( %i = 0; %i < %count; %i++ ) {  
      %object = $CurrentLevelDatablockSet.getObject( %i );  
      %object.delete();
   }  
	$CurrentLevelDatablockSet.deleteContents();
	exec(expandFilename(%script));
	exec(expandFilename(%datablock));
	SceneWindow2D.loadLevel(expandFilename(%file));
	
}

It was pretty simple. Ideally I want to break up my datablocks into sets of ones I never unload and ones I always unload, but I'm not to that point yet. When I do get there I'll just create two simsets and load into the appropriate one.
#6
06/11/2009 (12:43 pm)
I noticed this call $CurrentLevelDatablockSet.deleteContents(); but it's giving an error that says it is an unknown command.
I can't find that function on the TDN site also.
Is this a custom function? If so, does it do something special?
This is because it seems like it will be doing the same thing that was done above it:
%count = $CurrentLevelDatablockSet.getCount(); 
for( %i = 0; %i < %count; %i++ ) {    
      %object = $CurrentLevelDatablockSet.getObject( %i );    
      %object.delete();  
   }

Also, in your code example, you exec %script for the corresponding level. Is that where you perform other execs like what is usually done in startGame(%level) function?
#7
06/11/2009 (3:54 pm)
I'm not sure the difference between %object.delete() and deleteContents(). I hate scripting and the documentation for torque script is abysmal, so I have to figure out what stuff does from examples and trial and error. I also changed a few other things around when i did that addition, so it could of been the other things I did. However I can now load/unload these 2 levels all day long and my game doesn't crash, so something I changed fixed it.

Don't know what startGame() does. The exec(%script) is where I load level specific script code for game logic, which I have very little of, but I do do some things in script if it's super simple and rarely used.
#8
06/12/2009 (9:00 pm)
I just tested it and the new for loop with the %object.delete() call is required for the memory to be free. I believe the %object.delete() does a native delete of the object freeing all it's resources. The deleteContents() only removes the object from the SimSet but doesn't actually delete the object that was in the SimSet.
#9
06/15/2009 (4:59 pm)
For those interested there was a memory leak due to a logic error in the code above. It should be:
%count = $CurrentLevelDatablockSet.getCount();  
   for( %i = %count-1; %i >= 0; %i-- ) {  
      %object = $CurrentLevelDatablockSet.getObject( %i );  
      %object.delete();
   }

you have to count down because when you remove index[0] it shifts the others down, which causes you to advance out of bounds and miss half of the objects in the datablock. Alternatively you could do:
%count = $CurrentLevelDatablockSet.getCount();  
   for( %i = 0; < %count; %i++ ) {  
      %object = $CurrentLevelDatablockSet.getObject(0);  
      %object.delete();
   }

Using Instruments Object Allocations I see that my total memory and texture memory go up and down respectively as I move between levels and drop back down fully to the expected level indicating no memory leaks.
#10
06/15/2009 (6:34 pm)
Removing the bug tag from this post because I found out it was a problem in my code. The problem is every object in the scenegraph is deleted by the game engine. The issue is that I dynamically create, add to scene graph, and remove from the scenegraph and then delete the object. The problem is that I don't know when my behavior is destroyed which of the objects I created are in the scene graph or not in the scene graph, so I can't tell if they have been deleted and thus my reference points to garbage or if I need to delete them to prevent a memory leak.

Very annoying, I have to create my object and add to scene graph and then use setVisible(false)/true to control it being seen and never remove it from the scene graph!
#11
06/16/2009 (6:37 am)
Quote:he problem is every object in the scenegraph is deleted by the game engine.
As a simple solution you could create a pool scenegraph which holds them from which you push them over to the real scene graph as you need them and potentially back if they aren't needed anymore for reuse (as object creation is more or less the heaviest operation on the iphone, engine independent).
#12
06/16/2009 (7:05 am)
That's not a bad idea. Are there any resources discussing how to create/use multiple scene graphs? Right now I'm just creating all my objects at level load time (well all the non-dynamic objects) and just using setVisible to show/hide them.

Anyone know if an object that's in the scenegraph but has it's visibility set to false causes a noticeable performance hit?