Skip to content

Memory grows steadily during long sessions in same track, stuttering after ~20 minutes #46

Description

@zando95

The game gradually starts stuttering the longer a session runs, before finally reaching a very slow chug at a certain point. Quitting and relaunching the game immediately restores full speed, which is what made me think something was building up over the session. This is 0.1.0-beta.7.1 on Windows, built from the release source with build.bat (MinGW32).

I'm not a programmer; I used an AI assistant (Claude Opus 4.8 in Claude Code) to read the audio code, and to write this bug report. It read the actual function bodies rather than guessing, though it only searched platform/native_audio.c, so please treat this part as a lead rather than a diagnosis.

To check, I watched the process's private memory while playing normally, sampling every 15 seconds. It was at 243 MB at 4 minutes, 261 MB at 8 minutes, then 486 MB shortly after that, 710 MB at 11 minutes, and still 710 MB at 15 minutes. It climbs in large steps and never comes back down. A separate 12-minute run showed the same shape and ended around 646 MB. I also saw brief spikes that settled back down again — 486 up to 542 and back to 490, then 710 up to 770 and back to 710.

What Claude found is that the two XA decode arenas, s_audio.xaPcmArena and s_audio.xaPendingPcmArena, grow their capacity but nothing in that file ever gives the memory back. NativeAudio_ArenaReset's entire body is arena->used = 0. NativeAudio_FreeXA is a memset of s_audio.xa plus a call to ArenaReset. NativeAudio_ArenaSwap swaps the two structs, so the previous buffer stays live in the pending slot. There is exactly one free() in the whole file, inside NativeAudio_ArenaEnsureCapacity, and it frees the old buffer while growing to a larger one. ArenaEnsureCapacity also allocates the new block before freeing the old, which would fit the temporary spikes above. What I can't tell you is whether a release path exists somewhere outside that file, or whether these arenas account for all of the growth rather than just some of it.

I also want to record a dead end so nobody else spends time on it. The voice PCM arena, s_audio.voicePcmArena, looked like the likelier candidate at first, since it grows the same way and a Time Trial Retry appeared to skip the path that resets it. I added temporary logging to check, and it didn't hold up. Over a 15-minute session that arena levelled off at 128 MiB and reset 10 times while total memory kept climbing to 710 MB, so it seems to be behaving fine and the growth is coming from somewhere else.

To reproduce, build the release source with build.bat, play any track, and watch the process's private bytes in Task Manager or Process Explorer. I played Time Trial and used the end-of-race Retry option to restart several times without going back to the menu, though I don't know whether Retry specifically matters. The timing matters here: the memory climb is visible within about 10 minutes, but it took about 20 minutes of play before the stuttering was unmistakable to me, so a 10-minute test can feel perfectly smooth while the numbers are already rising.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions