Audio: Implement support for ALC_EXT_thread_local_context

This commit is contained in:
SirLynix 2023-12-04 17:38:05 +01:00
parent 3e1b61070c
commit 3fb1096d07
3 changed files with 74 additions and 14 deletions

View File

@ -27,6 +27,7 @@ namespace Nz
enum class OpenALExtension
{
SourceLatency,
ThreadLocalContext,
Max = SourceLatency
};
@ -82,6 +83,8 @@ namespace Nz
OpenALDevice& operator=(const OpenALDevice&) = delete;
OpenALDevice& operator=(OpenALDevice&&) = delete;
static const OpenALDevice* GetCurrentDevice();
// We give each device its own set of function pointer, even though regular OpenAL extensions are always the same (for a set library).
// This makes it easier to wrap them (for error handling), and extension pointers are device-local anyway.
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) decltype(&::name) name;

View File

@ -28,6 +28,18 @@
#define NAZARA_AUDIO_AL_EXT_END()
#endif
#ifndef NAZARA_AUDIO_ALC_EXT_FUNCTION
#define NAZARA_AUDIO_ALC_EXT_FUNCTION(func) NAZARA_AUDIO_AL_EXT_FUNCTION(func)
#endif
#ifndef NAZARA_AUDIO_ALC_EXT_BEGIN
#define NAZARA_AUDIO_ALC_EXT_BEGIN(ext) NAZARA_AUDIO_AL_EXT_BEGIN(ext)
#endif
#ifndef NAZARA_AUDIO_ALC_EXT_END
#define NAZARA_AUDIO_ALC_EXT_END() NAZARA_AUDIO_AL_EXT_END()
#endif
NAZARA_AUDIO_AL_FUNCTION(alBuffer3f)
NAZARA_AUDIO_AL_FUNCTION(alBuffer3i)
NAZARA_AUDIO_AL_FUNCTION(alBufferData)
@ -102,7 +114,7 @@ NAZARA_AUDIO_AL_FUNCTION(alSourceStopv)
NAZARA_AUDIO_AL_FUNCTION(alSourceUnqueueBuffers)
NAZARA_AUDIO_AL_FUNCTION(alSpeedOfSound)
#ifndef NAZARA_PLATFORM_WEB
#ifdef AL_SOFT_source_latency
NAZARA_AUDIO_AL_EXT_BEGIN(AL_SOFT_source_latency)
NAZARA_AUDIO_AL_EXT_FUNCTION(alGetSource3dSOFT)
NAZARA_AUDIO_AL_EXT_FUNCTION(alGetSource3i64SOFT)
@ -140,9 +152,19 @@ NAZARA_AUDIO_ALC_FUNCTION(alcOpenDevice)
NAZARA_AUDIO_ALC_FUNCTION(alcProcessContext)
NAZARA_AUDIO_ALC_FUNCTION(alcSuspendContext)
#ifdef ALC_EXT_thread_local_context
NAZARA_AUDIO_ALC_EXT_BEGIN(ALC_EXT_thread_local_context)
NAZARA_AUDIO_ALC_EXT_FUNCTION(alcGetThreadContext)
NAZARA_AUDIO_ALC_EXT_FUNCTION(alcSetThreadContext)
NAZARA_AUDIO_ALC_EXT_END()
#endif
#undef NAZARA_AUDIO_AL_FUNCTION
#undef NAZARA_AUDIO_AL_ALC_FUNCTION
#undef NAZARA_AUDIO_AL_EXT_FUNCTION
#undef NAZARA_AUDIO_ALC_FUNCTION
#undef NAZARA_AUDIO_AL_EXT_BEGIN
#undef NAZARA_AUDIO_AL_EXT_END
#undef NAZARA_AUDIO_ALC_EXT_FUNCTION
#undef NAZARA_AUDIO_ALC_EXT_BEGIN
#undef NAZARA_AUDIO_ALC_EXT_END
#undef NAZARA_AUDIO_ALC_FUNCTION

View File

