From 4320a1483b22eff1cc49b13570054064b321473b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 12 Oct 2014 09:03:08 -0700 Subject: [PATCH] Make alcSuspendContext and alcProcessContext batch updates This behavior better matches Creative's hardware drivers and Rapture3D's OpenAL driver. A compatibility environment variable is provided to restore the old no-op behavior for any app that behaves badly from this change (set __ALSOFT_SUSPEND_CONTEXT to "ignore"). If too many apps have a problem with this, the default behavior may need to be changed to ignore, with the env var providing an option to defer/batch instead. --- Alc/ALc.c | 139 ++++++++++++++++++++++++++++++++++++++++++++-- OpenAL32/Include/alMain.h | 3 + OpenAL32/alState.c | 74 +----------------------- env-vars.txt | 10 ++++ 4 files changed, 150 insertions(+), 76 deletions(-) diff --git a/Alc/ALc.c b/Alc/ALc.c index 04c3f398..781e0729 100644 --- a/Alc/ALc.c +++ b/Alc/ALc.c @@ -748,6 +748,11 @@ static alonce_flag alc_config_once = AL_ONCE_FLAG_INIT; /* Default effect that applies to sources that don't have an effect on send 0 */ static ALeffect DefaultEffect; +/* Flag to specify if alcSuspendContext/alcProcessContext should defer/process + * updates. + */ +static ALCboolean SuspendDefers = ALC_TRUE; + /************************************************ * ALC information @@ -908,6 +913,18 @@ static void alc_initconfig(void) } ReadALConfig(); + str = getenv("__ALSOFT_SUSPEND_CONTEXT"); + if(str && *str) + { + if(strcasecmp(str, "ignore") == 0) + { + SuspendDefers = ALC_FALSE; + TRACE("Selected context suspend behavior, \"ignore\"\n"); + } + else + ERR("Unhandled context suspend behavior setting: \"%s\"\n", str); + } + capfilter = 0; #if defined(HAVE_SSE4_1) capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1; @@ -1538,6 +1555,98 @@ void SetDefaultChannelOrder(ALCdevice *device) extern inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel chan); +/* ALCcontext_DeferUpdates + * + * Defers/suspends updates for the given context's listener and sources. This + * does *NOT* stop mixing, but rather prevents certain property changes from + * taking effect. + */ +void ALCcontext_DeferUpdates(ALCcontext *context) +{ + ALCdevice *device = context->Device; + FPUCtl oldMode; + + SetMixerFPUMode(&oldMode); + + V0(device->Backend,lock)(); + if(!ExchangeInt(&context->DeferUpdates, AL_TRUE)) + { + ALboolean UpdateSources; + ALvoice *voice, *voice_end; + ALeffectslot **slot, **slot_end; + /* Make sure all pending updates are performed */ + UpdateSources = ATOMIC_EXCHANGE(ALenum, &context->UpdateSources, AL_FALSE); + + voice = context->Voices; + voice_end = voice + context->VoiceCount; + while(voice != voice_end) + { + ALsource *source = voice->Source; + if(!source) goto next; + + if(source->state != AL_PLAYING && source->state != AL_PAUSED) + { + voice->Source = NULL; + continue; + } + + if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE) || UpdateSources) + voice->Update(voice, source, context); + next: + voice++; + } + + slot = VECTOR_ITER_BEGIN(context->ActiveAuxSlots); + slot_end = VECTOR_ITER_END(context->ActiveAuxSlots); + while(slot != slot_end) + { + if(ATOMIC_EXCHANGE(ALenum, &(*slot)->NeedsUpdate, AL_FALSE)) + V((*slot)->EffectState,update)(context->Device, *slot); + slot++; + } + } + V0(device->Backend,unlock)(); + + RestoreFPUMode(&oldMode); +} + +/* ALCcontext_ProcessUpdates + * + * Resumes update processing after being deferred. + */ +void ALCcontext_ProcessUpdates(ALCcontext *context) +{ + ALCdevice *device = context->Device; + + V0(device->Backend,lock)(); + if(ExchangeInt(&context->DeferUpdates, AL_FALSE)) + { + ALsizei pos; + + LockUIntMapRead(&context->SourceMap); + for(pos = 0;pos < context->SourceMap.size;pos++) + { + ALsource *Source = context->SourceMap.array[pos].value; + ALenum new_state; + + if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && + Source->Offset >= 0.0) + { + ReadLock(&Source->queue_lock); + ApplyOffset(Source); + ReadUnlock(&Source->queue_lock); + } + + new_state = ExchangeInt(&Source->new_state, AL_NONE); + if(new_state) + SetSourceState(Source, context, new_state); + } + UnlockUIntMapRead(&context->SourceMap); + } + V0(device->Backend,unlock)(); +} + + /* alcSetError * * Stores the latest ALC device error @@ -2307,18 +2416,40 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) /* alcSuspendContext * - * Not functional + * Suspends updates for the given context */ -ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *UNUSED(context)) +ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *context) { + if(!SuspendDefers) + return; + + context = VerifyContext(context); + if(!context) + alcSetError(NULL, ALC_INVALID_CONTEXT); + else + { + ALCcontext_DeferUpdates(context); + ALCcontext_DecRef(context); + } } /* alcProcessContext * - * Not functional + * Resumes processing updates for the given context */ -ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *UNUSED(context)) +ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *context) { + if(!SuspendDefers) + return; + + context = VerifyContext(context); + if(!context) + alcSetError(NULL, ALC_INVALID_CONTEXT); + else + { + ALCcontext_ProcessUpdates(context); + ALCcontext_DecRef(context); + } } diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 8cbac90e..191c35b7 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -777,6 +777,9 @@ void ALCdevice_Lock(ALCdevice *device); void ALCdevice_Unlock(ALCdevice *device); ALint64 ALCdevice_GetLatency(ALCdevice *device); +void ALCcontext_DeferUpdates(ALCcontext *context); +void ALCcontext_ProcessUpdates(ALCcontext *context); + inline void LockContext(ALCcontext *context) { ALCdevice_Lock(context->Device); } diff --git a/OpenAL32/alState.c b/OpenAL32/alState.c index b4f17b9d..180a8c04 100644 --- a/OpenAL32/alState.c +++ b/OpenAL32/alState.c @@ -712,52 +712,7 @@ AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void) context = GetContextRef(); if(!context) return; - if(!context->DeferUpdates) - { - ALboolean UpdateSources; - ALvoice *voice, *voice_end; - ALeffectslot **slot, **slot_end; - FPUCtl oldMode; - - SetMixerFPUMode(&oldMode); - - LockContext(context); - context->DeferUpdates = AL_TRUE; - - /* Make sure all pending updates are performed */ - UpdateSources = ATOMIC_EXCHANGE(ALenum, &context->UpdateSources, AL_FALSE); - - voice = context->Voices; - voice_end = voice + context->VoiceCount; - while(voice != voice_end) - { - ALsource *source = voice->Source; - if(!source) goto next; - - if(source->state != AL_PLAYING && source->state != AL_PAUSED) - { - voice->Source = NULL; - continue; - } - - if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE) || UpdateSources) - voice->Update(voice, source, context); - next: - voice++; - } - - slot = VECTOR_ITER_BEGIN(context->ActiveAuxSlots); - slot_end = VECTOR_ITER_END(context->ActiveAuxSlots); - while(slot != slot_end) - { - if(ATOMIC_EXCHANGE(ALenum, &(*slot)->NeedsUpdate, AL_FALSE)) - V((*slot)->EffectState,update)(context->Device, *slot); - slot++; - } - - UnlockContext(context); - RestoreFPUMode(&oldMode); - } + ALCcontext_DeferUpdates(context); ALCcontext_DecRef(context); } @@ -769,32 +724,7 @@ AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void) context = GetContextRef(); if(!context) return; - LockContext(context); - if(ExchangeInt(&context->DeferUpdates, AL_FALSE)) - { - ALsizei pos; - - LockUIntMapRead(&context->SourceMap); - for(pos = 0;pos < context->SourceMap.size;pos++) - { - ALsource *Source = context->SourceMap.array[pos].value; - ALenum new_state; - - if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) && - Source->Offset >= 0.0) - { - ReadLock(&Source->queue_lock); - ApplyOffset(Source); - ReadUnlock(&Source->queue_lock); - } - - new_state = ExchangeInt(&Source->new_state, AL_NONE); - if(new_state) - SetSourceState(Source, context, new_state); - } - UnlockUIntMapRead(&context->SourceMap); - } - UnlockContext(context); + ALCcontext_ProcessUpdates(context); ALCcontext_DecRef(context); } diff --git a/env-vars.txt b/env-vars.txt index 47dce032..487067d5 100644 --- a/env-vars.txt +++ b/env-vars.txt @@ -69,3 +69,13 @@ sound (i.e., sounds that are supposed to be behind you sound like they're in front, and vice-versa). Setting this to "true" or "1" will negate the localized Z coordinate to attempt to fix output for apps that have incorrect front/back panning. + +__ALSOFT_SUSPEND_CONTEXT +Due to the OpenAL spec not being very clear about them, behavior of the +alcSuspendContext and alcProcessContext methods has varied, and because of +that, previous versions of OpenAL Soft had them no-op. Creative's hardware +drivers and the Rapture3D driver, however, use these methods to batch changes +and protect against partial updates, which some applications make use of. In an +attempt to standardize on that behavior, OpenAL Soft has changed those methods +accordingly. Setting this to "ignore" restores the previous no-op behavior for +applications that interact poorly with the new behavior. -- 2.11.4.GIT