aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2020-02-21 20:14:28 -0800
committerChris Robinson <[email protected]>2020-02-21 20:14:28 -0800
commita8162a77c2d610e00740a34ae20606ad4b981cf5 (patch)
tree0ac5ec808bc44f51818f3dcfc1d3fce56892fd7f
parent6e6a30679e54f83faa0e2149df3f638de1466396 (diff)
Use an array of ALvoice pointers for the active voices
This allows growing the array atomically with the mixer since the ALvoice objects themselves don't move, and a new larger array of them can be swapped in without blocking the mixer.
-rw-r--r--al/source.cpp59
-rw-r--r--alc/alc.cpp87
-rw-r--r--alc/alcontext.h23
-rw-r--r--alc/alu.cpp27
-rw-r--r--alc/voice.h46
5 files changed, 130 insertions, 112 deletions
diff --git a/al/source.cpp b/al/source.cpp
index ace69310..9d6fd8be 100644
--- a/al/source.cpp
+++ b/al/source.cpp
@@ -78,13 +78,14 @@ using std::chrono::nanoseconds;
ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context)
{
+ auto voicelist = context->getVoicesSpan();
ALuint idx{source->VoiceIdx};
- if(idx < context->mVoices.size())
+ if(idx < voicelist.size())
{
ALuint sid{source->id};
- ALvoice &voice = context->mVoices[idx];
- if(voice.mSourceID.load(std::memory_order_acquire) == sid)
- return &voice;
+ ALvoice *voice = voicelist[idx];
+ if(voice->mSourceID.load(std::memory_order_acquire) == sid)
+ return voice;
}
source->VoiceIdx = INVALID_VOICE_IDX;
return nullptr;
@@ -2679,21 +2680,27 @@ START_API_FUNC
}
/* Count the number of reusable voices. */
+ auto voicelist = context->getVoicesSpan();
size_t free_voices{0};
- for(const ALvoice &voice : context->mVoices)
+ for(const ALvoice *voice : voicelist)
{
- free_voices += (voice.mPlayState.load(std::memory_order_acquire) == ALvoice::Stopped
- && voice.mSourceID.load(std::memory_order_relaxed) == 0u
- && voice.mPendingStop.load(std::memory_order_relaxed) == false);
+ free_voices += (voice->mPlayState.load(std::memory_order_acquire) == ALvoice::Stopped
+ && voice->mSourceID.load(std::memory_order_relaxed) == 0u
+ && voice->mPendingStop.load(std::memory_order_relaxed) == false);
if(free_voices == srchandles.size())
break;
}
if UNLIKELY(srchandles.size() != free_voices)
{
- BackendLockGuard __{*device->Backend};
- /* Increase the number of voices to handle the request. */
- const size_t need_voices{srchandles.size() - free_voices};
- context->mVoices.resize(context->mVoices.size() + need_voices);
+ const size_t inc_amount{srchandles.size() - free_voices};
+ auto &allvoices = *context->mVoices.load(std::memory_order_relaxed);
+ if(inc_amount > allvoices.size() - voicelist.size())
+ {
+ /* Increase the number of voices to handle the request. */
+ context->allocVoices(inc_amount > (allvoices.size() - voicelist.size()));
+ }
+ context->mActiveVoiceCount.fetch_add(inc_amount, std::memory_order_release);
+ voicelist = context->getVoicesSpan();
}
VoiceChange *tail{}, *cur{};
@@ -2765,17 +2772,18 @@ START_API_FUNC
}
/* Look for an unused voice to play this source with. */
- auto find_voice = [](const ALvoice &v) noexcept -> bool
+ ALuint vidx{0};
+ for(ALvoice *v : voicelist)
{
- return v.mPlayState.load(std::memory_order_acquire) == ALvoice::Stopped
- && v.mSourceID.load(std::memory_order_relaxed) == 0u
- && v.mPendingStop.load(std::memory_order_relaxed) == false;
+ if(v->mPlayState.load(std::memory_order_acquire) == ALvoice::Stopped
+ && v->mSourceID.load(std::memory_order_relaxed) == 0u
+ && v->mPendingStop.load(std::memory_order_relaxed) == false)
+ {
+ voice = v;
+ break;
+ }
+ ++vidx;
};
- auto voices_end = context->mVoices.data() + context->mVoices.size();
- voice = std::find_if(context->mVoices.data(), voices_end, find_voice);
- assert(voice != voices_end);
-
- auto vidx = static_cast<ALuint>(std::distance(context->mVoices.data(), voice));
/* A source that's not playing or paused has any offset applied when it
* starts playing.
@@ -3352,13 +3360,14 @@ ALsource::~ALsource()
void UpdateAllSourceProps(ALCcontext *context)
{
std::lock_guard<std::mutex> _{context->mSourceLock};
- std::for_each(context->mVoices.begin(), context->mVoices.end(),
- [context](ALvoice &voice) -> void
+ auto voicelist = context->getVoicesSpan();
+ std::for_each(voicelist.begin(), voicelist.end(),
+ [context](ALvoice *voice) -> void
{
- ALuint sid{voice.mSourceID.load(std::memory_order_acquire)};
+ ALuint sid{voice->mSourceID.load(std::memory_order_acquire)};
ALsource *source = sid ? LookupSource(context, sid) : nullptr;
if(source && !source->PropsClean.test_and_set(std::memory_order_acq_rel))
- UpdateSourceProps(source, &voice, context);
+ UpdateSourceProps(source, voice, context);
}
);
}
diff --git a/alc/alc.cpp b/alc/alc.cpp
index 46e930ad..bc411a75 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -1653,12 +1653,44 @@ void ALCcontext::allocVoiceChanges(size_t addcount)
for(size_t i{1};i < clustersize;++i)
cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed);
cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed);
- mVoiceChangeCluster.emplace_back(std::move(cluster));
- mVoiceChangeTail = mVoiceChangeCluster.back().get();
+ mVoiceChangeClusters.emplace_back(std::move(cluster));
+ mVoiceChangeTail = mVoiceChangeClusters.back().get();
--addcount;
}
}
+void ALCcontext::allocVoices(size_t addcount)
+{
+ constexpr size_t clustersize{4};
+ /* Convert element count to cluster count. */
+ addcount = (addcount+(clustersize-1)) / clustersize;
+
+ if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size())
+ throw std::runtime_error{"Allocating too many voices"};
+
+ auto newarray = ALvoiceArray::Create((mVoiceClusters.size()+addcount) * clustersize);
+ while(addcount)
+ {
+ mVoiceClusters.emplace_back(VoiceCluster{new ALvoice[clustersize]});
+ --addcount;
+ }
+
+ auto voice_iter = newarray->begin();
+ for(VoiceCluster &cluster : mVoiceClusters)
+ {
+ for(size_t i{0};i < clustersize;++i)
+ *(voice_iter++) = &cluster[i];
+ }
+
+ if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel))
+ {
+ ALuint refcount;
+ while((refcount=mDevice->MixCount.load(std::memory_order_acquire))&1)
+ std::this_thread::yield();
+ delete oldvoices;
+ }
+}
+
/* alcSetError
*
@@ -2277,45 +2309,47 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
const ALuint num_sends{device->NumAuxSends};
/* Clear extraneous property set sends. */
- auto clear_sends = [num_sends](ALvoice &voice) -> void
+ auto clear_sends = [num_sends](ALvoice *voice) -> void
{
- std::fill(std::begin(voice.mProps.Send)+num_sends, std::end(voice.mProps.Send),
+ std::fill(std::begin(voice->mProps.Send)+num_sends, std::end(voice->mProps.Send),
ALvoiceProps::SendData{});
- std::fill(voice.mSend.begin()+num_sends, voice.mSend.end(), ALvoice::TargetData{});
+ std::fill(voice->mSend.begin()+num_sends, voice->mSend.end(),
+ ALvoice::TargetData{});
auto clear_chan_sends = [num_sends](ALvoice::ChannelData &chandata) -> void
{
std::fill(chandata.mWetParams.begin()+num_sends, chandata.mWetParams.end(),
SendParams{});
};
- std::for_each(voice.mChans.begin(), voice.mChans.end(), clear_chan_sends);
+ std::for_each(voice->mChans.begin(), voice->mChans.end(), clear_chan_sends);
};
- std::for_each(context->mVoices.begin(), context->mVoices.end(), clear_sends);
+ auto voicelist = context->getVoicesSpan();
+ std::for_each(voicelist.begin(), voicelist.end(), clear_sends);
}
- auto reset_voice = [device](ALvoice &voice) -> void
+ auto reset_voice = [device](ALvoice *voice) -> void
{
- delete voice.mUpdate.exchange(nullptr, std::memory_order_acq_rel);
+ delete voice->mUpdate.exchange(nullptr, std::memory_order_acq_rel);
/* Force the voice to stopped if it was stopping. */
ALvoice::State vstate{ALvoice::Stopping};
- voice.mPlayState.compare_exchange_strong(vstate, ALvoice::Stopped,
+ voice->mPlayState.compare_exchange_strong(vstate, ALvoice::Stopped,
std::memory_order_acquire, std::memory_order_acquire);
- if(voice.mSourceID.load(std::memory_order_relaxed) == 0u)
+ if(voice->mSourceID.load(std::memory_order_relaxed) == 0u)
return;
- voice.mStep = 0;
- voice.mFlags |= VOICE_IS_FADING;
+ voice->mStep = 0;
+ voice->mFlags |= VOICE_IS_FADING;
- if((voice.mFmtChannels == FmtBFormat2D || voice.mFmtChannels == FmtBFormat3D)
- && device->mAmbiOrder > voice.mAmbiOrder)
+ if((voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtBFormat3D)
+ && device->mAmbiOrder > voice->mAmbiOrder)
{
- const uint8_t *OrderFromChan{(voice.mFmtChannels == FmtBFormat2D) ?
+ const uint8_t *OrderFromChan{(voice->mFmtChannels == FmtBFormat2D) ?
AmbiIndex::OrderFrom2DChannel.data() :
AmbiIndex::OrderFromChannel.data()};
const BandSplitter splitter{400.0f / static_cast<float>(device->Frequency)};
- const auto scales = BFormatDec::GetHFOrderScales(voice.mAmbiOrder,
+ const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder,
device->mAmbiOrder);
auto init_ambi = [device,&scales,&OrderFromChan,splitter](ALvoice::ChannelData &chandata) -> void
{
@@ -2325,10 +2359,10 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
};
- std::for_each(voice.mChans.begin(), voice.mChans.begin()+voice.mNumChannels,
+ std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
init_ambi);
- voice.mFlags |= VOICE_IS_AMBISONIC;
+ voice->mFlags |= VOICE_IS_AMBISONIC;
}
else
{
@@ -2339,10 +2373,10 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
};
- std::for_each(voice.mChans.begin(), voice.mChans.begin()+voice.mNumChannels,
+ std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
clear_prevs);
- voice.mFlags &= ~VOICE_IS_AMBISONIC;
+ voice->mFlags &= ~VOICE_IS_AMBISONIC;
}
if(device->AvgSpeakerDist > 0.0f)
@@ -2352,11 +2386,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
(device->AvgSpeakerDist * static_cast<float>(device->Frequency))};
auto init_nfc = [w1](ALvoice::ChannelData &chandata) -> void
{ chandata.mDryParams.NFCtrlFilter.init(w1); };
- std::for_each(voice.mChans.begin(), voice.mChans.begin()+voice.mNumChannels,
+ std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
init_nfc);
}
};
- std::for_each(context->mVoices.begin(), context->mVoices.end(), reset_voice);
+ auto voicelist = context->getVoicesSpan();
+ std::for_each(voicelist.begin(), voicelist.end(), reset_voice);
srclock.unlock();
context->mPropsClean.test_and_set(std::memory_order_release);
@@ -2523,7 +2558,7 @@ ALCcontext::~ALCcontext()
}
TRACE("Freed %zu voice property object%s\n", count, (count==1)?"":"s");
- mVoices.clear();
+ delete mVoices.exchange(nullptr, std::memory_order_relaxed);
count = 0;
ALlistenerProps *lprops{mListener.Params.Update.exchange(nullptr, std::memory_order_relaxed)};
@@ -2611,8 +2646,8 @@ void ALCcontext::init()
StartEventThrd(this);
- mVoices.reserve(256);
- mVoices.resize(64);
+ allocVoices(256);
+ mActiveVoiceCount.store(64, std::memory_order_relaxed);
}
bool ALCcontext::deinit()
diff --git a/alc/alcontext.h b/alc/alcontext.h
index 59fda1a7..199f207d 100644
--- a/alc/alcontext.h
+++ b/alc/alcontext.h
@@ -145,7 +145,7 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext> {
* in clusters that are stored in a vector for easy automatic cleanup.
*/
using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>;
- al::vector<VoiceChangeCluster> mVoiceChangeCluster;
+ al::vector<VoiceChangeCluster> mVoiceChangeClusters;
/* The voice change tail is the beginning of the "free" elements, up to and
* *excluding* the current. If tail==current, there's no free elements and
@@ -157,7 +157,26 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext> {
void allocVoiceChanges(size_t addcount);
- al::vector<ALvoice> mVoices;
+
+ using VoiceCluster = std::unique_ptr<ALvoice[]>;
+ al::vector<VoiceCluster> mVoiceClusters;
+
+ using ALvoiceArray = al::FlexArray<ALvoice*>;
+ std::atomic<ALvoiceArray*> mVoices{};
+ std::atomic<size_t> mActiveVoiceCount{};
+
+ void allocVoices(size_t addcount);
+ al::span<ALvoice*> getVoicesSpan() const noexcept
+ {
+ return {mVoices.load(std::memory_order_relaxed)->data(),
+ mActiveVoiceCount.load(std::memory_order_relaxed)};
+ }
+ al::span<ALvoice*> getVoicesSpanAcquired() const noexcept
+ {
+ return {mVoices.load(std::memory_order_acquire)->data(),
+ mActiveVoiceCount.load(std::memory_order_acquire)};
+ }
+
using ALeffectslotArray = al::FlexArray<ALeffectslot*>;
std::atomic<ALeffectslotArray*> mActiveAuxSlots{nullptr};
diff --git a/alc/alu.cpp b/alc/alu.cpp
index 9039fb9c..4e554219 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -1663,7 +1663,7 @@ void ProcessVoiceChanges(ALCcontext *ctx)
}
void ProcessParamUpdates(ALCcontext *ctx, const ALeffectslotArray &slots,
- const al::span<ALvoice> voices)
+ const al::span<ALvoice*> voices)
{
ProcessVoiceChanges(ctx);
@@ -1676,8 +1676,8 @@ void ProcessParamUpdates(ALCcontext *ctx, const ALeffectslotArray &slots,
for(ALeffectslot *slot : slots)
force |= CalcEffectSlotParams(slot, sorted_slots, ctx);
- auto calc_params = [ctx,force](ALvoice &voice) -> void
- { CalcSourceParams(&voice, ctx, force); };
+ auto calc_params = [ctx,force](ALvoice *voice) -> void
+ { CalcSourceParams(voice, ctx, force); };
std::for_each(voices.begin(), voices.end(), calc_params);
}
IncrementRef(ctx->mUpdateCount);
@@ -1688,7 +1688,7 @@ void ProcessContext(ALCcontext *ctx, const ALuint SamplesToDo)
ASSUME(SamplesToDo > 0);
const ALeffectslotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire);
- const al::span<ALvoice> voices{ctx->mVoices.data(), ctx->mVoices.size()};
+ const al::span<ALvoice*> voices{ctx->getVoicesSpanAcquired()};
/* Process pending propery updates for objects on the context. */
ProcessParamUpdates(ctx, auxslots, voices);
@@ -1702,10 +1702,10 @@ void ProcessContext(ALCcontext *ctx, const ALuint SamplesToDo)
});
/* Process voices that have a playing source. */
- auto mix_voice = [SamplesToDo,ctx](ALvoice &voice) -> void
+ auto mix_voice = [SamplesToDo,ctx](ALvoice *voice) -> void
{
- const ALvoice::State vstate{voice.mPlayState.load(std::memory_order_acquire)};
- if(vstate != ALvoice::Stopped) voice.mix(vstate, ctx, SamplesToDo);
+ const ALvoice::State vstate{voice->mPlayState.load(std::memory_order_acquire)};
+ if(vstate != ALvoice::Stopped) voice->mix(vstate, ctx, SamplesToDo);
};
std::for_each(voices.begin(), voices.end(), mix_voice);
@@ -2097,14 +2097,15 @@ void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
}
}
- auto stop_voice = [](ALvoice &voice) -> void
+ auto voicelist = ctx->getVoicesSpanAcquired();
+ auto stop_voice = [](ALvoice *voice) -> void
{
- voice.mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
- voice.mLoopBuffer.store(nullptr, std::memory_order_relaxed);
- voice.mSourceID.store(0u, std::memory_order_relaxed);
- voice.mPlayState.store(ALvoice::Stopped, std::memory_order_release);
+ voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed);
+ voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed);
+ voice->mSourceID.store(0u, std::memory_order_relaxed);
+ voice->mPlayState.store(ALvoice::Stopped, std::memory_order_release);
};
- std::for_each(ctx->mVoices.begin(), ctx->mVoices.end(), stop_voice);
+ std::for_each(voicelist.begin(), voicelist.end(), stop_voice);
}
IncrementRef(device->MixCount);
}
diff --git a/alc/voice.h b/alc/voice.h
index e8daa0c7..555874c4 100644
--- a/alc/voice.h
+++ b/alc/voice.h
@@ -260,54 +260,8 @@ struct ALvoice {
ALvoice() = default;
ALvoice(const ALvoice&) = delete;
- ALvoice(ALvoice&& rhs) noexcept { *this = std::move(rhs); }
~ALvoice() { delete mUpdate.exchange(nullptr, std::memory_order_acq_rel); }
ALvoice& operator=(const ALvoice&) = delete;
- ALvoice& operator=(ALvoice&& rhs) noexcept
- {
- ALvoiceProps *old_update{mUpdate.load(std::memory_order_relaxed)};
- mUpdate.store(rhs.mUpdate.exchange(old_update, std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mProps = rhs.mProps;
-
- mSourceID.store(rhs.mSourceID.load(std::memory_order_relaxed), std::memory_order_relaxed);
- mPlayState.store(rhs.mPlayState.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
- mPendingStop.store(rhs.mPendingStop.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mPosition.store(rhs.mPosition.load(std::memory_order_relaxed), std::memory_order_relaxed);
- mPositionFrac.store(rhs.mPositionFrac.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mCurrentBuffer.store(rhs.mCurrentBuffer.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
- mLoopBuffer.store(rhs.mLoopBuffer.load(std::memory_order_relaxed),
- std::memory_order_relaxed);
-
- mFmtChannels = rhs.mFmtChannels;
- mFrequency = rhs.mFrequency;
- mNumChannels = rhs.mNumChannels;
- mSampleSize = rhs.mSampleSize;
- mAmbiLayout = rhs.mAmbiLayout;
- mAmbiScaling = rhs.mAmbiScaling;
- mAmbiOrder = rhs.mAmbiOrder;
-
- mStep = rhs.mStep;
- mResampler = rhs.mResampler;
-
- mResampleState = rhs.mResampleState;
-
- mFlags = rhs.mFlags;
- mNumCallbackSamples = rhs.mNumCallbackSamples;
-
- mDirect = rhs.mDirect;
- mSend = rhs.mSend;
- mChans = rhs.mChans;
-
- return *this;
- }
void mix(const State vstate, ALCcontext *Context, const ALuint SamplesToDo);
};