@ -23,7 +23,9 @@ namespace Nz
#include <Nazara/Audio/OpenALFunctions.hpp>
};
thread_local const OpenALDevice* s_currentALDevice;
// Regular OpenAL contexts are process-wide, but ALC_EXT_thread_local_context allows thread-local contexts
const OpenALDevice* s_currentGlobalALDevice;
thread_local const OpenALDevice* s_currentThreadALDevice;
template<typename FuncType, std::size_t FuncIndex, typename>
struct ALWrapper;
@ -35,7 +37,7 @@ namespace Nz
{
return [](Args... args) -> Ret
{
const OpenALDevice* device = s_currentALDevice; //< pay TLS cost once
const OpenALDevice* device = OpenALDevice::GetCurrentDevice();
assert(device);
FuncType funcPtr = reinterpret_cast<FuncType>(device->GetFunctionByIndex(FuncIndex));
@ -121,7 +123,7 @@ namespace Nz
if (m_library.alcMakeContextCurrent(m_context) != AL_TRUE)
throw std::runtime_error("failed to activate OpenAL context");
s_currentALDevice = this;
s_currentGlobalALDevice = this;
SymbolLoader loader(*this);
#ifdef NAZARA_DEBUG
@ -131,6 +133,7 @@ namespace Nz
#define NAZARA_AUDIO_AL_FUNCTION(name) loader.Load<decltype(&::name), UnderlyingCast(FunctionIndex:: name), false>(name, #name, library.name);
#define NAZARA_AUDIO_ALC_FUNCTION(name) loader.Load<decltype(&::name), UnderlyingCast(FunctionIndex:: name), true>(name, #name, library.name);
#define NAZARA_AUDIO_AL_EXT_FUNCTION(name) loader.Load<decltype(&::name), UnderlyingCast(FunctionIndex:: name), false>(name, #name, nullptr);
#define NAZARA_AUDIO_ALC_EXT_FUNCTION(name) loader.Load<decltype(&::name), UnderlyingCast(FunctionIndex:: name), true>(name, #name, nullptr);
#include <Nazara/Audio/OpenALFunctions.hpp>
m_renderer = reinterpret_cast<const char*>(alGetString(AL_RENDERER));
@ -157,6 +160,9 @@ namespace Nz
if (library.alIsExtensionPresent("AL_SOFT_source_latency"))
m_extensionStatus[OpenALExtension::SourceLatency] = true;
if (library.alIsExtensionPresent("ALC_EXT_thread_local_context"))
m_extensionStatus[OpenALExtension::ThreadLocalContext] = true;
SetListenerDirection(Vector3f::Forward());
}
@ -164,20 +170,23 @@ namespace Nz
{
NAZARA_USE_ANONYMOUS_NAMESPACE
MakeContextCurrent();
// alcMakeContextCurrent resets the thread context
alcMakeContextCurrent(m_context);
alcDestroyContext(m_context);
alcCloseDevice(m_device);
if (s_currentALDevice == this)
s_currentALDevice = nullptr;
if (s_currentThreadALDevice)
s_currentThreadALDevice = nullptr;
if (s_currentGlobalALDevice == this)
s_currentGlobalALDevice = nullptr;
}
bool OpenALDevice::ClearErrorFlag() const
{
NAZARA_USE_ANONYMOUS_NAMESPACE
assert(s_currentALDevice == this);
assert(GetCurrentDevice() == this);
alGetError();
return true;
@ -322,10 +331,27 @@ namespace Nz
{
NAZARA_USE_ANONYMOUS_NAMESPACE
if (s_currentALDevice != this)
if (alcSetThreadContext)
{
m_library.alcMakeContextCurrent(m_context);
s_currentALDevice = this;
if (s_currentThreadALDevice != this)
{
alcSetThreadContext(m_context);
s_currentThreadALDevice = this;
}
}
else
{
if (s_currentGlobalALDevice != this)
{
/*
From EXT_thread_local_context:
alcMakeContextCurrent changes the current process-wide context and set the current thread-local context to NULL.
This has the side effect of changing the current thread-local context, so that the new current process-wide context will be used.
*/
alcMakeContextCurrent(m_context);
s_currentGlobalALDevice = this;
s_currentThreadALDevice = nullptr;
}
}
}
@ -358,7 +384,7 @@ namespace Nz
{
NAZARA_USE_ANONYMOUS_NAMESPACE
assert(s_currentALDevice == this);
assert(GetCurrentDevice() == this);
bool hasAnyError = false;
if (ALuint lastError = alGetError(); lastError != AL_NO_ERROR)
@ -485,4 +511,13 @@ namespace Nz
alSpeedOfSound(speed);
}
const OpenALDevice* OpenALDevice::GetCurrentDevice()
{
const OpenALDevice* device = s_currentThreadALDevice;
if (device)
return device;
return s_currentGlobalALDevice;
}
}