From f09de2547631e839ed33965e010c355f81129900 Mon Sep 17 00:00:00 2001 From: GaryOderNichts <12049776+GaryOderNichts@users.noreply.github.com> Date: Wed, 1 Jan 2025 17:27:35 +0100 Subject: [PATCH] wiiu/audio: Open device in new thread AX initialization needs to be done on CPU1, otherwise interrupts won't be handled properly. Previously this was attmpted to be done using `OSSetThreadAffinity` to change the affinity of the current thread. Unfortunately `OSSetThreadAffinity` does not work when called for the currently running thread. This caused issues when an SDL audio device was opened from a thread not running on CPU1. This commit now creates a new thread running on CPU1 for AX initialization, and joins it before moving on. --- src/audio/wiiu/SDL_wiiuaudio.c | 81 ++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/src/audio/wiiu/SDL_wiiuaudio.c b/src/audio/wiiu/SDL_wiiuaudio.c index e6f128915db8c..da02839dc3036 100644 --- a/src/audio/wiiu/SDL_wiiuaudio.c +++ b/src/audio/wiiu/SDL_wiiuaudio.c @@ -61,13 +61,11 @@ static SDL_AudioDevice* cb_this; /* +1, but never goes above NUM_BUFFERS */ #define next_id(id) (id + 1) % NUM_BUFFERS -static int WIIUAUDIO_OpenDevice(_THIS, const char* devname) { - int ret = 0; +static int _WIIUAUDIO_OpenDeviceFunction(_THIS) { AXVoiceOffsets offs; AXVoiceVeData vol = { .volume = 0x8000, }; - uint32_t old_affinity; float srcratio; Uint8* mixbuf = NULL; uint32_t mixbuf_allocation_count = 0; @@ -80,10 +78,6 @@ static int WIIUAUDIO_OpenDevice(_THIS, const char* devname) { SDL_zerop(this->hidden); -/* We *must not* change cores when setting stuff up */ - old_affinity = OSGetThreadAffinity(OSGetCurrentThread()); - OSSetThreadAffinity(OSGetCurrentThread(), AX_MAIN_AFFINITY); - /* Take a quick aside to init the wiiu audio */ if (!AXIsInit()) { /* Init the AX audio engine */ @@ -147,8 +141,7 @@ static int WIIUAUDIO_OpenDevice(_THIS, const char* devname) { if (!mixbuf) { printf("Couldn't allocate mix buffer\n"); - ret = SDL_OutOfMemory(); - goto end; + return SDL_OutOfMemory(); } memset(mixbuf, 0, this->spec.size * NUM_BUFFERS); @@ -163,8 +156,7 @@ static int WIIUAUDIO_OpenDevice(_THIS, const char* devname) { if (this->hidden->deintvbuf == NULL) { AXQuit(); printf("DEBUG: Couldn't allocate deinterleave buffer"); - ret = SDL_SetError("Couldn't allocate deinterleave buffer"); - goto end; + return SDL_SetError("Couldn't allocate deinterleave buffer"); } @@ -174,8 +166,7 @@ static int WIIUAUDIO_OpenDevice(_THIS, const char* devname) { if (!this->hidden->voice[i]) { AXQuit(); printf("DEBUG: couldn't get voice\n"); - ret = SDL_OutOfMemory(); - goto end; + return SDL_OutOfMemory(); } /* Start messing with it */ @@ -247,10 +238,64 @@ static int WIIUAUDIO_OpenDevice(_THIS, const char* devname) { cb_this = this; //wish there was a better way AXRegisterAppFrameCallback(_WIIUAUDIO_framecallback); -end: ; -/* Put the thread affinity back to normal - we won't call any more AX funcs */ - OSSetThreadAffinity(OSGetCurrentThread(), old_affinity); - return ret; + return 0; +} + +static void _WIIUAUDIO_ThreadDeallocator(OSThread *thread, void *stack) { + free(thread); + free(stack); +} + +static void _WIIUAUDIO_ThreadCleanup(OSThread *thread, void *stack) { +} + +static int WIIUAUDIO_OpenDevice(_THIS, const char *devname) { + int result; + + /* AX functions need to run from the same core. + Since we cannot easily change the affinity of the currently running thread, we create a new one if necessary. + This thread only runs on CPU1 (AX_MAIN_AFFINITY) and will be joined after initialization is done. */ + if (OSGetThreadAffinity(OSGetCurrentThread()) != AX_MAIN_AFFINITY) { + OSThread *thread; + uint32_t stackSize = 32 * 1024; + uint8_t *stack; + int32_t priority = OSGetThreadPriority(OSGetCurrentThread()); + + thread = (OSThread *)memalign(16, sizeof(OSThread)); + if (!thread) { + return SDL_OutOfMemory(); + } + + stack = memalign(16, stackSize); + if (!stack) { + free(thread); + return SDL_OutOfMemory(); + } + + if (!OSCreateThread(thread, + (OSThreadEntryPointFn)_WIIUAUDIO_OpenDeviceFunction, + (int32_t)this, + NULL, + stack + stackSize, + stackSize, + priority, + AX_MAIN_AFFINITY)) + { + return SDL_SetError("OSCreateThread() failed"); + } + + OSSetThreadDeallocator(thread, &_WIIUAUDIO_ThreadDeallocator); + OSSetThreadCleanupCallback(thread, &_WIIUAUDIO_ThreadCleanup); + OSResumeThread(thread); + + if (!OSJoinThread(thread, &result)) { + return SDL_SetError("OSJoinThread() failed"); + } + } else { + result = _WIIUAUDIO_OpenDeviceFunction(this); + } + + return result; } /* Called every 3ms before a frame of audio is rendered. Keep it fast! */ @@ -405,7 +450,7 @@ static void WIIUAUDIO_ThreadInit(_THIS) { OSSetThreadPriority(currentThread, priority); } -static SDL_bool WIIUAUDIO_Init(SDL_AudioDriverImpl* impl) { +static SDL_bool WIIUAUDIO_Init(SDL_AudioDriverImpl *impl) { impl->OpenDevice = WIIUAUDIO_OpenDevice; impl->PlayDevice = WIIUAUDIO_PlayDevice; impl->WaitDevice = WIIUAUDIO_WaitDevice;