Dynamically allocate voice channel data

Rather than allocating for a full 8 channels for each voice, when the vast
majority will only need 1 or 2. The voice channel data is relatively big since
it needs to hold HRTF coefficients and history, and this will allow increasing
the maximum number of buffer channels without an obscene memory increase.
This commit is contained in:
Chris Robinson
2020-03-25 21:06:24 -07:00
parent cae78e79e8
commit a27096dd63
5 changed files with 61 additions and 71 deletions
+13 -13
View File
@@ -441,9 +441,9 @@ void InitVoice(ALvoice *voice, ALsource *source, ALbufferlistitem *BufferList, A
voice->mLoopBuffer.store(source->Looping ? source->queue : nullptr, std::memory_order_relaxed);
ALbuffer *buffer{BufferList->mBuffer};
ALuint num_channels{buffer->channelsFromFmt()};
voice->mFrequency = buffer->Frequency;
voice->mFmtChannels = buffer->mFmtChannels;
voice->mNumChannels = buffer->channelsFromFmt();
voice->mSampleSize = buffer->bytesFromFmt();
voice->mAmbiLayout = static_cast<AmbiLayout>(buffer->AmbiLayout);
voice->mAmbiScaling = static_cast<AmbiNorm>(buffer->AmbiScaling);
@@ -458,6 +458,11 @@ void InitVoice(ALvoice *voice, ALsource *source, ALbufferlistitem *BufferList, A
*/
voice->mStep = 0;
if(voice->mChans.capacity() > 2 && num_channels < voice->mChans.capacity())
al::vector<ALvoice::ChannelData>{}.swap(voice->mChans);
voice->mChans.reserve(maxu(2, num_channels));
voice->mChans.resize(num_channels);
/* Don't need to set the VOICE_IS_AMBISONIC flag if the device is not
* higher order than the voice. No HF scaling is necessary to mix it.
*/
@@ -467,43 +472,38 @@ void InitVoice(ALvoice *voice, ALsource *source, ALbufferlistitem *BufferList, A
const uint8_t *OrderFromChan{(voice->mFmtChannels == FmtBFormat2D) ?
AmbiIndex::OrderFrom2DChannel.data() :
AmbiIndex::OrderFromChannel.data()};
const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder, device->mAmbiOrder);
const BandSplitter splitter{400.0f / static_cast<float>(device->Frequency)};
const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder, device->mAmbiOrder);
auto init_ambi = [device,&scales,&OrderFromChan,splitter](ALvoice::ChannelData &chandata) -> void
for(auto &chandata : voice->mChans)
{
chandata.mPrevSamples.fill(0.0f);
chandata.mAmbiScale = scales[*(OrderFromChan++)];
chandata.mAmbiSplitter = splitter;
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
};
std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
init_ambi);
}
voice->mFlags |= VOICE_IS_AMBISONIC;
}
else
{
/* Clear previous samples. */
auto clear_prevs = [device](ALvoice::ChannelData &chandata) -> void
for(auto &chandata : voice->mChans)
{
chandata.mPrevSamples.fill(0.0f);
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
};
std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
clear_prevs);
}
}
if(device->AvgSpeakerDist > 0.0f)
{
const ALfloat w1{SPEEDOFSOUNDMETRESPERSEC /
(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, init_nfc);
for(auto &chandata : voice->mChans)
chandata.mDryParams.NFCtrlFilter.init(w1);
}
source->PropsClean.test_and_set(std::memory_order_acq_rel);
+18 -26
View File
@@ -1656,14 +1656,16 @@ void ALCcontext::allocVoiceChanges(size_t addcount)
void ALCcontext::allocVoices(size_t addcount)
{
constexpr size_t clustersize{4};
constexpr size_t clustersize{32};
/* 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"};
const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize};
TRACE("Increasing allocated voices to %zu\n", totalcount);
auto newarray = ALvoiceArray::Create((mVoiceClusters.size()+addcount) * clustersize);
auto newarray = ALvoiceArray::Create(totalcount);
while(addcount)
{
mVoiceClusters.emplace_back(std::make_unique<ALvoice[]>(clustersize));
@@ -2292,28 +2294,26 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
vprops = next;
}
auto voicelist = context->getVoicesSpan();
if(device->NumAuxSends < old_sends)
{
const ALuint num_sends{device->NumAuxSends};
/* Clear extraneous property set sends. */
auto clear_sends = [num_sends](ALvoice *voice) -> void
for(ALvoice *voice : voicelist)
{
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{});
auto clear_chan_sends = [num_sends](ALvoice::ChannelData &chandata) -> void
for(auto &chandata : voice->mChans)
{
std::fill(chandata.mWetParams.begin()+num_sends, chandata.mWetParams.end(),
SendParams{});
};
std::for_each(voice->mChans.begin(), voice->mChans.end(), clear_chan_sends);
};
auto voicelist = context->getVoicesSpan();
std::for_each(voicelist.begin(), voicelist.end(), clear_sends);
}
}
}
auto reset_voice = [device](ALvoice *voice) -> void
for(ALvoice *voice : voicelist)
{
delete voice->mUpdate.exchange(nullptr, std::memory_order_acq_rel);
@@ -2322,7 +2322,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
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)
return;
continue;
voice->mStep = 0;
voice->mFlags |= VOICE_IS_FADING;
@@ -2338,30 +2338,26 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder,
device->mAmbiOrder);
auto init_ambi = [device,&scales,&OrderFromChan,splitter](ALvoice::ChannelData &chandata) -> void
for(auto &chandata : voice->mChans)
{
chandata.mPrevSamples.fill(0.0f);
chandata.mAmbiScale = scales[*(OrderFromChan++)];
chandata.mAmbiSplitter = splitter;
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
};
std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
init_ambi);
}
voice->mFlags |= VOICE_IS_AMBISONIC;
}
else
{
/* Clear previous samples. */
auto clear_prevs = [device](ALvoice::ChannelData &chandata) -> void
for(auto &chandata : voice->mChans)
{
chandata.mPrevSamples.fill(0.0f);
chandata.mDryParams = DirectParams{};
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
};
std::for_each(voice->mChans.begin(), voice->mChans.begin()+voice->mNumChannels,
clear_prevs);
}
voice->mFlags &= ~VOICE_IS_AMBISONIC;
}
@@ -2371,14 +2367,10 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
/* Reinitialize the NFC filters for new parameters. */
const ALfloat w1{SPEEDOFSOUNDMETRESPERSEC /
(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,
init_nfc);
for(auto &chandata : voice->mChans)
chandata.mDryParams.NFCtrlFilter.init(w1);
}
};
auto voicelist = context->getVoicesSpan();
std::for_each(voicelist.begin(), voicelist.end(), reset_voice);
}
srclock.unlock();
context->mPropsClean.test_and_set(std::memory_order_release);
+15 -16
View File
@@ -744,17 +744,16 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
const auto Frequency = static_cast<ALfloat>(Device->Frequency);
const ALuint NumSends{Device->NumAuxSends};
const ALuint num_channels{voice->mNumChannels};
const size_t num_channels{voice->mChans.size()};
ASSUME(num_channels > 0);
auto clear_target = [NumSends](ALvoice::ChannelData &chandata) -> void
for(auto &chandata : voice->mChans)
{
chandata.mDryParams.Hrtf.Target = HrtfFilter{};
chandata.mDryParams.Gains.Target.fill(0.0f);
std::for_each(chandata.mWetParams.begin(), chandata.mWetParams.begin()+NumSends,
[](SendParams &params) -> void { params.Gains.Target.fill(0.0f); });
};
std::for_each(voice->mChans.begin(), voice->mChans.begin()+num_channels, clear_target);
}
DirectMode DirectChannels{props->DirectChannels};
const ChanMap *chans{nullptr};
@@ -922,7 +921,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
static const uint8_t ChansPerOrder[MAX_AMBI_ORDER+1]{1, 3, 5, 7,};
static const uint8_t OrderOffset[MAX_AMBI_ORDER+1]{0, 1, 4, 9,};
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
{
const size_t acn{index_map[c]};
const size_t order{AmbiIndex::OrderFromChannel[acn]};
@@ -954,7 +953,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
*/
voice->mDirect.Buffer = Device->RealOut.Buffer;
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
{
ALuint idx{GetChannelIdxByName(Device->RealOut, chans[c].channel)};
if(idx != INVALID_CHANNEL_INDEX)
@@ -979,7 +978,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
/* Auxiliary sends still use normal channel panning since they mix to
* B-Format, which can't channel-match.
*/
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
{
ALfloat coeffs[MAX_AMBI_CHANNELS];
CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
@@ -1013,7 +1012,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base * downmix_gain;
/* Remaining channels use the same results as the first. */
for(ALuint c{1};c < num_channels;c++)
for(size_t c{1};c < num_channels;c++)
{
/* Skip LFE */
if(chans[c].channel == LFE) continue;
@@ -1026,7 +1025,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
ALfloat coeffs[MAX_AMBI_CHANNELS];
CalcDirectionCoeffs({xpos, ypos, zpos}, Spread, coeffs);
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
{
/* Skip LFE */
if(chans[c].channel == LFE)
@@ -1045,7 +1044,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
* relative location around the listener, providing "virtual
* speaker" responses.
*/
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
{
/* Skip LFE */
if(chans[c].channel == LFE)
@@ -1091,7 +1090,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
const ALfloat w0{SPEEDOFSOUNDMETRESPERSEC / (mdist * Frequency)};
/* Adjust NFC filters. */
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
voice->mChans[c].mDryParams.NFCtrlFilter.adjust(w0);
voice->mFlags |= VOICE_HAS_NFC;
@@ -1110,7 +1109,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread, coeffs);
}
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
{
/* Special-case LFE */
if(chans[c].channel == LFE)
@@ -1142,13 +1141,13 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
* infinite distance, which results in a w0 of 0.
*/
constexpr float w0{0.0f};
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
voice->mChans[c].mDryParams.NFCtrlFilter.adjust(w0);
voice->mFlags |= VOICE_HAS_NFC;
}
for(ALuint c{0};c < num_channels;c++)
for(size_t c{0};c < num_channels;c++)
{
/* Special-case LFE */
if(chans[c].channel == LFE)
@@ -1193,7 +1192,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
auto &highpass = voice->mChans[0].mDryParams.HighPass;
lowpass.setParamsFromSlope(BiquadType::HighShelf, hfNorm, DryGain.HF, 1.0f);
highpass.setParamsFromSlope(BiquadType::LowShelf, lfNorm, DryGain.LF, 1.0f);
for(ALuint c{1};c < num_channels;c++)
for(size_t c{1};c < num_channels;c++)
{
voice->mChans[c].mDryParams.LowPass.copyParamsFrom(lowpass);
voice->mChans[c].mDryParams.HighPass.copyParamsFrom(highpass);
@@ -1212,7 +1211,7 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat xpos, const ALfloat ypo
auto &highpass = voice->mChans[0].mWetParams[i].HighPass;
lowpass.setParamsFromSlope(BiquadType::HighShelf, hfNorm, WetGain[i].HF, 1.0f);
highpass.setParamsFromSlope(BiquadType::LowShelf, lfNorm, WetGain[i].LF, 1.0f);
for(ALuint c{1};c < num_channels;c++)
for(size_t c{1};c < num_channels;c++)
{
voice->mChans[c].mWetParams[i].LowPass.copyParamsFrom(lowpass);
voice->mChans[c].mWetParams[i].HighPass.copyParamsFrom(highpass);
+14 -14
View File
@@ -553,7 +553,6 @@ void ALvoice::mix(const State vstate, ALCcontext *Context, const ALuint SamplesT
ALuint DataPosFrac{mPositionFrac.load(std::memory_order_relaxed)};
ALbufferlistitem *BufferListItem{mCurrentBuffer.load(std::memory_order_relaxed)};
ALbufferlistitem *BufferLoopItem{mLoopBuffer.load(std::memory_order_relaxed)};
const ALuint NumChannels{mNumChannels};
const ALuint SampleSize{mSampleSize};
const ALuint increment{mStep};
if UNLIKELY(increment < 1)
@@ -566,10 +565,10 @@ void ALvoice::mix(const State vstate, ALCcontext *Context, const ALuint SamplesT
return;
}
ASSUME(NumChannels > 0);
ASSUME(SampleSize > 0);
ASSUME(increment > 0);
const auto FrameSize = size_t{NumChannels} * SampleSize;
const size_t FrameSize{mChans.size() * SampleSize};
ASSUME(FrameSize > 0);
ALCdevice *Device{Context->mDevice.get()};
const ALuint NumSends{Device->NumAuxSends};
@@ -582,9 +581,8 @@ void ALvoice::mix(const State vstate, ALCcontext *Context, const ALuint SamplesT
if(!Counter)
{
/* No fading, just overwrite the old/current params. */
for(ALuint chan{0};chan < NumChannels;chan++)
for(auto &chandata : mChans)
{
ChannelData &chandata = mChans[chan];
{
DirectParams &parms = chandata.mDryParams;
if(!(mFlags&VOICE_HAS_HRTF))
@@ -604,9 +602,9 @@ void ALvoice::mix(const State vstate, ALCcontext *Context, const ALuint SamplesT
}
else if((mFlags&VOICE_HAS_HRTF))
{
for(ALuint chan{0};chan < NumChannels;chan++)
for(auto &chandata : mChans)
{
DirectParams &parms = mChans[chan].mDryParams;
DirectParams &parms = chandata.mDryParams;
if(!(parms.Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
{
/* The old HRTF params are silent, so overwrite the old
@@ -680,10 +678,12 @@ void ALvoice::mix(const State vstate, ALCcontext *Context, const ALuint SamplesT
}
ASSUME(DstBufferSize > 0);
for(ALuint chan{0};chan < NumChannels;chan++)
for(auto &chandata : mChans)
{
ChannelData &chandata = mChans[chan];
const al::span<ALfloat> SrcData{Device->SourceData, SrcBufferSize};
const size_t num_chans{mChans.size()};
const auto chan = static_cast<size_t>(std::distance(mChans.data(),
std::addressof(chandata)));
const al::span<float> SrcData{Device->SourceData, SrcBufferSize};
/* Load the previous samples into the source data first, then load
* what we can from the buffer queue.
@@ -695,13 +695,13 @@ void ALvoice::mix(const State vstate, ALCcontext *Context, const ALuint SamplesT
srciter = std::copy(chandata.mPrevSamples.begin()+(MAX_RESAMPLER_PADDING>>1),
chandata.mPrevSamples.end(), srciter);
else if((mFlags&VOICE_IS_STATIC))
srciter = LoadBufferStatic(BufferListItem, BufferLoopItem, NumChannels,
srciter = LoadBufferStatic(BufferListItem, BufferLoopItem, num_chans,
SampleSize, chan, DataPosInt, {srciter, SrcData.end()});
else if((mFlags&VOICE_IS_CALLBACK))
srciter = LoadBufferCallback(BufferListItem, NumChannels, SampleSize, chan,
srciter = LoadBufferCallback(BufferListItem, num_chans, SampleSize, chan,
mNumCallbackSamples, {srciter, SrcData.end()});
else
srciter = LoadBufferQueue(BufferListItem, BufferLoopItem, NumChannels,
srciter = LoadBufferQueue(BufferListItem, BufferLoopItem, num_chans,
SampleSize, chan, DataPosInt, {srciter, SrcData.end()});
if UNLIKELY(srciter != SrcData.end())
+1 -2
View File
@@ -226,7 +226,6 @@ struct ALvoice {
/* Properties for the attached buffer(s). */
FmtChannels mFmtChannels;
ALuint mFrequency;
ALuint mNumChannels;
ALuint mSampleSize;
AmbiLayout mAmbiLayout;
AmbiNorm mAmbiScaling;
@@ -258,7 +257,7 @@ struct ALvoice {
DirectParams mDryParams;
std::array<SendParams,MAX_SENDS> mWetParams;
};
std::array<ChannelData,MAX_INPUT_CHANNELS> mChans;
al::vector<ChannelData> mChans{2};
ALvoice() = default;
ALvoice(const ALvoice&) = delete;