Game Development Community

Huge memory leak with sfxPlayOnce() [w/ fix] - RESOLVED

by Manoel Neto · in Torque 3D Professional · 03/03/2011 (5:45 am) · 9 replies

My game is leaking around 50MBs per session. After lots of hair pulling (the memory profiler doesn't work when linking with LibBullet, making it very hard to track leaks), I found the culprit was a 5MB non-streaming OGG audio track. Here's how to leak memory:

$mySound = sfxPlayOnce(SomeAudioProfile,"art/sound/myBigSound.ogg"); // Loads the uncompressed .ogg into RAM
sfxStopAndDelete($mySound); // Stops the sound, but DOES NOT FREE THE RAM ALLOCATED BY sfxPlayOnce!!!!!

Even if I switch the track to streaming, there are tons of smaller audio files which can't be set to streaming and will leak and build up over time. I even suspect the buffer used by streaming tracks might leak as well.

Off to find a fix.

#1
03/03/2011 (6:06 am)
Looks like the vector of once played sources isn't purged... Propably there is a timer missing, which checks for deletion after some time. Search for mPlayOnceSources.
I try to give assistance!

Edit:
Ok, propably, that's it, look in:
SFXSystem::_onRemoveSource
...
if( iter != end )
{
      delete *iter;  // <- Added delete for not dangling pointers
      mPlayOnceSources.erase_fast( iter );
}

HTH ;-)
#2
03/03/2011 (6:16 am)
I found the reason behind the problem, but I'm still thinking about how to fix this. Basically, sfxPlayOnce() creates a temporary SFXProfile, which is used to create an audio source via SFX->playOnce( tempProfile ). It even has a comment that says:
// Set profile to auto-delete when SFXSource releases its reference.
...and calls setAutoDelete() on tempProfile.

The SFXSource does get deleted. The temporary SFXProfile doesn't.

The problem is that the SFXSource *never* increases the temp SFXProfile's reference count (the only reference to it is set when it is added to RootGroup). Therefore, it never decrements the reference count, and the autoDelete does nothing.

Also, even if it did get a proper reference, setAutoDelete() is not working since beta2. When the SimObjectPtr was re-written to be nothing more than a WeakRefPtr, it lost the capability of auto-deleting SimObjects. The *only* place that checks for the AutoDelete flag is SimObject::unregisterReference(), and as of beta3 that method is only called when using setCopySource().

Since StrongRefPtr cannot delete SimObjects' either upon decRefCount(), there is no built-in ways to automatically delete a SimObject via reference counting.
#3
03/03/2011 (6:40 am)
StrongWeakRefPtr? Don't have the source in front of me to verify, but from my recollection they seem like what you are describing a need for.

Have not used them myself, though... just remember them because of the name.

#4
03/03/2011 (7:00 am)
StrongWeakRefPtr won't do, because it ultimately calls destroySelf() when the refcount reaches zero. But SimObject::destroySelf() does nothing - SimObjects must be deleted via deleteObject exclusively. This means using StrongRefPtrs and StrongWeakRefPtrs on SimObjects is a sure path into memory leaks.

I'm working on upgrading SimObjectPtrs so they can properly delete SimObjects via refcount and then fix the SFXSource to hold a reference to the SFXProfile.

I posted about autoDelete bug in a new thread, since it's an issue on its own:
www.garagegames.com/community/forums/viewthread/124691
#5
03/03/2011 (8:14 am)
Going through the code, I can see that the SFXSource *does* store a reference to the SFXProfile in a SimObjectPtr already. This means that adding support for autoDelete to SimObjectPtr will automatically fix the problem.
#7
03/03/2011 (12:04 pm)
Nice work - thanks!
#8
03/15/2011 (2:56 pm)
Greetings!

Logged as THREED-1463. Thanks!

- Dave
#9
04/18/2011 (4:14 pm)
Fixed in 1.1 Final and Preview.