sezero / quakespasm

QuakeSpasm -- A modern, cross-platform Quake game engine based on FitzQuake.
https://sourceforge.net/projects/quakespasm/
GNU General Public License v2.0
227 stars 96 forks source link

[BUG] - USB audio headset not outputting any sound, prevents the engine from gracefully shutting down #63

Closed vide0hanz closed 1 year ago

vide0hanz commented 1 year ago

Key Details

Tested on Quakespasm, vkQuake, and Ironwail; all exhibit the same issue:

When I have my headphones plugged into any USB port on my computer, I do not get any audio from the game running in the aforementioned source ports. Additionally, this bug seems to prevent the game from shutting down as well - when attempting to quit the game from the main menu or the console, the window just hangs there and never disappears; I have to kill it's process from the terminal for it to go away.

When switching my default audio device to something other than the USB headset audio, everything works as expected. Weirdly this seems to only happen on my headphones, as I recall using other USB audio devices (DACs, etc) without any issue on this same PC. I've tried swapping different USB cables and ports to rule out faulty hardware, but the issue persists.

Audio from my headphones works fine everywhere else too, including other games which use SDL as well (again, not sure if this is relevant or not, possibly related to #23 ?)

sezero commented 1 year ago

@icculus: any ideas?

icculus commented 1 year ago

Does quakespasm publish a log file that would tell us what SDL audio backend it ended up using?

Also: if you can run it under a debugger and break when it has hung, to get a backtrace, that would be super-helpful.

sezero commented 1 year ago

@vide0hanz: Hit Shift-Esc key combination to bring down the game console after qs starts and read the audio initialization report.

I can't debug anything here because I can't reproduce your environment at all.

vide0hanz commented 1 year ago

Quakespasm 0.95.1 - headphones connected via USB:

Sound Initialization
SDL audio spec  : 44100 Hz, 1024 samples, 2 channels
SDL audio driver: pulseaudio - MOMENTUM 3 Digital Stereo (IEC958), 65536 bytes buffer
Audio: 16 bit, stereo, 44100 Hz
CDAudio disabled at compile time

Default sink set to Analog Stereo via USB:

Sound Initialization
SDL audio spec  : 44100 Hz, 1024 samples, 2 channels
SDL audio driver: pulseaudio - MOMENTUM 3 Analog Stereo, 65536 bytes buffer
Audio: 16 bit, stereo, 44100 Hz
CDAudio disabled at compile time

Default sink set to Pro Audio via USB:

Sound Initialization
SDL audio spec  : 44100 Hz, 1024 samples, 2 channels
SDL audio driver: pulseaudio - MOMENTUM 3 Pro, 65536 bytes buffer
Audio: 16 bit, stereo, 44100 Hz
CDAudio disabled at compile time

Same as above, connected via Bluetooth (this works correctly):

Sound Initialization
SDL audio spec  : 44100 Hz, 1024 samples, 2 channels
SDL audio driver: pulseaudio - MOMENTUM 3, 65536 bytes buffer
Audio: 16 bit, stereo, 44100 Hz
CDAudio disabled at compile time

The audio seems to be initialized correctly in in all cases, if there's anything else I can provide please let me know.

icculus commented 1 year ago

Run the game with the SDL_AUDIODRIVER=alsa environment variable set. This might not work at all, or it might fix the problem as a (temporary!) workaround. I imagine this is something our PulseAudio code is triggering, and just want to confirm it.

vide0hanz commented 1 year ago
Sound Initialization
SDL audio spec  : 44100 Hz, 1024 samples, 2 channels
SDL audio driver: alsa - HDA Intel PCH, ALC1220 Analog, 65536 bytes buffer
Audio: 16 bit, stereo, 44100 Hz
CDAudio disabled at compile time

@icculus Confirming that this works! Tried on the other sourceports as well, everything checks out now. Perhaps it is worth noting that I am using Pipewire on my system. I haven't tried removing that from the equation yet but I imagine it shouldn't matter.

icculus commented 1 year ago

In that case, it might make sense to try SDL_AUDIODRIVER=pipewire to make the game use PipeWire directly; it currently favors PulseAudio, which maybe has a conflict with PipeWire in some weird corner case...?

vide0hanz commented 1 year ago

First of all, thanks for pointing me in the right direction @icculus

Yes, setting SDL_AUDIODRIVER=alsa and SDL_AUDIODIRVER=pipewire both work as expected when pipewire is installed.

For good measure I went ahead and removed the pipewire-pulse package from my system and replaced it with pulseaudio and everything worked fine once again. However, when I reintroduced the pipewire-pulse package the issue came back.

My understanding is that pipewire-pulse is a drop-in replacement for pulseaudio and any applications which depend on pulseaudio shouldn't know the difference. So far this has been in line with my experience using it, up until this obscure corner case with the Quake sourceports 😄

sezero commented 1 year ago

So, is this a pipewire-pulse issue, or some oversight in SDL's pulseaudio use? If the former, we can close this.

icculus commented 1 year ago

Yeah, I would need to see where the program ended up hanging in a debugger to go any further here. It's 100% possible that we did something incorrect that happened to work with native PulseAudio but fails with Pipewire's compatibility layer.

But I'd just say use the environment variable and force it to use pipewire directly and consider it fixed. :)

icculus commented 1 year ago

(Also I suspect my PulseAudio rewrite in SDL3 would fix this, since it cleans out a race condition, but SDL3 will probably favor pipewire over pulse anyhow.)

vide0hanz commented 1 year ago

I'm happy to do this if either of you can point me in the right direction for how to do so. Would I need to compile with make DEBUG=1 or something like that?

sezero commented 1 year ago

I'm happy to do this if either of you can point me in the right direction for how to do so. Would I need to compile with make DEBUG=1 or something like that?

make DEBUG=1 will make qs be built with debug symbols. SDL2 itself needs building with debug symbols too.

icculus commented 1 year ago

SDL2 itself needs building with debug symbols too.

If Arch doesn't have a way to say "give me debug symbols for the SDL2 package" (and it MIGHT, i just don't have any Arch experience at all!), here's how to build SDL2 from source...

