Store effect slots in groups of 64

Now that their wet buffers are allocated dynamically, the ALeffectslot object
itself is rather small.
This commit is contained in:
Chris Robinson
2019-02-20 22:00:26 -08:00
parent c43381d811
commit 3966665ca3
5 changed files with 178 additions and 58 deletions
+24 -13
View File
@@ -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
+17 -5
View File
@@ -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};
+1 -1
View File
@@ -117,7 +117,7 @@ struct ALeffectslot {
static ALeffectslotArray *CreatePtrArray(size_t count) noexcept;
DEF_NEWDEL(ALeffectslot)
DEF_PLACE_NEWDEL()
};
ALenum InitEffectSlot(ALeffectslot *slot);
+128 -36
View File
@@ -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;
return context->EffectSlotList[id].get();
EffectSlotSubList &sublist{context->EffectSlotList[lidx]};
if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx)))
return nullptr;
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;
}
+8 -3
View File
@@ -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;
return context->EffectSlotList[id].get();
EffectSlotSubList &sublist{context->EffectSlotList[lidx]};
if(UNLIKELY(sublist.FreeMask & (1_u64 << slidx)))
return nullptr;
return sublist.EffectSlots + slidx;
}