Rework effect slot buffer setting

Rather than creating an effect-specific buffer that gets passed along as a
property, the buffer is set the effect state when the effect state is created,
the device is updated, or the buffer is changed. The buffer can only be set
while the effect slot isn't playing, so it won't be changed or updated while
the mixer is processing the effect state.
This commit is contained in:
Chris Robinson 2020-09-05 20:48:56 -07:00
parent 9975aeb37f
commit c52bf8c401
8 changed files with 25 additions and 79 deletions

View File

@ -484,7 +484,6 @@ START_API_FUNC
SETERR_RETURN(context, AL_INVALID_NAME,, "Invalid effect slot ID %u", effectslot);
ALeffectslot *target{};
ALbuffer *buffer{};
ALCdevice *device{};
ALenum err{};
switch(param)
@ -552,8 +551,13 @@ START_API_FUNC
case AL_BUFFER:
device = context->mDevice.get();
if(slot->mState == SlotState::Playing)
SETERR_RETURN(context, AL_INVALID_OPERATION,,
"Setting buffer on playing effect slot %u", slot->id);
{
std::lock_guard<std::mutex> ___{device->BufferLock};
ALbuffer *buffer{};
if(value)
{
buffer = LookupBuffer(device, static_cast<ALuint>(value));
@ -569,13 +573,9 @@ START_API_FUNC
DecrementRef(oldbuffer->ref);
slot->Buffer = buffer;
slot->Effect.Buffer = nullptr;
if(buffer)
{
FPUCtl mixer_mode{};
auto *state = slot->Effect.State.get();
slot->Effect.Buffer.reset(state->createBuffer(device, buffer->mBuffer));
}
FPUCtl mixer_mode{};
auto *state = slot->Effect.State.get();
state->setBuffer(device, buffer ? &buffer->mBuffer : nullptr);
}
break;
@ -819,8 +819,6 @@ ALeffectslot::~ALeffectslot()
if(Params.mEffectState)
Params.mEffectState->release();
if(Params.mEffectBuffer)
Params.mEffectBuffer->release();
}
ALenum ALeffectslot::init()
@ -856,9 +854,8 @@ ALenum ALeffectslot::initEffect(ALeffect *effect, ALCcontext *context)
{
FPUCtl mixer_mode{};
State->deviceUpdate(Device);
Effect.Buffer = nullptr;
if(Buffer)
Effect.Buffer.reset(State->createBuffer(Device, Buffer->mBuffer));
State->setBuffer(Device, &Buffer->mBuffer);
}
if(!effect)
@ -882,7 +879,6 @@ ALenum ALeffectslot::initEffect(ALeffect *effect, ALCcontext *context)
while(props)
{
props->State = nullptr;
props->Buffer = nullptr;
props = props->next.load(std::memory_order_relaxed);
}
@ -912,7 +908,6 @@ void ALeffectslot::updateProps(ALCcontext *context)
props->Type = Effect.Type;
props->Props = Effect.Props;
props->State = Effect.State;
props->Buffer = Effect.Buffer;
/* Set the new container for updating internal parameters. */
props = Params.Update.exchange(props, std::memory_order_acq_rel);
@ -922,7 +917,6 @@ void ALeffectslot::updateProps(ALCcontext *context)
* freelist.
*/
props->State = nullptr;
props->Buffer = nullptr;
AtomicReplaceHead(context->mFreeEffectslotProps, props);
}
}

View File

@ -32,7 +32,6 @@ struct ALeffectslotProps {
EffectProps Props;
al::intrusive_ptr<EffectState> State;
al::intrusive_ptr<EffectBufferBase> Buffer;
std::atomic<ALeffectslotProps*> next;
@ -57,7 +56,6 @@ struct ALeffectslot {
EffectProps Props{};
al::intrusive_ptr<EffectState> State;
al::intrusive_ptr<EffectBufferBase> Buffer;
} Effect;
std::atomic_flag PropsClean;
@ -76,7 +74,6 @@ struct ALeffectslot {
ALenum EffectType{AL_EFFECT_NULL};
EffectProps mEffectProps{};
EffectState *mEffectState{nullptr};
EffectBufferBase *mEffectBuffer{nullptr};
float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */
float DecayTime{0.0f};

View File

@ -60,11 +60,6 @@ static int EventThread(ALCcontext *context)
evt.u.mEffectState->release();
continue;
}
if(evt.EnumType == EventType_ReleaseEffectBuffer)
{
evt.u.mEffectBuffer->release();
continue;
}
ALbitfieldSOFT enabledevts{context->mEnabledEvts.load(std::memory_order_acquire)};
if(!context->mEventCb) continue;

View File

@ -6,7 +6,6 @@
#include "almalloc.h"
struct EffectBufferBase;
struct EffectState;
@ -24,7 +23,6 @@ enum {
/* Internal events. */
EventType_ReleaseEffectState = 65536,
EventType_ReleaseEffectBuffer,
};
struct AsyncEvent {
@ -46,7 +44,6 @@ struct AsyncEvent {
ALchar msg[232];
} user;
EffectState *mEffectState;
EffectBufferBase *mEffectBuffer;
} u{};
AsyncEvent() noexcept = default;

View File

@ -2102,10 +2102,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
state->mOutTarget = device->Dry.Buffer;
state->deviceUpdate(device);
if(ALbuffer *buffer{slot->Buffer})
{
slot->Effect.Buffer = nullptr;
slot->Effect.Buffer.reset(state->createBuffer(device, buffer->mBuffer));
}
state->setBuffer(device, &buffer->mBuffer);
slot->updateProps(context);
}
@ -2128,10 +2125,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
state->mOutTarget = device->Dry.Buffer;
state->deviceUpdate(device);
if(ALbuffer *buffer{slot->Buffer})
{
slot->Effect.Buffer = nullptr;
slot->Effect.Buffer.reset(state->createBuffer(device, buffer->mBuffer));
}
state->setBuffer(device, &buffer->mBuffer);
slot->updateProps(context);
}
}

