Run SFX in a separate thread (for #1511).

This commit is contained in:
hiker 2014-09-22 17:04:14 +10:00
parent 2b86705dc0
commit 99fd49bdc0
4 changed files with 98 additions and 19 deletions

View File

@ -24,6 +24,7 @@
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "race/race_manager.hpp" #include "race/race_manager.hpp"
#include <pthread.h>
#include <stdexcept> #include <stdexcept>
#include <algorithm> #include <algorithm>
#include <map> #include <map>
@ -68,6 +69,7 @@ void SFXManager::destroy()
*/ */
SFXManager::SFXManager() SFXManager::SFXManager()
{ {
// The sound manager initialises OpenAL // The sound manager initialises OpenAL
m_initialized = music_manager->initialized(); m_initialized = music_manager->initialized();
m_master_gain = UserConfigParams::m_sfx_volume; m_master_gain = UserConfigParams::m_sfx_volume;
@ -75,11 +77,39 @@ SFXManager::SFXManager()
m_position = Vec3(0,0,0); m_position = Vec3(0,0,0);
loadSfx(); loadSfx();
pthread_cond_init(&m_cond_request, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// Should be the default, but just in case:
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
m_thread_id.setAtomic(new pthread_t());
// The thread is created even if there atm sfx are disabled
// (since the user might enable it later).
int error = pthread_create(m_thread_id.getData(), &attr,
&SFXManager::mainLoop, this);
if (error)
{
m_thread_id.lock();
delete m_thread_id.getData();
m_thread_id.unlock();
m_thread_id.setAtomic(0);
Log::error("HTTP Manager", "Could not create thread, error=%d.",
errno);
}
pthread_attr_destroy(&attr);
if (!sfxAllowed()) return; if (!sfxAllowed()) return;
setMasterSFXVolume( UserConfigParams::m_sfx_volume ); setMasterSFXVolume( UserConfigParams::m_sfx_volume );
m_sfx_to_play.lock(); m_sfx_to_play.lock();
m_sfx_to_play.getData().clear(); m_sfx_to_play.getData().clear();
m_sfx_to_play.unlock(); m_sfx_to_play.unlock();
} // SoundManager } // SoundManager
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -87,6 +117,12 @@ SFXManager::SFXManager()
*/ */
SFXManager::~SFXManager() SFXManager::~SFXManager()
{ {
m_thread_id.lock();
pthread_join(*m_thread_id.getData(), NULL);
delete m_thread_id.getData();
m_thread_id.unlock();
pthread_cond_destroy(&m_cond_request);
// ---- clear m_all_sfx // ---- clear m_all_sfx
const int sfx_amount = m_all_sfx.size(); const int sfx_amount = m_all_sfx.size();
for (int n=0; n<sfx_amount; n++) for (int n=0; n<sfx_amount; n++)
@ -131,29 +167,68 @@ void SFXManager::queue(SFXBase *sfx)
{ {
// Don't add sfx that are either not working correctly (e.g. because sfx // Don't add sfx that are either not working correctly (e.g. because sfx
// are disabled); // are disabled);
if(sfx->getStatus()==SFX_UNKNOWN ) return; if(sfx && sfx->getStatus()==SFX_UNKNOWN ) return;
m_sfx_to_play.lock(); m_sfx_to_play.lock();
m_sfx_to_play.getData().push_back(sfx); m_sfx_to_play.getData().push_back(sfx);
m_sfx_to_play.unlock(); m_sfx_to_play.unlock();
} // playSFX // Wake up the sfx thread
pthread_cond_signal(&m_cond_request);
} // queue
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Starts all sfx that are queues to be started. Called once per frame. /** Puts a NULL request into the queue, which will trigger the thread to
* exit.
*/ */
void SFXManager::update() void SFXManager::stopThread()
{ {
m_sfx_to_play.lock(); queue(NULL);
while(!m_sfx_to_play.getData().empty()) } // stopThread
//----------------------------------------------------------------------------
/** This loops runs in a different threads, and starts sfx to be played.
* This can sometimes take up to 5 ms, so it needs to be handled in a thread
* in order to avoid rendering delays.
* \param obj A pointer to the SFX singleton.
*/
void* SFXManager::mainLoop(void *obj)
{
SFXManager *me = (SFXManager*)obj;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
me->m_sfx_to_play.lock();
// Wait till we have an empty sfx in the queue
while (me->m_sfx_to_play.getData().empty() ||
me->m_sfx_to_play.getData().front()!=NULL )
{ {
SFXBase *sfx = m_sfx_to_play.getData().front(); bool empty = me->m_sfx_to_play.getData().empty();
m_sfx_to_play.getData().erase(m_sfx_to_play.getData().begin());
m_sfx_to_play.unlock(); // Wait in cond_wait for a request to arrive. The 'while' is necessary
sfx->reallyPlayNow(); // since "spurious wakeups from the pthread_cond_wait ... may occur"
m_sfx_to_play.lock(); // (pthread_cond_wait man page)!
} // while !empty while (empty)
m_sfx_to_play.unlock(); {
} // update pthread_cond_wait(&me->m_cond_request, me->m_sfx_to_play.getMutex());
empty = me->m_sfx_to_play.getData().empty();
}
SFXBase *current = me->m_sfx_to_play.getData().front();
me->m_sfx_to_play.getData().erase(me->m_sfx_to_play.getData().begin());
if (!current) // empty sfx indicates to abort the sfx manager
break;
me->m_sfx_to_play.unlock();
current->reallyPlayNow();
me->m_sfx_to_play.lock();
} // while
return NULL;
} // mainLoop
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
/** Called then sound is globally switched on or off. It either pauses or /** Called then sound is globally switched on or off. It either pauses or

View File

@ -110,15 +110,21 @@ private:
/** Master gain value, taken from the user config value. */ /** Master gain value, taken from the user config value. */
float m_master_gain; float m_master_gain;
/** Thread id of the thread running in this object. */
Synchronised<pthread_t *> m_thread_id;
/** A conditional variable to wake up the main loop. */
pthread_cond_t m_cond_request;
void loadSfx(); void loadSfx();
SFXManager(); SFXManager();
virtual ~SFXManager(); virtual ~SFXManager();
static void* mainLoop(void *obj);
public: public:
static void create(); static void create();
static void destroy(); static void destroy();
void queue(SFXBase *sfx); void queue(SFXBase *sfx);
void update();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Static function to get the singleton sfx manager. */ /** Static function to get the singleton sfx manager. */
static SFXManager *get() static SFXManager *get()
@ -128,6 +134,7 @@ public:
} // get } // get
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void stopThread();
bool sfxAllowed(); bool sfxAllowed();
SFXBuffer* loadSingleSfx(const XMLNode* node, SFXBuffer* loadSingleSfx(const XMLNode* node,
const std::string &path=std::string(""), const std::string &path=std::string(""),

View File

@ -1413,8 +1413,6 @@ int main(int argc, char *argv[] )
} }
#endif #endif
return 0 ; return 0 ;
} // main } // main
@ -1439,6 +1437,7 @@ static void cleanSuperTuxKart()
if(Online::RequestManager::isRunning()) if(Online::RequestManager::isRunning())
Online::RequestManager::get()->stopNetworkThread(); Online::RequestManager::get()->stopNetworkThread();
SFXManager::get()->stopThread();
irr_driver->updateConfigIfRelevant(); irr_driver->updateConfigIfRelevant();
AchievementsManager::destroy(); AchievementsManager::destroy();
Referee::cleanup(); Referee::cleanup();

View File

@ -22,7 +22,6 @@
#include <assert.h> #include <assert.h>
#include "audio/music_manager.hpp" #include "audio/music_manager.hpp"
#include "audio/sfx_manager.hpp"
#include "config/user_config.hpp" #include "config/user_config.hpp"
#include "graphics/irr_driver.hpp" #include "graphics/irr_driver.hpp"
#include "graphics/material_manager.hpp" #include "graphics/material_manager.hpp"
@ -140,7 +139,6 @@ void MainLoop::run()
{ {
PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00);
music_manager->update(dt); music_manager->update(dt);
SFXManager::get()->update();
input_manager->update(dt); input_manager->update(dt);
#ifdef ENABLE_WIIUSE #ifdef ENABLE_WIIUSE