I, like many others, am using OpenAL as my sound driver in Vista due to FreeSound having a 1 second delay and DirectSound being a bit noisy/screwy.
*Can someone else please help me to verify this bug?*
Anyway, I noticed that my game was eating up the memory in Task Manager (all my framework games do on XP and Vista), even though the GC is correct. When I switch to FreeAudio they are totally fine, no leak.
I've spent ages trying to duplicate the issue in a small app, and I can only duplicate when debugging for some reason. I think it's a GC issue with OpenAL. I really wish I could pinpoint it more but I've spent a day on it and I'm getting nowhere, hopefully BRL can figure it out because they can get into the OpenAL modules and check stuff out.
Check out the following code: You need a test1.ogg (say a 500K music file) and the two OpenAL files (OpenAL32.dll and wrap_oal,dll) in the same folder.
Directions to spot bug:
1) Run the code in DEBUG mode and then launch task manager.
2) Press Space. This will stop at the DebugStop.
3) Step to the GCCollect() line and check the memory in task manager.
4) Step again. Note how the memory is THE SAME. (If you change the FREE_AUDIO flag to 1 and do the same test, the memory will go down as expected.)
5) For ultra weirdness keep stepping until you get past the music = LoadSound line. Note how the memory goes up (as expected because it's loading in more music). Now press F5 to resume program flow. Memory remains high. This is a big memory leak. Press space again and repeat. Memory keeps going up.
HOWEVER, if you repeat the above but press F5 to resume BEFORE getting to step 5 the memory will not go up and there will be no leak! Same if you take out the DebugStop or run in non-debug mode. How weird is that?
This makes me think there's some kind of GC timing thing going on which screws up the OpenAL memory allocation in certain situations only. It's definitely screwing up all the time in my full games as I've said but I'm not doing anything different from the above code (well I'm not calling GCCollect manually, that's just to demo the bug), although I do have other sound channels on the go at the same time which may make a difference.
One last thing, I wondered if it was because of this:
When using OpenAL a TChannel is actually a TOpenALChannel which contains a _Source field of type TOpenALSource. This in turn contains a field called _Sound of type TOpenALSound which points to the sound sample. When you call StopChannel the _Source and the _Sound fields are not nulled or anything. So when the channel is nulled, I wonder if those other pointers are properly nulled (they should be right?) because if they are NOT then when you null your sound sample and expect the GC to clean it up, it WON'T because there will still be a non-null reference to it in the _Sound field! I actually tried manually nulling that field (had to modify the OpenALAudio module to make TOpenALSource public) but it didn't seem to fix the problem, so this could be a red herring...
*Can someone else please help me to verify this bug?*
Anyway, I noticed that my game was eating up the memory in Task Manager (all my framework games do on XP and Vista), even though the GC is correct. When I switch to FreeAudio they are totally fine, no leak.
I've spent ages trying to duplicate the issue in a small app, and I can only duplicate when debugging for some reason. I think it's a GC issue with OpenAL. I really wish I could pinpoint it more but I've spent a day on it and I'm getting nowhere, hopefully BRL can figure it out because they can get into the OpenAL modules and check stuff out.
Check out the following code: You need a test1.ogg (say a 500K music file) and the two OpenAL files (OpenAL32.dll and wrap_oal,dll) in the same folder.
SuperStrict Const FREE_AUDIO%=0 If FREE_AUDIO=0 Then EnableOpenALAudio() If Not SetAudioDriver("OpenAL Generic Software") Then Notify("Failed to set Audio Driver") EndIf EndIf AppTitle = "Open AL Bug Test" Graphics 400,300,0 Type TGame Field channel:TChannel Field music: TSound Method Init() channel = AllocChannel() End Method Method LoadMusic(name$) If channel<>Null And ChannelPlaying(channel) Then StopChannel(channel) DebugStop channel = Null music = Null GCCollect() 'The Task Manager memory drops for FreeAudio but does not for OpenAL. 'Remake the channel because StopChannel will have killed it but left the pointer intact. channel = AllocChannel() EndIf music = LoadSound(name+".ogg",SOUND_LOOP) End Method Method PlayMusic() If channel<>Null And music<>Null Then CueSound(music, channel) SetChannelVolume(channel,1) ResumeChannel(channel) EndIf End Method Method ChangeScreen() LoadMusic("test1") PlayMusic() End Method End Type Global Game:TGame = New TGame Game.Init() Game.ChangeScreen() While Not KeyHit(KEY_ESCAPE) If KeyHit(KEY_SPACE) Then Game.ChangeScreen() EndIf Cls If FREE_AUDIO=1 Then DrawText "Driver=FreeAudio",0,0 Else DrawText "Driver=OpenAL",0,0 EndIf DrawText "Press <space> to change screens.",0,20 DrawText "GC Mem = "+GCMemAlloced(),0,40 Flip Wend
Directions to spot bug:
1) Run the code in DEBUG mode and then launch task manager.
2) Press Space. This will stop at the DebugStop.
3) Step to the GCCollect() line and check the memory in task manager.
4) Step again. Note how the memory is THE SAME. (If you change the FREE_AUDIO flag to 1 and do the same test, the memory will go down as expected.)
5) For ultra weirdness keep stepping until you get past the music = LoadSound line. Note how the memory goes up (as expected because it's loading in more music). Now press F5 to resume program flow. Memory remains high. This is a big memory leak. Press space again and repeat. Memory keeps going up.
HOWEVER, if you repeat the above but press F5 to resume BEFORE getting to step 5 the memory will not go up and there will be no leak! Same if you take out the DebugStop or run in non-debug mode. How weird is that?
This makes me think there's some kind of GC timing thing going on which screws up the OpenAL memory allocation in certain situations only. It's definitely screwing up all the time in my full games as I've said but I'm not doing anything different from the above code (well I'm not calling GCCollect manually, that's just to demo the bug), although I do have other sound channels on the go at the same time which may make a difference.
One last thing, I wondered if it was because of this:
When using OpenAL a TChannel is actually a TOpenALChannel which contains a _Source field of type TOpenALSource. This in turn contains a field called _Sound of type TOpenALSound which points to the sound sample. When you call StopChannel the _Source and the _Sound fields are not nulled or anything. So when the channel is nulled, I wonder if those other pointers are properly nulled (they should be right?) because if they are NOT then when you null your sound sample and expect the GC to clean it up, it WON'T because there will still be a non-null reference to it in the _Sound field! I actually tried manually nulling that field (had to modify the OpenALAudio module to make TOpenALSource public) but it didn't seem to fix the problem, so this could be a red herring...