Commit 76b0d9cf authored by Richard Gobeille's avatar Richard Gobeille
Browse files

audiolib: offload initial ov_open_callbacks() etc calls for vorbis into worker...

audiolib: offload initial ov_open_callbacks() etc calls for vorbis into worker threads to avoid VM stalls when playing tons of .oggs from script
parent 991cbd06
......@@ -69,15 +69,14 @@ extern int (*MV_Printf)(const char *fmt, ...);
const char *MV_ErrorString(int ErrorNumber);
extern int MV_Locked;
static inline void MV_Lock(void)
extern thread_local int MV_Locked;
static FORCE_INLINE void MV_Lock(void)
{
extern void SoundDriver_PCM_Lock(void);
if (!MV_Locked++)
SoundDriver_PCM_Lock();
}
static inline void MV_Unlock(void)
static FORCE_INLINE void MV_Unlock(void)
{
extern void SoundDriver_PCM_Unlock(void);
......
......@@ -172,7 +172,7 @@ typedef struct VoiceNode
uint32_t SamplingRate;
uint32_t RateScale;
uint32_t position;
int Paused;
std::atomic<int> Paused;
int handle;
int priority;
......
......@@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "compat.h"
#include "drivers.h"
#include "fx_man.h"
#include "libasync_config.h"
#include "linklist.h"
#include "osd.h"
#include "pitch.h"
......@@ -50,6 +51,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
int MV_XMPInterpolation = XMP_INTERP_NEAREST;
#endif
static void MV_StopVoice(VoiceNode *voice);
static void MV_ServiceVoc(void);
......@@ -100,8 +102,7 @@ int MV_ErrorCode = MV_NotInstalled;
fix16_t MV_GlobalVolume = fix16_one;
fix16_t MV_VolumeSmoothFactor = fix16_one;
int MV_Locked;
thread_local int MV_Locked;
char *MV_MusicBuffer;
static void (*MV_MusicCallback)(void);
......@@ -175,7 +176,7 @@ void MV_PlayVoice(VoiceNode *voice)
MV_Lock();
LL::SortedInsert(&VoiceList, voice, &VoiceNode::priority);
voice->PannedVolume = voice->GoalVolume;
voice->Paused = false;
voice->Paused.store(false, std::memory_order_release);
MV_Unlock();
}
......@@ -281,7 +282,7 @@ static void MV_ServiceVoc(void)
{
next = voice->next;
if (voice->Paused)
if (voice->Paused.load(std::memory_order_acquire))
continue;
if (voice->priority == FX_MUSIC_PRIORITY)
......@@ -358,7 +359,8 @@ static inline void MV_EndService(void) { MV_Unlock(); }
int MV_VoicePlaying(int handle)
{
Bassert(handle <= MV_MaxVoices);
return MV_Installed && MV_Handles[handle - MV_MINVOICEHANDLE] != nullptr;
auto voice = MV_Handles[handle - MV_MINVOICEHANDLE];
return MV_Installed && voice != nullptr && !voice->Paused.load(std::memory_order_relaxed);
}
int MV_KillAllVoices(void)
......@@ -451,6 +453,7 @@ static inline void MV_FinishAllocation(VoiceNode* voice, uint32_t const allocsiz
voice->rawdatasiz = allocsize;
voice->rawdataptr = Xaligned_alloc(16, allocsize);
Bmemset(voice->rawdataptr, 0, allocsize);
}
VoiceNode *MV_AllocVoice(int priority, uint32_t allocsize /* = 0 */)
......@@ -462,7 +465,7 @@ VoiceNode *MV_AllocVoice(int priority, uint32_t allocsize /* = 0 */)
{
auto voice = MV_GetLowestPriorityVoice();
if (voice != &VoiceList && voice->priority <= priority && voice->handle >= MV_MINVOICEHANDLE)
if (voice != &VoiceList && voice->priority <= priority && voice->handle >= MV_MINVOICEHANDLE && FX_SoundValidAndActive(voice->handle))
MV_Kill(voice->handle);
if (LL::Empty(&VoicePool))
......@@ -485,12 +488,12 @@ VoiceNode *MV_AllocVoice(int priority, uint32_t allocsize /* = 0 */)
handle = MV_MINVOICEHANDLE;
} while (MV_Handles[handle - MV_MINVOICEHANDLE] != nullptr);
MV_Handles[handle - MV_MINVOICEHANDLE] = voice;
MV_Unlock();
voice->length = 0;
voice->BlockLength = 0;
voice->handle = handle;
voice->next = voice->prev = nullptr;
MV_Unlock();
if (allocsize)
MV_FinishAllocation(voice, allocsize);
......@@ -505,17 +508,10 @@ int MV_VoiceAvailable(int priority)
return TRUE;
MV_Lock();
auto const voice = MV_GetLowestPriorityVoice();
if (voice == &VoiceList || voice->priority > priority)
{
MV_Unlock();
return FALSE;
}
MV_Unlock();
return TRUE;
return (voice == &VoiceList || voice->priority > priority) ? FALSE : TRUE;
}
void MV_SetVoicePitch(VoiceNode *voice, uint32_t rate, int pitchoffset)
......@@ -624,7 +620,7 @@ int MV_PauseVoice(int handle, int pause)
if (voice == nullptr)
return MV_Error;
voice->Paused = pause;
voice->Paused.store(pause, std::memory_order_release);
MV_EndService();
return MV_Ok;
......
......@@ -32,17 +32,19 @@
#ifdef HAVE_VORBIS
#define BLOCKSIZE MV_MIXBUFFERSIZE
#include "libasync_config.h"
#define OGG_IMPL
#define VORBIS_IMPL
#define OV_EXCLUDE_STATIC_CALLBACKS
#include "minivorbis.h"
#define BLOCKSIZE MV_MIXBUFFERSIZE
typedef struct {
void * ptr;
async::task<void> task;
size_t length;
size_t pos;
......@@ -208,6 +210,12 @@ static playbackstatus MV_GetNextVorbisBlock(VoiceNode *voice)
int bytesread = 0;
auto vd = (vorbis_data *)voice->rawdataptr;
if (!vd)
{
MV_Printf("null rawdataptr\n");
return NoMoreData;
}
do
{
#ifdef USING_TREMOR
......@@ -323,6 +331,8 @@ int MV_PlayVorbis3D(char *ptr, uint32_t length, int loophow, int pitchoffset, in
MV_PanTable[angle][vol].left, MV_PanTable[angle][vol].right, priority, volume, callbackval);
}
static constexpr ov_callbacks vorbis_callbacks = { read_vorbis, seek_vorbis, close_vorbis, tell_vorbis };
int MV_PlayVorbis(char *ptr, uint32_t length, int loopstart, int loopend, int pitchoffset, int vol, int left, int right, int priority, fix16_t volume, intptr_t callbackval)
{
UNREFERENCED_PARAMETER(loopend);
......@@ -334,48 +344,75 @@ int MV_PlayVorbis(char *ptr, uint32_t length, int loopstart, int loopend, int pi
if (voice == nullptr)
return MV_SetErrorCode(MV_NoVoices);
vorbis_data *vd = (vorbis_data *)voice->rawdataptr;
auto vd = (vorbis_data *)voice->rawdataptr;
vd->ptr = ptr;
vd->pos = 0;
vd->length = length;
vd->lastbitstream = -1;
static ov_callbacks vorbis_callbacks = { read_vorbis, seek_vorbis, close_vorbis, tell_vorbis };
int status = ov_open_callbacks((void *)vd, &vd->vf, 0, 0, vorbis_callbacks);
vorbis_info *vi;
if (status < 0 || ((vi = ov_info(&vd->vf, 0)) == nullptr) || vi->channels < 1 || vi->channels > 2)
{
if (status == 0)
ov_clear(&vd->vf);
else
MV_Printf("MV_PlayVorbis: err %d\n", status);
ALIGNED_FREE_AND_NULL(voice->rawdataptr);
return MV_SetErrorCode(MV_InvalidFile);
}
// pitchoffset is passed into the worker tasks using the preexisting lastbitstream member
vd->lastbitstream = pitchoffset;
voice->wavetype = FMT_VORBIS;
voice->bits = 16;
voice->channels = vi->channels;
voice->GetSound = MV_GetNextVorbisBlock;
voice->NextBlock = vd->block;
voice->priority = priority;
voice->callbackval = callbackval;
voice->Loop = { nullptr, nullptr, 0, (loopstart >= 0) };
voice->GetSound = MV_GetNextVorbisBlock;
voice->Paused = true;
// load loop tags from metadata
if (auto comment = ov_comment(&vd->vf, 0))
MV_GetVorbisCommentLoops(voice, comment);
MV_SetVoicePitch(voice, vi->rate, pitchoffset);
MV_SetVoiceMixMode(voice);
MV_SetVoiceVolume(voice, vol, left, right, volume);
MV_PlayVoice(voice);
//if (vd->task.valid() && !vd->task.ready())
// vd->task.wait();
vd->task = async::spawn([voice]
{
#if defined _WIN32 && !defined NDEBUG
debugThreadName("MV_PlayVorbis");
#endif
auto vd = (vorbis_data *)voice->rawdataptr;
// yoinked ptr indicates we're in some shitshow scenario where we tried to cancel
// the voice before the decoder even got a chance to start initializing
if (!vd)
{
voice->rawdatasiz = 0;
MV_PlayVoice(voice);
return;
}
int status = ov_open_callbacks((void *)vd, &vd->vf, 0, 0, vorbis_callbacks);
vorbis_info *vi;
if (status < 0 || ((vi = ov_info(&vd->vf, 0)) == nullptr) || vi->channels < 1 || vi->channels > 2)
{
if (status == 0)
ov_clear(&vd->vf);
else
MV_Printf("MV_PlayVorbis: err %d\n", status);
ALIGNED_FREE_AND_NULL(voice->rawdataptr);
voice->rawdatasiz = 0;
MV_SetErrorCode(MV_InvalidFile);
MV_PlayVoice(voice);
return;
}
voice->channels = vi->channels;
// load loop tags from metadata
if (auto comment = ov_comment(&vd->vf, 0))
MV_GetVorbisCommentLoops(voice, comment);
MV_SetVoicePitch(voice, vi->rate, vd->lastbitstream);
vd->lastbitstream = -1;
MV_PlayVoice(voice);
});
return voice->handle;
}
......@@ -384,16 +421,15 @@ void MV_ReleaseVorbisVoice(VoiceNode *voice)
Bassert(voice->wavetype == FMT_VORBIS && voice->rawdataptr != nullptr && voice->rawdatasiz == sizeof(vorbis_data));
auto vd = (vorbis_data *)voice->rawdataptr;
//vd->task.wait();
ov_clear(&vd->vf);
if (MV_LazyAlloc)
{
ov_clear(&vd->vf);
return;
}
voice->rawdataptr = nullptr;
voice->rawdatasiz = 0;
ov_clear(&vd->vf);
ALIGNED_FREE_AND_NULL(vd);
}
#else
......
......@@ -10,6 +10,9 @@
#include "renderlayer.h"
#include "mimalloc.h"
#define LIBASYNC_IMPLEMENTATION
#include "libasync_config.h"
// video
#ifdef _WIN32
#include "winbits.h"
......
......@@ -473,7 +473,7 @@ void S_Cleanup(void)
int const spriteNum = voice.owner;
Bassert((unsigned)snd->playing <= MAXSOUNDINSTANCES);
//Bassert((unsigned)snd->playing <= MAXSOUNDINSTANCES);
--snd->playing;
Bassert(snd->playing >= 0);
......@@ -647,10 +647,8 @@ static int S_GetSlot(int soundNum)
{
auto &voice = snd->voices[slot];
if (!FX_SoundValidAndActive(voice.handle))
return slot;
if (voice.dist >= dist)
if (voice.dist == UINT16_MAX) return slot;
if (voice.dist >= dist && FX_SoundValidAndActive(voice.handle))
{
dist = voice.dist;
bestslot = slot;
......@@ -658,12 +656,12 @@ static int S_GetSlot(int soundNum)
}
while (++slot < MAXSOUNDINSTANCES);
if (slot == MAXSOUNDINSTANCES)
{
slot = bestslot;
FX_StopSound(snd->voices[slot].handle);
S_Cleanup();
}
if (!FX_SoundValidAndActive(snd->voices[bestslot].handle))
return MAXSOUNDINSTANCES;
slot = bestslot;
FX_StopSound(snd->voices[slot].handle);
S_Cleanup();
return slot;
}
......@@ -899,7 +897,7 @@ int S_PlaySound3D(int num, int spriteNum, const vec3_t& pos)
}
snd->playing++;
Bassert(snd->playing <= MAXSOUNDINSTANCES);
//Bassert(snd->playing <= MAXSOUNDINSTANCES);
S_FillVoiceInfo(&snd->voices[sndSlot], spriteNum, voice, sndist >> 6);
if (snd->flags & SF_TALK)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment