diff options
-rw-r--r-- | Alc/alc.cpp | 37 | ||||
-rw-r--r-- | Alc/alcontext.h | 22 | ||||
-rw-r--r-- | OpenAL32/Include/alAuxEffectSlot.h | 2 | ||||
-rw-r--r-- | OpenAL32/alAuxEffectSlot.cpp | 164 | ||||
-rw-r--r-- | OpenAL32/alSource.cpp | 11 |
5 files changed, 178 insertions, 58 deletions
diff --git a/Alc/alc.cpp b/Alc/alc.cpp index 55104f9e..99247753 100644 --- a/Alc/alc.cpp +++ b/Alc/alc.cpp @@ -2081,18 +2081,26 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) std::unique_lock<std::mutex> proplock{context->PropLock}; std::unique_lock<std::mutex> slotlock{context->EffectSlotLock}; - for(auto &slot : context->EffectSlotList) + for(auto &sublist : context->EffectSlotList) { - if(!slot) continue; - aluInitEffectPanning(slot.get()); + uint64_t usemask = ~sublist.FreeMask; + while(usemask) + { + ALsizei idx = CTZ64(usemask); + ALeffectslot *slot = sublist.EffectSlots + idx; - EffectState *state{slot->Effect.State}; - state->mOutBuffer = device->Dry.Buffer; - state->mOutChannels = device->Dry.NumChannels; - if(state->deviceUpdate(device) == AL_FALSE) - update_failed = AL_TRUE; - else - UpdateEffectSlotProps(slot.get(), context); + usemask &= ~(1_u64 << idx); + + aluInitEffectPanning(slot); + + EffectState *state{slot->Effect.State}; + state->mOutBuffer = device->Dry.Buffer; + state->mOutChannels = device->Dry.NumChannels; + if(state->deviceUpdate(device) == AL_FALSE) + update_failed = AL_TRUE; + else + UpdateEffectSlotProps(slot, context); + } } slotlock.unlock(); @@ -2415,12 +2423,14 @@ ALCcontext::~ALCcontext() delete ActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed); DefaultSlot = nullptr; - count = std::count_if(EffectSlotList.cbegin(), EffectSlotList.cend(), - [](const ALeffectslotPtr &slot) noexcept -> bool { return slot != nullptr; } + count = std::accumulate(EffectSlotList.cbegin(), EffectSlotList.cend(), size_t{0u}, + [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t + { return cur + POPCNT64(~sublist.FreeMask); } ); if(count > 0) WARN(SZFMT " AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s"); EffectSlotList.clear(); + NumEffectSlots = 0; count = 0; ALvoiceProps *vprops{FreeVoiceProps.exchange(nullptr, std::memory_order_acquire)}; @@ -3440,7 +3450,8 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin if(DefaultEffect.type != AL_EFFECT_NULL && dev->Type == Playback) { - ALContext->DefaultSlot = al::make_unique<ALeffectslot>(); + void *ptr{al_calloc(16, sizeof(ALeffectslot))}; + ALContext->DefaultSlot = std::unique_ptr<ALeffectslot>{new (ptr) ALeffectslot{}}; if(InitEffectSlot(ALContext->DefaultSlot.get()) == AL_NO_ERROR) aluInitEffectPanning(ALContext->DefaultSlot.get()); else diff --git a/Alc/alcontext.h b/Alc/alcontext.h index bdf5f2fe..2c4ad1d3 100644 --- a/Alc/alcontext.h +++ b/Alc/alcontext.h @@ -56,10 +56,21 @@ struct SourceSubList { { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } }; -/* Effect slots are rather large, and apps aren't likely to have more than one - * or two (let alone 64), so hold them individually. - */ -using ALeffectslotPtr = std::unique_ptr<ALeffectslot>; +struct EffectSlotSubList { + uint64_t FreeMask{~0_u64}; + ALeffectslot *EffectSlots{nullptr}; /* 64 */ + + EffectSlotSubList() noexcept = default; + EffectSlotSubList(const EffectSlotSubList&) = delete; + EffectSlotSubList(EffectSlotSubList&& rhs) noexcept + : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} + { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } + ~EffectSlotSubList(); + + EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; + EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } +}; struct ALCcontext { RefCount ref{1u}; @@ -68,7 +79,8 @@ struct ALCcontext { ALuint NumSources{0}; std::mutex SourceLock; - al::vector<ALeffectslotPtr> EffectSlotList; + al::vector<EffectSlotSubList> EffectSlotList; + ALuint NumEffectSlots{0u}; std::mutex EffectSlotLock; std::atomic<ALenum> LastError{AL_NO_ERROR}; diff --git a/OpenAL32/Include/alAuxEffectSlot.h b/OpenAL32/Include/alAuxEffectSlot.h index ccb01266..6c060fe6 100644 --- a/OpenAL32/Include/alAuxEffectSlot.h +++ b/OpenAL32/Include/alAuxEffectSlot.h @@ -117,7 +117,7 @@ struct ALeffectslot { static ALeffectslotArray *CreatePtrArray(size_t count) noexcept; - DEF_NEWDEL(ALeffectslot) + DEF_PLACE_NEWDEL() }; ALenum InitEffectSlot(ALeffectslot *slot); diff --git a/OpenAL32/alAuxEffectSlot.cpp b/OpenAL32/alAuxEffectSlot.cpp index c9803819..07c6e69e 100644 --- a/OpenAL32/alAuxEffectSlot.cpp +++ b/OpenAL32/alAuxEffectSlot.cpp @@ -44,10 +44,15 @@ namespace { inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept { - --id; - if(UNLIKELY(id >= context->EffectSlotList.size())) + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= context->EffectSlotList.size())) + return nullptr; + EffectSlotSubList &sublist{context->EffectSlotList[lidx]}; + if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx))) return nullptr; - return context->EffectSlotList[id].get(); + return sublist.EffectSlots + slidx; } inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept @@ -176,6 +181,86 @@ inline EffectStateFactory *getFactoryByType(ALenum type) } +ALeffectslot *AllocEffectSlot(ALCcontext *context) +{ + ALCdevice *device{context->Device}; + std::lock_guard<std::mutex> _{context->EffectSlotLock}; + if(context->NumEffectSlots >= device->AuxiliaryEffectSlotMax) + { + alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit", + device->AuxiliaryEffectSlotMax); + return nullptr; + } + auto sublist = std::find_if(context->EffectSlotList.begin(), context->EffectSlotList.end(), + [](const EffectSlotSubList &entry) noexcept -> bool + { return entry.FreeMask != 0; } + ); + auto lidx = static_cast<ALsizei>(std::distance(context->EffectSlotList.begin(), sublist)); + ALeffectslot *slot; + ALsizei slidx; + if(LIKELY(sublist != context->EffectSlotList.end())) + { + slidx = CTZ64(sublist->FreeMask); + slot = sublist->EffectSlots + slidx; + } + else + { + /* Don't allocate so many list entries that the 32-bit ID could + * overflow... + */ + if(UNLIKELY(context->EffectSlotList.size() >= 1<<25)) + { + alSetError(context, AL_OUT_OF_MEMORY, "Too many effect slots allocated"); + return nullptr; + } + context->EffectSlotList.emplace_back(); + sublist = context->EffectSlotList.end() - 1; + + sublist->FreeMask = ~0_u64; + sublist->EffectSlots = static_cast<ALeffectslot*>(al_calloc(16, sizeof(ALeffectslot)*64)); + if(UNLIKELY(!sublist->EffectSlots)) + { + context->EffectSlotList.pop_back(); + alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate effect slot batch"); + return nullptr; + } + + slidx = 0; + slot = sublist->EffectSlots + slidx; + } + + slot = new (slot) ALeffectslot{}; + ALenum err{InitEffectSlot(slot)}; + if(err != AL_NO_ERROR) + { + slot->~ALeffectslot(); + alSetError(context, err, "Effect slot object initialization failed"); + return nullptr; + } + aluInitEffectPanning(slot); + + /* Add 1 to avoid source ID 0. */ + slot->id = ((lidx<<6) | slidx) + 1; + + context->NumEffectSlots += 1; + sublist->FreeMask &= ~(1_u64 << slidx); + + return slot; +} + +void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot) +{ + ALuint id = slot->id - 1; + ALsizei lidx = id >> 6; + ALsizei slidx = id & 0x3f; + + slot->~ALeffectslot(); + + context->EffectSlotList[lidx].FreeMask |= 1_u64 << slidx; + context->NumEffectSlots--; +} + + #define DO_UPDATEPROPS() do { \ if(!context->DeferUpdates.load(std::memory_order_acquire)) \ UpdateEffectSlotProps(slot, context.get()); \ @@ -204,45 +289,35 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Generating %d effect slots", n); if(n == 0) return; - std::unique_lock<std::mutex> slotlock{context->EffectSlotLock}; - ALCdevice *device{context->Device}; - for(ALsizei cur{0};cur < n;cur++) + if(n == 1) { - auto iter = std::find_if(context->EffectSlotList.begin(), context->EffectSlotList.end(), - [](const ALeffectslotPtr &entry) noexcept -> bool - { return !entry; } - ); - if(iter == context->EffectSlotList.end()) - { - if(UNLIKELY(device->AuxiliaryEffectSlotMax == context->EffectSlotList.size())) + ALeffectslot *slot{AllocEffectSlot(context.get())}; + if(!slot) return; + effectslots[0] = slot->id; + } + else + { + auto tempids = al::vector<ALuint>(n); + auto alloc_end = std::find_if_not(tempids.begin(), tempids.end(), + [&context](ALuint &id) -> bool { - slotlock.unlock(); - alDeleteAuxiliaryEffectSlots(cur, effectslots); - alSetError(context.get(), AL_OUT_OF_MEMORY, - "Exceeding %u auxiliary effect slot limit", device->AuxiliaryEffectSlotMax); - return; + ALeffectslot *slot{AllocEffectSlot(context.get())}; + if(!slot) return false; + id = slot->id; + return true; } - context->EffectSlotList.emplace_back(nullptr); - iter = context->EffectSlotList.end() - 1; - } - - *iter = al::make_unique<ALeffectslot>(); - ALenum err{InitEffectSlot(iter->get())}; - if(err != AL_NO_ERROR) + ); + if(alloc_end != tempids.end()) { - *iter = nullptr; - slotlock.unlock(); - - alDeleteAuxiliaryEffectSlots(cur, effectslots); - alSetError(context.get(), err, "Effect slot object allocation failed"); + auto count = static_cast<ALsizei>(std::distance(tempids.begin(), alloc_end)); + alDeleteAuxiliaryEffectSlots(count, tempids.data()); return; } - aluInitEffectPanning(iter->get()); - auto id = static_cast<ALuint>(std::distance(context->EffectSlotList.begin(), iter) + 1); - (*iter)->id = id; - effectslots[cur] = id; + std::copy(tempids.cbegin(), tempids.cend(), effectslots); } + + std::unique_lock<std::mutex> slotlock{context->EffectSlotLock}; AddActiveEffectSlots(effectslots, n, context.get()); } @@ -280,8 +355,11 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint * // All effectslots are valid, remove and delete them RemoveActiveEffectSlots(effectslots, n, context.get()); std::for_each(effectslots, effectslots_end, - [&context](ALuint id) noexcept -> void - { context->EffectSlotList[id-1] = nullptr; } + [&context](ALuint sid) -> void + { + ALeffectslot *slot{LookupEffectSlot(context.get(), sid)}; + if(slot) FreeEffectSlot(context.get(), slot); + } ); } @@ -714,3 +792,17 @@ void UpdateAllEffectSlotProps(ALCcontext *context) UpdateEffectSlotProps(slot, context); } } + +EffectSlotSubList::~EffectSlotSubList() +{ + uint64_t usemask{~FreeMask}; + while(usemask) + { + ALsizei idx{CTZ64(usemask)}; + EffectSlots[idx].~ALeffectslot(); + usemask &= ~(1_u64 << idx); + } + FreeMask = ~usemask; + al_free(EffectSlots); + EffectSlots = nullptr; +} diff --git a/OpenAL32/alSource.cpp b/OpenAL32/alSource.cpp index dd418a58..cf466c32 100644 --- a/OpenAL32/alSource.cpp +++ b/OpenAL32/alSource.cpp @@ -582,10 +582,15 @@ inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept { - --id; - if(UNLIKELY(id >= context->EffectSlotList.size())) + ALuint lidx = (id-1) >> 6; + ALsizei slidx = (id-1) & 0x3f; + + if(UNLIKELY(lidx >= context->EffectSlotList.size())) + return nullptr; + EffectSlotSubList &sublist{context->EffectSlotList[lidx]}; + if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx))) return nullptr; - return context->EffectSlotList[id].get(); + return sublist.EffectSlots + slidx; } |