git clone https://github.com/libsdl-org/SDL -b SDL2
cd SDL
mkdir buildbot
cd buildbot
CFLAGS="-O0 -ggdb3" ../configure
make -j8
sudo make install
sudo ldconfig

Then run the game and see if it still works. :) If it does, run it under gdb ...

# (just add "gdb" in front of however you might normally run it)
gdb quakespasm

When the game hangs, go back to the terminal where you ran gdb and hit ctrl-c. Then type thread apply all bt to get a backtrace. Paste all the junk it spits out from that command into this bug report. We might ask you to do this again with some extra commands if necessary, but that will probably cover it.

When done with this SDL2 build, you can remove it so it goes back to Arch's package:

sudo make uninstall
sudo ldconfig

(Apologies if you know all this, I'm just covering all the bases here. If unexpected things go wrong, too, we can walk you through it.)

vide0hanz commented 1 year ago

Thank you so much for the detailed instructions! I will do this later today and report back later with what I find out.

vide0hanz commented 1 year ago

EDIT: Disregard the following, here is the backtrace:

Thread 3 (Thread 0x7fffdc8376c0 (LWP 85442) "SDLAudioP1"):
#0  0x00007ffff7c86d06 in ppoll () from /usr/lib/libc.so.6
#1  0x00007fffe0b834f3 in pa_mainloop_poll () from /usr/lib/libpulse.so.0
#2  0x00007fffe0b8d42c in pa_mainloop_iterate () from /usr/lib/libpulse.so.0
#3  0x00007ffff76574a7 in PULSEAUDIO_PlayDevice (this=0x55555665ba70) at /home/vide0hanz/Projects/SDL/src/audio/pulseaudio/SDL_pulseaudio.c:377
#4  0x00007ffff742d2ad in SDL_RunAudio (devicep=0x55555665ba70) at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:749
#5  0x00007ffff74d6504 in SDL_RunThread (thread=0x555556667710) at /home/vide0hanz/Projects/SDL/src/thread/SDL_thread.c:292
#6  0x00007ffff76b848a in RunThread (data=0x555556667710) at /home/vide0hanz/Projects/SDL/src/thread/pthread/SDL_systhread.c:76
#7  0x00007ffff7c1044b in ?? () from /usr/lib/libc.so.6
#8  0x00007ffff7c93e40 in ?? () from /usr/lib/libc.so.6
--Type <RET> for more, q to quit, c to continue without paging--

Thread 2 (Thread 0x7fffe397f6c0 (LWP 85441) "PulseHotplug"):
#0  0x00007ffff7c86d06 in ppoll () from /usr/lib/libc.so.6
#1  0x00007fffe0b834f3 in pa_mainloop_poll () from /usr/lib/libpulse.so.0
#2  0x00007fffe0b8d42c in pa_mainloop_iterate () from /usr/lib/libpulse.so.0
#3  0x00007fffe0b8d4e1 in pa_mainloop_run () from /usr/lib/libpulse.so.0
#4  0x00007ffff76585fb in HotplugThread (data=0x0) at /home/vide0hanz/Projects/SDL/src/audio/pulseaudio/SDL_pulseaudio.c:804
#5  0x00007ffff74d6504 in SDL_RunThread (thread=0x55555665b870) at /home/vide0hanz/Projects/SDL/src/thread/SDL_thread.c:292
#6  0x00007ffff76b848a in RunThread (data=0x55555665b870) at /home/vide0hanz/Projects/SDL/src/thread/pthread/SDL_systhread.c:76
#7  0x00007ffff7c1044b in ?? () from /usr/lib/libc.so.6
#8  0x00007ffff7c93e40 in ?? () from /usr/lib/libc.so.6

--Type <RET> for more, q to quit, c to continue without paging--
Thread 1 (Thread 0x7ffff7920cc0 (LWP 85435) "quakespasm"):
#0  0x00007ffff7c0cf0e in ?? () from /usr/lib/libc.so.6
#1  0x00007ffff7c12023 in ?? () from /usr/lib/libc.so.6
#2  0x00007ffff76b88f3 in SDL_SYS_WaitThread (thread=0x555556667710) at /home/vide0hanz/Projects/SDL/src/thread/pthread/SDL_systhread.c:290
#3  0x00007ffff74d67fc in SDL_WaitThread_REAL (thread=0x555556667710, status=0x0) at /home/vide0hanz/Projects/SDL/src/thread/SDL_thread.c:442
#4  0x00007ffff742e428 in close_audio_device (device=0x55555665ba70) at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:1163
#5  0x00007ffff742f691 in SDL_CloseAudioDevice_REAL (devid=1) at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:1629
#6  0x00007ffff742f6a2 in SDL_CloseAudio_REAL () at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:1634
#7  0x00007ffff744c8b7 in SDL_CloseAudio () at /home/vide0hanz/Projects/SDL/src/dynapi/SDL_dynapi_procs.h:130
#8  0x00007ffff7d84693 in ?? () from /usr/lib/libSDL-1.2.so.0
--Type <RET> for more, q to quit, c to continue without paging--
#9  0x00005555555a6ded in SNDDMA_Shutdown () at snd_sdl.c:184
#10 0x000055555559ed41 in S_Shutdown () at snd_dma.c:253
#11 0x00005555555d8fea in Host_Shutdown () at host.c:939
#12 0x00005555555f8680 in Sys_Quit () at sys_sdl_unix.c:424
#13 0x00005555555d9034 in Host_Quit_f () at host_cmd.c:48
#14 0x00005555555c1283 in M_Draw () at menu.c:2640
#15 0x000055555556ec54 in SCR_UpdateScreen () at gl_screen.c:1107
#16 0x00005555555d89c6 in _Host_Frame (time=0.00100000005) at host.c:744
#17 0x00005555555d8b98 in Host_Frame (time=0.00100000005) at host.c:786
#18 0x00005555555f8bce in main (argc=1, argv=0x7fffffffe078) at main_sdl.c:148

ORIGINAL COMMENT

Well, I followed your instructions and I'm not getting anything at all. Here is what I did:

Compiled quakespasm like this:

git clone git@github.com:sezero/quakespasm.git
cd quakespasm
cd Quake
make DEBUG=1

And here is the output I got: log.txt

Compiled SDL2 like this:

git clone https://github.com/libsdl-org/SDL -b SDL2
cd SDL
mkdir buildbot
cd buildbot
CFLAGS="-O0 -ggdb3" ../configure
make -j8
sudo make install
sudo ldconfig

Here is the output for CFLAGS="O0 -ggdb3" ../configure: configlog.txt

Here is the output when running sudo make clean install (did this after initial sudo make install): install_log.txt

And the config.log file: config.log

After I compiled everything I ran gdb quakespasm from inside the directory I compiled Quakespasm in, and nothing happened (I'm assuming this is normal behavior):

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from quakespasm...

Ran Quakespasm like this from another terminal window: LD_PRELOAD=/usr/local/lib/libSDL2-2.0.so.0.2701.0 quakespasm

When I triggered the bug, I went back to the terminal where gdb quakespasm was ran

(gdb) Quit
(gdb) thread apply all bt
(gdb) 

And...nothing :)

I repeated the compilation steps a couple times just to be sure I didn't miss anything. Got any other suggestions?

vide0hanz commented 1 year ago

Doh, it's always something trivial. I feel a little silly for missing this one... I've never done this before and wasn't aware I needed to specify run after passing the quakespasm binary to gdb 😅

Thread 3 (Thread 0x7fffdc8376c0 (LWP 85442) "SDLAudioP1"):
#0  0x00007ffff7c86d06 in ppoll () from /usr/lib/libc.so.6
#1  0x00007fffe0b834f3 in pa_mainloop_poll () from /usr/lib/libpulse.so.0
#2  0x00007fffe0b8d42c in pa_mainloop_iterate () from /usr/lib/libpulse.so.0
#3  0x00007ffff76574a7 in PULSEAUDIO_PlayDevice (this=0x55555665ba70) at /home/vide0hanz/Projects/SDL/src/audio/pulseaudio/SDL_pulseaudio.c:377
#4  0x00007ffff742d2ad in SDL_RunAudio (devicep=0x55555665ba70) at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:749
#5  0x00007ffff74d6504 in SDL_RunThread (thread=0x555556667710) at /home/vide0hanz/Projects/SDL/src/thread/SDL_thread.c:292
#6  0x00007ffff76b848a in RunThread (data=0x555556667710) at /home/vide0hanz/Projects/SDL/src/thread/pthread/SDL_systhread.c:76
#7  0x00007ffff7c1044b in ?? () from /usr/lib/libc.so.6
#8  0x00007ffff7c93e40 in ?? () from /usr/lib/libc.so.6
--Type <RET> for more, q to quit, c to continue without paging--

Thread 2 (Thread 0x7fffe397f6c0 (LWP 85441) "PulseHotplug"):
#0  0x00007ffff7c86d06 in ppoll () from /usr/lib/libc.so.6
#1  0x00007fffe0b834f3 in pa_mainloop_poll () from /usr/lib/libpulse.so.0
#2  0x00007fffe0b8d42c in pa_mainloop_iterate () from /usr/lib/libpulse.so.0
#3  0x00007fffe0b8d4e1 in pa_mainloop_run () from /usr/lib/libpulse.so.0
#4  0x00007ffff76585fb in HotplugThread (data=0x0) at /home/vide0hanz/Projects/SDL/src/audio/pulseaudio/SDL_pulseaudio.c:804
#5  0x00007ffff74d6504 in SDL_RunThread (thread=0x55555665b870) at /home/vide0hanz/Projects/SDL/src/thread/SDL_thread.c:292
#6  0x00007ffff76b848a in RunThread (data=0x55555665b870) at /home/vide0hanz/Projects/SDL/src/thread/pthread/SDL_systhread.c:76
#7  0x00007ffff7c1044b in ?? () from /usr/lib/libc.so.6
#8  0x00007ffff7c93e40 in ?? () from /usr/lib/libc.so.6

--Type <RET> for more, q to quit, c to continue without paging--
Thread 1 (Thread 0x7ffff7920cc0 (LWP 85435) "quakespasm"):
#0  0x00007ffff7c0cf0e in ?? () from /usr/lib/libc.so.6
#1  0x00007ffff7c12023 in ?? () from /usr/lib/libc.so.6
#2  0x00007ffff76b88f3 in SDL_SYS_WaitThread (thread=0x555556667710) at /home/vide0hanz/Projects/SDL/src/thread/pthread/SDL_systhread.c:290
#3  0x00007ffff74d67fc in SDL_WaitThread_REAL (thread=0x555556667710, status=0x0) at /home/vide0hanz/Projects/SDL/src/thread/SDL_thread.c:442
#4  0x00007ffff742e428 in close_audio_device (device=0x55555665ba70) at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:1163
#5  0x00007ffff742f691 in SDL_CloseAudioDevice_REAL (devid=1) at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:1629
#6  0x00007ffff742f6a2 in SDL_CloseAudio_REAL () at /home/vide0hanz/Projects/SDL/src/audio/SDL_audio.c:1634
#7  0x00007ffff744c8b7 in SDL_CloseAudio () at /home/vide0hanz/Projects/SDL/src/dynapi/SDL_dynapi_procs.h:130
#8  0x00007ffff7d84693 in ?? () from /usr/lib/libSDL-1.2.so.0
--Type <RET> for more, q to quit, c to continue without paging--
#9  0x00005555555a6ded in SNDDMA_Shutdown () at snd_sdl.c:184
#10 0x000055555559ed41 in S_Shutdown () at snd_dma.c:253
#11 0x00005555555d8fea in Host_Shutdown () at host.c:939
#12 0x00005555555f8680 in Sys_Quit () at sys_sdl_unix.c:424
#13 0x00005555555d9034 in Host_Quit_f () at host_cmd.c:48
#14 0x00005555555c1283 in M_Draw () at menu.c:2640
#15 0x000055555556ec54 in SCR_UpdateScreen () at gl_screen.c:1107
#16 0x00005555555d89c6 in _Host_Frame (time=0.00100000005) at host.c:744
#17 0x00005555555d8b98 in Host_Frame (time=0.00100000005) at host.c:786
#18 0x00005555555f8bce in main (argc=1, argv=0x7fffffffe078) at main_sdl.c:148

@icculus is this what you're after?

icculus commented 1 year ago

is this what you're after?

Exactly this, thank you!

So it looks like it's waiting for the SDL audio thread to quit, but the audio thread is hung up in ppoll().

I can't help but wonder if this is fixed by https://github.com/libsdl-org/SDL/commit/35292d7dba88faa667f86e77c63651d19ef49178, which I wrote for SDL2, but was too massive a change, so it went into SDL3 instead. But since both the hotplug thread and the device thread are sitting in pa_mainloop_iterate, trying to use the same socket at the same time, suggests it might be hitting the threading issue that prompted the patch.

vide0hanz commented 1 year ago

Do you have a test build or a patch I could use to check? I thought that it'd be fairly straightforward to just compile the latest build with the files you linked but I was wrong.

icculus commented 1 year ago

I'll try to put together something for you soon.

icculus commented 1 year ago

The fix I mentioned has been merged into SDL's SDL2 branch; if you can build SDL from source, you could try this out and see i the problem goes away with PulseAudio.

vide0hanz commented 1 year ago

Well, the issue with the game hanging on exit no longer occurs and I no longer seem to get any tracebacks (assuming this is normal since nothing is hung up), however, I still do not have any audio unless I explicitly set SDL_AUDIODRIVER to either "alsa" or "pipewire".

Tested this on latest commit for quakespasm and built SDL2 from source.

I can't help but wonder if this is a coincidence though since I'm getting the same behavior on both the SDL2 library I compiled from source, as well as the package in the arch repos which happened to be updated to SDL version 2.28.0 on 6/21. Also, pipewire got an update on 6/28 which is the same day you merged your fix!

With that in mind, I decided to downgrade my pipewire and SDL packages to a few versions back to see if I could recreate the original problem but I still got the same behavior mentioned above. So... honestly out of ideas at this point! It's starting to seem like a bit of a fluke. It's entirely possible that the issue was with pipewire and not SDL, I just can't say for sure.

In any case, I'm personally completely fine with setting SDL_AUDIODRIVER=pipewire and calling it a day :)

icculus commented 1 year ago

I'm fine with that, too; @sezero, I think we're okay to close this issue, if you agree.

sezero commented 1 year ago

OK then, closing.