View File

@ -505,24 +505,6 @@ bool CalcEffectSlotParams(ALeffectslot *slot, ALeffectslot **sorted_slots, ALCco
}
}
EffectBufferBase *buffer{props->Buffer.release()};
EffectBufferBase *oldbuffer{slot->Params.mEffectBuffer};
slot->Params.mEffectBuffer = buffer;
if(oldbuffer && !oldbuffer->releaseIfNoDelete())
{
RingBuffer *ring{context->mAsyncEvents.get()};
auto evt_vec = ring->getWriteVector();
if LIKELY(evt_vec.first.len > 0)
{
AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_ReleaseEffectBuffer}};
evt->u.mEffectBuffer = oldbuffer;
ring->writeAdvance(1);
}
else
props->Buffer.reset(oldbuffer);
}
AtomicReplaceHead(context->mFreeEffectslotProps, props);
EffectTarget output;

View File

@ -155,10 +155,6 @@ const EffectVtable T##_vtable = { \
}
struct EffectBufferBase : public al::intrusive_ref<EffectBufferBase> {
virtual ~EffectBufferBase() = default;
};
struct EffectTarget {
MixParams *Main;
RealMixParams *RealOut;
@ -171,13 +167,7 @@ struct EffectState : public al::intrusive_ref<EffectState> {
virtual ~EffectState() = default;
virtual void deviceUpdate(const ALCdevice *device) = 0;
/* Implementations are currently required to copy the buffer data if they
* wish to hold on to it, as there's no guarantee the buffer won't be
* deleted or altered during a mix.
*/
virtual EffectBufferBase *createBuffer(const ALCdevice* /*device*/,
const BufferStorage& /*buffer*/)
{ return nullptr; }
virtual void setBuffer(const ALCdevice* /*device*/, const BufferStorage* /*buffer*/) { }
virtual void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) = 0;
virtual void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) = 0;
};

View File

@ -97,7 +97,7 @@ using complex_d = std::complex<double>;
constexpr size_t ConvolveUpdateSize{1024};
constexpr size_t ConvolveUpdateSamples{ConvolveUpdateSize / 2};
struct ConvolutionFilter final : public EffectBufferBase {
struct ConvolutionFilter {
FmtChannels mChannels{};
AmbiLayout mAmbiLayout{};
AmbiScaling mAmbiScaling{};
@ -385,13 +385,13 @@ void ConvolutionFilter::process(const size_t samplesToDo,
struct ConvolutionState final : public EffectState {
ConvolutionFilter *mFilter{};
std::unique_ptr<ConvolutionFilter> mFilter;
ConvolutionState() = default;
~ConvolutionState() override = default;
void deviceUpdate(const ALCdevice *device) override;
EffectBufferBase *createBuffer(const ALCdevice *device, const BufferStorage &buffer) override;
void setBuffer(const ALCdevice *device, const BufferStorage *buffer) override;
void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;
@ -402,29 +402,26 @@ void ConvolutionState::deviceUpdate(const ALCdevice* /*device*/)
{
}
EffectBufferBase *ConvolutionState::createBuffer(const ALCdevice *device,
const BufferStorage &buffer)
void ConvolutionState::setBuffer(const ALCdevice *device, const BufferStorage *buffer)
{
mFilter = nullptr;
/* An empty buffer doesn't need a convolution filter. */
if(buffer.mSampleLen < 1) return nullptr;
if(!buffer || buffer->mSampleLen < 1) return;
auto numChannels = ChannelsFromFmt(buffer.mChannels,
minu(buffer.mAmbiOrder, device->mAmbiOrder));
auto numChannels = ChannelsFromFmt(buffer->mChannels,
minu(buffer->mAmbiOrder, device->mAmbiOrder));
al::intrusive_ptr<ConvolutionFilter> filter{new ConvolutionFilter{numChannels}};
if LIKELY(filter->init(device, buffer))
return filter.release();
return nullptr;
mFilter.reset(new ConvolutionFilter{numChannels});
if(!mFilter->init(device, *buffer))
mFilter = nullptr;
}
void ConvolutionState::update(const ALCcontext *context, const ALeffectslot *slot,
const EffectProps *props, const EffectTarget target)
{
mFilter = static_cast<ConvolutionFilter*>(slot->Params.mEffectBuffer);
if(!mFilter) return;
mFilter->update(mOutTarget, context, slot, props, target);
if(mFilter)
mFilter->update(mOutTarget, context, slot, props, target);
}
void ConvolutionState::process(const size_t samplesToDo,