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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user