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.
This commit is contained in:
Chris Robinson
2014-10-12 09:03:08 -07:00
parent a77387b549
commit 4320a1483b
4 changed files with 150 additions and 76 deletions
+135 -4
View File
@@ -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);
}
}
+3
View File
@@ -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); }
+2 -72
View File
@@ -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);
}
+10
View File
@@ -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.