diff --git a/src/util/dyn_libs.cpp b/src/util/dyn_libs.cpp index 5ddf98081..ba9f73cc6 100644 --- a/src/util/dyn_libs.cpp +++ b/src/util/dyn_libs.cpp @@ -17,6 +17,7 @@ #include "common/error.h" #include "common/log.h" +#include #include LOG_CHANNEL(DynamicLibrary); @@ -59,6 +60,8 @@ static constexpr int SPIRV_CROSS_MAJOR_VERSION = SPVC_C_API_VERSION_MAJOR; namespace { struct Locals { + ~Locals(); + DynamicLibrary freetype_library; std::once_flag freetype_init_flag; DynamicLibrary plutosvg_library; @@ -73,6 +76,7 @@ struct Locals std::once_flag libzip_init_flag; DynamicLibrary sdl_library; std::once_flag sdl_init_flag; + std::atomic sdl_init_subsystems{0}; DynamicLibrary sqlite_library; std::once_flag sqlite_init_flag; DynamicLibrary shaderc_library; @@ -80,10 +84,6 @@ struct Locals shaderc_compiler_t shaderc_compiler; DynamicLibrary spirv_cross_library; std::once_flag spirv_cross_init_flag; - -#if defined(_DEBUG) || defined(_DEVEL) - ~Locals(); -#endif }; } // namespace @@ -99,14 +99,14 @@ DynShaderc g_dyn_shaderc; DynSpirvCross g_dyn_spirv_cross; static Locals s_locals; -#if defined(_DEBUG) || defined(_DEVEL) - Locals::~Locals() { DebugAssert(!s_locals.shaderc_compiler); -} -#endif + // Quit any persistent SDL subsystems before unloading the library. + if (const SDL_InitFlags active_subsystems = s_locals.sdl_init_subsystems.load(std::memory_order_acquire)) + g_dyn_sdl.SDL_QuitSubSystem(active_subsystems); +} static bool LoadDynLib(const char* libname, int major_version, DynamicLibrary& dynlib, std::once_flag& once_flag, std::span symbols, Error* const error, @@ -273,7 +273,7 @@ static bool SDLLoadCallback(Error* const error) return true; } -bool DynSDL::Open(Error* error) +bool DynSDL::Open(Error* const error) { if (s_locals.sdl_library.IsOpen()) [[likely]] return true; @@ -282,6 +282,31 @@ bool DynSDL::Open(Error* error) SDLLoadCallback); } +bool DynSDL::InitSubSystem(SDL_InitFlags flags, Error* const error) +{ + // anything added? + const SDL_InitFlags prev_subsystems = s_locals.sdl_init_subsystems.fetch_or(flags, std::memory_order_acq_rel); + const SDL_InitFlags subsystems_to_init = (flags & ~prev_subsystems); + if (subsystems_to_init == 0 || SDL_InitSubSystem(subsystems_to_init)) + return true; + + s_locals.sdl_init_subsystems.fetch_and(~subsystems_to_init, std::memory_order_acq_rel); + + const char* sdl_error = SDL_GetError(); + Error::SetStringFmt(error, "SDL_InitSubSystem(0x{:08X}) failed: {}", static_cast(subsystems_to_init), + sdl_error ? sdl_error : ""); + return false; +} + +void DynSDL::QuitSubSystem(SDL_InitFlags flags) +{ + // anything removed? + const SDL_InitFlags prev_subsystems = s_locals.sdl_init_subsystems.fetch_and(~flags, std::memory_order_acq_rel); + const SDL_InitFlags subsystems_to_release = (flags & prev_subsystems); + if (subsystems_to_release != 0) + SDL_QuitSubSystem(subsystems_to_release); +} + static const DynamicLibrary::SymbolTable s_shaderc_symbols[] = { #define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_shaderc.F}, DYN_SHADERC_FUNCTIONS(RESOLVE_SYMBOL) diff --git a/src/util/dyn_sdl.h b/src/util/dyn_sdl.h index 7183f33ac..5dc94a112 100644 --- a/src/util/dyn_sdl.h +++ b/src/util/dyn_sdl.h @@ -75,7 +75,9 @@ struct DynSDL DYN_SDL_FUNCTIONS(ADD_FUNC) #undef ADD_FUNC - bool Open(Error* error); + bool Open(Error* const error); + bool InitSubSystem(SDL_InitFlags flags, Error* const error); + void QuitSubSystem(SDL_InitFlags flags); }; extern DynSDL g_dyn_sdl; diff --git a/src/util/sdl_audio_stream.cpp b/src/util/sdl_audio_stream.cpp index a8f0778e2..d5b0f1f25 100644 --- a/src/util/sdl_audio_stream.cpp +++ b/src/util/sdl_audio_stream.cpp @@ -33,28 +33,6 @@ protected: }; } // namespace -static bool InitializeSDLAudio(Error* error) -{ - static bool initialized = false; - if (initialized) - return true; - - if (!g_dyn_sdl.Open(error)) - return false; - - // May as well keep it alive until the process exits. - if (!g_dyn_sdl.SDL_InitSubSystem(SDL_INIT_AUDIO)) - { - Error::SetStringFmt(error, "SDL_InitSubSystem(SDL_INIT_AUDIO) failed: {}", g_dyn_sdl.SDL_GetError()); - return false; - } - - std::atexit([]() { g_dyn_sdl.SDL_QuitSubSystem(SDL_INIT_AUDIO); }); - - initialized = true; - return true; -} - SDLAudioStream::SDLAudioStream(AudioStreamSource* source, u32 channels) : m_source(source), m_channels(channels) { } @@ -129,7 +107,8 @@ std::unique_ptr AudioStream::CreateSDLAudioStream(u32 sample_rate, bool output_latency_minimal, AudioStreamSource* source, bool auto_start, Error* error) { - if (!InitializeSDLAudio(error)) + // May as well keep it alive until the process exits. + if (!g_dyn_sdl.Open(error) || !g_dyn_sdl.InitSubSystem(SDL_INIT_AUDIO, error)) return {}; std::unique_ptr stream = std::make_unique(source, channels); diff --git a/src/util/sdl_input_source.cpp b/src/util/sdl_input_source.cpp index d007dd324..3774b522d 100644 --- a/src/util/sdl_input_source.cpp +++ b/src/util/sdl_input_source.cpp @@ -582,15 +582,13 @@ void SDLInputSource::SetHints() bool SDLInputSource::InitializeSubsystem() { - if (!g_dyn_sdl.SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) + if (Error error; !g_dyn_sdl.InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC, &error)) { - ERROR_LOG("SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC) failed"); + ERROR_LOG(error.GetDescription()); return false; } // we should open the controllers as the connected events come in, so no need to do any more here - m_sdl_subsystem_initialized = true; - int mapping_count = 0; g_dyn_sdl.SDL_free(g_dyn_sdl.SDL_GetGamepadMappings(&mapping_count)); INFO_LOG("{} controller mappings are loaded.", mapping_count); @@ -603,11 +601,7 @@ void SDLInputSource::ShutdownSubsystem() while (!m_controllers.empty()) CloseDevice(m_controllers.begin()->joystick_id); - if (m_sdl_subsystem_initialized) - { - g_dyn_sdl.SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); - m_sdl_subsystem_initialized = false; - } + g_dyn_sdl.QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); } void SDLInputSource::PollEvents()